oidc

package
v0.52.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package oidc implements OpenID Connect authentication.

Uses coreos/go-oidc + golang.org/x/oauth2 to talk to any spec-compliant IdP (Keycloak, Auth0, Authentik, Dex, Okta, Google, …). On successful callback the user is upserted into the local users table, then a normal local session token is minted so the rest of the request lifecycle can stay driver-agnostic.

Index

Constants

View Source
const AuditActionAccountPending = "auth.account_pending"

AuditActionAccountPending is the audit action of an account an SSO sign-in opened switched off, because the identity provider did not say its address is verified.

View Source
const TenantConfigKey = "tenant_provider_id"

TenantConfigKey is the Init key that pins a driver to one tenant (int64): what a tenant's own OIDC is built with (authsetup), so it serves its tenant and no other from the moment it exists. Never a page field.

View Source
const TrustEmailKey = "trust_email"

TrustEmailKey is the provider setting that takes every address the identity provider sends as verified (bool, default off).

Variables

This section is empty.

Functions

func WithFlow added in v0.50.0

func WithFlow(ctx context.Context, f Flow) context.Context

WithFlow stamps a flow on the request context StartFlow and HandleCallback are given.

Types

type Driver

type Driver struct {
	// contains filtered or unexported fields
}

Driver is the OIDC auth driver.

func New

func New(store db.Store) *Driver

New constructs an empty OIDC driver - Init must be called. It signs people in to the platform's own tenant until it is pinned or a flow names another.

func (*Driver) Authenticate

func (d *Driver) Authenticate(r *http.Request) (*model.User, error)

Authenticate falls back to the local driver's session token contract — once the OIDC callback completed, downstream requests carry the same session cookie that the local driver knows how to validate.

func (*Driver) BoundTenant added in v0.50.0

func (d *Driver) BoundTenant() (Tenant, bool)

BoundTenant is the tenant the driver signs people in to when a sign-in names no other, and whether it is pinned there (serves that tenant only).

func (*Driver) Capabilities

func (d *Driver) Capabilities() auth.Capabilities

Capabilities implements auth.Driver.

func (*Driver) EndSessionURL

func (d *Driver) EndSessionURL(_ *http.Request, idToken, postLogoutRedirect string) string

EndSessionURL implements auth.OIDCLogoutDriver: where to send the browser so the IdP ends its own session too (OpenID Connect RP-Initiated Logout 1.0).

⚠ Without this, signing out of filex was not signing out. The IdP's session stayed open, so in SSO-first mode (FILEX_OIDC_AUTO_REDIRECT) the login page went straight back to the IdP, which issued a new code without a form: the same account was signed in again ~0.5 s later (measured on Keycloak 26), and on a shared computer the next person got the previous one's files.

id_token_hint is what lets the IdP end the session without first asking "Do you want to log out?" (Keycloak) — or at all (IdPs that require it). An expired token is fine: the spec has the IdP accept it, and by sign-out time it usually has expired.

"" — keep sign-out local — when the IdP advertises no end_session_endpoint, no id_token was kept for the session, or the one kept was issued by another issuer (a provider re-pointed between sign-in and sign-out): one IdP is never handed a token another one issued.

func (*Driver) HandleCallback

func (d *Driver) HandleCallback(w http.ResponseWriter, r *http.Request) (*model.User, string, error)

HandleCallback processes ?code= and ?state= from the IdP. It returns the upserted local user plus the local session token.

func (*Driver) Init

func (d *Driver) Init(ctx context.Context, cfg map[string]any) error

Init configures the driver. Required keys: issuer, client_id, client_secret, redirect_url. Optional: scopes, role_claim, admin_group.

func (*Driver) Name

func (d *Driver) Name() string

Name implements auth.Driver.

func (*Driver) Probe

func (d *Driver) Probe(ctx context.Context, cfg map[string]any, _ *http.Request) []auth.ProbeCheck

Probe tests an OpenID Connect configuration as far as it can be tested without a person signing in:

required    issuer, client ID and return address are filled in
discovery   <issuer>/.well-known/openid-configuration answers JSON
issuer      the document names the SAME issuer (the spec requires it, and
            go-oidc refuses a mismatch at sign-in)
endpoints   it names an authorization, a token and a key endpoint
client      the token endpoint accepts the client ID and secret — asked
            with a client-credentials request, which proves who the client
            is and grants nothing a person could use
redirect    NOT checkable here: an identity provider judges the return
            address only inside a real sign-in, so the result says so

⚠ "client" is only as good as the answer can be read. `unauthorized_client` (with 400, or 401 from Keycloak) or 200 means the client authenticated and is merely not allowed that grant; otherwise 401 or `invalid_client` means the ID or the secret was refused; anything else is reported as not checked rather than guessed. A client with no secret (a public client) cannot be checked without a sign-in at all, and is said to be.

func (*Driver) SetProviderID

func (d *Driver) SetProviderID(id int64)

SetProviderID pins this driver instance to a tenant: it signs people in to that tenant only, and a flow started for another is refused. Called by the multi-provider dispatcher right after Init.

func (*Driver) StartFlow

func (d *Driver) StartFlow(w http.ResponseWriter, r *http.Request) error

StartFlow redirects the browser to the IdP authorization endpoint.

type Flow added in v0.50.0

type Flow struct {
	RedirectURL string
	Tenant      Tenant
}

Flow is what a dispatcher decided for ONE sign-in through a driver it shares between tenants (a sign-in provider bound to several tenants, docs/TENANT-ADMIN.md): the redirect URI of the address the flow started on (the identity provider sends the browser back there, and the code exchange must name the same one) and the tenant the account is looked up and created in. The zero value changes nothing: the driver's own redirect URL and tenant stand.

type Tenant added in v0.50.0

type Tenant struct {
	// ProviderID is the tenant's row. 0 with Main: the platform's own tenant,
	// read at the callback (the supertenant row).
	ProviderID int64
	// Main: the platform's own tenant, which also owns an account with no
	// tenant at all (identity.Tenant.Owns).
	Main bool
}

Tenant is the tenant an SSO sign-in's account belongs to: it is looked up there, created there, and an account of any other tenant is refused (the meaning auth.LoginRealm.Admits gives a password sign-in).

func MainTenant added in v0.50.0

func MainTenant() Tenant

MainTenant is the platform's own tenant, its row read at the callback.

func (Tenant) IsSet added in v0.50.0

func (t Tenant) IsSet() bool

IsSet reports whether t names a tenant at all.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL