183 lines
5.5 KiB
Markdown
183 lines
5.5 KiB
Markdown
# 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=<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": "[email protected]"
|
|
}
|
|
```
|
|
|
|
Headers:
|
|
- `Content-Type: application/json`
|
|
- `X-Visione-Signature: <UPPERCASE_HEX_HMAC_SHA256>`
|
|
|
|
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
|