feat: refractor CIE auth stack configuration for improved environment variable management and docker compatibility

This commit is contained in:
gabdevele
2026-06-17 18:08:25 +02:00 Unverified
parent 69e71ec601
commit e1a009047c
7 changed files with 136 additions and 95 deletions
+8
View File
@@ -0,0 +1,8 @@
CIE_AUTH_ALLOWED_HOSTS=cie-auth.example.it,auth.example.it
DJANGO_TRUST_ANCHOR=https://federation-authority.example.it
DJANGO_PROVIDER_ISSUER=https://provider.example.it/oidc/op
DJANGO_RP_BACKEND_EXCHANGE_URL=https://visione-backend.example.it/api/v1/auth/cie/exchange
DJANGO_RP_SHARED_SECRET=change-me
FEDERATION_AUTHORITY_SECRET_KEY=change-me
PROVIDER_SECRET_KEY=change-me
RELYING_PARTY_SECRET_KEY=change-me
+41 -69
View File
@@ -1,60 +1,58 @@
# Visione Local CIE Stack
# Visione CIE Auth Stack
This folder is a ready-to-run local stack for CIE auth integration with Visione.
Included services:
This folder contains deployable CIE auth services for Visione:
- `federation-authority`
- `provider`
- `relying-party`
They run through one compose file and use the modified relying party bridge for Visione.
It is now structured to work with Docker Compose and Dokploy without host networking.
## Prerequisites
- Visione backend running on `http://127.0.0.1:5555`
- Docker with Compose
## Configuration
Copy [.env.example](/workspace/cie-auth/.env.example) to `.env` and set only what changes in real deploy:
- public domains / internal URLs
- Django secret keys
- shared secret between relying party and Visione backend
## Visione config
Set in `config.json`:
Main variables:
- `CIE_AUTH_ALLOWED_HOSTS`
- `DJANGO_TRUST_ANCHOR`
- `DJANGO_PROVIDER_ISSUER`
- `DJANGO_RP_BACKEND_EXCHANGE_URL`
- `DJANGO_RP_SHARED_SECRET`
- `FEDERATION_AUTHORITY_SECRET_KEY`
- `PROVIDER_SECRET_KEY`
- `RELYING_PARTY_SECRET_KEY`
```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"
}
```
## Deploy with Dokploy
Use [docker-compose.yml](/workspace/cie-auth/docker-compose.yml) as compose source.
Expose:
- `federation-authority` on port `8000`
- `provider` on port `8002`
- `relying-party` on port `8001`
Keep same secret also in:
- `relying_party/relying_party/settingslocal.py`
`VISIONE_SHARED_SECRET`
Each service persists its own SQLite DB in a named volume.
## Start stack
From repo root:
## Local start
```bash
docker compose up -d
docker compose -f infra/cie-auth/docker-compose.yml up --build -d
docker compose up --build -d
```
This starts:
- MySQL + Mailpit from root compose
- authority on `127.0.0.1:8000`
- provider on `127.0.0.1:8002`
- relying party on `127.0.0.1:8001`
## Visione backend
Visione backend must expose exchange endpoint configured in `DJANGO_RP_BACKEND_EXCHANGE_URL`.
Shared secret must match backend `cie_auth.shared_secret`.
## Start Visione
From repo root:
## Bootstrap behavior
On first boot, each Django service:
1. runs migrations
2. loads `dumps/example.json` only if DB is empty
```bash
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build
./build/visione-backend
```
Disable fixture import per service with:
- `FEDERATION_AUTHORITY_LOAD_FIXTURE_ON_EMPTY_DB=false`
- `PROVIDER_LOAD_FIXTURE_ON_EMPTY_DB=false`
- `RELYING_PARTY_LOAD_FIXTURE_ON_EMPTY_DB=false`
## Smoke test
Start login:
```bash
curl -sS -X POST http://127.0.0.1:5555/api/v1/auth/cie/start \
@@ -62,45 +60,19 @@ curl -sS -X POST http://127.0.0.1:5555/api/v1/auth/cie/start \
-d '{"frontend_redirect_uri":"http://localhost:3000/auth/cie/callback"}'
```
Expected JSON:
- `authorization_url`
- `state`
Then:
1. open `authorization_url` in browser
2. complete demo CIE login
3. browser lands on `http://localhost:3000/auth/cie/callback?state=...`
Poll result:
Then complete login and poll:
```bash
curl -sS http://127.0.0.1:5555/api/v1/auth/cie/session/<state>
```
Expected success:
- `status = "authenticated"`
- `auth.token` present
## Frontend flow
1. call `POST /api/v1/auth/cie/start`
2. redirect browser to `authorization_url`
3. callback page reads `state`
4. poll `GET /api/v1/auth/cie/session/{state}`
5. when authenticated, save `auth.token`
6. call `GET /api/v1/me`
## Reset local auth DBs
If you want a clean auth stack:
## Reset data
```bash
rm -f infra/cie-auth/federation_authority/db.sqlite3
rm -f infra/cie-auth/provider/db.sqlite3
rm -f infra/cie-auth/relying_party/db.sqlite3
docker compose -f infra/cie-auth/docker-compose.yml down
docker compose -f infra/cie-auth/docker-compose.yml up --build -d
docker compose down -v
```
## Logs
```bash
docker compose -f infra/cie-auth/docker-compose.yml logs -f
docker compose logs -f
```
+37 -3
View File
@@ -3,33 +3,67 @@ services:
build:
context: .
dockerfile: Dockerfile
network_mode: host
working_dir: /app/federation_authority
command: ["/app/federation_authority", "8000"]
environment:
DJANGO_SECRET_KEY: ${FEDERATION_AUTHORITY_SECRET_KEY:-visione-cie-authority-local-dev}
DJANGO_ALLOWED_HOSTS: ${CIE_AUTH_ALLOWED_HOSTS:-*}
DJANGO_TRUST_ANCHOR: ${DJANGO_TRUST_ANCHOR:-http://federation-authority:8000}
LOAD_FIXTURE_ON_EMPTY_DB: ${FEDERATION_AUTHORITY_LOAD_FIXTURE_ON_EMPTY_DB:-true}
ports:
- "${FEDERATION_AUTHORITY_PORT:-8000}:8000"
volumes:
- ./federation_authority:/app/federation_authority
- federation-authority-data:/app/federation_authority/db
restart: unless-stopped
provider:
build:
context: .
dockerfile: Dockerfile
network_mode: host
working_dir: /app/provider
command: ["/app/provider", "8002"]
environment:
DJANGO_SECRET_KEY: ${PROVIDER_SECRET_KEY:-visione-cie-provider-local-dev}
DJANGO_ALLOWED_HOSTS: ${CIE_AUTH_ALLOWED_HOSTS:-*}
DJANGO_TRUST_ANCHOR: ${DJANGO_TRUST_ANCHOR:-http://federation-authority:8000}
DJANGO_PROVIDER_ISSUER: ${DJANGO_PROVIDER_ISSUER:-http://provider:8002/oidc/op}
LOAD_FIXTURE_ON_EMPTY_DB: ${PROVIDER_LOAD_FIXTURE_ON_EMPTY_DB:-true}
ports:
- "${PROVIDER_PORT:-8002}:8002"
volumes:
- ./provider:/app/provider
- provider-data:/app/provider/db
depends_on:
- federation-authority
restart: unless-stopped
relying-party:
build:
context: .
dockerfile: Dockerfile
network_mode: host
working_dir: /app/relying_party
command: ["/app/relying_party", "8001"]
environment:
DJANGO_SECRET_KEY: ${RELYING_PARTY_SECRET_KEY:-visione-cie-rp-local-dev}
DJANGO_ALLOWED_HOSTS: ${CIE_AUTH_ALLOWED_HOSTS:-*}
DJANGO_TRUST_ANCHOR: ${DJANGO_TRUST_ANCHOR:-http://federation-authority:8000}
DJANGO_PROVIDER_ISSUER: ${DJANGO_PROVIDER_ISSUER:-http://provider:8002/oidc/op}
DJANGO_RP_BACKEND_EXCHANGE_URL: ${DJANGO_RP_BACKEND_EXCHANGE_URL:-http://visione-backend:5555/api/v1/auth/cie/exchange}
DJANGO_RP_SHARED_SECRET: ${DJANGO_RP_SHARED_SECRET:-change-me-for-local-dev}
DJANGO_RP_DEFAULT_PROVIDER: ${DJANGO_RP_DEFAULT_PROVIDER:-http://provider:8002/oidc/op}
LOAD_FIXTURE_ON_EMPTY_DB: ${RELYING_PARTY_LOAD_FIXTURE_ON_EMPTY_DB:-true}
ports:
- "${RELYING_PARTY_PORT:-8001}:8001"
volumes:
- ./relying_party:/app/relying_party
- relying-party-data:/app/relying_party/db
depends_on:
- federation-authority
- provider
restart: unless-stopped
volumes:
federation-authority-data:
provider-data:
relying-party-data:
+1 -1
View File
@@ -8,7 +8,7 @@ cd "$APP_DIR"
mkdir -p logs static
if [ ! -f db.sqlite3 ]; then
if [ ! -f "$APP_DIR/db.sqlite3" ] && [ "${LOAD_FIXTURE_ON_EMPTY_DB:-true}" = "true" ]; then
python manage.py migrate
python manage.py loaddata dumps/example.json
else
@@ -3,12 +3,18 @@ import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'visione-cie-authority-local-dev'
DEBUG = True
def env_bool(name, default=False):
value = os.getenv(name)
if value is None:
return default
return value.lower() in {"1", "true", "yes", "on"}
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'visione-cie-authority-local-dev')
DEBUG = env_bool('DJANGO_DEBUG', False)
ADMIN_PATH = 'admin/'
APPEND_SLASH = False
OIDCFED_DEFAULT_TRUST_ANCHOR = "http://127.0.0.1:8000"
OIDCFED_DEFAULT_TRUST_ANCHOR = os.getenv("DJANGO_TRUST_ANCHOR", "http://127.0.0.1:8000")
OIDCFED_TRUST_ANCHORS = [OIDCFED_DEFAULT_TRUST_ANCHOR]
OIDCFED_PROVIDER_PROFILE = "spid"
OIDCFED_PROVIDER_MAX_REFRESH = 10
@@ -28,7 +34,7 @@ LOGIN_REDIRECT_URL = "/oidc/rp/echo_attributes"
LOGOUT_REDIRECT_URL = "/oidc/rp/landing"
LOGIN_URL = "/oidc/rp/landing"
ALLOWED_HOSTS = ['*']
ALLOWED_HOSTS = [item.strip() for item in os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',') if item.strip()]
AUTH_USER_MODEL = 'spid_cie_oidc_accounts.User'
INSTALLED_APPS = [
@@ -85,7 +91,7 @@ LOGGING = {
'handlers': {
'console': {
'formatter': 'default',
'level': 'DEBUG',
'level': 'INFO',
'class': 'logging.StreamHandler',
},
},
+16 -7
View File
@@ -3,23 +3,32 @@ import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'visione-cie-provider-local-dev'
DEBUG = True
def env_bool(name, default=False):
value = os.getenv(name)
if value is None:
return default
return value.lower() in {"1", "true", "yes", "on"}
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'visione-cie-provider-local-dev')
DEBUG = env_bool('DJANGO_DEBUG', False)
ADMIN_PATH = 'admin/'
APPEND_SLASH = False
OIDCFED_DEFAULT_TRUST_ANCHOR = "http://127.0.0.1:8000"
OIDCFED_DEFAULT_TRUST_ANCHOR = os.getenv("DJANGO_TRUST_ANCHOR", "http://127.0.0.1:8000")
OIDCFED_TRUST_ANCHORS = [OIDCFED_DEFAULT_TRUST_ANCHOR]
OIDCFED_PROVIDER_PROFILE = "cie"
OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME = 3600
OIDCFED_REQUIRED_TRUST_MARKS = []
provider_issuer = os.getenv("DJANGO_PROVIDER_ISSUER", "http://127.0.0.1:8002/oidc/op")
OIDCFED_IDENTITY_PROVIDERS = {
"spid": {
"http://127.0.0.1:8000/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR,
f"{OIDCFED_DEFAULT_TRUST_ANCHOR}/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR,
},
"cie": {
"http://127.0.0.1:8002/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR,
provider_issuer: OIDCFED_DEFAULT_TRUST_ANCHOR,
},
}
@@ -32,7 +41,7 @@ LOGIN_REDIRECT_URL = "/oidc/op/landing"
LOGOUT_REDIRECT_URL = "/oidc/op/landing"
LOGIN_URL = "/oidc/op/landing"
ALLOWED_HOSTS = ['*']
ALLOWED_HOSTS = [item.strip() for item in os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',') if item.strip()]
AUTH_USER_MODEL = 'spid_cie_oidc_accounts.User'
INSTALLED_APPS = [
@@ -86,7 +95,7 @@ LOGGING = {
'handlers': {
'console': {
'formatter': 'default',
'level': 'DEBUG',
'level': 'INFO',
'class': 'logging.StreamHandler',
},
},
+22 -10
View File
@@ -3,21 +3,30 @@ import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'visione-cie-rp-local-dev'
DEBUG = True
def env_bool(name, default=False):
value = os.getenv(name)
if value is None:
return default
return value.lower() in {"1", "true", "yes", "on"}
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'visione-cie-rp-local-dev')
DEBUG = env_bool('DJANGO_DEBUG', False)
ADMIN_PATH = 'admin/'
APPEND_SLASH = False
OIDCFED_DEFAULT_TRUST_ANCHOR = "http://127.0.0.1:8000"
OIDCFED_DEFAULT_TRUST_ANCHOR = os.getenv("DJANGO_TRUST_ANCHOR", "http://127.0.0.1:8000")
OIDCFED_TRUST_ANCHORS = [OIDCFED_DEFAULT_TRUST_ANCHOR]
OIDCFED_REQUIRED_TRUST_MARKS = []
provider_issuer = os.getenv("DJANGO_PROVIDER_ISSUER", "http://127.0.0.1:8002/oidc/op")
OIDCFED_IDENTITY_PROVIDERS = {
"spid": {
"http://127.0.0.1:8000/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR,
f"{OIDCFED_DEFAULT_TRUST_ANCHOR}/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR,
},
"cie": {
"http://127.0.0.1:8002/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR,
provider_issuer: OIDCFED_DEFAULT_TRUST_ANCHOR,
},
}
@@ -30,12 +39,15 @@ LOGIN_REDIRECT_URL = "/visione/complete"
LOGOUT_REDIRECT_URL = "/oidc/rp/landing"
LOGIN_URL = "/oidc/rp/landing"
VISIONE_BACKEND_EXCHANGE_URL = "http://127.0.0.1:5555/api/v1/auth/cie/exchange"
VISIONE_SHARED_SECRET = "change-me-for-local-dev"
VISIONE_DEFAULT_PROVIDER = "http://127.0.0.1:8002/oidc/op"
VISIONE_BACKEND_EXCHANGE_URL = os.getenv(
"DJANGO_RP_BACKEND_EXCHANGE_URL",
"http://127.0.0.1:5555/api/v1/auth/cie/exchange"
)
VISIONE_SHARED_SECRET = os.getenv("DJANGO_RP_SHARED_SECRET", "change-me-for-local-dev")
VISIONE_DEFAULT_PROVIDER = os.getenv("DJANGO_RP_DEFAULT_PROVIDER", provider_issuer)
VISIONE_DEFAULT_PROFILE = "cie"
ALLOWED_HOSTS = ['*']
ALLOWED_HOSTS = [item.strip() for item in os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',') if item.strip()]
AUTH_USER_MODEL = 'spid_cie_oidc_accounts.User'
INSTALLED_APPS = [
@@ -89,7 +101,7 @@ LOGGING = {
'handlers': {
'console': {
'formatter': 'default',
'level': 'DEBUG',
'level': 'INFO',
'class': 'logging.StreamHandler',
},
},