From 347c3953943a1e0b7d84d5555727fae2f1d104be Mon Sep 17 00:00:00 2001 From: phil Date: Thu, 20 Feb 2025 21:16:43 +0100 Subject: [PATCH] Fix auth provider resources --- src/oidc_test/main.py | 25 +++++++++++++------------ src/oidc_test/resource_server.py | 25 ++++++++++++++++++++----- src/oidc_test/templates/home.html | 29 +++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/oidc_test/main.py b/src/oidc_test/main.py index 8808562..54d69c5 100644 --- a/src/oidc_test/main.py +++ b/src/oidc_test/main.py @@ -108,7 +108,6 @@ async def home( "show_token": settings.show_token, "user": user, "now": datetime.now(), - "auth_provider": provider, } if provider is None or token is None: context["providers"] = providers @@ -117,26 +116,28 @@ async def home( context["access_token_parsed"] = None context["refresh_token_parsed"] = None context["resources"] = None + context["auth_provider"] = None else: + context["auth_provider"] = provider context["access_token"] = token["access_token"] try: access_token_parsed = provider.decode(token["access_token"], verify_signature=False) + context["access_token_parsed"] = access_token_parsed except PyJWTError as err: access_token_parsed = {"Cannot parse": err.__class__.__name__} try: - context["access_token_scope"] = access_token_parsed["scope"] - except KeyError: - context["access_token_scope"] = None - context["id_token_parsed"] = provider.decode(token["id_token"], verify_signature=False) - context["access_token_parsed"] = access_token_parsed + id_token_parsed = provider.decode(token["id_token"], verify_signature=False) + context["id_token_parsed"] = id_token_parsed + except PyJWTError as err: + id_token_parsed = {"Cannot parse": err.__class__.__name__} + try: + refresh_token_parsed = provider.decode(token["refresh_token"], verify_signature=False) + context["refresh_token_parsed"] = refresh_token_parsed + except PyJWTError as err: + refresh_token_parsed = {"Cannot parse": err.__class__.__name__} + context["access_token_scope"] = access_token_parsed.get("scope") context["resources"] = registry.resources context["resource_providers"] = provider.resource_providers - try: - context["refresh_token_parsed"] = provider.decode( - token["refresh_token"], verify_signature=False - ) - except PyJWTError as err: - context["refresh_token_parsed"] = {"Cannot parse": err.__class__.__name__} return templates.TemplateResponse(name="home.html", request=request, context=context) diff --git a/src/oidc_test/resource_server.py b/src/oidc_test/resource_server.py index 604052c..ddc5762 100644 --- a/src/oidc_test/resource_server.py +++ b/src/oidc_test/resource_server.py @@ -1,9 +1,10 @@ from typing import Annotated, Any import logging +from json import JSONDecodeError from authlib.oauth2.rfc6749 import OAuth2Token -from httpx import AsyncClient -from jwt.exceptions import ExpiredSignatureError, InvalidTokenError +from httpx import AsyncClient, HTTPError +from jwt.exceptions import DecodeError, ExpiredSignatureError, InvalidTokenError from fastapi import FastAPI, HTTPException, Depends, Request, status from fastapi.middleware.cors import CORSMiddleware @@ -84,6 +85,10 @@ async def get_resource( "auth_provider": user.auth_provider_id, }, ) + except HTTPError as err: + raise HTTPException( + status.HTTP_503_SERVICE_UNAVAILABLE, err.__class__.__name__ + ) except Exception as err: raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR, err.__class__.__name__ @@ -151,7 +156,7 @@ async def get_auth_provider_resource( ) -> ProcessResult: if token is None: raise HTTPException(status.HTTP_401_UNAUTHORIZED, "No auth token") - access_token = token["access_token"] + access_token = token async with AsyncClient() as client: resp = await client.get( url=provider.get_resource_url(resource_name), @@ -165,9 +170,19 @@ async def get_auth_provider_resource( # Only a demo, real application would really process the response resp_length = len(resp.text) if resp_length > 1024: - return ProcessResult(msg=f"The resource is too long ({resp_length} bytes) to show here") + return ProcessResult( + msg=f"The resource is too long ({resp_length} bytes) to show in this demo, here is just the begining in raw format", + start=resp.text[:100] + "...", + ) else: - return ProcessResult(**resp.json()) + try: + resp_json = resp.json() + except JSONDecodeError: + return ProcessResult(msg="The resource is not formatted in JSON", text=resp.text) + if isinstance(resp_json, dict): + return ProcessResult(**resp.json()) + elif isinstance(resp_json, list): + return ProcessResult(**{str(i): line for i, line in enumerate(resp_json)}) # @resource_server.get("/public") diff --git a/src/oidc_test/templates/home.html b/src/oidc_test/templates/home.html index 3c1ff3c..b4460ee 100644 --- a/src/oidc_test/templates/home.html +++ b/src/oidc_test/templates/home.html @@ -66,12 +66,8 @@ {% endif %}
- {% if resources %} -

- This application provides all these resources, eventually protected with scope or roles: -

+

This application provides all these resources, eventually protected with scope or roles:

{% endif %} + {% if auth_provider.resources %} +

{{ auth_provider.name }} is also defined as a provider for these resources:

+ + {% endif %} {% if resource_providers %} -

{{ auth_provider.name }} allows this applicaiton to request resources from third party resource providers:

+

{{ auth_provider.name }} allows this application to request resources from third party resource providers:

{% for resource_provider in resource_providers %}