Documentation
¶
Overview ¶
Package oidc is the OpenID Connect provider: authorization code flow with PKCE (S256) and nonce, state bound to the server-side session, RP-initiated logout through the discovered end_session_endpoint and front-channel logout.
A successful callback stores an auth.Identity in the shared session.Store, so the one session.Guard of the application authenticates OIDC users like every other provider's. Several providers (one per IdP, distinct names) share one Store. Roles and groups come from the access token, else from the ID token (Keycloak's realm_access, resource_access and groups claims); set OnLogin to a keycloak.Client's Enrich to read them from the Keycloak Admin API instead.
The package never logs; outcomes go to the optional auth.Observer.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ClaimMapper ¶
ClaimMapper is a pure function from ID token claims to a Profile. It must not keep or change the map.
type Options ¶
type Options struct {
Name string // required; [a-z0-9-]{1,32}; not "password", "github", "apikey", "session", "jwt"; becomes Identity.Provider
Issuer string // required; https, or http on a loopback host; discovery at <Issuer>/.well-known/openid-configuration
ClientID, ClientSecret string // required (confidential client)
Scopes []string // default ["openid","profile","email"]; "openid" added first when missing; order kept, duplicates dropped; empty or space-containing entries are an error
ExternalURL string // required: absolute public base URL; redirect URI = ExternalURL + PathPrefix + "/callback"
PathPrefix string // default "/auth/oidc/" + Name; segments of A-Z a-z 0-9 . _ ~ -, no trailing "/", no "." or ".." segment
PostLogoutURL string // default ExternalURL + "/"; path or absolute http(s) URL, no fragment; a path is made absolute with ExternalURL
ErrorURL string // optional: callback failures (refusals and code=internal) redirect to ErrorURL?code=<code> instead of answering JSON; same rules as PostLogoutURL
ClaimMapper ClaimMapper // default DefaultClaimMapper
AllowedEmailDomains []string // optional: requires email_verified=true and a matching domain (ASCII, case-insensitive, exact)
DisableIframeLogoutDetection bool // stop treating Sec-Fetch-Dest: iframe on /logout as front-channel; a cross-site iframe load of /logout is then refused (403)
Sessions *session.Store // required; not SameSite Strict (the callback would arrive without the cookie)
HTTPClient *http.Client // default 10 s timeout; used for discovery, token exchange, JWKS; a redirect fails the call
OnLogin auth.Hook // e.g. keycloak.Client.Enrich, or an app closure calling it and then provisioning
OnLogout auth.LogoutHook // runs after a session was ended by /logout or /frontchannel-logout
Observer auth.Observer // optional; one outcome per request
}
Options configure a Provider.
type Profile ¶
type Profile struct {
Username, Name, Email, Picture string
}
Profile is what a ClaimMapper reads from ID token claims.
func DefaultClaimMapper ¶
DefaultClaimMapper reads preferred_username, name, email and picture; a claim that is missing or not a string stays empty. Fits Keycloak and most standard providers.
func GoogleClaimMapper ¶
GoogleClaimMapper reads name, email and picture; Username is the part of email before "@" (Google sends no preferred_username).
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider is one OpenID Connect identity provider. It is safe for concurrent use.
func New ¶
New validates options and runs discovery (network I/O with ctx; fails fast). It reads end_session_endpoint and the token endpoint's client authentication from the provider metadata and refuses discovered endpoints that are not https (or http on a loopback host), like the Issuer. The JWKS is fetched later, on the first callback, with HTTPClient. New does not modify options, and changing them afterwards has no effect.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"github.com/govengers/auth"
"github.com/govengers/auth/internal/fakeprovider"
"github.com/govengers/auth/oidc"
"github.com/govengers/auth/session"
)
// startExampleIssuer serves the test fake in place of Keycloak or Google; a real application passes its
// IdP's issuer URL instead.
func startExampleIssuer() (*httptest.Server, error) {
server := httptest.NewUnstartedServer(nil)
handler, err := fakeprovider.NewOIDCHandler(&fakeprovider.OIDCOptions{
IssuerURL: "http://" + server.Listener.Addr().String(), ClientID: "app", ClientSecret: "app-secret",
})
if err != nil {
server.Close()
return nil, err
}
server.Config.Handler = handler
server.Start()
return server, nil
}
func main() {
issuer, err := startExampleIssuer()
if err != nil {
fmt.Println(err)
return
}
defer issuer.Close()
backend, err := session.NewMemoryBackend(nil)
if err != nil {
fmt.Println(err)
return
}
store, err := session.NewStore(&session.StoreOptions{Backend: backend, EncryptionKey: []byte("0123456789abcdef0123456789abcdef")})
if err != nil {
fmt.Println(err)
return
}
provider, err := oidc.New(context.Background(), &oidc.Options{
Name: "keycloak",
Issuer: issuer.URL, // e.g. https://sso.example.com/realms/staff
ClientID: "app",
ClientSecret: "app-secret",
ExternalURL: "https://app.example.com", // register https://app.example.com/auth/oidc/keycloak/callback at the IdP
Sessions: store,
})
if err != nil {
fmt.Println(err)
return
}
guard, err := session.NewGuard(&session.GuardOptions{Store: store, LoginURL: provider.LoginURL()})
if err != nil {
fmt.Println(err)
return
}
serveMux := http.NewServeMux()
auth.RegisterRoutes(serveMux, provider.Routes())
serveMux.Handle("GET /reports", guard.RequireUI()(auth.IdentityHandler()))
recorder := httptest.NewRecorder()
serveMux.ServeHTTP(recorder, httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/reports", nil))
for _, route := range provider.Routes() {
fmt.Println(route.Method, route.Path)
}
fmt.Println(recorder.Code, recorder.Header().Get("Location"))
}
Output: GET /auth/oidc/keycloak/login GET /auth/oidc/keycloak/callback GET /auth/oidc/keycloak/logout GET /auth/oidc/keycloak/frontchannel-logout 302 https://app.example.com/auth/oidc/keycloak/login?return_to=%2Freports
func (*Provider) LoginURL ¶
LoginURL returns ExternalURL + PathPrefix + "/login", absolute so a forward-auth redirect reaches the browser unchanged (session.GuardOptions.LoginURL).
Directories
¶
| Path | Synopsis |
|---|---|
|
Package keycloak enriches OIDC identities from the Keycloak Admin API: composite realm roles, client roles of one client, group paths and user attributes, and refuses users without the required roles or groups.
|
Package keycloak enriches OIDC identities from the Keycloak Admin API: composite realm roles, client roles of one client, group paths and user attributes, and refuses users without the required roles or groups. |