|
| 1 | +from rest_framework.test import APIClient |
| 2 | +from rest_framework import status |
| 3 | + |
| 4 | +""" |
| 5 | +Tests for .well-known/oauth-authorization-server endpoint |
| 6 | +RFC8414-compliant OAuth Authorization Server Metadata with LOLA extensions |
| 7 | +""" |
| 8 | + |
| 9 | +# Validate that discovery endpoint returns RFC8414-compliant OAuth metadata. |
| 10 | +def test_rfc8414_returns_valid_metadata(): |
| 11 | + |
| 12 | + client = APIClient() |
| 13 | + |
| 14 | + response = client.get('/.well-known/oauth-authorization-server') |
| 15 | + |
| 16 | + assert response.status_code == status.HTTP_200_OK |
| 17 | + assert response['Content-Type'] == 'application/json' |
| 18 | + assert response['Access-Control-Allow-Origin'] == '*' |
| 19 | + |
| 20 | + data = response.json() |
| 21 | + |
| 22 | + required_fields = [ |
| 23 | + 'issuer', |
| 24 | + 'authorization_endpoint', |
| 25 | + 'token_endpoint', |
| 26 | + 'scopes_supported', |
| 27 | + 'response_types_supported', |
| 28 | + 'grant_types_supported' |
| 29 | + ] |
| 30 | + |
| 31 | + for field in required_fields: |
| 32 | + assert field in data, f"Missing required OAuth field: {field}" |
| 33 | + |
| 34 | +# Verify LOLA-specific parameters are included for account portability discovery |
| 35 | +def test_rfc8414_includes_lola_parameters(): |
| 36 | + |
| 37 | + client = APIClient() |
| 38 | + |
| 39 | + response = client.get('/.well-known/oauth-authorization-server') |
| 40 | + data = response.json() |
| 41 | + |
| 42 | + # LOLA scope should be supported |
| 43 | + assert 'activitypub_account_portability' in data['scopes_supported'], \ |
| 44 | + "LOLA scope 'activitypub_account_portability' must be in scopes_supported" |
| 45 | + |
| 46 | + # LOLA endpoint parameter should be present (LOLA extension to RFC8414) |
| 47 | + assert 'activitypub_account_portability' in data, \ |
| 48 | + "LOLA parameter 'activitypub_account_portability' must be present" |
| 49 | + assert data['activitypub_account_portability'].endswith('/oauth/authorize/'), \ |
| 50 | + "LOLA portability endpoint should point to OAuth authorization endpoint" |
| 51 | + |
| 52 | + |
| 53 | +# Ensure all URLs in discovery response are absolute for federation compatibility |
| 54 | +def test_rfc8414_urls_are_absolute(): |
| 55 | + |
| 56 | + client = APIClient() |
| 57 | + |
| 58 | + response = client.get('/.well-known/oauth-authorization-server') |
| 59 | + data = response.json() |
| 60 | + |
| 61 | + # All URL fields must be absolute for proper federation |
| 62 | + url_fields = [ |
| 63 | + 'issuer', |
| 64 | + 'authorization_endpoint', |
| 65 | + 'token_endpoint', |
| 66 | + 'activitypub_account_portability' |
| 67 | + ] |
| 68 | + |
| 69 | + for field in url_fields: |
| 70 | + url = data[field] |
| 71 | + assert url.startswith('http'), \ |
| 72 | + f"{field} should be absolute URL starting with http/https: {url}" |
| 73 | + |
| 74 | + |
| 75 | +def test_rfc8414_authorization_code_flow_support(): |
| 76 | + """ |
| 77 | + Verify that the authorization code flow is properly advertised. |
| 78 | + |
| 79 | + LOLA uses OAuth 2.0 authorization code flow, so the metadata must |
| 80 | + indicate support for 'code' response type and 'authorization_code' grant type. |
| 81 | + """ |
| 82 | + client = APIClient() |
| 83 | + |
| 84 | + response = client.get('/.well-known/oauth-authorization-server') |
| 85 | + data = response.json() |
| 86 | + |
| 87 | + # Authorization code flow requirements |
| 88 | + assert 'code' in data['response_types_supported'], \ |
| 89 | + "Must support 'code' response type for authorization code flow" |
| 90 | + assert 'authorization_code' in data['grant_types_supported'], \ |
| 91 | + "Must support 'authorization_code' grant type" |
| 92 | + |
| 93 | + |
| 94 | +# Verify CORS headers are present to enable cross-origin discovery |
| 95 | +def test_rfc8414_cors_headers_for_federation(): |
| 96 | + |
| 97 | + client = APIClient() |
| 98 | + |
| 99 | + response = client.get('/.well-known/oauth-authorization-server') |
| 100 | + |
| 101 | + # CORS headers are essential for federation |
| 102 | + assert 'Access-Control-Allow-Origin' in response, \ |
| 103 | + "CORS header 'Access-Control-Allow-Origin' must be present" |
| 104 | + assert response['Access-Control-Allow-Origin'] == '*', \ |
| 105 | + "CORS should allow all origins for public discovery" |
| 106 | + |
| 107 | + |
| 108 | +# Verify that OAuth endpoint URLs match the actual django-oauth-toolkit routes. |
| 109 | +def test_rfc8414_oauth_endpoints_match(): |
| 110 | + |
| 111 | + client = APIClient() |
| 112 | + |
| 113 | + response = client.get('/.well-known/oauth-authorization-server') |
| 114 | + data = response.json() |
| 115 | + |
| 116 | + """ |
| 117 | + Check that OAuth endpoints use the correct paths |
| 118 | + This ensures consistency between the advertised endpoints and the actual OAuth implementation. |
| 119 | + """ |
| 120 | + assert '/oauth/authorize/' in data['authorization_endpoint'], \ |
| 121 | + "Authorization endpoint should use /oauth/authorize/ path" |
| 122 | + assert '/oauth/token/' in data['token_endpoint'], \ |
| 123 | + "Token endpoint should use /oauth/token/ path" |
| 124 | + |
| 125 | + assert data['activitypub_account_portability'] == data['authorization_endpoint'], \ |
| 126 | + "LOLA portability endpoint should point to the same authorization endpoint" |
0 commit comments