Files
backend/docs/OIDC_CIE_IMPLEMENTATION_PLAN.md
T

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/provider
    • spid-cie-oidc-django/spid_cie_oidc/provider
    • spid-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.py
    • spid-cie-oidc-django/spid_cie_oidc/relying_party/views/rp_callback.py

Current Status

  • common/OidcJwt.* now owns JWT/JWS/JWK work through jwt-cpp
  • common/OidcCrypto.* isolates OpenSSL-only JWE compact parsing and decrypt
  • POST /api/v1/auth/cie/start signs the request object with RP signing key
  • callback exchanges code with private_key_jwt, verifies token JWS and at_hash
  • userinfo GET 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/start
    • GET /api/v1/auth/cie/callback
    • GET /api/v1/auth/cie/session/{state}
    • GET /oidc/rp/.well-known/openid-federation
    • GET /oidc/rp/resolve
    • GET /oidc/rp/jwks.json

Residual Backlog

Phase 1: Dependencies and crypto plumbing

  • Done for demo scope:
    • jwt-cpp handles 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
  • 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 / iat skew checks still thin
    • no refresh-token lifecycle use yet
  • 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
    • auth payload only on success

Phase 8: Tests and docs

  • Unit tests added for:
    • JWT parse/sign/verify
    • kid lookup
    • at_hash helper
    • 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_tokens after OIDC success
  • Use fiscal_code as 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.json aligned 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

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_url present
  • state present
  • one row in oidc_authentications with phase AUTHORIZATION_REDIRECT_READY

Complete browser flow

  1. Open returned authorization_url.
  2. Authenticate with the demo CIE user configured by the Django fixtures.
  3. Accept consent if requested.
  4. Provider redirects to backend callback.
  5. 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.token present

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 URL
  • token_exchange_failed: provider callback reached but token endpoint unavailable or RP assertion invalid
  • invalid_userinfo_response: JWE decryption key mismatch or unexpected provider response format
  • at_hash_mismatch: provider tokens not aligned with received access_token