Gå til hovedindhold

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

  1. Applications → Create App Integration → OIDC, Web Application.
  2. Sign-in redirect URIs: https://frem.sh/user/oauth2/<auth-source-name>/callback
  3. Sign-out redirect URIs: https://frem.sh/<your-org>/logout (valgfri, men anbefales)
  4. Assignments: tildel applikationen til de grupper eller brugere, der skal have adgang til fremforge.
  5. Kopiér Client ID, Client Secret og Okta domain (f.eks. https://acme.okta.com).
    • Issuer URL = https://acme.okta.com (eller https://acme.okta.com/oauth2/default ved brugerdefinerede authorization servers)

Microsoft Entra (Azure AD)

  1. App registrations → New registration, navn “fremforge”, supported account types = single-tenant.
  2. Redirect URI: Web → https://frem.sh/user/oauth2/<auth-source-name>/callback
  3. Certificates & secrets → New client secret, og kopiér Value med det samme (den vises kun én gang).
  4. Overview: kopiér Application (client) ID og Directory (tenant) ID.
    • Issuer URL = https://login.microsoftonline.com/<tenant-id>/v2.0
  5. Token configuration → Add groups claim, og vælg Security groups. Det udfylder det groups-claim, som fremforge bruger til at mappe grupper til teams.
  6. API permissions: kontrollér, at openid, profile og email er givet (det er de som standard).

Google Workspace

  1. Google Cloud Console → APIs & Services → Credentials → Create credentials → OAuth client ID.
  2. Application type: Web application.
  3. Authorised redirect URIs: https://frem.sh/user/oauth2/<auth-source-name>/callback
  4. Kopiér Client ID og Client Secret.
    • Issuer URL = https://accounts.google.com
  5. 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

  1. Applications → Providers → Create → OAuth2/OpenID Connect Provider.
  2. Redirect URIs: https://frem.sh/user/oauth2/<auth-source-name>/callback
  3. Signing Key: vælg dit Authentik-signeringscertifikat.
  4. 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>/).
  5. Opret en Application, og knyt den til provideren; sæt launch-URL’en til frem.sh/user/login?redirect_to=/<your-org>.

Keycloak

  1. Your Realm → Clients → Create client.
  2. Client type: OpenID Connect. Client ID: fremforge.
  3. Valid redirect URIs: https://frem.sh/user/oauth2/<auth-source-name>/callback
  4. Fanen Credentials: kopiér Client Secret.
    • Issuer URL = https://keycloak.acme.internal/realms/<your-realm>

Auth0 / Okta Customer Identity Cloud

  1. Applications → Create Application → Regular Web Application.
  2. Allowed Callback URLs: https://frem.sh/user/oauth2/<auth-source-name>/callback
  3. Allowed Logout URLs: https://frem.sh/<your-org>/logout
  4. Settings: kopiér Domain, Client ID og Client Secret.
    • Issuer URL = https://<your-auth0-domain>/

Trin 3: registrér udbyderen i fremforge

  1. Administration → SSO, rul ned til Tilføj en ny SSO-kilde, og klik på Start wizard (åbner /_admin/sso/wizard/start).
  2. Vælg OpenID Connect som kildetype, og fortsæt.
  3. 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-configuration og finder selv endpoints.
  4. 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 til email)
    • claimet display_name → fulde navn (standard: name)
  5. 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/domains body: { "domain": "acme.com" } og derefter POST /_app/api/v1/orgs/:slug/sso/domains/:id/verify
  • Registrér OIDC-kilden POST /_app/api/v1/orgs/:slug/sso med en body, der indeholder type: "oidc", issuer_url, client_id, client_secret
  • Opdatér mapningen fra gruppe til team PUT /_app/api/v1/orgs/:slug/sso body: { "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/sso

Den fulde OpenAPI-specifikation finder du i referencen til det offentlige REST API.

Hvad sker der ved login

  1. Brugeren klikker på Sign in with <name> på frem.sh/user/login?redirect_to=/<your-org>.
  2. fremforge sender brugeren videre til IdP’ens authorization-endpoint med scope=openid email profile.
  3. Brugeren logger ind hos IdP’en (MFA håndhæves af IdP’en, ikke af fremforge).
  4. IdP’en sender brugeren tilbage til frem.sh/user/oauth2/<auth-source-name>/callback med en authorization code.
  5. 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.
  6. 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:

  1. Konfigurér OIDC, så medlemmerne kan logge ind via IdP’en.
  2. Slå SCIM til, så IdP’en automatisk sender ændringer i brugere og grupper.
  3. 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:

  1. Generér en ny secret i IdP’en.
  2. Administration → SSO → <autentificeringskilde> → Redigér → Client Secret → indsæt den nye værdi → Gem.
  3. 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=login mod din IdP. IdP’en SKAL bede om legitimationsoplysninger og MFA igen; vi kontrollerer, at det returnerede ID-tokens auth_time ligger inden for 5 minutter fra nu. Break-glass med lokale legitimationsoplysninger via /user/login virker 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).

  1. I din IdP registrerer du en ny OIDC-applikation:

    FeltVærdi
    ApplikationstypeWeb (confidential)
    Grant typesAuthorization Code, med PKCE
    Redirect URIhttps://frem.sh/<your-org>/_admin/-/stepup/callback
    Godkendelsesmetode for token-endpointclient_secret_basic eller client_secret_post
    Påkrævede scopesopenid, email
    Claimet auth_timePåkrævet i ID-tokenet
    prompt=loginRespekteres (standardadfærd i de fleste IdP’er)
  2. 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
  3. Verificér: åbn https://frem.sh/<your-org>/_admin/billing i et nyt browservindue. Du bør blive sendt via din IdP, blive bedt om at logge ind igen og lande på faktureringssiden. Revisionsloggen viser en org-session.step-up.ok-hændelse med auth_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=login på 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