diff --git a/id.saladeaula.digital/app/config.py b/id.saladeaula.digital/app/config.py index 22523a0..3fada31 100644 --- a/id.saladeaula.digital/app/config.py +++ b/id.saladeaula.digital/app/config.py @@ -4,9 +4,9 @@ ISSUER: str = os.getenv('ISSUER') # type: ignore OAUTH2_TABLE: str = os.getenv('OAUTH2_TABLE') # type: ignore OAUTH2_SCOPES_SUPPORTED: str = os.getenv('OAUTH2_SCOPES_SUPPORTED', '') +OAUTH2_REFRESH_TOKEN_EXPIRES_IN = 86_400 * 7 # 7 days JWT_SECRET: str = os.environ.get('JWT_SECRET') # type: ignore JWT_ALGORITHM = 'HS256' -# JWT_EXP_SECONDS = 900 # 15 minutes SESSION_EXPIRES_IN = 86400 * 30 # 30 days diff --git a/id.saladeaula.digital/app/integrations/apigateway_oauth2/authorization_server.py b/id.saladeaula.digital/app/integrations/apigateway_oauth2/authorization_server.py index 2e990c7..91d6bd2 100644 --- a/id.saladeaula.digital/app/integrations/apigateway_oauth2/authorization_server.py +++ b/id.saladeaula.digital/app/integrations/apigateway_oauth2/authorization_server.py @@ -74,6 +74,7 @@ class AuthorizationServer(oauth2.AuthorizationServer): 'created_at': now_, } ) + transact.put( item={ 'id': 'OAUTH2#TOKEN', @@ -156,6 +157,7 @@ class AuthorizationServer(oauth2.AuthorizationServer): body, headers, ): + logger.debug('handle_response', status=status, body=body) return Response( status_code=status, body=body, diff --git a/id.saladeaula.digital/app/oauth2.py b/id.saladeaula.digital/app/oauth2.py index f092e3a..0f20af1 100644 --- a/id.saladeaula.digital/app/oauth2.py +++ b/id.saladeaula.digital/app/oauth2.py @@ -309,8 +309,8 @@ class IssuerParameter(rfc9207.IssuerParameter): GRANT_TYPES_EXPIRES_IN = { - 'authorization_code': 60 * 10, # 10 minutes - 'refresh_token': 86_400 * 7, # 7 days + 'authorization_code': 60 * 3, # 3 minutes + 'refresh_token': 60 * 30, # 30 minutes } diff --git a/id.saladeaula.digital/tests/routes/test_token.py b/id.saladeaula.digital/tests/routes/test_token.py index b80c9d0..4409ea0 100644 --- a/id.saladeaula.digital/tests/routes/test_token.py +++ b/id.saladeaula.digital/tests/routes/test_token.py @@ -1,4 +1,6 @@ import json +import pprint +from base64 import b64encode from http import HTTPMethod, HTTPStatus from urllib.parse import urlencode @@ -14,7 +16,7 @@ def test_token( http_api_proxy: HttpApiProxy, lambda_context: LambdaContext, ): - client_id = 'd72d4005-1fa7-4430-9754-80d5e2487bb6' + client_id = '8c5e92b0-9ed4-451e-8935-66084d8544b1' r = app.lambda_handler( http_api_proxy( @@ -39,7 +41,7 @@ def test_token( assert r['statusCode'] == HTTPStatus.OK r = json.loads(r['body']) - assert r['expires_in'] == 600 + assert r['expires_in'] == 180 tokens = dynamodb_persistence_layer.query( key_cond_expr='#pk = :pk', @@ -50,7 +52,7 @@ def test_token( ':pk': 'OAUTH2#TOKEN', }, ) - assert len(tokens['items']) == 2 + assert len(tokens['items']) == 3 r = app.lambda_handler( http_api_proxy( @@ -79,4 +81,46 @@ def test_token( ':pk': 'OAUTH2#TOKEN', }, ) - assert len(r['items']) == 3 + assert len(r['items']) == 4 + + +def test_refresh_token( + app, + seeds, + dynamodb_persistence_layer: DynamoDBPersistenceLayer, + http_api_proxy: HttpApiProxy, + lambda_context: LambdaContext, +): + client_id = 'd72d4005-1fa7-4430-9754-80d5e2487bb6' + client_secret = '1nFD8alDbGHgc3g1RLY960xyRJVee0SlMoIB0MUlSuiJy28W' + auth = b64encode(f'{client_id}:{client_secret}'.encode()).decode() + r = app.lambda_handler( + http_api_proxy( + raw_path='/token', + method=HTTPMethod.POST, + headers={ + 'Authorization': f'Basic {auth}', + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body=urlencode( + { + 'grant_type': 'refresh_token', + 'refresh_token': 'CyF3Ik3b9hMIo3REVv27gZAHd7dvwZq6QrkhWr7qHEen4UVy', + 'client_id': client_id, + } + ), + ), + lambda_context, + ) + + r = dynamodb_persistence_layer.query( + key_cond_expr='#pk = :pk', + expr_attr_name={ + '#pk': 'id', + }, + expr_attr_values={ + ':pk': 'OAUTH2#TOKEN', + }, + ) + + assert len(r['items']) == 2 diff --git a/id.saladeaula.digital/tests/seeds.jsonl b/id.saladeaula.digital/tests/seeds.jsonl index 2b4fd41..3924801 100644 --- a/id.saladeaula.digital/tests/seeds.jsonl +++ b/id.saladeaula.digital/tests/seeds.jsonl @@ -1,9 +1,12 @@ // OAuth2 -{"id": "OAUTH2", "sk": "CLIENT_ID#d72d4005-1fa7-4430-9754-80d5e2487bb6", "client_secret": "1nFD8alDbGHgc3g1RLY960xyRJVee0SlMoIB0MUlSuiJy28W", "name": "pytest 1", "scope": "openid profile", "redirect_uris": ["https://localhost/callback"], "response_types": ["code"], "grant_types": ["authorization_code", "refresh_token"], "scope": "openid profile email offline_access read:users", "token_endpoint_auth_method": "none"} +{"id": "OAUTH2", "sk": "CLIENT_ID#d72d4005-1fa7-4430-9754-80d5e2487bb6", "client_secret": "1nFD8alDbGHgc3g1RLY960xyRJVee0SlMoIB0MUlSuiJy28W", "name": "pytest 1", "scope": "openid profile", "redirect_uris": ["https://localhost/callback"], "response_types": ["code"], "grant_types": ["authorization_code", "refresh_token"], "scope": "openid profile email offline_access read:users", "token_endpoint_auth_method": "client_secret_basic"} +{"id": "OAUTH2", "sk": "CLIENT_ID#8c5e92b0-9ed4-451e-8935-66084d8544b1", "client_secret": "1nFD8alDbGHgc3g1RLY960xyRJVee0SlMoIB0MUlSuiJy28W", "name": "pytest 1", "scope": "openid profile", "redirect_uris": ["https://localhost/callback"], "response_types": ["code"], "grant_types": ["authorization_code", "refresh_token"], "scope": "openid profile email offline_access read:users", "token_endpoint_auth_method": "none"} {"id": "OAUTH2", "sk": "CLIENT_ID#6ebe1709-0831-455c-84c0-d4c753bf33c6", "client_secret": "1nFD8alDbGHgc3g1RLY960xyRJVee0SlMoIB0MUlSuiJy28W", "name": "pytest 2", "scope": "openid profile", "redirect_uris": ["https://localhost/callback"], "response_types": ["code"], "grant_types": ["authorization_code", "refresh_token"], "scope": "openid profile email offline_access", "token_endpoint_auth_method": "none"} {"id": "OAUTH2", "sk": "CLIENT_ID#1db63660-063d-4280-b2ea-388aca4a9459", "client_secret": "1nFD8alDbGHgc3g1RLY960xyRJVee0SlMoIB0MUlSuiJy28W", "name": "pytest 3", "scope": "openid profile", "redirect_uris": ["https://localhost/callback"], "response_types": ["code"], "grant_types": ["authorization_code", "refresh_token"], "scope": "openid profile email offline_access read:users", "token_endpoint_auth_method": "client_secret_basic"} {"id": "OAUTH2#CODE", "sk": "CODE#kyqp3oSuRFTfuBaCmq3XOgGWg67l42Kt3D6xPEj7Yd3MLdi9", "client_id": "d72d4005-1fa7-4430-9754-80d5e2487bb6", "redirect_uri": "https://localhost/callback", "user_id": "357db1c5-7442-4075-98a3-fbe5c938a419", "nonce": null, "scope": "openid profile email read:users", "response_type": "code", "code_challenge": "ejYEIGKQUgMnNh4eV0sftb0hXdLwkvKm6OHXRYvC--I", "code_challenge_method": "S256", "created_at": "2025-08-07T12:38:26.550431-03:00"} +{"id": "OAUTH2#TOKEN", "sk": "REFRESH_TOKEN#CyF3Ik3b9hMIo3REVv27gZAHd7dvwZq6QrkhWr7qHEen4UVy", "client_id": "d72d4005-1fa7-4430-9754-80d5e2487bb6", "token_type": "Bearer", "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6IlRjT0VuV3JGSUFEYlZJNjJlY1pzU28ydEI1eW5mbkZZNTZ0Uy05b0stNW8ifQ.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0IiwiZXhwIjoxNzU5NTg2NzgzLCJjbGllbnRfaWQiOiJkNzJkNDAwNS0xZmE3LTQ0MzAtOTc1NC04MGQ1ZTI0ODdiYjYiLCJpYXQiOjE3NTg5ODE5ODMsImp0aSI6Ik9uVzRIZm1FdFl2a21CbE4iLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIHJlYWQ6dXNlcnMiLCJzdWIiOiIzNTdkYjFjNS03NDQyLTQwNzUtOThhMy1mYmU1YzkzOGE0MTkiLCJhdWQiOiJkNzJkNDAwNS0xZmE3LTQ0MzAtOTc1NC04MGQ1ZTI0ODdiYjYifQ.i0NVgvPuf5jvl8JcYNsVCzjVUTDLihgQO4LmLeNijx9Ed3p_EgtVtcHFWFvEebe_LwTuDDtIJveH22Piyp4zresNSc_YNumnuvoY1aNd0ic2RIEtXaklRroq0xHwL_IVT-Dt6P9xL5Hyygx47Pvmci4U3wWK32a6Sb1Mm7ZZgXA00xWI1bJ_zwxFLvDkHDp9nrAa_vEWN6zRBcWc7JYNsgiaPMC0DoL8it0k48_g44zfsjGAZLcWFMoPlYt3wIcQQDeCKMsSJI0VPnqKK0pq4OOVs-pjkMyAU5aEMPvVOwdAL3VZY16RXt3eTzsmMH1XoRdCMP6UAx4ZS10RLGUPeA", "scope": "openid profile email read:users", "user": {"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "name": "S\u00e9rgio R Siqueira", "email": "sergio@somosbeta.com.br", "email_verified": false}, "expires_in": 180, "issued_at": 1758981984, "ttl": 1759586784} + {"id": "email", "sk": "sergio@somosbeta.com.br", "user_id": "357db1c5-7442-4075-98a3-fbe5c938a419"} {"id": "cpf", "sk": "07879819908", "user_id": "357db1c5-7442-4075-98a3-fbe5c938a419"} @@ -18,4 +21,4 @@ {"id": "fd5914ec-fd37-458b-b6b9-8aeab38b666b", "sk": "0", "name": "Johnny Cash", "email": "johnny@johnnycash.com"} {"id": "fd5914ec-fd37-458b-b6b9-8aeab38b666b", "sk": "PASSWORD", "hash": "$pbkdf2-sha256$29000$IuTcm7M2BiAEgPB.b.3dGw$d8xVCbx8zxg7MeQBrOvCOgniiilsIHEMHzoH/OXftLQ"} -{"id": "fd5914ec-fd37-458b-b6b9-8aeab38b666b", "sk": "SCOPE", "scope": "openid"} +{"id": "fd5914ec-fd37-458b-b6b9-8aeab38b666b", "sk": "SCOPE", "scope": "openid"} \ No newline at end of file