Naar de hoofdinhoud

Single sign-on met OIDC

fremforge ondersteunt OpenID Connect (OIDC) voor single sign-on. OIDC is het single-sign-onprotocol dat fremforge ondersteunt. Elke grote IdP (Entra, Okta, Google Workspace, Auth0, Authentik, Keycloak) ondersteunt OIDC rechtstreeks. De levenscyclus van ondertekeningssleutels verloopt automatisch via de discovery- en JWKS-endpoints van OIDC.

OIDC is op dit moment het enige single-sign-onprotocol dat fremforge biedt. SAML is nog niet beschikbaar; Microsoft Entra ID en ADFS ondersteunen allebei OIDC.

Inrichting

Stap 1: verifieer een domein

Organisatiebeheer → SSO → Geverifieerde domeinen (klap de ingeklapte sectie uit) → voeg je e-maildomein toe (bijv. acme.com). Bewijs dat je het domein beheert op een van deze manieren:

  • DNS-TXT-record: voeg _fremforge-verification=<token> toe aan de DNS van je domein en klik daarna op Verifiëren.
  • HTTP-bestand: serveer het token op https://acme.com/.well-known/fremforge-verification en klik daarna op Verifiëren.

Domeinverificatie is verplicht voordat je een auth-bron registreert. Dit doe je maar één keer.

Stap 2: maak een OAuth 2.0-applicatie aan in je IdP

fremforge heeft drie waarden van je IdP nodig: Client ID, Client Secret en de Issuer URL (ook wel de root van de OIDC-discovery-URL genoemd). Stel je IdP als volgt in.

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 (optioneel, maar aanbevolen)
  4. Assignments: wijs de applicatie toe aan de groepen of gebruikers die toegang tot fremforge moeten krijgen.
  5. Kopieer: Client ID, Client Secret en het Okta domain (bijv. https://acme.okta.com).
    • Issuer URL = https://acme.okta.com (of https://acme.okta.com/oauth2/default voor eigen autorisatieservers)

Microsoft Entra (Azure AD)

  1. App registrations → New registration, naam “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, kopieer de Value meteen (wordt maar één keer getoond).
  4. Overview: kopieer de Application (client) ID en de Directory (tenant) ID.
    • Issuer URL = https://login.microsoftonline.com/<tenant-id>/v2.0
  5. Token configuration → Add groups claim, kies Security groups. Dit vult de claim groups die fremforge gebruikt om teams te koppelen.
  6. API permissions: controleer dat openid, profile en email zijn toegekend (standaard is dat zo).

ADFS

ADFS op Windows Server 2016 en later is een OpenID Connect-provider.

  1. AD FS Management → Application Groups → Add Application Group, naam “fremforge”, sjabloon Server application (onder Standalone applications) of Server application accessing a web API.
  2. Kopieer de Client Identifier. Redirect URI: https://frem.sh/user/oauth2/<auth-source-name>/callback.
  3. Configure Application Credentials → Generate a shared secret, kopieer het (wordt maar één keer getoond).
  4. Ken de scopes openid, profile en email toe, en voeg issuance transform rules toe die email en name (en een groepsclaim, als je teams koppelt) in het ID-token zetten.
    • Issuer URL = https://<your-adfs-host>/adfs (discovery op https://<your-adfs-host>/adfs/.well-known/openid-configuration)

MFA-verificatie via OIDC is nog niet getest met ADFS. Zet het eerst aan voor één testgebruiker en controleer of het inloggen lukt.

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. Kopieer Client ID en Client Secret.
    • Issuer URL = https://accounts.google.com
  5. Beperk in Google Workspace Admin → Security → API controls → Domain-wide delegation de OAuth-toestemming tot je domein, zodat alleen leden van je workspace kunnen authenticeren.

Authentik

  1. Applications → Providers → Create → OAuth2/OpenID Connect Provider.
  2. Redirect URIs: https://frem.sh/user/oauth2/<auth-source-name>/callback
  3. Signing Key: kies je Authentik-ondertekeningscertificaat.
  4. Kopieer Client ID, Client Secret en de OpenID Configuration URL (haal /.well-known/openid-configuration eraf; de issuer-URL is de root, bijv. https://sso.acme.internal/application/o/<slug>/).
  5. Maak een Application aan en koppel die aan de provider; stel de launch-URL in op 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. Tabblad Credentials: kopieer het 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: kopieer Domain, Client ID en Client Secret.
    • Issuer URL = https://<your-auth0-domain>/

Stap 3: registreer de provider in fremforge

  1. Organisatiebeheer → SSO, scrol naar Een nieuwe SSO-bron toevoegen en klik op Start wizard (opent /_admin/sso/wizard/start).
  2. Kies OpenID Connect als type bron en ga verder.
  3. Vul in:
    • Weergavenaam: wordt getoond op de knop op de inlogpagina, bijv. “Sign in with Okta”.
    • Client ID en Client Secret uit stap 2.
    • Uitgever-URL: fremforge haalt <issuer>/.well-known/openid-configuration op om de endpoints automatisch te vinden.
  4. Attribute mapping (de optionele standaardwaarden werken voor de meeste IdP’s):
    • claim email → e-mailadres in fremforge (standaard: email)
    • claim username → Forgejo-gebruikersnaam (standaard: preferred_username → valt terug op email)
    • claim display_name → volledige naam (standaard: name)
  5. Loop de controle- en bevestigingsschermen van de wizard door. fremforge haalt de discovery-gegevens op en controleert de verbinding voordat de bron wordt opgeslagen.

Na het opslaan verschijnt er een knop Sign in with <name> op de inlogpagina van je organisatie op frem.sh/user/login?redirect_to=/<your-org>.

Via de REST API

De OIDC-auth-bron kan ook programmatisch worden geregistreerd, handig als je het inrichten van organisaties scriptmatig doet of de inrichting vanuit CI aanstuurt. Dezelfde provisionOidcAuthSource draait als in de beheerwizard.

  • Verifieer eerst een domein POST /_app/api/v1/orgs/:slug/sso/domains body: { "domain": "acme.com" } daarna POST /_app/api/v1/orgs/:slug/sso/domains/:id/verify
  • Registreer de OIDC-bron POST /_app/api/v1/orgs/:slug/sso, de body bevat type: "oidc", issuer_url, client_id, client_secret
  • Werk de koppeling groep→team bij PUT /_app/api/v1/orgs/:slug/sso body: { "id": "...", "group_team_map": { ... } }
  • Toon de bronnen GET /_app/api/v1/orgs/:slug/sso

Vereist een Personal Access Token (PAT) met de scope 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

Zie de referentie van de publieke REST API voor de volledige OpenAPI-specificatie.

Wat er gebeurt bij het inloggen

  1. De gebruiker klikt op Sign in with <name> op frem.sh/user/login?redirect_to=/<your-org>.
  2. fremforge stuurt door naar het autorisatie-endpoint van je IdP met scope=openid email profile.
  3. De gebruiker authenticeert zich bij de IdP (MFA wordt door de IdP afgedwongen, niet door fremforge).
  4. De IdP stuurt terug naar frem.sh/user/oauth2/<auth-source-name>/callback met een autorisatiecode.
  5. fremforge wisselt de code in voor tokens, valideert de handtekening van het ID-token tegen de JWKS van de IdP, haalt de claims voor e-mail en gebruikersnaam eruit, en maakt de Forgejo-gebruiker aan of werkt die bij.
  6. Staat Require SSO aan (Organisatiebeheer → SSO → Beleid), dan is direct inloggen met een wachtwoord geblokkeerd. Alleen de route via de IdP werkt.

SSO verplichten

Organisatiebeheer → SSO → Beleid → Require SSO for all members. Zodra dit aanstaat:

  • Moeten bestaande leden zich bij de volgende keer inloggen opnieuw via de IdP authenticeren.
  • Blijven PAT’s en SSH-sleutels geldig als authenticatiemiddel, maar met Require SSO aan wordt de IdP-sessie bij Git-handelingen elke 15 minuten opnieuw gevalideerd. Een gebruiker die in de IdP wordt gedeactiveerd, verliest dus binnen 15 minuten de toegang voor push en pull, ook als zijn SSH-sleutel nog geregistreerd staat. Het apart intrekken van sleutels is niet nodig.
  • Worden nieuwe uitnodigingen voor leden aangemaakt als accounts die alleen via SSO werken.

Combineer met SCIM voor volledig levenscyclusbeheer

OIDC regelt de authenticatie. Wil je het aanmaken en opheffen van gebruikers automatiseren (accounts aanmaken bij indiensttreding, deactiveren bij vertrek, groepslidmaatschappen synchroniseren), combineer OIDC dan met SCIM 2.0-provisioning.

De meeste zakelijke IdP’s (Okta, Entra) ondersteunen OIDC en SCIM tegelijk. De gebruikelijke inrichting:

  1. Stel OIDC in, zodat leden via de IdP kunnen inloggen.
  2. Schakel SCIM in, zodat de IdP wijzigingen in gebruikers en groepen automatisch doorstuurt.
  3. Schakel Require SSO in. Met SCIM actief blokkeert het opheffen van de gebruiker in de IdP direct diens toegang tot fremforge.

Tokenrotatie

fremforge cachet de JWKS (ondertekeningssleutels) van de IdP en ververst die volgens een schema en bij een mislukte handtekeningvalidatie. Sleutelrotatie aan de kant van de IdP (bijv. de automatische sleutelrotatie van Okta) wordt onzichtbaar afgehandeld. Er is geen handmatige actie nodig.

Roteer je het Client Secret in de IdP:

De kaart van de auth-bron in Organisatiebeheer → SSO heeft geen veld voor het client secret, en een auth-bron kan na het aanmaken niet meer worden bewerkt. Wil je het secret wijzigen, neem dan contact op met support@frem.sh voordat je het oude in de IdP intrekt: wij werken het voor je bij, en inloggen blijft werken.

OIDC uitschakelen

Wil je inloggen via OIDC stoppen, verwijder dan de auth-bron: Organisatiebeheer → SSO → <auth-bron> → Verwijderen. Inloggen via de bron stopt meteen. Heeft nog geen lid via de bron ingelogd, dan wordt die verwijderd; hebben leden dat wel, dan houdt fremforge een uitgeschakelde kopie met een andere naam, zodat hun accounts blijven. Hun accounts, sessies en toegangstokens worden niet ingetrokken: om die toegang te beëindigen, verwijder je die leden uit de organisatie of deprovisioneer je ze via SCIM. Gebruikers die alleen de OIDC-route hadden, vallen terug op e-mail en wachtwoord (als er een lokaal wachtwoord is ingesteld) of kunnen niet meer inloggen; zorg dus eerst dat een break-glass-eigenaar nog met een lokaal wachtwoord kan inloggen. Mislukt het verwijderen, dan meldt de console dat, verandert er niets en werkt inloggen via de bron gewoon door.

Er is geen schakelaar om een auth-bron tijdelijk uit en later weer aan te zetten; neem daarvoor contact op met support@frem.sh. Om verweesde accounts te voorkomen, verwijder je de auth-bron voordat je de applicatie in je IdP opheft, niet erna.

Problemen oplossen

Redirect-URI komt niet overeen

De IdP weigert de callback als de redirect-URI die in de IdP is geregistreerd niet exact overeenkomt met https://frem.sh/user/oauth2/<auth-source-name>/callback. <auth-source-name> is de Name die je opgaf bij het toevoegen van de auth-bron in fremforge (bijv. okta-primary, entra-prod); de OAuth2-router van Forgejo gebruikt die om de callback af te handelen. Kopieer de exacte callback-URL uit het veld Redirect URI dat de SSO-wizard toont terwijl je de auth-bron toevoegt (Organisatiebeheer → SSO → + SSO-bron toevoegen) in plaats van hem met de hand over te typen; de URL is hoofdlettergevoelig en een slash aan het eind maakt verschil.

invalid_client bij het inwisselen van tokens

Client ID of Client Secret klopt niet. Kopieer ze opnieuw uit de console van de IdP. Sommige IdP’s tonen het secret maar één keer, bij het aanmaken. Genereer een nieuw secret als het oorspronkelijke niet is bewaard.

Ontbrekende claims (gebruikersnaam of e-mail leeg)

Controleer het beleid van de IdP voor het vrijgeven van attributen en claims. fremforge vereist minimaal de claim email in het ID-token. Geeft je IdP claims alleen vrij met expliciete rechten (Entra vereist bijv. de API-rechten profile en email), zorg dan dat die zijn toegekend en dat er toestemming voor is gegeven.

Gebruikers aangemaakt, maar inloggen lukt niet

Staat Require SSO aan en komt het e-maildomein van de gebruiker niet overeen met een geverifieerd domein, dan blokkeert fremforge het inloggen. Verifieer het domein onder Organisatiebeheer → SSO → Geverifieerde domeinen. Het domein in de claim email van de gebruiker moet overeenkomen.

Step-up voor sessiebinding per organisatie

Naast de standaard SSO-login kan fremforge een verse, door de IdP bevestigde authenticatie afdwingen telkens wanneer een gebruiker naar de beheeromgeving van een andere organisatie gaat. Dit is de “sessiebinding per organisatie” die op de prijspagina wordt beloofd, gericht op gereguleerde sectoren: handig wanneer elke beheerhandeling over organisaties heen een door de IdP bevestigde authenticatie moet dragen die tot die specifieke organisatie is beperkt.

Er zijn twee sterkteniveaus. Binding staat standaard aan; welk niveau geldt, hangt ervan af of je de step-up-client hieronder koppelt:

  • Zachte binding (standaard als je geen step-up koppelt): de omweg bij de eerste toegang tot een andere organisatie loopt via de universele /user/login van Forgejo. Is je Forgejo-sessie nog geldig, dan is die omweg onzichtbaar en wordt er niets gevraagd. Het bewijst dus dat de browser een geldige sessie heeft, niet dat de gebruiker zich opnieuw heeft aangemeld. Gebruik harde binding als je per organisatie een verse authenticatie nodig hebt. Break-glass met lokale inloggegevens blijft intact.
  • Harde binding (koppel hieronder): de omweg loopt via het step-up-endpoint van fremforge, dat een OIDC-authenticatie met prompt=login bij je IdP start. De IdP MOET opnieuw om inloggegevens en MFA vragen; wij controleren of de auth_time in het teruggegeven ID-token niet meer dan 5 minuten oud is. Break-glass met lokale inloggegevens via /user/login blijft werken voor eigenaren van de organisatie; harde binding verandert alleen de route voor toegang tot andere organisaties, niet de universele login.

Harde binding koppelen

Harde binding vereist een TWEEDE OIDC-client in je IdP naast de client die Forgejo gebruikt (dezelfde issuer, een aparte client_id + client_secret en een aparte redirect-URI).

  1. Registreer in je IdP een nieuwe OIDC-applicatie:

    VeldWaarde
    Application typeWeb (confidential)
    Grant typesAuthorization Code, met PKCE
    Redirect URIhttps://frem.sh/<your-org>/_admin/-/stepup/callback
    Token endpoint auth methodclient_secret_basic of client_secret_post
    Vereiste scopesopenid, email
    Claim auth_timeVerplicht in het ID-token
    prompt=loginGerespecteerd (standaardgedrag bij de meeste IdP’s)
  2. Ga in fremforge naar Organisatiebeheer → SSO, klap Add OIDC auth source uit en vul de optionele velden voor Per-org session-binding step-up onderaan het formulier in:

    • Step-up Client ID: uit de nieuwe app in je IdP
    • Step-up Client Secret: uit de nieuwe app in je IdP, at rest versleuteld met AES-256-GCM (met een via HKDF afgeleide subsleutel) voordat het wordt opgeslagen
    • Step-up Redirect URI: https://frem.sh/<your-org>/_admin/-/stepup/callback
  3. Controleer: open https://frem.sh/<your-org>/_admin/billing in een nieuw browservenster. Je hoort via je IdP te worden omgeleid, opnieuw om inloggegevens te worden gevraagd en weer op de facturatiepagina uit te komen. Het auditlog toont een event org-session.step-up.ok met auth_time.

Laat je de step-upvelden leeg, dan valt de auth-bron terug op zachte binding; handig voor organisaties die SSO bij het inloggen willen maar geen door de IdP bevestigde authenticatie per organisatie nodig hebben.

Problemen met harde binding oplossen

  • “IdP did not emit auth_time”: de IdP gaf een ID-token terug zonder de claim auth_time. fremforge vereist die, omdat we daarmee controleren hoe vers de nieuwe authenticatie is. Controleer het claimbeleid van de IdP; de meeste hebben een schakelaar voor “include auth_time”.
  • “auth_time too old”: de IdP respecteerde prompt=login op protocolniveau, maar vroeg de gebruiker niet echt opnieuw om in te loggen. Verscherp het beleid voor “session lifetime” of “max age” van de IdP voor deze client.
  • “step-up cookie binding mismatch”: de gebruiker wisselde halverwege van identiteit (opende bijv. een tweede browsertabblad en meldde zich aan als een andere gebruiker). Begin opnieuw vanaf /<your-org>/_admin/billing.
  • “step-up redirect URI rejected”: de URI kwam niet door onze controle op publieke HTTPS (privé-, loopback- en metadata-adressen worden geblokkeerd). Gebruik de standaardvorm frem.sh/_app.

Zie ook