add scope
This commit is contained in:
@@ -117,8 +117,6 @@ class AuthorizationServer(oauth2.AuthorizationServer):
|
||||
exc_cls=ClientNotFoundError,
|
||||
)
|
||||
|
||||
_, client_id = client.get(DYNAMODB_SORT_KEY, '').split('#')
|
||||
|
||||
return OAuth2Client(
|
||||
client_id=client_id,
|
||||
client_secret=client['client_secret'],
|
||||
|
||||
@@ -72,8 +72,9 @@ class AuthorizationCodeGrant(grants.AuthorizationCodeGrant):
|
||||
raise ValueError('Missing request user')
|
||||
|
||||
client_id: str = request.payload.client_id
|
||||
scope: str = request.payload.scope
|
||||
data: dict = request.payload.data
|
||||
user: dict = request.user
|
||||
user_id: str = request.user
|
||||
nonce: str | None = data.get('nonce')
|
||||
code_challenge: str | None = data.get('code_challenge')
|
||||
code_challenge_method: str | None = data.get('code_challenge_method')
|
||||
@@ -87,9 +88,9 @@ class AuthorizationCodeGrant(grants.AuthorizationCodeGrant):
|
||||
'sk': f'CODE#{code}',
|
||||
'redirect_uri': request.payload.redirect_uri,
|
||||
'response_type': request.payload.response_type,
|
||||
'scope': request.payload.scope,
|
||||
'scope': scope,
|
||||
'client_id': client_id,
|
||||
'user_id': user['id'],
|
||||
'user_id': user_id,
|
||||
'nonce': nonce,
|
||||
'code_challenge': code_challenge,
|
||||
'code_challenge_method': code_challenge_method,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from http import HTTPStatus, client
|
||||
from http.cookies import SimpleCookie
|
||||
|
||||
import jwt
|
||||
from authlib.oauth2.rfc6749 import errors
|
||||
from authlib.oauth2.rfc6749.util import scope_to_list
|
||||
from aws_lambda_powertools import Logger
|
||||
from aws_lambda_powertools.event_handler.api_gateway import Router
|
||||
from aws_lambda_powertools.event_handler.exceptions import BadRequestError
|
||||
from aws_lambda_powertools.event_handler.exceptions import BadRequestError, ServiceError
|
||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
||||
|
||||
from boto3clients import dynamodb_client
|
||||
@@ -26,15 +28,24 @@ def authorize():
|
||||
raise BadRequestError('Missing session_id')
|
||||
|
||||
try:
|
||||
user_id = verify_session(session_id)
|
||||
sub, session_scope = verify_session(session_id)
|
||||
grant = server.get_consent_grant(
|
||||
request=router.current_event,
|
||||
end_user={'id': user_id},
|
||||
end_user=sub,
|
||||
)
|
||||
req_scopes = set(scope_to_list(grant.request.payload.scope))
|
||||
user_scopes = set(scope_to_list(session_scope)) if session_scope else set()
|
||||
client_scopes = set(scope_to_list(grant.client.scope))
|
||||
|
||||
if not req_scopes.issubset(
|
||||
client_scopes
|
||||
& (user_scopes | {'openid', 'email', 'profile', 'offline_access'})
|
||||
):
|
||||
raise errors.InvalidScopeError(status_code=HTTPStatus.UNAUTHORIZED)
|
||||
|
||||
return server.create_authorization_response(
|
||||
request=router.current_event,
|
||||
grant_user={'id': user_id},
|
||||
grant_user=sub,
|
||||
grant=grant,
|
||||
)
|
||||
except jwt.exceptions.InvalidTokenError as err:
|
||||
@@ -42,10 +53,13 @@ def authorize():
|
||||
raise BadRequestError(str(err))
|
||||
except errors.OAuth2Error as err:
|
||||
logger.exception(err)
|
||||
return dict(err.get_body())
|
||||
raise ServiceError(
|
||||
status_code=err.status_code,
|
||||
msg=dict(err.get_body()), # type: ignore
|
||||
)
|
||||
|
||||
|
||||
def verify_session(session_id: str) -> str:
|
||||
def verify_session(session_id: str) -> tuple[str, str | None]:
|
||||
payload = jwt.decode(
|
||||
session_id,
|
||||
JWT_SECRET,
|
||||
@@ -65,7 +79,7 @@ def verify_session(session_id: str) -> str:
|
||||
exc_cls=SessionRevokedError,
|
||||
)
|
||||
|
||||
return payload['sub']
|
||||
return payload['sub'], payload.get('scope')
|
||||
|
||||
|
||||
def _parse_cookies(cookies: list[str] | None) -> dict[str, str]:
|
||||
|
||||
@@ -26,7 +26,11 @@ def session(
|
||||
username: Annotated[str, Body()],
|
||||
password: Annotated[str, Body()],
|
||||
):
|
||||
user_id, password_hash = _get_user(username)
|
||||
(
|
||||
user_id,
|
||||
password_hash,
|
||||
scope,
|
||||
) = _get_user(username)
|
||||
|
||||
if not pbkdf2_sha256.verify(password, password_hash):
|
||||
raise ForbiddenError('Invalid credentials')
|
||||
@@ -36,7 +40,7 @@ def session(
|
||||
cookies=[
|
||||
Cookie(
|
||||
name='session_id',
|
||||
value=new_session(user_id),
|
||||
value=new_session(user_id, scope),
|
||||
http_only=True,
|
||||
secure=True,
|
||||
same_site=None,
|
||||
@@ -46,7 +50,7 @@ def session(
|
||||
)
|
||||
|
||||
|
||||
def _get_user(username: str) -> tuple[str, str]:
|
||||
def _get_user(username: str) -> tuple[str, str, str | None]:
|
||||
sk = SortKey(username, path_spec='user_id')
|
||||
user = oauth2_layer.collection.get_items(
|
||||
KeyPair(pk='email', sk=sk, rename_key=sk.path_spec)
|
||||
@@ -57,15 +61,33 @@ def _get_user(username: str) -> tuple[str, str]:
|
||||
if not user:
|
||||
raise UserNotFoundError()
|
||||
|
||||
password = oauth2_layer.collection.get_item(
|
||||
KeyPair(user['user_id'], 'PASSWORD'),
|
||||
exc_cls=UserNotFoundError,
|
||||
userdata = oauth2_layer.collection.get_items(
|
||||
KeyPair(
|
||||
pk=user['user_id'],
|
||||
sk=SortKey(
|
||||
sk='PASSWORD',
|
||||
path_spec='hash',
|
||||
rename_key='password',
|
||||
),
|
||||
)
|
||||
+ KeyPair(
|
||||
pk=user['user_id'],
|
||||
sk=SortKey(
|
||||
sk='SCOPE',
|
||||
path_spec='scope',
|
||||
rename_key='scope',
|
||||
),
|
||||
),
|
||||
flatten_top=False,
|
||||
)
|
||||
|
||||
return user['user_id'], password['hash']
|
||||
if not userdata:
|
||||
raise UserNotFoundError()
|
||||
|
||||
return user['user_id'], userdata['password'], userdata.get('scope')
|
||||
|
||||
|
||||
def new_session(sub: str) -> str:
|
||||
def new_session(sub: str, scope: str | None) -> str:
|
||||
now_ = now()
|
||||
sid = str(uuid4())
|
||||
exp = ttl(start_dt=now_, seconds=JWT_EXP_SECONDS)
|
||||
@@ -76,6 +98,7 @@ def new_session(sub: str) -> str:
|
||||
'iss': ISSUER,
|
||||
'iat': int(now_.timestamp()),
|
||||
'exp': exp,
|
||||
'scope': scope,
|
||||
},
|
||||
JWT_SECRET,
|
||||
algorithm=JWT_ALGORITHM,
|
||||
|
||||
Reference in New Issue
Block a user