Autenticazione SSO e OAuth
Configura l'accesso con Google, GitHub, HuggingFace e OIDC generico in Potato. Limita l'accesso per dominio email o organizzazione GitHub e usa l'SSO insieme all'accesso locale con password.
Novità in v2.3.0
Per impostazione predefinita, Potato usa un semplice accesso basato sul nome utente, in cui gli annotatori digitano un nome utente qualsiasi per entrare. Questo va bene per lo sviluppo locale e i piccoli progetti, ma i deployment in produzione hanno bisogno di un'autenticazione vera e propria per impedire accessi non autorizzati, confermare l'identità degli annotatori e soddisfare i requisiti istituzionali.
L'SSO si configura in un unico punto. Imposta authentication.method su oauth, poi elenca uno o più provider sotto authentication.providers. Ogni provider ha il proprio pulsante nella pagina di accesso.
Potato include i metadati degli endpoint per tre provider: google, github e huggingface. Qualsiasi altro nome di provider viene trattato come OIDC generico e richiede una discovery_url.
Avvio rapido
Una configurazione minima per Google:
authentication:
method: "oauth"
providers:
google:
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
user_identity_field: "email"
secret_key: ${POTATO_SECRET_KEY}Imposta i segreti nell'ambiente anziché nel file:
export GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="your-client-secret"
export POTATO_SECRET_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"secret_key è una chiave di primo livello e non fa parte del blocco authentication. Impostala, oppure imposta POTATO_SECRET_KEY. Se mancano entrambe, Potato genera una chiave casuale, il che significa che ogni riavvio invalida tutte le sessioni, e registra un avviso quando OAuth è configurato senza di essa.
Potato espone due route OAuth. /auth/login/<provider> avvia il flusso e /auth/callback/<provider> riceve la risposta. Il callback è l'URL che registri presso il provider, quindi per Google è https://your-domain.com/auth/callback/google. Non esiste una chiave di configurazione redirect_uri; la route deriva dal nome del provider.
Crea credenziali OAuth 2.0 di tipo "Web application" nella Google Cloud Console e aggiungi https://your-domain.com/auth/callback/google come URI di reindirizzamento autorizzato. Per lo sviluppo locale è accettato http://localhost:8000/auth/callback/google.
authentication:
method: "oauth"
providers:
google:
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
allowed_domain: "umich.edu"allowed_domain accetta una singola stringa di dominio. Possono accedere solo gli account la cui email appartiene a quel dominio.
GitHub
Registra una OAuth app in GitHub Settings > Developer settings > OAuth Apps, con l'URL di callback impostato su https://your-domain.com/auth/callback/github.
authentication:
method: "oauth"
providers:
github:
client_id: ${GITHUB_CLIENT_ID}
client_secret: ${GITHUB_CLIENT_SECRET}
allowed_org: "my-research-lab"allowed_org accetta un singolo nome di organizzazione. Quando è impostato, Potato richiede automaticamente lo scope read:org, quindi non devi elencare tu gli scope. L'annotatore deve avere un'appartenenza pubblica all'organizzazione perché il controllo la veda.
HuggingFace
Crea un'applicazione OAuth su huggingface.co/settings/applications.
authentication:
method: "oauth"
providers:
huggingface:
client_id: ${HF_CLIENT_ID}
client_secret: ${HF_CLIENT_SECRET}
user_identity_field: "username"Il provider HuggingFace si collega all'endpoint di discovery OIDC di HuggingFace e richiede openid profile email per impostazione predefinita.
OIDC generico
Qualsiasi nome di provider diverso da google, github e huggingface viene trattato come OIDC generico e richiede discovery_url. Assegnagli un display_name perché il pulsante di accesso sia leggibile.
authentication:
method: "oauth"
providers:
oidc:
display_name: "University SSO"
discovery_url: "https://sso.university.edu/.well-known/openid-configuration"
client_id: ${OIDC_CLIENT_ID}
client_secret: ${OIDC_CLIENT_SECRET}
scopes:
- "openid"
- "email"
- "profile"L'URL di callback da registrare presso il provider segue la chiave del provider, quindi un blocco chiamato oidc usa https://your-domain.com/auth/callback/oidc.
URL di discovery dei provider più comuni:
| Provider | URL di discovery |
|---|---|
| Okta | https://your-org.okta.com/.well-known/openid-configuration |
| Azure AD | https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration |
| Auth0 | https://your-domain.auth0.com/.well-known/openid-configuration |
| Keycloak | https://keycloak.example.com/realms/{realm}/.well-known/openid-configuration |
Il valore predefinito di scopes è ["openid", "email", "profile"].
Più provider
providers accoglie tutte le voci che servono, e ognuna mostra il proprio pulsante di accesso.
authentication:
method: "oauth"
providers:
google:
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
github:
client_id: ${GITHUB_CLIENT_ID}
client_secret: ${GITHUB_CLIENT_SECRET}
oidc:
display_name: "University SSO"
discovery_url: "https://sso.university.edu/.well-known/openid-configuration"
client_id: ${OIDC_CLIENT_ID}
client_secret: ${OIDC_CLIENT_SECRET}Con user_identity_field: "email", chi accede tramite Google e in seguito tramite il provider dell'università con lo stesso indirizzo è lo stesso utente Potato e ritrova le stesse assegnazioni.
Identità e registrazione automatica
user_identity_field stabilisce quale claim del provider diventa il nome utente Potato. Accetta email, username, sub o name, e il valore predefinito è email. Il valore conta perché le directory di output degli annotatori prendono il nome dall'utente, quindi cambiarlo a metà studio separa un annotatore dal lavoro svolto in precedenza.
auto_register controlla se il primo accesso riuscito crea l'account Potato. Il valore predefinito è true.
authentication:
method: "oauth"
providers:
google:
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
allowed_domain: "umich.edu"
user_identity_field: "email"
auto_register: trueIl controllo degli accessi passa dal provider e non da un elenco di indirizzi nella configurazione. Per limitare chi può accedere, usa allowed_domain per Google, allowed_org per GitHub, oppure i controlli sui gruppi e sull'assegnazione delle app del tuo provider OIDC.
Modalità mista
allow_local_login aggiunge il modulo con nome utente e password sotto i pulsanti SSO. Il valore predefinito è false, quindi l'SSO sostituisce l'accesso locale a meno che tu non lo attivi.
require_password: true
user_config:
allow_all_users: true
authentication:
method: "oauth"
providers:
google:
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
allow_local_login: trueDurante una migrazione i due convivono per un periodo. Sposta gli annotatori sul provider, poi rimuovi allow_local_login quando tutti hanno effettuato l'accesso tramite il provider. La modalità mista copre anche gli studi che uniscono annotatori dell'istituzione ad annotatori esterni che non hanno un account presso la tua istituzione.
Gli account locali in modalità mista usano lo stesso hashing PBKDF2 con salt di qualsiasi altro account con password. Consulta Gestione delle password per le procedure di reimpostazione.
Sessioni
La durata della sessione è una chiave di primo livello, espressa in giorni:
session_lifetime_days: 7È una secret_key stabile a far sopravvivere le sessioni a un riavvio.
Accesso amministratore
Gli endpoint di amministrazione si autenticano con una chiave API anziché tramite il blocco OAuth:
admin_api_key: ${POTATO_ADMIN_API_KEY}Potato legge admin_api_key prima dalla configurazione, poi dalla variabile d'ambiente POTATO_ADMIN_API_KEY. Se nessuna delle due è impostata, genera una chiave e la scrive in admin_api_key.txt nella directory del task. Chi chiama la presenta nell'header X-API-Key.
HTTPS
In produzione i provider OAuth richiedono URI di reindirizzamento HTTPS. localhost è esente, quindi lo sviluppo su HTTP funziona senza certificato.
Dietro un reverse proxy, inoltra lo schema originale, altrimenti l'URL di callback costruito da Potato userà http e il provider lo rifiuterà:
location / {
proxy_pass http://localhost:8000;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}Riferimento di configurazione
| Chiave | Tipo | Predefinito | Descrizione |
|---|---|---|---|
authentication.method | string | in_memory | in_memory, database, clerk o oauth |
authentication.providers | dict | obbligatorio per oauth | Una voce per provider, con il nome del provider come chiave |
authentication.user_identity_field | string | email | email, username, sub o name |
authentication.allow_local_login | bool | false | Mostra il modulo della password accanto ai pulsanti SSO |
authentication.auto_register | bool | true | Crea l'account Potato al primo accesso |
secret_key | string | casuale | Di primo livello. Serve un valore stabile per sessioni persistenti |
session_lifetime_days | int | 7 | Durata della sessione, di primo livello |
admin_api_key | string | generata | Chiave di primo livello per gli endpoint di amministrazione |
Chiavi per provider:
| Chiave | Tipo | Obbligatoria | Descrizione |
|---|---|---|---|
client_id | string | sì | Fornito dal provider |
client_secret | string | sì | Fornito dal provider |
discovery_url | string | per OIDC generico | Endpoint di discovery OIDC |
display_name | string | no | Testo del pulsante di accesso |
allowed_domain | string | no | Limita a un dominio email |
allowed_org | string | no | Limita a un'organizzazione GitHub |
scopes | list | no | Predefinito ["openid", "email", "profile"] |
Esempio completo
annotation_task_name: "Annotation Project"
task_dir: "."
authentication:
method: "oauth"
providers:
google:
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
allowed_domain: "umich.edu"
user_identity_field: "email"
auto_register: true
secret_key: ${POTATO_SECRET_KEY}
session_lifetime_days: 7
admin_api_key: ${POTATO_ADMIN_API_KEY}
data_files:
- "data/instances.jsonl"
item_properties:
id_key: id
text_key: text
annotation_schemes:
- annotation_type: radio
name: sentiment
description: "What is the sentiment of this text?"
labels: [Positive, Neutral, Negative]
output_annotation_dir: "output/"
export_annotation_format: "jsonl"Risoluzione dei problemi
Il provider rifiuta l'URI di reindirizzamento. L'URI registrato presso il provider deve corrispondere esattamente a /auth/callback/<provider>. Lo schema e qualsiasi percorso finale contano ai fini della corrispondenza, quindi un proxy che non inoltra X-Forwarded-Proto produce un callback http che il provider rifiuta.
Dopo un riavvio tutti risultano disconnessi. secret_key non è impostata, quindi Potato ne ha generata una nuova all'avvio.
Una restrizione per organizzazione GitHub respinge un membro. La sua appartenenza all'organizzazione è privata. O la rende pubblica, o rimuovi allowed_org e controlli l'accesso in un altro modo.
La validazione fallisce con "providers is required". method: "oauth" richiede almeno una voce sotto providers. Il valore del metodo è oauth, non il nome di un provider.
Ulteriori letture
- Gestione delle password -- account locali, hashing e procedure di reimpostazione
- Accesso senza password -- accesso con il solo nome utente per task aperti
- Integrazione con il crowdsourcing -- autenticazione su piattaforme di crowdsourcing
- Dashboard di amministrazione -- gestione di utenti e permessi
Per i dettagli di implementazione, consulta la documentazione sorgente.