# Visione CIE Authentication Integration ## Goal Keep Visione backend simple. Visione does not implement OIDC Federation or CIE protocol details directly. `spid-cie-oidc-django` handles CIE login. Visione only manages: - local login `state` - user provisioning - external identity linking - local bearer token issuance ## Components - Visione backend: `POST /api/v1/auth/cie/start`, `POST /api/v1/auth/cie/exchange`, `GET /api/v1/auth/cie/session/{state}` - Django RP service: `examples/relying_party` - Frontend: browser redirect + polling ## End-to-End Flow 1. Frontend calls `POST /api/v1/auth/cie/start` with `frontend_redirect_uri`. 2. Visione stores row in `cie_auth_sessions` and returns `authorization_url`. 3. Frontend redirects browser to returned Django URL. 4. Django RP performs CIE login with provider. 5. On success Django extracts user attributes and sends signed JSON payload to Visione `POST /api/v1/auth/cie/exchange`. 6. Visione verifies HMAC signature, validates `state`, finds or creates user, stores `user_external_identities`, issues `api_tokens`, marks session authenticated. 7. Visione responds to Django with HTTP redirect to `frontend_redirect_uri?state=`. 8. Frontend reads `state` from query string and polls `GET /api/v1/auth/cie/session/{state}` until authenticated. 9. Frontend stores returned Visione bearer token and calls `GET /api/v1/me`. ## Why This Architecture - No OIDC/Federation crypto in C++ backend. - No CIE protocol tables spread across Visione domain code. - Django demo repo stays reusable and isolated. - Visione keeps final auth authority for application tokens and user model. ## Visione Backend Config `config.json`: ```json "cie_auth": { "service_base_url": "http://127.0.0.1:8001", "start_path": "/visione/start", "shared_secret": "change-me-for-local-dev", "state_ttl_minutes": 10, "default_frontend_redirect_uri": "http://localhost:3000/auth/cie/callback" } ``` Meaning: - `service_base_url`: Django RP base URL - `start_path`: Django entrypoint used by frontend redirect - `shared_secret`: HMAC secret shared only between Visione and Django - `state_ttl_minutes`: local login state lifetime - `default_frontend_redirect_uri`: fallback if frontend omits value ## Visione DB Additions - `cie_auth_sessions` stores local state, frontend redirect, final user id, final bearer token - `user_external_identities` stores `provider`, `issuer`, `subject`, `fiscal_code`, raw claims User matching strategy: 1. exact external identity on `provider + issuer + subject` 2. fallback reunification on `fiscal_code` 3. otherwise create new `citizen` ## Signed Exchange Contract Django sends `POST /api/v1/auth/cie/exchange` with JSON: ```json { "state": "visione-login-state", "issuer": "http://127.0.0.1:8002/oidc/op", "subject": "provider-subject", "fiscal_code": "RSSMRA80A01H501U", "first_name": "Mario", "last_name": "Rossi", "email": "mario.rossi@example.com" } ``` Headers: - `Content-Type: application/json` - `X-Visione-Signature: ` Signature algorithm: - payload bytes exactly as sent - key = `cie_auth.shared_secret` - HMAC-SHA256 - uppercase hex output ## Django Example Customization Modified files in `spid-cie-oidc-django/examples/relying_party`: - `relying_party/settingslocal.py.example` - `relying_party/urls.py` - `relying_party/visione_views.py` Added endpoints: - `GET /visione/start` stores Visione `state` in Django session, then redirects to `/oidc/rp/authorization` - `GET /visione/complete` reads `oidc_rp_user_attrs`, signs payload, calls Visione exchange endpoint, then redirects browser to frontend callback URL returned by Visione Important: - default OIDC callback remains managed by `spid_cie_oidc.relying_party` - `LOGIN_REDIRECT_URL` now points to `/visione/complete` - no need to modify RP metadata dump callback URI for this integration ## Frontend Responsibilities 1. Call: ```http POST /api/v1/auth/cie/start Content-Type: application/json ``` ```json { "frontend_redirect_uri": "http://localhost:3000/auth/cie/callback" } ``` 2. Redirect browser to `authorization_url`. 3. On frontend callback page, read `state` from query string. 4. Poll: ```http GET /api/v1/auth/cie/session/{state} ``` 5. Handle states: - `pending`: keep polling - `authenticated`: save `auth.token` - `failed`: show error and restart flow 6. After token, call `GET /api/v1/me`. Frontend does not need: - CIE secrets - Django shared secret - OIDC token parsing ## Local Dev Setup 1. Start Visione dependencies: ```bash docker compose up -d ``` 2. Run Visione: ```bash cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug cmake --build build ./build/visione-backend ``` 3. In separate repo checkout for `spid-cie-oidc-django`, configure: - federation authority example - provider example - relying party example with `VISIONE_BACKEND_EXCHANGE_URL` - same `VISIONE_SHARED_SECRET` as Visione `cie_auth.shared_secret` 4. Start Django RP on `http://127.0.0.1:8001`. ## Security Notes - Shared secret must never reach frontend. - `cie_auth.shared_secret` must change outside local dev. - Keep `state_ttl_minutes` short. - Visione bearer token stays out of query string; only `state` goes to frontend. - Django service is trusted bridge. Protect it as internal auth component. ## Future Improvements - Mark failed sessions explicitly when Django login fails - Rotate HMAC secret through environment variables - Add replay protection with timestamp/nonce in signed payload - Add integration tests between Django RP and Visione