This commit is contained in:
2025-08-05 21:14:09 -03:00
parent 5c57da7ecb
commit f96ad67eeb
27 changed files with 1960 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
from uuid import uuid4
from authlib.oauth2 import OAuth2Error
from authlib.oauth2.rfc6749 import errors
from aws_lambda_powertools.event_handler.api_gateway import Router
from oauth2 import authorization
router = Router()
@router.get('/authorize')
def authorize():
user = {
'id': str(uuid4()),
'sub': 'sergio@somosbeta.com.br',
}
try:
grant = authorization.get_consent_grant(
request=router.current_event,
end_user=user,
)
except OAuth2Error as err:
return dict(err.get_body())
try:
return authorization.create_authorization_response(
request=router.current_event,
grant_user=user,
grant=grant,
)
except errors.OAuth2Error:
return {}

View File

@@ -0,0 +1,8 @@
from aws_lambda_powertools.event_handler.api_gateway import Router
router = Router()
@router.get('/jwks.json')
def jwks():
return {}

View File

@@ -0,0 +1,46 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
</head>
<body
class="bg-black text-white flex items-center justify-center min-h-screen"
>
<form
method="POST"
action="/login"
class="bg-gray-900 p-8 rounded-lg shadow-lg w-full max-w-sm space-y-4"
>
<div>
<label for="username" class="block mb-1">Email ou CPF</label>
<input
type="text"
id="username"
name="username"
class="w-full p-2 rounded bg-gray-800 text-white border border-gray-700"
required
/>
</div>
<div>
<label for="password" class="block mb-1">Senha</label>
<input
type="password"
id="password"
name="password"
class="w-full p-2 rounded bg-gray-800 text-white border border-gray-700"
required
/>
</div>
<button
type="submit"
class="w-full bg-blue-600 hover:bg-blue-700 transition-colors p-2 rounded font-semibold"
>
Entrar
</button>
</form>
</body>
</html>

View File

@@ -0,0 +1,82 @@
from http import HTTPStatus
from pathlib import Path
from typing import Annotated
from aws_lambda_powertools.event_handler import (
Response,
)
from aws_lambda_powertools.event_handler.api_gateway import Router
from aws_lambda_powertools.event_handler.exceptions import ForbiddenError, NotFoundError
from aws_lambda_powertools.event_handler.openapi.params import Form
from aws_lambda_powertools.shared.cookies import Cookie
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
from passlib.hash import pbkdf2_sha256
from boto3clients import dynamodb_client
from config import OAUTH2_TABLE
from jose_ import generate_jwt
router = Router()
oauth2_layer = DynamoDBPersistenceLayer(OAUTH2_TABLE, dynamodb_client)
@router.get('/login')
def login_form():
html = Path(__file__).with_name('login.html').read_text(encoding='utf-8')
return Response(
body=html,
status_code=HTTPStatus.OK.value,
content_type='text/html',
)
@router.post('/login')
def login(
username: Annotated[str, Form()],
password: Annotated[str, Form()],
):
user_id, password_hash = _get_user(username)
if not pbkdf2_sha256.verify(password, password_hash):
raise ForbiddenError('Invalid credentials')
jwt_token = generate_jwt(user_id, username)
return Response(
status_code=HTTPStatus.OK,
cookies=[
Cookie(
name='id_token',
value=jwt_token,
http_only=True,
same_site=None,
),
],
)
def _get_user(username: str) -> tuple[str, str]:
r = oauth2_layer.collection.get_item(
# Post-migration: uncomment the following line
# KeyPair('EMAIL', username),
KeyPair('email', username),
exc_cls=EmailNotFoundError,
)
password = oauth2_layer.collection.get_item(
KeyPair(r['user_id'], 'PASSWORD'),
exc_cls=UserNotFoundError,
)
return r['user_id'], password['hash']
class EmailNotFoundError(NotFoundError):
def __init__(self, *_):
super().__init__('Email not found')
class UserNotFoundError(NotFoundError):
def __init__(self, *_):
super().__init__('User not found')

View File

@@ -0,0 +1,25 @@
from aws_lambda_powertools.event_handler.api_gateway import Router
from config import ISSUER, JWT_ALGORITHM
router = Router()
@router.get('/.well-known/openid-configuration')
def openid_configuration():
return {
'issuer': ISSUER,
'authorization_endpoint': f'{ISSUER}/authorize',
'token_endpoint': f'{ISSUER}/token',
'userinfo_endpoint': f'{ISSUER}/userinfo',
'jwks_uri': f'{ISSUER}/jwks.json',
'scopes_supported': ['openid', 'profile', 'email'],
'response_types_supported': ['code'],
'grant_types_supported': ['authorization_code', 'refresh_token'],
'subject_types_supported': ['public'],
'id_token_signing_alg_values_supported': [JWT_ALGORITHM],
'token_endpoint_auth_methods_supported': [
'client_secret_basic',
'client_secret_post',
],
}

View File

@@ -0,0 +1,8 @@
from aws_lambda_powertools.event_handler.api_gateway import Router
router = Router()
@router.get('/token')
def token():
return {}

View File

@@ -0,0 +1,8 @@
from aws_lambda_powertools.event_handler.api_gateway import Router
router = Router()
@router.get('/userinfo')
def userinfo():
return {}