Single sign-on med OIDC
Oversat fra den engelske original. Hvis de to versioner er forskellige, gælder den engelske. Oversættelsen er endnu ikke korrekturlæst af en dansk modersmålsbruger.
fremforge understøtter OpenID Connect (OIDC) til single sign-on. OIDC er den anbefalede protokol for de fleste organisationer. Alle store identitetsudbydere (IdP’er) – Entra, Okta, Google Workspace, Auth0, Authentik og Keycloak – understøtter OIDC direkte. Signeringsnøglernes livscyklus håndteres automatisk via OIDC’s discovery- og JWKS-endpoints, og der er færre ting at fejlsøge end med SAML.
Brug kun SAML, når dit IdP-team specifikt kræver det (ældre føderationsaftaler, interoperabilitet i regulerede brancher eller IdP’er fra før OIDC). Begge protokoller ender i den samme Forgejo-session og giver samme sikkerhed. OIDC kræver mindre drift over tid.
Opsætning
Trin 1: verificér et domæne
Administration → SSO → Verificerede domæner (fold sektionen ud) → tilføj dit e-maildomæne (f.eks. acme.com). Bevis, at du kontrollerer domænet, på en af disse måder:
- DNS TXT-post: tilføj
_fremforge-verification=<token>til domænets DNS, og klik derefter på Verificér. - HTTP-fil: læg tokenet på
https://acme.com/.well-known/fremforge-verification, og klik derefter på Verificér.
Domænet skal være verificeret, før du kan registrere en autentificeringskilde. Én verificering dækker både OIDC og SAML, og du gør det kun én gang.
Trin 2: opret en OAuth 2.0-applikation i din IdP
fremforge skal bruge tre værdier fra din IdP: Client ID, Client Secret og Issuer URL (også kaldet roden af OIDC-discovery-URL’en). Konfigurér din IdP sådan her.
Okta
- Applications → Create App Integration → OIDC, Web Application.
- Sign-in redirect URIs:
https://frem.sh/user/oauth2/<auth-source-name>/callback - Sign-out redirect URIs:
https://frem.sh/<your-org>/logout(valgfri, men anbefales) - Assignments: tildel applikationen til de grupper eller brugere, der skal have adgang til fremforge.
- Kopiér Client ID, Client Secret og Okta domain (f.eks.
https://acme.okta.com).- Issuer URL =
https://acme.okta.com(ellerhttps://acme.okta.com/oauth2/defaultved brugerdefinerede authorization servers)
- Issuer URL =
Microsoft Entra (Azure AD)
- App registrations → New registration, navn “fremforge”, supported account types = single-tenant.
- Redirect URI: Web →
https://frem.sh/user/oauth2/<auth-source-name>/callback - Certificates & secrets → New client secret, og kopiér Value med det samme (den vises kun én gang).
- Overview: kopiér Application (client) ID og Directory (tenant) ID.
- Issuer URL =
https://login.microsoftonline.com/<tenant-id>/v2.0
- Issuer URL =
- Token configuration → Add groups claim, og vælg Security groups. Det udfylder det
groups-claim, som fremforge bruger til at mappe grupper til teams. - API permissions: kontrollér, at
openid,profileogemailer givet (det er de som standard).
Google Workspace
- Google Cloud Console → APIs & Services → Credentials → Create credentials → OAuth client ID.
- Application type: Web application.
- Authorised redirect URIs:
https://frem.sh/user/oauth2/<auth-source-name>/callback - Kopiér Client ID og Client Secret.
- Issuer URL =
https://accounts.google.com
- Issuer URL =
- Under Google Workspace Admin → Security → API controls → Domain-wide delegation begrænser du OAuth-samtykke til dit domæne, så kun medlemmer af jeres workspace kan logge ind.
Authentik
- Applications → Providers → Create → OAuth2/OpenID Connect Provider.
- Redirect URIs:
https://frem.sh/user/oauth2/<auth-source-name>/callback - Signing Key: vælg dit Authentik-signeringscertifikat.
- Kopiér Client ID, Client Secret og OpenID Configuration URL (fjern
/.well-known/openid-configuration; issuer-URL’en er roden, f.eks.https://sso.acme.internal/application/o/<slug>/). - Opret en Application, og knyt den til provideren; sæt launch-URL’en til
frem.sh/user/login?redirect_to=/<your-org>.
Keycloak
- Your Realm → Clients → Create client.
- Client type: OpenID Connect. Client ID:
fremforge. - Valid redirect URIs:
https://frem.sh/user/oauth2/<auth-source-name>/callback - Fanen Credentials: kopiér Client Secret.
- Issuer URL =
https://keycloak.acme.internal/realms/<your-realm>
- Issuer URL =
Auth0 / Okta Customer Identity Cloud
- Applications → Create Application → Regular Web Application.
- Allowed Callback URLs:
https://frem.sh/user/oauth2/<auth-source-name>/callback - Allowed Logout URLs:
https://frem.sh/<your-org>/logout - Settings: kopiér Domain, Client ID og Client Secret.
- Issuer URL =
https://<your-auth0-domain>/
- Issuer URL =
Trin 3: registrér udbyderen i fremforge
- Administration → SSO, rul ned til Tilføj en ny SSO-kilde, og klik på Start wizard (åbner
/_admin/sso/wizard/start). - Vælg OpenID Connect som kildetype, og fortsæt.
- Udfyld:
- Visningsnavn, som står på knappen på login-siden, f.eks. “Log ind med Okta”.
- Client ID og Client Secret fra trin 2.
- Udsteder-URL: fremforge henter
<issuer>/.well-known/openid-configurationog finder selv endpoints.
- Attribute mapping (standardværdierne virker for de fleste IdP’er):
- claimet
email→ e-mail i fremforge (standard:email) - claimet
username→ brugernavn i Forgejo (standard:preferred_username→ falder tilbage tilemail) - claimet
display_name→ fulde navn (standard:name)
- claimet
- Gå guidens gennemse- og bekræft-skærme igennem. fremforge henter discovery-dokumentet og kontrollerer forbindelsen, før kilden gemmes.
Når kilden er gemt, vises en Sign in with <name>-knap på din organisations login-side på frem.sh/user/login?redirect_to=/<your-org>.
Via REST API
OIDC-autentificeringskilden kan også registreres programmatisk, hvilket er praktisk, når du scripter oprettelsen af organisationer eller styrer opsætningen fra CI. Det er den samme provisionOidcAuthSource, som admin-guiden kører.
- Verificér først et domæne
POST /_app/api/v1/orgs/:slug/sso/domainsbody:{ "domain": "acme.com" }og derefterPOST /_app/api/v1/orgs/:slug/sso/domains/:id/verify - Registrér OIDC-kilden
POST /_app/api/v1/orgs/:slug/ssomed en body, der indeholdertype: "oidc",issuer_url,client_id,client_secret - Opdatér mapningen fra gruppe til team
PUT /_app/api/v1/orgs/:slug/ssobody:{ "id": "...", "group_team_map": { ... } } - List kilderne
GET /_app/api/v1/orgs/:slug/sso
Kræver et Personal Access Token (PAT) med scopet sso:write:
curl -X POST \
-H "Authorization: Bearer ${FREMFORGE_PAT}" \
-H "Content-Type: application/json" \
-d '{
"name": "acme-okta",
"type": "oidc",
"issuer_url": "https://acme.okta.com",
"client_id": "0oa1abc...",
"client_secret": "s3cr3t..."
}' \
https://frem.sh/_app/api/v1/orgs/acme/ssoDen fulde OpenAPI-specifikation finder du i referencen til det offentlige REST API.
Hvad sker der ved login
- Brugeren klikker på Sign in with <name> på
frem.sh/user/login?redirect_to=/<your-org>. - fremforge sender brugeren videre til IdP’ens authorization-endpoint med
scope=openid email profile. - Brugeren logger ind hos IdP’en (MFA håndhæves af IdP’en, ikke af fremforge).
- IdP’en sender brugeren tilbage til
frem.sh/user/oauth2/<auth-source-name>/callbackmed en authorization code. - fremforge veksler koden til tokens, validerer ID-tokenets signatur mod IdP’ens JWKS, udtrækker claims for e-mail og brugernavn og opretter eller opdaterer Forgejo-brugeren.
- Hvis Require SSO er slået til (Administration → SSO → Politikker), er direkte login med adgangskode blokeret. Kun vejen via IdP’en virker.
Require SSO
Administration → SSO → Politikker → Require SSO for all members. Når det er slået til:
- Eksisterende medlemmer skal logge ind igen via IdP’en ved næste login.
- PAT’er og SSH-nøgler kan stadig bruges til godkendelse, men med Require SSO slået til bliver IdP-sessionen genvalideret hvert 15. minut ved Git-operationer. En bruger, der deaktiveres i IdP’en, mister derfor adgang til push og pull inden for 15 minutter, selv om vedkommendes SSH-nøgle stadig er registreret. Nøglen skal ikke tilbagekaldes separat.
- Nye medlemmer, der inviteres, oprettes som rene SSO-konti.
Brug SCIM til hele livscyklussen
OIDC håndterer godkendelse. Hvis du vil automatisere oprettelse og nedlæggelse af brugere (opret konti ved ansættelse, deaktivér ved fratrædelse, synkronisér gruppemedlemskaber), så brug OIDC sammen med SCIM 2.0-provisionering.
De fleste enterprise-IdP’er (Okta, Entra) understøtter OIDC og SCIM samtidig. Den typiske opsætning:
- Konfigurér OIDC, så medlemmerne kan logge ind via IdP’en.
- Slå SCIM til, så IdP’en automatisk sender ændringer i brugere og grupper.
- Slå Require SSO til. Med SCIM aktivt blokerer en nedlæggelse af brugeren i IdP’en med det samme vedkommendes adgang til fremforge.
Rotation af nøgler og secrets
fremforge cacher IdP’ens JWKS (signeringsnøgler) og opdaterer dem efter en fast plan og når en signaturvalidering fejler. Nøglerotation på IdP-siden (f.eks. Oktas automatiske nøglerotation) håndteres derfor uden videre. Du skal ikke gøre noget manuelt.
Hvis du roterer Client Secret i IdP’en:
- Generér en ny secret i IdP’en.
- Administration → SSO → <autentificeringskilde> → Redigér → Client Secret → indsæt den nye værdi → Gem.
- Den gamle secret kan tilbagekaldes i IdP’en, så snart du har gemt.
Slå OIDC fra
Administration → SSO → <autentificeringskilde> → Deaktivér. Brugere, der kun har OIDC-vejen, falder tilbage til e-mail og adgangskode (hvis der er sat en lokal adgangskode), eller de kan ikke logge ind, før kilden er slået til igen. Slå SSO fra, før du nedlægger IdP-applikationen, ikke bagefter, så du undgår forældreløse konti.
Fejlfinding
Redirect URI passer ikke
IdP’en afviser callbacket, hvis den redirect URI, der er registreret i IdP’en, ikke præcis svarer til https://frem.sh/user/oauth2/<auth-source-name>/callback. <auth-source-name> er det Name, du angav, da du tilføjede autentificeringskilden i fremforge (f.eks. okta-primary, entra-prod); Forgejos OAuth2-router bruger det til at sende callbacket det rigtige sted hen. Kopiér den præcise callback-URL fra Administration → SSO → <autentificeringskilde> → Detaljer i stedet for at skrive den selv; der skelnes mellem store og små bogstaver, og en afsluttende skråstreg gør en forskel.
invalid_client ved token-udveksling
Client ID eller Client Secret er forkert. Kopiér dem igen fra IdP-konsollen. Nogle IdP’er viser kun secret’en én gang, når den oprettes. Generér en ny secret, hvis du ikke fik den oprindelige med.
Claims mangler (brugernavn eller e-mail er tom)
Tjek IdP’ens politik for, hvilke attributter og claims den udleverer. fremforge kræver som minimum claimet email i ID-tokenet. Hvis din IdP kun udleverer claims med eksplicitte tilladelser (Entra kræver f.eks. API-tilladelserne profile og email), så sørg for, at de er givet, og at der er givet samtykke.
Brugerne er oprettet, men kan ikke logge ind
Hvis Require SSO er slået til, og brugerens e-maildomæne ikke svarer til et verificeret domæne, blokerer fremforge login. Verificér domænet under Administration → SSO → Verificerede domæner. Domænet i brugerens email-claim skal stemme overens.
Step-up med sessionsbinding pr. organisation
Ud over det almindelige SSO-login kan fremforge kræve en ny, IdP-attesteret godkendelse, hver gang en bruger går ind på administrationsfladen i en anden organisation. Det er den styrke af “sessionsbinding pr. organisation” til regulerede sektorer, som prissiden lover, og den er nyttig, når hver administrativ handling på tværs af organisationer skal bære en IdP-attesteret godkendelse, der gælder netop den organisation.
Der er to styrkeniveauer, og begge er slået til som standard:
- Blød binding (standard, hvis du ikke opsætter step-up): ved første adgang til en anden organisation går turen via Forgejos fælles
/user/login. Hvis din Forgejo-session er frisk, mærker du ikke noget til det. Break-glass med lokale legitimationsoplysninger virker stadig. - Hård binding (opsætning nedenfor): turen går via fremforges step-up-endpoint, som starter en OIDC AuthN med
prompt=loginmod din IdP. IdP’en SKAL bede om legitimationsoplysninger og MFA igen; vi kontrollerer, at det returnerede ID-tokensauth_timeligger inden for 5 minutter fra nu. Break-glass med lokale legitimationsoplysninger via/user/loginvirker stadig for organisationsejere; hård binding ændrer kun vejen ind i en anden organisation, ikke det fælles login.
Opsætning af hård binding
Hård binding kræver en ANDEN OIDC-klient i din IdP ved siden af den, Forgejo bruger (samme issuer, separat client_id + client_secret, separat redirect URI).
I din IdP registrerer du en ny OIDC-applikation:
Felt Værdi Applikationstype Web (confidential) Grant types Authorization Code, med PKCE Redirect URI https://frem.sh/<your-org>/_admin/-/stepup/callbackGodkendelsesmetode for token-endpoint client_secret_basicellerclient_secret_postPåkrævede scopes openid,emailClaimet auth_timePåkrævet i ID-tokenet prompt=loginRespekteres (standardadfærd i de fleste IdP’er) I fremforge går du til Administration → SSO, folder Add OIDC auth source ud og udfylder de valgfrie felter under Per-org session-binding step-up nederst i formularen:
- Step-up Client ID: fra den nye app i din IdP
- Step-up Client Secret: fra den nye app i din IdP; den krypteres med AES-256-GCM (en HKDF-afledt undernøgle), før den gemmes
- Step-up Redirect URI:
https://frem.sh/<your-org>/_admin/-/stepup/callback
Verificér: åbn
https://frem.sh/<your-org>/_admin/billingi et nyt browservindue. Du bør blive sendt via din IdP, blive bedt om at logge ind igen og lande på faktureringssiden. Revisionsloggen viser enorg-session.step-up.ok-hændelse medauth_time.
Hvis step-up-felterne efterlades tomme, falder autentificeringskilden tilbage til blød binding. Det passer til organisationer, der vil have SSO ved login, men ikke har brug for IdP-attestering pr. organisation.
Fejlfinding af hård binding
- “IdP did not emit auth_time”: IdP’en returnerede et ID-token uden claimet
auth_time. fremforge kræver det, fordi det er sådan, vi kontrollerer, at godkendelsen er ny. Tjek IdP’ens claim-politik; de fleste har en indstilling til at “include auth_time”. - “auth_time too old”: IdP’en accepterede
prompt=loginpå protokolniveau, men bad ikke rent faktisk brugeren om at logge ind igen. Stram IdP’ens politik for “session lifetime” eller “max age” for denne klient. - “step-up cookie binding mismatch”: brugeren skiftede identitet undervejs (åbnede f.eks. en ekstra fane og loggede ind som en anden bruger). Start forfra fra
/<your-org>/_admin/billing. - “step-up redirect URI rejected”: URI’en blev afvist af vores kontrol af offentlig HTTPS (private, loopback- og metadata-adresser er blokeret). Brug den kanoniske
frem.sh/_app-form.
Krydshenvisninger
- SAML 2.0 SSO, når dit IdP-team kræver SAML
- SCIM 2.0-brugerprovisionering, automatisér brugernes livscyklus via din IdP’s SCIM-klient
- Sikkerhed og forsyningskæde, fremforges sikkerhedsmodel omkring SSO-sessioner
- Administration, overblik over alle administrationsfaner