async def login( response: JSONResponse, internal_user: str = Depends(auth_token_scheme) ) -> JSONResponse: """ Login endpoint for authenticating a user after he has received an authentication token. If the token is valid it generates an access token and inserts it in a HTTPOnly cookie. Args: internal_auth_token: Internal authentication token Returns: response: A JSON response with the status of the user's session """ async with exception_handling(): access_token = await auth_util.create_internal_access_token( InternalAccessTokenData( sub=internal_user.internal_sub_id, ) ) response = JSONResponse( content=jsonable_encoder({ "userLoggedIn": True, "userName": internal_user.username, }), ) # Make this a secure cookie for production use response.set_cookie(key="access_token", value=f"Bearer {access_token}", httponly=True) return response
async def __call__(self, request: Request) -> Optional[str]: async with exception_handling(): authorization: str = request.headers.get("Authorization") scheme, internal_auth_token = get_authorization_scheme_param( authorization) if not authorization or scheme.lower() != "bearer": raise UnauthorizedUser("Invalid authentication token") internal_user = await auth_util.validate_internal_auth_token( internal_auth_token) return internal_user
async def __call__(self, request: Request) -> InternalUser: async with exception_handling(): internal_access_token: str = request.cookies.get('access_token') if not internal_access_token: raise UnauthorizedUser("Invalid access token cookie") # Remove Bearer internal_access_token = internal_access_token.split()[1] internal_user = await auth_util.validate_internal_access_token( internal_access_token) return internal_user
async def __call__(self, request: Request) -> InternalUser: async with exception_handling(): # State token from redirect state_csrf_token: str = request.query_params.get("state") # State token from cookie state_csrf_token_cookie: str = request.cookies.get('state') if not state_csrf_token_cookie: raise UnauthorizedUser("Invalid state token") # Remove Bearer state_csrf_token_cookie = state_csrf_token_cookie.split()[1] await auth_util.validate_state_csrf_token(state_csrf_token, state_csrf_token_cookie)
async def azure_login_callback( request: Request, _ = Depends(csrf_token_redirect_cookie_scheme) ): """ Callback triggered when the user logs in to Azure's pop-up. Receives an authentication_token from Azure which then exchanges for an access_token. The latter is used to gain user information from Azure's userinfo_endpoint. Args: request: The incoming request as redirected by Azure """ async with exception_handling(): code = request.query_params.get("code") if not code: raise AuthorizationException("Missing external authentication token") provider = await auth_providers.get_auth_provider(config.AZURE) # Authenticate token and get user's info from external provider external_user = await provider.get_user( auth_token=ExternalAuthToken(code=code) ) # Get or create the internal user internal_user = await db_client.get_user_by_external_sub_id(external_user) if internal_user is None: internal_user = await db_client.create_internal_user(external_user) internal_auth_token = await auth_util.create_internal_auth_token(internal_user) # Redirect the user to the home page redirect_url = f"{config.FRONTEND_URL}?authToken={internal_auth_token}" response = RedirectResponse(url=redirect_url) # Delete state cookie. No longer required response.delete_cookie(key="state") return response
async def login_redirect(auth_provider: str): """ Redirects the user to the external authentication pop-up Args: auth_provider: The authentication provider (i.e google-iodc) Returns: Redirect response to the external provider's auth endpoint """ async with exception_handling(): provider = await auth_providers.get_auth_provider(auth_provider) request_uri, state_csrf_token = await provider.get_request_uri() response = RedirectResponse(url=request_uri) # Make this a secure cookie for production use response.set_cookie(key="state", value=f"Bearer {state_csrf_token}", httponly=True) return response
async def logout( response: JSONResponse, internal_user: str = Depends(access_token_cookie_scheme) ) -> JSONResponse: """ Logout endpoint for deleting the HTTPOnly cookie on the user's browser. Args: internal_auth_token: Internal authentication token Returns: response: A JSON response with the status of the user's session """ async with exception_handling(): response = JSONResponse( content=jsonable_encoder({ "userLoggedIn": False, }), ) response.delete_cookie(key="access_token") return response
async def user_session_status( internal_user: InternalUser = Depends(access_token_cookie_scheme) ) -> JSONResponse: """ User status endpoint for checking whether the user currently holds an HTTPOnly cookie with a valid access token. Args: internal_user: A user object that has meaning in this application Returns: response: A JSON response with the status of the user's session """ async with exception_handling(): logged_id = True if internal_user else False response = JSONResponse( content=jsonable_encoder({ "userLoggedIn": logged_id, "userName": internal_user.username, }), ) return response
async def shutdown_event(): """ Shutdown functionality """ async with exception_handling(): await db_client.end_session() await db_client.close_connection()
async def startup_event(): """ Startup functionality """ async with exception_handling(): await db_client.start_session()