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-verificationen 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
- 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(optioneel, maar aanbevolen) - Assignments: wijs de applicatie toe aan de groepen of gebruikers die toegang tot fremforge moeten krijgen.
- Kopieer: Client ID, Client Secret en het Okta domain (bijv.
https://acme.okta.com).- Issuer URL =
https://acme.okta.com(ofhttps://acme.okta.com/oauth2/defaultvoor eigen autorisatieservers)
- Issuer URL =
Microsoft Entra (Azure AD)
- App registrations → New registration, naam “fremforge”, supported account types = single-tenant.
- Redirect URI: Web →
https://frem.sh/user/oauth2/<auth-source-name>/callback - Certificates & secrets → New client secret, kopieer de Value meteen (wordt maar één keer getoond).
- Overview: kopieer de Application (client) ID en de Directory (tenant) ID.
- Issuer URL =
https://login.microsoftonline.com/<tenant-id>/v2.0
- Issuer URL =
- Token configuration → Add groups claim, kies Security groups. Dit vult de claim
groupsdie fremforge gebruikt om teams te koppelen. - API permissions: controleer dat
openid,profileenemailzijn toegekend (standaard is dat zo).
ADFS
ADFS op Windows Server 2016 en later is een OpenID Connect-provider.
- AD FS Management → Application Groups → Add Application Group, naam “fremforge”, sjabloon Server application (onder Standalone applications) of Server application accessing a web API.
- Kopieer de Client Identifier. Redirect URI:
https://frem.sh/user/oauth2/<auth-source-name>/callback. - Configure Application Credentials → Generate a shared secret, kopieer het (wordt maar één keer getoond).
- Ken de scopes
openid,profileenemailtoe, en voeg issuance transform rules toe dieemailenname(en een groepsclaim, als je teams koppelt) in het ID-token zetten.- Issuer URL =
https://<your-adfs-host>/adfs(discovery ophttps://<your-adfs-host>/adfs/.well-known/openid-configuration)
- Issuer URL =
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
- 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 - Kopieer Client ID en Client Secret.
- Issuer URL =
https://accounts.google.com
- Issuer URL =
- 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
- Applications → Providers → Create → OAuth2/OpenID Connect Provider.
- Redirect URIs:
https://frem.sh/user/oauth2/<auth-source-name>/callback - Signing Key: kies je Authentik-ondertekeningscertificaat.
- Kopieer Client ID, Client Secret en de OpenID Configuration URL (haal
/.well-known/openid-configurationeraf; de issuer-URL is de root, bijv.https://sso.acme.internal/application/o/<slug>/). - 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
- Your Realm → Clients → Create client.
- Client type: OpenID Connect. Client-ID:
fremforge. - Valid redirect URIs:
https://frem.sh/user/oauth2/<auth-source-name>/callback - Tabblad Credentials: kopieer het 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: kopieer Domain, Client ID en Client Secret.
- Issuer URL =
https://<your-auth0-domain>/
- Issuer URL =
Stap 3: registreer de provider in fremforge
- Organisatiebeheer → SSO, scrol naar Een nieuwe SSO-bron toevoegen en klik op Start wizard (opent
/_admin/sso/wizard/start). - Kies OpenID Connect als type bron en ga verder.
- 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-configurationop om de endpoints automatisch te vinden.
- 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 opemail) - claim
display_name→ volledige naam (standaard:name)
- claim
- 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/domainsbody:{ "domain": "acme.com" }daarnaPOST /_app/api/v1/orgs/:slug/sso/domains/:id/verify - Registreer de OIDC-bron
POST /_app/api/v1/orgs/:slug/sso, de body bevattype: "oidc",issuer_url,client_id,client_secret - Werk de koppeling groep→team bij
PUT /_app/api/v1/orgs/:slug/ssobody:{ "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/ssoZie de referentie van de publieke REST API voor de volledige OpenAPI-specificatie.
Wat er gebeurt bij het inloggen
- De gebruiker klikt op Sign in with <name> op
frem.sh/user/login?redirect_to=/<your-org>. - fremforge stuurt door naar het autorisatie-endpoint van je IdP met
scope=openid email profile. - De gebruiker authenticeert zich bij de IdP (MFA wordt door de IdP afgedwongen, niet door fremforge).
- De IdP stuurt terug naar
frem.sh/user/oauth2/<auth-source-name>/callbackmet een autorisatiecode. - 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.
- 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:
- Stel OIDC in, zodat leden via de IdP kunnen inloggen.
- Schakel SCIM in, zodat de IdP wijzigingen in gebruikers en groepen automatisch doorstuurt.
- 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/loginvan 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=loginbij je IdP start. De IdP MOET opnieuw om inloggegevens en MFA vragen; wij controleren of deauth_timein het teruggegeven ID-token niet meer dan 5 minuten oud is. Break-glass met lokale inloggegevens via/user/loginblijft 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).
Registreer in je IdP een nieuwe OIDC-applicatie:
Veld Waarde Application type Web (confidential) Grant types Authorization Code, met PKCE Redirect URI https://frem.sh/<your-org>/_admin/-/stepup/callbackToken endpoint auth method client_secret_basicofclient_secret_postVereiste scopes openid,emailClaim auth_timeVerplicht in het ID-token prompt=loginGerespecteerd (standaardgedrag bij de meeste IdP’s) 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
Controleer: open
https://frem.sh/<your-org>/_admin/billingin 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 eventorg-session.step-up.okmetauth_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=loginop 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
- SAML: waarom SAML nog niet beschikbaar is
- SCIM 2.0-gebruikersprovisioning: automatiseer de levenscyclus van gebruikers via de SCIM-client van je IdP
- Beveiliging en toeleveringsketen: het beveiligingsmodel van fremforge rond SSO-sessies
- Organisatiebeheer: overzicht van alle beheertabbladen