7.1 KiB
7.1 KiB
Visione CIE OIDC RP Technical Plan
Goal
Implement Visione backend as SPID/CIE OIDC Federation Relying Party compatible
with local demo provider contained in spid-cie-oidc-django/examples/provider.
Scope target for first real implementation:
- compatibility with demo provider only
- CIE profile only
- Visione backend remains source of truth for application auth
- frontend drives browser redirect flow
Local Reference Mapping
- Demo OP/federation behavior source:
spid-cie-oidc-django/examples/providerspid-cie-oidc-django/spid_cie_oidc/providerspid-cie-oidc-django/spid_cie_oidc/entity
- Demo RP behavior to emulate in Drogon:
spid-cie-oidc-django/spid_cie_oidc/relying_party/views/rp_begin.pyspid-cie-oidc-django/spid_cie_oidc/relying_party/views/rp_callback.py
Current Status
common/OidcJwt.*now owns JWT/JWS/JWK work throughjwt-cppcommon/OidcCrypto.*isolates OpenSSL-only JWE compact parsing and decryptPOST /api/v1/auth/cie/startsigns the request object with RP signing key- callback exchanges code with
private_key_jwt, verifies token JWS andat_hash userinfoGET supports plain JSON and demo-compatible nested JWE -> JWS- merged OIDC claims persist through
user_external_identities - frontend polling contract is active through
GET /api/v1/auth/cie/session/{state}
Foundation Added In This Step
- config placeholders in
config.json - DB schema for OIDC auth sessions, tokens, federation cache, external identities
- Drogon controller skeleton in
controllers/OidcController.* - service skeleton in
services/OidcRpService.* - RP config helper in
common/OidcRpConfig.* - RP public endpoints reserved:
POST /api/v1/auth/cie/startGET /api/v1/auth/cie/callbackGET /api/v1/auth/cie/session/{state}GET /oidc/rp/.well-known/openid-federationGET /oidc/rp/resolveGET /oidc/rp/jwks.json
Residual Backlog
Phase 1: Dependencies and crypto plumbing
- Done for demo scope:
jwt-cpphandles JWT parsing, signing and signature verification- custom OpenSSL code remains only for JWE decryption in
OidcCrypto - focused unit tests cover JWT helpers and both supported JWE CBC/HMAC modes
Phase 2: RP metadata and JWKS
- Done for demo flow.
- Residual gap:
- verify metadata fields against non-demo providers before reuse
Phase 3: Federation trust resolution
- Current cut:
- fetches trust-anchor
resolve - extracts OP metadata/JWKS/endpoints
- fetches trust-anchor
- Not done:
- full trust-chain signature validation
- federation cache population/reuse
- multi-provider or multi-anchor logic
Phase 4: Authorization start
- Done.
- Demo scope now sent explicitly as
openid cie_demo.
Phase 5: Callback and token exchange
- Done for demo provider.
- Residual gap:
- explicit
exp/iatskew checks still thin - no refresh-token lifecycle use yet
- explicit
Phase 6: UserInfo and local account link
- Done for demo provider, including JWE decrypt + JWS verify + merged claims.
Phase 7: Frontend completion contract
- Done.
- Session payload now exposes:
- public
status - internal
phase authpayload only on success
- public
Phase 8: Tests and docs
- Unit tests added for:
- JWT parse/sign/verify
kidlookupat_hashhelper- JWE decrypt for
A256CBC-HS512 - JWE decrypt for
A128CBC-HS256 - invalid JWE tag / unsupported enc
- merged claim precedence and fiscal code normalization
- Still missing:
- automated integration tests against live demo Django OP
Data Model Notes
oidc_authentications
Stores single login transaction state and provider snapshot.
oidc_authentication_tokens
Stores authorization code and received tokens.
oidc_federation_cache
Stores fetched federation docs with expiry.
user_external_identities
Decouples local user from external subject identifiers.
Current Decisions
- Keep OTP auth alive in parallel
- Use local
api_tokensafter OIDC success - Use
fiscal_codeas strongest user reunification key - Do not expose Visione token in query string
- Prefer external validated libs over handwritten JWT/JWK code
Ready Criteria For Real Implementation
- foundation schema merged
- config keys finalized
- dependency choice finalized
- endpoint shapes stable enough for frontend
When these are acceptable, next step is real implementation of Phase 1-4.
Demo CIE Runbook
Prerequisites
- MySQL and Mailpit running locally through this repo
docker compose - RP keys available in
secrets/oidc/demo-rp-jwks.json - backend
config.jsonaligned with:- trust anchor
http://127.0.0.1:8000 - provider issuer
http://127.0.0.1:8002/oidc/op - callback
http://127.0.0.1:5555/api/v1/auth/cie/callback
- trust anchor
Bootstrap local dependencies
docker compose up -d
cmake -S . -B build_local -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build_local
Bootstrap demo federation authority
cd spid-cie-oidc-django/examples/federation_authority
cp federation_authority/settingslocal.py.example federation_authority/settingslocal.py
python3 -m pip install --upgrade pip
python3 -m pip install -e ../..
python3 -m pip install "design-django-theme==v1.4.8"
./manage.py migrate
./manage.py loaddata dumps/example.json
./manage.py runserver 0.0.0.0:8000
Bootstrap demo provider
cd spid-cie-oidc-django/examples/provider
cp provider/settingslocal.py.example provider/settingslocal.py
./manage.py migrate
./manage.py loaddata dumps/example.json
./manage.py runserver 0.0.0.0:8002
Start Visione backend
./build_local/visione-backend
Start login
curl -sS -X POST http://127.0.0.1:5555/api/v1/auth/cie/start \
-H 'Content-Type: application/json' \
-d '{"provider":"cie_demo","frontend_redirect_uri":"http://localhost:3000/auth/cie/callback"}'
Expected result:
authorization_urlpresentstatepresent- one row in
oidc_authenticationswith phaseAUTHORIZATION_REDIRECT_READY
Complete browser flow
- Open returned
authorization_url. - Authenticate with the demo CIE user configured by the Django fixtures.
- Accept consent if requested.
- Provider redirects to backend callback.
- Backend redirects frontend to
frontend_redirect_uri?state=....
Poll login result
curl -sS http://127.0.0.1:5555/api/v1/auth/cie/session/<state>
Expected success payload:
status = "authenticated"phase = "AUTHENTICATED"auth.tokenpresent
Minimal DB verification
SELECT state,status,authenticated_user_id FROM oidc_authentications ORDER BY id DESC LIMIT 3;
SELECT authentication_id,token_type,scope FROM oidc_authentication_tokens ORDER BY id DESC LIMIT 3;
SELECT user_id,provider,issuer,subject,fiscal_code FROM user_external_identities ORDER BY id DESC LIMIT 3;
SELECT user_id,expires_at FROM api_tokens ORDER BY id DESC LIMIT 3;
Common failures
provider_metadata_missing: federation authority not started or wrong trust anchor URLtoken_exchange_failed: provider callback reached but token endpoint unavailable or RP assertion invalidinvalid_userinfo_response: JWE decryption key mismatch or unexpected provider response formatat_hash_mismatch: provider tokens not aligned with receivedaccess_token