Agentnativ autentisering
fremforge godtar tre autentiseringsmønstre for OAuth2 / OIDC for kallere som ikke er mennesker (agenter, skript, AI-assistenter, CI-runnere, leverandørintegrasjoner). Alle tre bruker det samme maskineriet med tillatelseslister for scopes per kunde og revisjonskjede som innlogging for brukere – ingen skyggevei, ingen egen tillitsbutikk.
Den felles forutsetningen for alle flyter som involverer kundens IdP: registrer IdP-en din som en OIDC-autentiseringskilde under Organisasjonsadministrasjon → SSO → Autentiseringskilder, bekreft domenet, og konfigurer tillatelseslister for scopes per app på redigeringssiden for autentiseringskilden under Exchangeable scopes. Den samme registreringen driver føderert innlogging for brukere, så en kunde som bruker OIDC-SSO, har allerede grunnlaget på plass.
Implementasjonsmerknad: Endepunktene
/_app/api/v1/auth/token-exchangeog/_app/api/v1/oauth/tokenbetjenes av fremforges kontrollplan – de er ikke Forgejos egne endepunkter. SDK-forfattere bør brukehttps://frem.sh/_app/api/v1/*nøyaktig som dokumentert.
De tre flytene i korte trekk
| Flyt | Spesifikasjon | Rollen til kundens IdP | Passer best for |
|---|---|---|---|
| 1. Direkte JWT-bearer | – (uformelt, «Bearer JWT») | Utsteder JWT-en; fremforge verifiserer | Arbeidslastidentiteter (Entra workload identity, Okta-tjenestekonto, Google-tjenestekonto) som allerede utsteder JWT-er bundet til kunden |
| 2. Token exchange (RFC 8693) | RFC 8693 | Utsteder subject_token (+ valgfri actor_token for OBO) | CI-workflower, planlagte agenter, on-behalf-of-flyter (OBO) der revisjonen må navngi personen og agenten som handler for vedkommende |
3. client_credentials (RFC 6749 §4.4) | RFC 6749 | Ingen – fremforge utsteder både client_id og client_secret | Apper som ikke har noen IdP i det hele tatt, eller der kunden foretrekker fremforge som IdP |
Alle tre flytene gir et kortlivet (≤ 1 time) ffp_-tilgangstoken bundet til én kunde og ett sett med scopes. RBAC, frekvensbegrensning, revisjonshendelser og sperrer for isolasjon mellom kunder er identiske med en PAT utstedt av et menneske.
Flyt 1 – JWT fra kundens IdP direkte som bearer
Agenten presenterer JWT-en fra IdP-en direkte som Authorization: Bearer <jwt> på et hvilket som helst /api/v1/*-endepunkt. fremforge verifiserer JWT-en mot den registrerte autentiseringskilden din, bruker snittet av scopes fra kildens app-tildeling og returnerer svaret – ingen ekstra rundtur, ingen lagret legitimasjon.
Endepunkt: en hvilken som helst frem.sh/_app/api/v1/*-rute.
Oppsett (én gang, per autentiseringskilde):
- Organisasjonsadministrasjon → SSO → registrer en OIDC-kilde for IdP-en din (eller gjenbruk en eksisterende som brukes til innlogging for brukere).
- Konfigurer tillatelseslister for scopes per app under Edit auth source → App grants. Tillatelseslisten binder en IdP-applikasjon (matchet på JWT-ens claim
azpelleraud[0]) til et maksimalt sett med scopes. - Organisasjonsadministrasjon → Programmatisk tilgang → Administrer JWT-bearer + bytte → slå på Tillat direkte JWT-bearer for kilden.
- (Bare IdP-er med flere kunder) Skriv inn en claim assertion som binder kilden til din kunde –
{"tid": "<your-Entra-tenant-guid>"}for Entra/common/,{"hd": "yourdomain.com"}for Google Workspace osv. Administrasjonsskjemaet nekter å slå på direkte bearer for en utsteder-URL med flere kunder uten claim assertions.
Krav til tokenet:
isssamsvarer med den registrerte kilde-URL-en (normalisert uten hensyn til store og små bokstaver, tolererer avsluttende skråstrek).audsamsvarer enten med kildens konfigurerte Audience override ELLER med den kanoniske målgruppen per kunde,https://frem.sh/<your-tenant-slug>.expligger i framtiden,iatligger ikke urimelig langt tilbake (jose håndhever 30 sekunders klokkeavvik).- Alle
claim_assertionssom er konfigurert på kilden, samsvarer med den verifiserte nyttelasten. - Signeringsnøkkelen løses opp via OIDC discovery + JWKS (bufret i 10 minutter, kan styres av kunden gjennom vanlig nøkkelrotasjon i IdP-en).
Eksempel (Entra workload identity):
# 1. Acquire a JWT from your IdP (managed identity, federated identity,
# or workload-identity SDK) with aud=https://frem.sh/<your-slug>
ACCESS_TOKEN=$(az account get-access-token \
--resource https://frem.sh/acme \
--query accessToken -o tsv)
# 2. Call any fremforge API directly
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
https://frem.sh/_app/api/v1/orgs/acme/reposSikkerhetsegenskaper:
- JWT-en er legitimasjonen. Ingen PAT-rad opprettes i fremforges database. Tilbakekalling = la
expløpe ut, ELLER fjern IdP-kilden / slå av direkte bearer (neste forespørsel avvises). - Binding på tvers av kunder er umulig av konstruksjon: en målgruppe per kunde binder JWT-en til én slug, og tabellen auth_sources håndhever en UNIQUE-begrensning på databasenivå som hindrer to kunder i å registrere samme utsteder for direkte bearer.
- Øvre grense for scopes fra kildens tildeling per app. Scopes som flytter penger (
billing:write,sso:write,policy:write,orgs:write), krever eksplisitte oppføringer i tillatelseslisten per app – de gis ALDRI av en tillatelsesliste som er NULL.
Flyt 2 – RFC 8693 token exchange (med OBO)
Agenten presenterer en JWT fra IdP-en som subject_token og bytter den mot en kortlivet fremforge-PAT (ffp_). Parameteren actor_token muliggjør on-behalf-of-delegering – agenten handler for en navngitt person. fremforge har ingen agentidentiteter: personen er aktøren i revisjonsloggen, og agenten er en detalj i hver revisjonsrad.
Endepunkt: POST frem.sh/_app/api/v1/auth/token-exchange.
Eksempel (enkel exchange):
curl -X POST https://frem.sh/_app/api/v1/auth/token-exchange \
-H "Content-Type: application/json" \
-d '{
"subject_token": "<customer-idp-jwt>",
"tenant_slug": "acme",
"scopes": ["orgs:read", "findings:write"],
"name": "ci-build-bot"
}'Eksempel (OBO – agenten handler på vegne av Alice):
curl -X POST https://frem.sh/_app/api/v1/auth/token-exchange \
-H "Content-Type: application/json" \
-d '{
"subject_token": "<alice-idp-jwt>",
"actor_token": "<agent-idp-jwt>",
"tenant_slug": "acme",
"scopes": ["findings:read", "audit:read"]
}'Be bare om scopes som token exchange kan gi: settet som kan byttes (orgs:read, orgs:write, audit:read, exports:read, exports:write, findings:read, findings:write, components:read, deployments:report, runners:read, runners:write, sboms:write, seats:read, seats:write, billing:read, billing:write, sso:read, sso:write, policy:read, policy:write), innsnevret av app-tildelingen på autentiseringskilden din. Et scope utenfor ett av dem droppes, og en forespørsel uten noe igjen avvises med no-scopes-grantable. Tilgang til repositoryer og issues er ikke med i fremforge-API-ets sett med scopes, så det kan ikke byttes.
Hver revisjonshendelse som den utstedte PAT-en skriver, navngir Alice som actor, som oidc:idp:<alice-sub> (den verifiserte sub fra subject-tokenet). Agenten er en detalj i raden: fields.agent er oidc:agent:<agent-sub> (den verifiserte sub fra actor-tokenet), og fields.via_token er id-en til den utstedte PAT-en. Selve byttet registreres som api.token.exchange.obo, også med Alice som aktør og agenten i fields.agent. Begge verdiene kommer bare fra de verifiserte JWT-ene; ingenting i forespørselens body kan navngi eller endre personen.
Uten en actor_token navngir et bytte med kundens IdP likevel personen som aktør, med via_token og uten agent. Et bytte med runner-OIDC har ingen person (en CI-jobb er ikke en person), så hendelsene beholder actor=token:<id>.
Samsvarsspørringer som «alle handlinger Anthropic-agenten min utførte på vegne av Alice forrige kvartal» filtrerer revisjonsloggen på Alice og leser fields.agent. Aktørfilteret matcher aktøren, kolonnen on_behalf_of og fields.agent, så et filter på oidc:agent:<agent-sub> finner alt den agenten gjorde, for alle personer. I revisjonsloggen i administrasjonen matcher filteret en delstreng, og slike rader har merket via agent; GET /orgs/{org}/audit?actor= matcher eksakt. Hendelser skrevet før 2026-10-23 navnga agenten som aktør og Alice i on_behalf_of. Hashkjeden dekker dem, så de beholder den formen, vises som on behalf of Alice, og de samme filtrene finner dem.
Forskjell fra flyt 1: token exchange oppretter en lagret PAT (så den kan ha et eksplisitt delsett av scopes og et eget navn). Flyt 1 er flyktig. Velg exchange når agenten skal gjøre mange kall i én workflow-kjøring; velg direkte bearer når hvert kall er uavhengig.
Flyt 3 – client_credentials (fremforge som IdP)
For apper uten IdP, eller der kunden foretrekker ett sett med legitimasjon som administreres i fremforge. Organisasjonsadministratoren utsteder et par (client_id, client_secret), appen autentiserer med Authorization: Basic (eller POST-skjema) og får en kortlivet ffp_-PAT.
Oppsett:
- Organisasjonsadministrasjon → Programmatisk tilgang → Administrer JWT-bearer + bytte → Opprett ny klient (under avsnittet
client_credentials). - Velg et navn, en øvre grense for scopes og en valgfri tillatelsesliste for målgrupper.
- Kopier
client_secretmed en gang – den vises én gang og aldri igjen.
Endepunkt: POST frem.sh/_app/api/v1/oauth/token.
Eksempel:
curl -X POST https://frem.sh/_app/api/v1/oauth/token \
-u "ffc_<client-id>:ffs_<client-secret>" \
-d "grant_type=client_credentials" \
-d "scope=findings:write runners:read"Svar:
{
"access_token": "ffp_...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "findings:write runners:read",
"audience": "https://frem.sh/acme"
}Rotasjon: trykk når som helst på knappen Roter i administrasjonsgrensesnittet. Den nåværende secreten forblir gyldig sammen med den nye i 24 timer, så du kan rulle ut konfigurasjonen uten nedetid. Etter 24-timersvinduet autentiserer bare den nye secreten.
Tilbakekalling: et klikk på Tilbakekall ugyldiggjør klienten OG tilbakekaller i kaskade hvert ffp_-token den noen gang har utstedt, i én transaksjon – feilsikkert selv om operatørpoden drepes midt i tilbakekallingen.
Hva de tre flytene har felles
| Egenskap | Slik håndheves den |
|---|---|
| Isolasjon mellom kunder | Hvert utstedt token har tenant_id satt; requireTenantBinding avviser kall på tvers av kunder med 404. UNIQUE på databasenivå hindrer at én utsteder gjøres krav på av to kunder for direkte bearer. |
| Øvre grense for scopes | Alle tre tar snittet mot et operatørstyrt sett av scopes som kan byttes OG kundens tillatelsesliste per IdP / per klient. Scopes som flytter penger (billing:write, sso:write, policy:write, orgs:write), krever eksplisitte oppføringer i tillatelseslisten – de gis aldri som standard. |
| TTL | Alle utstedte tokener har en grense på 1 time. Kunder som vil ha legitimasjon med lengre levetid, bruker utstedelsen i administrasjonsgrensesnittet (personlige tilgangstokener). |
| Revisjonskjede | Hver utstedelse skriver en audit_event med actor, handling og scopes. For et bytte med kundens IdP er aktøren personen, og agenten (for OBO) er fields.agent; hver senere hendelse tokenet skriver, har personen som aktør pluss agent og via_token. Kjedens hash dekker fields_json, inkludert begge. |
| Frekvensbegrensning | Token-endepunktene er frekvensbegrenset per IP. /auth/token-exchange og /oauth/token deler en grense på 30/min for å begrense CPU-kostnaden ved JWT-verifisering av falske inndata. |
| Nåbarhet for JWKS | Alle utgående henting av JWKS og discovery går gjennom en begrenset utgående gateway – SSRF, private IP-er og skymetadata blokkeres i nettverksleddet. JWKS bufres i 10 min. |
| EU-lokalisering | Kjeden når aldri et USA-hostet Sigstore-, Auth0- eller Okta-endepunkt – verifiseringen skjer helt innenfor fremforges eu-de-fotavtrykk i T Cloud Public. Utgående trafikk til kundens IdP går bare gjennom SSRF-proxyet henting. |
Sammendrag av trusselmodellen
| Trussel | Tiltak |
|---|---|
Kunden registrerer en Entra /common/-URL med flere kunder uten claim_assertion for tid | Administrasjonsskjemaet nekter; hjelpefunksjonen isMultiTenantIssuerUrl() flagger Entra (v1 + v2), Google Workspace og Microsofts Gov- / Kina-skyer |
To kunder deler en iss-URL for IdP | Delvis UNIQUE-indeks på (LOWER(RTRIM(iss,'/'))) WHERE direkte bearer er slått på – den andre registreringen avvises i databaselaget |
| JWT fra kundens IdP spilles av mot feil kunde | Målgruppe per kunde, https://frem.sh/<slug> – et token utstedt for aud=https://frem.sh/acme kan bokstavelig talt ikke bestå målgruppesjekken til kunden initech |
Lekket client_secret | Organisasjonsadministratoren kjører Revoke → en kaskadetransaksjon fjerner klienten OG hver PAT den har utstedt; rotasjon støtter et 24-timers vindu med to secrets for bytte uten nedetid |
| Forfalskede claims i OBO-actor_token | jose verifiserer signatur + iss + aud mot den samme kilden som subject; sub er uforanderlig, email/name stoles aldri på |
| JWKS-bufferen er utdatert etter nøkkelrotasjon i IdP-en | Fast TTL på 10 min – driftsmessig avgrenset |
| Internt vertsnavn i IdP-ens discovery-dokument (DNS rebind / SSRF) | Den begrensede utgående gatewayen blokkerer hentingen; en https://-sjekk i koden gir dybdeforsvar |
Hurtigreferanse
| Sti | Metode | Autentisering | TTL | Body |
|---|---|---|---|---|
/_app/api/v1/auth/token-exchange | POST | JWT-en ER autentiseringen | 1t | subject_token (+ valgfri actor_token), tenant_slug, scopes, name |
/_app/api/v1/oauth/token | POST | Basic ELLER skjema i body | 1t | grant_type=client_credentials, client_id, client_secret, scope, audience |
/api/v1/* (alle) | alle | Authorization: Bearer <jwt or ffp_*> | etter exp / etter PAT-raden | – |
Relatert
- SSO-oppsett (OIDC) – registrer IdP-en din både for innlogging for brukere og for agentautentisering
- Workflow-OIDC for skyautentisering – den utgående retningen (runner-VM → tillitspolicyer i AWS/GCP/Azure)
- Revisjonslogg + kjede – verifiser hva hver agent gjorde, med filter på personen eller på agenten
- API-tokener (PAT-er) – langlivede tokener utstedt av mennesker for den samme
/api/v1/*-flaten; utstedes under Brukerinnstillinger → API-tokener, se Autentiseringspolicy for maksimal levetid og rotasjonspolicy