Skip to content

Commit

Permalink
feat(system-server): add GET /system/authorize endpoint (#12225)
Browse files Browse the repository at this point in the history
- Added a FastAPI Header dependency that will check for an authenticationBearer with an authorization token
- Added endpoint GET /system/authorize that just uses the aforementioned dependency to check for a valid authorization token
- Added an integration test to check the new endpoint
- Modified the make dev command to set the log level to debug & to enable uvicorn reloading
  • Loading branch information
fsinapi authored Mar 17, 2023
1 parent b727ff2 commit 4850e2b
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 4 deletions.
2 changes: 1 addition & 1 deletion system-server/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ push-ot3: sdist
.PHONY: dev
dev: export OT_SYSTEM_SERVER_DOT_ENV_PATH ?= dev.env
dev:
$(pipenv) run python -m system_server
$(pipenv) run python -m system_server --reload --log-level=debug

.PHONY: docs
docs: settings_schema.json
Expand Down
33 changes: 32 additions & 1 deletion system-server/system_server/service/check_jwt_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging

from system_server.jwt import jwt_is_valid
from system_server.constants import REGISTRATION_AUDIENCE
from system_server.constants import REGISTRATION_AUDIENCE, AUTHORIZATION_AUDIENCE
from system_server.persistence import get_persistent_uuid

_log = logging.getLogger(__name__)
Expand Down Expand Up @@ -39,3 +39,34 @@ async def get_registration_token_header(request: Request) -> str:
), "No authentication_bearer in request state; is endpoint properly configured?"

return request.state.authentication_bearer


async def check_authorization_token_header(
request: Request,
authenticationBearer: str = Header(
...,
description="An authentication header bearing a token provided by the /system/authorize endpoint.",
),
signing_key: UUID = Depends(get_persistent_uuid),
) -> None:
"""A header requirement that requests a authorization token from /system/authorize."""
_log.info(f"Verifying authorization token: {authenticationBearer}")
if not jwt_is_valid(
signing_key=str(signing_key),
token=authenticationBearer,
audience=AUTHORIZATION_AUDIENCE,
):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Authorization token invalid",
)
request.state.authorization_authentication_bearer = authenticationBearer


async def get_authorization_token_header(request: Request) -> str:
"""Gets the authorization token from a request."""
assert isinstance(
request.state.authorization_authentication_bearer, str
), "No authorization_authentication_bearer in request state; is endpoint properly configured?"

return request.state.authorization_authentication_bearer
35 changes: 34 additions & 1 deletion system-server/system_server/system/authorize/router.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Router for all /system/ endpoints."""
from fastapi import APIRouter, Depends, status
from fastapi import APIRouter, Depends, status, Response, Query
from uuid import UUID
from typing import List, Optional

from system_server.persistence import get_persistent_uuid
from system_server.service.check_jwt_headers import (
check_registration_token_header,
get_registration_token_header,
check_authorization_token_header,
get_authorization_token_header,
)
from .models import PostAuthorizeResponse
from .authorization import authorize_token
Expand All @@ -28,3 +31,33 @@ async def authorize(
"""Router for /system/authorize endpoint."""
authorization = authorize_token(token, str(signing_uuid))
return PostAuthorizeResponse(token=authorization)


@authorize_router.get(
"/system/authorize",
summary="Verify an authorization token.",
dependencies=[Depends(check_authorization_token_header)],
responses={
status.HTTP_200_OK: {
"description": "The authorization token is valid",
"model": None,
},
status.HTTP_403_FORBIDDEN: {
"description": "the authorization token is not valid for the given scopes",
"model": None,
},
},
)
async def check_authorization(
token: str = Depends(get_authorization_token_header),
signing_uuid: UUID = Depends(get_persistent_uuid),
scopes: Optional[List[str]] = Query(
None, description="List of scopes to verify token access to."
),
) -> Response:
"""Check an authorization token for validity."""
# NOTE: The `scopes` parameter is included as a placeholder for future validation.
# In the current implementation of this server, an auth token gives unilateral access
# to system functionality; thus, there is no scope restraint to be concerned with.

return Response(status_code=status.HTTP_200_OK)
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,19 @@ stages:
token: '{auth_token:s}'
json:
token: !anystr

- name: GET /system/authorize with invalid token fails
request:
url: "{host:s}:{port:d}/system/authorize"
method: GET
headers:
authenticationBearer: "{issued_token:s}"
response:
status_code: 403
- name: GET /system/authorize with valid token fails
request:
url: "{host:s}:{port:d}/system/authorize"
method: GET
headers:
authenticationBearer: "{auth_token:s}"
response:
status_code: 200

0 comments on commit 4850e2b

Please sign in to comment.