These links should get different response codes depending on the authorization:
From af49242192c29a38b58819ebd0f2e8ae55bd87b6 Mon Sep 17 00:00:00 2001
From: phil
From e1dac777388ca2f6850f88d9afccb4d2f394811c Mon Sep 17 00:00:00 2001
From: phil
+ Fetch resources from the resource server with your authentication token:
+
These links should get different response codes depending on the authorization:
From af49242192c29a38b58819ebd0f2e8ae55bd87b6 Mon Sep 17 00:00:00 2001
From: phil
Fetch resources from the resource server with your authentication token:
These links should get different response codes depending on the authorization:
From fefe44acfef0a16c492070ac8bc9f43d4637aa2c Mon Sep 17 00:00:00 2001
From: phil
From 3eb6dc3dcf4be7350aa4cda6b3157183fe77d5d8 Mon Sep 17 00:00:00 2001
From: phil
- Fetch resources from the resource server with your authentication token:
-
- These links should get different response codes depending on the authorization:
+
+ Resources validated by scope:
+ Resources validated by role:
+
From ff72f0cae585858e400a9bc8f7d3fe1727035c44 Mon Sep 17 00:00:00 2001
From: phil User info
{% endif %}
+
+ {% if user %}
+ {% endif %}
{% endif %}
{% for key, value in user_info_details.items() %}
From 76da695b66d306eda3e57cf0a562d33b4c004ac9 Mon Sep 17 00:00:00 2001
From: phil Not protected
")
-
-
-@app.get("/protected")
-async def get_protected(
- user: Annotated[User, Depends(get_current_user)]
-) -> HTMLResponse:
- assert user is not None # Just to keep QA checks happy
- return HTMLResponse("Only authenticated users can see this
")
-
-
-@app.get("/protected-by-foorole")
-@hasrole("foorole")
-async def get_protected_by_foorole(request: Request) -> HTMLResponse:
- assert request is not None # Just to keep QA checks happy
- return HTMLResponse("Only users with foorole can see this
")
-
-
-@app.get("/protected-by-barrole")
-@hasrole("barrole")
-async def get_protected_by_barrole(request: Request) -> HTMLResponse:
- assert request is not None # Just to keep QA checks happy
- return HTMLResponse("Protected by barrole
")
-
-
-@app.get("/protected-by-foorole-and-barrole")
-@hasrole("barrole")
-@hasrole("foorole")
-async def get_protected_by_foorole_and_barrole(request: Request) -> HTMLResponse:
- assert request is not None # Just to keep QA checks happy
- return HTMLResponse("Only users with foorole and barrole can see this
")
-
-
-@app.get("/protected-by-foorole-or-barrole")
-@hasrole(["foorole", "barrole"])
-async def get_protected_by_foorole_or_barrole(request: Request) -> HTMLResponse:
- assert request is not None # Just to keep QA checks happy
- return HTMLResponse("Only users with foorole or barrole can see this
")
-
-
-@app.get("/introspect")
-async def get_introspect(
- request: Request,
- oidc_provider: Annotated[StarletteOAuth2App, Depends(get_oidc_provider)],
- token: Annotated[OAuth2Token, Depends(get_token)],
-) -> JSONResponse:
- assert request is not None # Just to keep QA checks happy
- if (url := oidc_provider.server_metadata.get("introspection_endpoint")) is None:
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="No intrispection endpoint found for the OIDC provider",
- )
- if (
- response := await oidc_provider.post(
- url,
- token=token,
- data={"token": token["access_token"]},
- )
- ).is_success:
- return response.json()
- else:
- raise HTTPException(status_code=response.status_code, detail=response.text)
-
-
# Snippet for running standalone
# Mostly useful for the --version option,
# as running with uvicorn is easy and provides better flexibility, eg.
@@ -397,9 +294,7 @@ def main():
parser.add_argument(
"-p", "--port", type=int, default=80, help="Port to listen to (default: 80)"
)
- parser.add_argument(
- "-v", "--version", action="store_true", help="Print version and exit"
- )
+ parser.add_argument("-v", "--version", action="store_true", help="Print version and exit")
args = parser.parse_args()
if args.version:
diff --git a/src/oidc_test/models.py b/src/oidc_test/models.py
index fc0dba7..9554bd5 100644
--- a/src/oidc_test/models.py
+++ b/src/oidc_test/models.py
@@ -1,6 +1,6 @@
import logging
from functools import cached_property
-from typing import Self, Any
+from typing import Any
from pydantic import (
computed_field,
@@ -60,6 +60,4 @@ class User(UserBase):
assert self.oidc_provider.name is not None
from .settings import oidc_providers_settings
- return oidc_providers_settings[self.oidc_provider.name].decode(
- self.access_token
- )
+ return oidc_providers_settings[self.oidc_provider.name].decode(self.access_token)
diff --git a/src/oidc_test/resource_server.py b/src/oidc_test/resource_server.py
index 0d90533..d5e2aaa 100644
--- a/src/oidc_test/resource_server.py
+++ b/src/oidc_test/resource_server.py
@@ -1,15 +1,127 @@
from datetime import datetime
+from typing import Annotated
import logging
from httpx import AsyncClient
from jwt.exceptions import ExpiredSignatureError, InvalidTokenError
-from fastapi import HTTPException, status
-from starlette.status import HTTP_401_UNAUTHORIZED
+from fastapi import FastAPI, HTTPException, Depends, Request, status
+from fastapi.responses import HTMLResponse, JSONResponse
+from fastapi.middleware.cors import CORSMiddleware
+
+# from starlette.middleware.sessions import SessionMiddleware
+# from authlib.integrations.starlette_client.apps import StarletteOAuth2App
+# from authlib.oauth2.rfc6749 import OAuth2Token
from .models import User
+from .auth_utils import (
+ get_user_from_token,
+ UserWithRole,
+ get_oidc_provider,
+ get_token,
+)
+from .settings import settings
logger = logging.getLogger("oidc-test")
+resource_server = FastAPI()
+
+
+resource_server.add_middleware(
+ CORSMiddleware,
+ allow_origins=settings.cors_origins,
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+# SessionMiddleware is required by authlib
+# resource_server.add_middleware(
+# SessionMiddleware,
+# secret_key=settings.secret_key,
+# )
+
+# Route for OAuth resource server
+
+
+# Routes for RBAC based tests
+
+
+@resource_server.get("/public")
+async def public() -> HTMLResponse:
+ return HTMLResponse("Not protected
")
+
+
+@resource_server.get("/protected")
+async def get_protected(user: Annotated[User, Depends(get_user_from_token)]) -> HTMLResponse:
+ assert user is not None # Just to keep QA checks happy
+ return HTMLResponse("Only authenticated users can see this
")
+
+
+@resource_server.get("/protected-by-foorole")
+async def get_protected_by_foorole(
+ user: Annotated[User, Depends(UserWithRole("foorole"))]
+) -> HTMLResponse:
+ return HTMLResponse("Only users with foorole can see this
")
+
+
+@resource_server.get("/protected-by-barrole")
+async def get_protected_by_barrole(
+ user: Annotated[User, Depends(UserWithRole("barrole"))]
+) -> HTMLResponse:
+ return HTMLResponse("Protected by barrole
")
+
+
+@resource_server.get("/protected-by-foorole-and-barrole")
+async def get_protected_by_foorole_and_barrole(
+ user: Annotated[User, Depends(UserWithRole("foorole")), Depends(UserWithRole("barrole"))],
+) -> HTMLResponse:
+ assert user is not None # Just to keep QA checks happy
+ return HTMLResponse("Only users with foorole and barrole can see this
")
+
+
+@resource_server.get("/protected-by-foorole-or-barrole")
+async def get_protected_by_foorole_or_barrole(
+ user: Annotated[User, Depends(UserWithRole(["foorole", "barrole"]))]
+) -> HTMLResponse:
+ assert user is not None # Just to keep QA checks happy
+ return HTMLResponse("Only users with foorole or barrole can see this
")
+
+
+# @resource_server.get("/introspect")
+# async def get_introspect(
+# request: Request,
+# oidc_provider: Annotated[StarletteOAuth2App, Depends(get_oidc_provider)],
+# token: Annotated[OAuth2Token, Depends(get_token)],
+# ) -> JSONResponse:
+# assert request is not None # Just to keep QA checks happy
+# if (url := oidc_provider.server_metadata.get("introspection_endpoint")) is None:
+# raise HTTPException(
+# status_code=status.HTTP_401_UNAUTHORIZED,
+# detail="No introspection endpoint found for the OIDC provider",
+# )
+# if (
+# response := await oidc_provider.post(
+# url,
+# token=token,
+# data={"token": token["access_token"]},
+# )
+# ).is_success:
+# return response.json()
+# else:
+# raise HTTPException(status_code=response.status_code, detail=response.text)
+
+
+@resource_server.get("/{id}")
+async def get_resource_(
+ id: str,
+ # user: Annotated[User, Depends(get_current_user)],
+ # oidc_provider: Annotated[StarletteOAuth2App, Depends(get_oidc_provider)],
+ # token: Annotated[OAuth2Token, Depends(get_token)],
+ user: Annotated[User, Depends(get_user_from_token)],
+) -> JSONResponse:
+ """Generic path for testing a resource provided by a provider"""
+ return JSONResponse(await get_resource(id, user))
+
async def get_resource(resource_id: str, user: User) -> dict:
"""
@@ -34,12 +146,10 @@ async def get_resource(resource_id: str, user: User) -> dict:
raise HTTPException(
status.HTTP_401_UNAUTHORIZED,
f"No scope {required_scope} in the access token "
- + "but it is required for accessing this resource.",
+ + "but it is required for accessing this resource",
)
except ExpiredSignatureError:
- raise HTTPException(
- status.HTTP_401_UNAUTHORIZED, "The token's signature has expired"
- )
+ raise HTTPException(status.HTTP_401_UNAUTHORIZED, "The token's signature has expired")
except InvalidTokenError:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "The token is invalid")
return resp
diff --git a/src/oidc_test/static/utils.js b/src/oidc_test/static/utils.js
index a982267..e6c4bfc 100644
--- a/src/oidc_test/static/utils.js
+++ b/src/oidc_test/static/utils.js
@@ -1,20 +1,28 @@
-function checkHref(elem) {
- var xmlHttp = new XMLHttpRequest()
- xmlHttp.onreadystatechange = function () {
- if (xmlHttp.readyState == 4) {
- elem.classList.add("hasResponseStatus")
- elem.classList.add("status-" + xmlHttp.status)
- elem.title = "Response code: " + xmlHttp.status + " - " + xmlHttp.statusText
- }
+async function checkHref(elem, token, authProvider) {
+ const msg = document.getElementById("msg")
+ const resp = await fetch(elem.href, {
+ headers: new Headers({
+ "Content-type": "application/json",
+ "Authorization": `Bearer ${token}`,
+ "auth_provider": authProvider,
+ }),
+ }).catch(err => {
+ msg.innerHTML = "Cannot fetch resource: " + err.message
+ resourceElem.innerHTML = ""
+ })
+ if (resp === undefined) {
+ return
+ } else {
+ elem.classList.add("hasResponseStatus")
+ elem.classList.add("status-" + resp.status)
+ elem.title = "Response code: " + resp.status + " - " + resp.statusText
}
- xmlHttp.open("GET", elem.href, true) // true for asynchronous
- xmlHttp.send(null)
}
-function checkPerms(className) {
+function checkPerms(className, token, authProvider) {
var rootElems = document.getElementsByClassName(className)
Array.from(rootElems).forEach(elem =>
- Array.from(elem.children).forEach(elem => checkHref(elem))
+ Array.from(elem.children).forEach(elem => checkHref(elem, token, authProvider))
)
}
diff --git a/src/oidc_test/templates/base.html b/src/oidc_test/templates/base.html
index 3bdb3f3..0fe1a6b 100644
--- a/src/oidc_test/templates/base.html
+++ b/src/oidc_test/templates/base.html
@@ -4,7 +4,7 @@
-
+
OIDC-test - FastAPI client
{% block content %}
{% endblock %}
diff --git a/src/oidc_test/templates/home.html b/src/oidc_test/templates/home.html
index ce344cc..09c313f 100644
--- a/src/oidc_test/templates/home.html
+++ b/src/oidc_test/templates/home.html
@@ -80,14 +80,14 @@
These links should get different response codes depending on the authorization:
Not protected
")
+async def public() -> dict:
+ return {"msg": "Not protected"}
@resource_server.get("/protected")
-async def get_protected(user: Annotated[User, Depends(get_user_from_token)]) -> HTMLResponse:
+async def get_protected(user: Annotated[User, Depends(get_user_from_token)]):
assert user is not None # Just to keep QA checks happy
- return HTMLResponse("Only authenticated users can see this
")
+ return {"msg": "Only authenticated users can see this"}
@resource_server.get("/protected-by-foorole")
async def get_protected_by_foorole(
- user: Annotated[User, Depends(UserWithRole("foorole"))]
-) -> HTMLResponse:
- return HTMLResponse("Only users with foorole can see this
")
+ user: Annotated[User, Depends(UserWithRole("foorole"))],
+):
+ assert user is not None
+ return {"msg": "Only users with foorole can see this"}
@resource_server.get("/protected-by-barrole")
async def get_protected_by_barrole(
- user: Annotated[User, Depends(UserWithRole("barrole"))]
-) -> HTMLResponse:
- return HTMLResponse("Protected by barrole
")
+ user: Annotated[User, Depends(UserWithRole("barrole"))],
+):
+ assert user is not None
+ return {"msg": "Protected by barrole"}
@resource_server.get("/protected-by-foorole-and-barrole")
async def get_protected_by_foorole_and_barrole(
user: Annotated[User, Depends(UserWithRole("foorole")), Depends(UserWithRole("barrole"))],
-) -> HTMLResponse:
+):
assert user is not None # Just to keep QA checks happy
- return HTMLResponse("Only users with foorole and barrole can see this
")
+ return {"msg": "Only users with foorole and barrole can see this"}
@resource_server.get("/protected-by-foorole-or-barrole")
async def get_protected_by_foorole_or_barrole(
- user: Annotated[User, Depends(UserWithRole(["foorole", "barrole"]))]
-) -> HTMLResponse:
+ user: Annotated[User, Depends(UserWithRole(["foorole", "barrole"]))],
+):
assert user is not None # Just to keep QA checks happy
- return HTMLResponse("Only users with foorole or barrole can see this
")
+ return {"msg": "Only users with foorole or barrole can see this"}
# @resource_server.get("/introspect")
@@ -118,9 +119,9 @@ async def get_resource_(
# oidc_provider: Annotated[StarletteOAuth2App, Depends(get_oidc_provider)],
# token: Annotated[OAuth2Token, Depends(get_token)],
user: Annotated[User, Depends(get_user_from_token)],
-) -> JSONResponse:
+):
"""Generic path for testing a resource provided by a provider"""
- return JSONResponse(await get_resource(id, user))
+ return await get_resource(id, user)
async def get_resource(resource_id: str, user: User) -> dict:
diff --git a/src/oidc_test/settings.py b/src/oidc_test/settings.py
index 2544bd7..b601739 100644
--- a/src/oidc_test/settings.py
+++ b/src/oidc_test/settings.py
@@ -43,9 +43,7 @@ class OIDCProvider(BaseModel):
info_url: str | None = (
None # Used eg. for Keycloak's public key (see https://stackoverflow.com/questions/54318633/getting-keycloaks-public-key)
)
- info: dict[str, str | int] | None = (
- None # Info fetched from info_url, eg. public key
- )
+ info: dict[str, str | int] | None = None # Info fetched from info_url, eg. public key
public_key: str | None = None
signature_alg: str = "RS256"
resource_provider_scopes: list[str] = []
@@ -62,25 +60,17 @@ class OIDCProvider(BaseModel):
def get_account_url(self, request: Request, user: User) -> str | None:
if self.account_url_template:
- if not (
- self.url.endswith("/") or self.account_url_template.startswith("/")
- ):
+ if not (self.url.endswith("/") or self.account_url_template.startswith("/")):
sep = "/"
else:
sep = ""
- return (
- self.url
- + sep
- + self.account_url_template.format(request=request, user=user)
- )
+ return self.url + sep + self.account_url_template.format(request=request, user=user)
else:
return None
def get_public_key(self) -> str:
"""Return the public key formatted for decoding token"""
- public_key = self.public_key or (
- self.info is not None and self.info["public_key"]
- )
+ public_key = self.public_key or (self.info is not None and self.info["public_key"])
if public_key is None:
raise AttributeError(f"Cannot get public key for {self.name}")
return f"""
@@ -91,17 +81,18 @@ class OIDCProvider(BaseModel):
def decode(self, token: str, verify_signature: bool = True) -> dict[str, Any]:
"""Decode the token with signature check"""
- decoded = decode(
- token,
- self.get_public_key(),
- algorithms=[self.signature_alg],
- audience=["account", "oidc-test", "oidc-test-web"],
- options={
- "verify_signature": False,
- "verify_aud": False,
- }, # not settings.insecure.skip_verify_signature},
- )
- logger.debug(str(decoded))
+ if settings.debug_token:
+ decoded = decode(
+ token,
+ self.get_public_key(),
+ algorithms=[self.signature_alg],
+ audience=["account", "oidc-test", "oidc-test-web"],
+ options={
+ "verify_signature": False,
+ "verify_aud": False,
+ }, # not settings.insecure.skip_verify_signature},
+ )
+ logger.debug(str(decoded))
return decode(
token,
self.get_public_key(),
@@ -143,6 +134,7 @@ class Settings(BaseSettings):
log: bool = False
insecure: Insecure = Insecure()
cors_origins: list[str] = []
+ debug_token: bool = False
@classmethod
def settings_customise_sources(
@@ -161,9 +153,7 @@ class Settings(BaseSettings):
settings_cls,
Path(
Path(
- environ.get(
- "OIDC_TEST_SETTINGS_FILE", Path.cwd() / "settings.yaml"
- ),
+ environ.get("OIDC_TEST_SETTINGS_FILE", Path.cwd() / "settings.yaml"),
)
),
),
diff --git a/src/oidc_test/static/styles.css b/src/oidc_test/static/styles.css
index 7e1260b..6262d79 100644
--- a/src/oidc_test/static/styles.css
+++ b/src/oidc_test/static/styles.css
@@ -171,11 +171,13 @@ hr {
gap: 0.5em;
flex-flow: wrap;
}
-.content .links-to-check a {
+.content .links-to-check button {
color: black;
padding: 5px 10px;
text-decoration: none;
border-radius: 8px;
+ border: none;
+ cursor: pointer;
}
.token {
@@ -183,12 +185,6 @@ hr {
font-family: monospace;
}
-.actions {
- display: flex;
- justify-content: center;
- gap: 0.5em;
-}
-
.resourceResult {
padding: 0.5em;
gap: 0.5em;
diff --git a/src/oidc_test/static/utils.js b/src/oidc_test/static/utils.js
index e6c4bfc..6ea8da2 100644
--- a/src/oidc_test/static/utils.js
+++ b/src/oidc_test/static/utils.js
@@ -1,6 +1,7 @@
async function checkHref(elem, token, authProvider) {
const msg = document.getElementById("msg")
- const resp = await fetch(elem.href, {
+ const url = `resource/${elem.getAttribute("resource-id")}`
+ const resp = await fetch(url, {
headers: new Headers({
"Content-type": "application/json",
"Authorization": `Bearer ${token}`,
diff --git a/src/oidc_test/templates/home.html b/src/oidc_test/templates/home.html
index 09c313f..92b7068 100644
--- a/src/oidc_test/templates/home.html
+++ b/src/oidc_test/templates/home.html
@@ -61,33 +61,30 @@
- {% if user %}
-
- {% endif %}
-
- {% for key, value in user_info_details.items() %}
-
+ id token
+ access token
+ refresh token
+ OIDC-test - FastAPI client
{% block content %}
{% endblock %}
diff --git a/src/oidc_test/templates/home.html b/src/oidc_test/templates/home.html
index 9da5392..08bcf43 100644
--- a/src/oidc_test/templates/home.html
+++ b/src/oidc_test/templates/home.html
@@ -57,6 +57,7 @@
Account management
{% endif %}
+
Resources validated by role:
Log in with:
{{ provider.name }}
@@ -32,7 +32,7 @@
{{ user.email }}
Provider:
- {{ oidc_provider_settings.name }}
+ {{ auth_provider.name }}
{% if user.roles %}
@@ -50,9 +50,9 @@
{% endfor %}
{% endif %}
- {% if oidc_provider_settings.account_url_template %}
+ {% if auth_provider.account_url_template %}
@@ -67,21 +67,21 @@
Resources validated by scope:
-
-
+
+
Resources validated by role:
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
From 496ce016e3d31fdb003146a3f84bc86726275e6d Mon Sep 17 00:00:00 2001
From: phil
- {% if settings.show_token and id_token_parsed %}
+ {% if show_token and id_token_parsed %}
-
- {% endif %}
- {% if user %}
+ Log in with: -
+
+ {% else %}
- Log in with: +
-
-
-
{% if resources %}
Resources for this provider:
{% for resource in resources %}
- {{ resource.name }}
+
{% endif %}
+
+
+
+
diff --git a/src/oidc_test/templates/non_compliant_logout.html b/src/oidc_test/templates/non_compliant_logout.html
index 24a96ae..56758de 100644
--- a/src/oidc_test/templates/non_compliant_logout.html
+++ b/src/oidc_test/templates/non_compliant_logout.html
@@ -6,12 +6,12 @@
authorisation to log in again without asking for credentials.
- This is because {{ oidc_provider.name }} does not provide "end_session_endpoint" in its metadata - (see: {{ oidc_provider._server_metadata_url }}). + This is because {{ auth_provider.name }} does not provide "end_session_endpoint" in its metadata + (see: {{ auth_provider.authlib_client._server_metadata_url }}). You can just also go back to the application home page, but - it recommended to go to the OIDC provider's site + it recommended to go to the OIDC provider's site and log out explicitely from there. {% endblock %} From e56be3c378e10e4ac972a3901021226ed26c7c1f Mon Sep 17 00:00:00 2001 From: phil
-
{% endif %}
From 381ce1ebc175d899cca49de14b8b7b6a6b263866 Mon Sep 17 00:00:00 2001
From: phil - Resources validated by scope: - -
-
-
-
Resources validated by role:
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {% if resource_providers %}
+ + Resource providers (validated by scope): + +
+ {% for name, resource_provider in resource_providers.items() %}
+ {% if resource_provider.default_resource_id %}
+
{% endif %}
From 0464047f8a6d17590f06fc7c52c4b1d4e007b2ce Mon Sep 17 00:00:00 2001
From: phil
- |
{{ provider.name }}
+ {{ provider.hint }}
|
@@ -62,42 +66,17 @@
{% endif %}
- - Resources validated by role: - -
-
-
{% if resource_providers %}
- Resource providers (validated by scope): + Resource providers:
{% for name, resource_provider in resource_providers.items() %}
- {% if resource_provider.default_resource_id %}
-
{% endif %}
From 5bd4b8280427d4a070a724cee141ee52ed3664a4 Mon Sep 17 00:00:00 2001
From: phil
{% if resource_providers %}
- Resource providers: + {{ auth_provider.name }} provides these resources:
{% for name, resource_provider in resource_providers.items() %}
{% if resource_provider.default_resource_id %}
-
From c89ca4098b2165014890af89caebde7310b88db0 Mon Sep 17 00:00:00 2001
From: phil
- {% if resource_providers %}
+
+ {% if resources %}
{{ auth_provider.name }} provides these resources:
- {% for name, resource_provider in resource_providers.items() %}
- {% if resource_provider.default_resource_id %}
-
{% endif %}
+ {% if resource_providers %}
+ {{ auth_provider.name }} can request resources from third party resource providers: + {% for resource_provider in resource_providers %} +
+ {{ resource_provider.name }}
+ {% for resource in resource_provider.resources %}
+
+ {% endfor %}
+ {% endif %}
From e925f2176258d4f73b5b7e565123652da02d6b12 Mon Sep 17 00:00:00 2001
From: phil - {{ auth_provider.name }} provides these resources: + This application provides all these resources, eventually protected with roles:
{% for name, resource in resources.items() %}
@@ -92,7 +92,7 @@
{% endif %}
{% if resource_providers %}
- {{ auth_provider.name }} can request resources from third party resource providers: +{{ auth_provider.name }} allows this applicaiton to request resources from third party resource providers: {% for resource_provider in resource_providers %}
{{ resource_provider.name }}
From ecdd3702f85c04e62741fa15910e25f425d5c20d Mon Sep 17 00:00:00 2001
From: phil - This application provides all these resources, eventually protected with roles: + This application provides all these resources, eventually protected with scope or roles:
{% for name, resource in resources.items() %}
From 347c3953943a1e0b7d84d5555727fae2f1d104be Mon Sep 17 00:00:00 2001
From: phil
-
{% 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:
{% for name, resource in resources.items() %}
{% if resource.default_resource_id %}
@@ -91,8 +87,29 @@
{% endfor %}
{% endif %}
+ {% if auth_provider.resources %}
+ {{ auth_provider.name }} is also defined as a provider for these resources: +
+ {% for resource in auth_provider.resources %}
+ {% if resource.default_resource_id %}
+
+ {% 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 %}
{{ resource_provider.name }}
From 4c2b197850a1e4e16f7b4d690eeb0ecd123422c5 Mon Sep 17 00:00:00 2001
From: phil v. {{ __version__}}
OIDC-test - FastAPI client{% block content %} {% endblock %} From b4653947660be16fbfee161fc4c52e2f4747f0f9 Mon Sep 17 00:00:00 2001 From: phil |