From 8c5352103a99f462a0f1275c068f09642dc6697b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Rame=CC=81?= Date: Tue, 24 Mar 2026 15:02:19 +0100 Subject: [PATCH] WARNING TO BRAINSTORM ON PROPAGATING AUTH TO IFRAME DOMAIN (separate flow with its own OIDC clientId or using current token?) --- env.d/development/common | 4 +- src/backend/core/api/viewsets.py | 29 ++++++++++ .../vault/VaultClientProvider.tsx | 56 ++++++++++++++----- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/env.d/development/common b/env.d/development/common index fad2bac5..7b168f15 100644 --- a/env.d/development/common +++ b/env.d/development/common @@ -51,8 +51,8 @@ LOGOUT_REDIRECT_URL=http://localhost:3000 OIDC_REDIRECT_ALLOWED_HOSTS="localhost:8083,localhost:3000" OIDC_AUTH_REQUEST_EXTRA_PARAMS={"acr_values": "eidas1"} -# Store OIDC tokens in the session. Needed by search/ endpoint. -# OIDC_STORE_ACCESS_TOKEN = True +# Store OIDC tokens in the session. Needed by search/ endpoint and encryption service. +OIDC_STORE_ACCESS_TOKEN = True # OIDC_STORE_REFRESH_TOKEN = True # Store the encrypted refresh token in the session. # Must be a valid Fernet key (32 url-safe base64-encoded bytes) diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 6f5a0565..8592b035 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -252,6 +252,35 @@ class UserViewSet( self.serializer_class(request.user, context=context).data ) + @drf.decorators.action( + detail=False, + methods=["get"], + url_name="get-access-token", + url_path="get-access-token", + permission_classes=[permissions.IsAuthenticated], + ) + def get_access_token(self, request): + """ + WARNING: Temporary endpoint — to be removed. + + TODO: Find a better way to propagate the OIDC access token to the frontend + so it can be passed to the VaultClient encryption library. + We should also consider having ProConnect flow directly on interface.encryption, + so it can obtain its own valid token when opened in a new tab (not as an iframe). + + Returns the OIDC access token stored in the Django session. + Requires OIDC_STORE_ACCESS_TOKEN=True in settings. + """ + access_token = request.session.get("oidc_access_token") + + if not access_token: + return drf.response.Response( + {"detail": "No access token available in session."}, + status=404, + ) + + return drf.response.Response({"access_token": access_token}) + class ResourceAccessViewsetMixin: """Mixin with methods common to all access viewsets.""" diff --git a/src/frontend/apps/impress/src/features/docs/doc-collaboration/vault/VaultClientProvider.tsx b/src/frontend/apps/impress/src/features/docs/doc-collaboration/vault/VaultClientProvider.tsx index 80f6251a..18e6e07b 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-collaboration/vault/VaultClientProvider.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-collaboration/vault/VaultClientProvider.tsx @@ -193,19 +193,39 @@ export function VaultClientProvider({ return; } - // In Docs, auth is cookie-based. The vault in dev mode (no VITE_JWKS_URL) - // falls back to the declared userId. In production, pass a real JWT. - // TODO: Pass real ProConnect JWT when available. - client.setAuthContext({ - token: 'session-cookie-auth', - userId: user.id, - }); + let cancelled = false; - setIsLoading(true); + async function setupAuth() { + // Fetch the OIDC access token from the Django session. + // WARNING: This uses a temporary endpoint (get-access-token) — see the + // backend TODO for finding a better way to propagate the access token. + let token = user!.id; // fallback: userId (vault dev mode without JWKS) - client - .hasKeys() - .then(async ({ hasKeys: exists }) => { + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_ORIGIN || ''}/api/v1.0/users/get-access-token/`, + { credentials: 'include' }, + ); + + if (response.ok) { + const data = await response.json(); + + if (data.access_token) { + token = data.access_token; + } + } + } catch { + // Endpoint not available — fall back to userId for dev mode + } + + if (cancelled) return; + + client.setAuthContext({ token, userId: user!.id }); + + setIsLoading(true); + + try { + const { hasKeys: exists } = await client.hasKeys(); setHasKeys(exists); if (exists) { @@ -214,12 +234,18 @@ export function VaultClientProvider({ } setIsReady(true); - setIsLoading(false); - }) - .catch((err) => { + } catch (err) { setError((err as Error).message); + } finally { setIsLoading(false); - }); + } + } + + void setupAuth(); + + return () => { + cancelled = true; + }; }, [clientInitialized, authenticated, user?.id]); const refreshKeyState = useCallback(async () => {