ssoadapter

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package ssoadapter provides a hardened, generic OpenID Connect implementation of the credbound.SSOProvider port, so hosts do not have to hand-roll the network side of SSO. Any spec-compliant OIDC issuer works through the generic adapter; Google and Microsoft Entra ID are plain OIDC issuers and need no dedicated code.

Responsibilities

The adapter owns the protocol exchange only: discovery, the authorization code redirect (with PKCE S256, state, and nonce), the code exchange, and ID token verification. Credbound keeps everything else — the sealed continuation carrying the adapter's opaque session, ceremony TTL, identity linking, persistence, audit, and revocation.

Registration

Register a provider by wiring it into credbound.Config.SSOProviders. Google through the generic adapter looks like this:

provider, err := ssoadapter.New(ssoadapter.Config{
	ConfigurationID: "0198b463-51a2-7cde-8000-0123456789ab", // UUIDv7 chosen by the host
	Kind:            credbound.SSOProviderGoogle,
	IssuerURL:       "https://accounts.google.com",
	ClientID:        os.Getenv("GOOGLE_CLIENT_ID"),
	ClientSecret:    os.Getenv("GOOGLE_CLIENT_SECRET"),
	RedirectURL:     "https://app.example.com/sso/callback",
})
if err != nil {
	log.Fatal(err)
}
manager, err := credbound.New(credbound.Config{
	Store:        store,
	Passwords:    hasher,
	SecretKey:    secretKey,
	SSOProviders: []credbound.SSOProvider{provider},
})

For Microsoft Entra ID use the tenant-specific issuer (https://login.microsoftonline.com/{tenant-id}/v2.0) with credbound.SSOProviderMicrosoft. The multi-tenant "common" endpoint is not supported because its issuer varies per tenant, which defeats strict issuer validation.

Callback handling

The host's HTTP callback handler forwards the provider response verbatim to credbound's FinishSSO. The adapter accepts the full callback URL, the bare query string, or a JSON object with "code" and "state" fields:

func callback(w http.ResponseWriter, r *http.Request) {
	continuation := readContinuationCookie(r)
	auth, err := manager.FinishSSO(r.Context(), continuation, []byte(r.URL.String()))
	// ...
}

Email trust

The adapter forwards the email claim only when the issuer asserts email_verified=true. An unverified email is attacker-controlled input at many IdPs (anyone can type an address into a profile), so surfacing it would let a hostile account impersonate a victim's address in credbound's identity records. Issuers that never send email_verified (Microsoft Entra ID among them) therefore produce identities without an email; linking and login still work because credbound keys SSO identities on issuer and subject, never on email.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrStateMismatch reports that the state parameter returned by the
	// issuer does not match the one issued in Begin.
	ErrStateMismatch = errors.New("ssoadapter: state parameter mismatch")
	// ErrNonceMismatch reports that the ID token nonce is missing or does
	// not match the one issued in Begin.
	ErrNonceMismatch = errors.New("ssoadapter: id token nonce mismatch")
)

Sentinel errors for the two verification failures hosts most often want to distinguish in logs. Credbound maps every Finish error to ErrInvalidCredentials before it reaches the end user.

Functions

This section is empty.

Types

type Config

type Config struct {
	// ConfigurationID is the host-chosen UUIDv7 under which credbound
	// indexes this provider and its linked identities. Required.
	ConfigurationID credbound.UUID
	// Kind is the credbound provider kind. Defaults to
	// credbound.SSOProviderOIDC; credbound.SSOProviderGoogle and
	// credbound.SSOProviderMicrosoft are also accepted since both speak
	// plain OIDC. Other kinds are rejected.
	Kind credbound.SSOProviderKind
	// IssuerURL is the OIDC issuer used for discovery and strict issuer
	// validation. Required. HTTPS is mandatory except for loopback hosts,
	// which may use HTTP for local development and tests.
	IssuerURL string
	// ClientID is the OAuth 2.0 client identifier. Required.
	ClientID string
	// ClientSecret authenticates the client at the token endpoint. Required
	// unless PublicClient is set; the default posture is a confidential
	// client.
	ClientSecret string
	// PublicClient marks this registration as a PKCE-only public client
	// without a client secret. When set, ClientSecret must be empty.
	PublicClient bool
	// RedirectURL is the callback URL registered at the issuer. Required.
	// HTTPS is mandatory except for loopback hosts.
	RedirectURL string
	// Scopes defaults to "openid email profile". The openid scope is always
	// enforced.
	Scopes []string
	// HTTPClient is used for discovery, key fetching, and the code
	// exchange. Defaults to a client with a 10 second timeout; a supplied
	// client without a timeout is shallow-copied and given the default.
	HTTPClient *http.Client
	// MetadataRefreshInterval bounds how long a discovered issuer document is
	// cached before it is re-discovered, so a rotated endpoint or jwks_uri is
	// picked up without a redeploy — the same posture as the SAML adapter's
	// metadata TTL. Zero uses a default of 12 hours.
	MetadataRefreshInterval time.Duration
	// Clock supplies the current time for token validation and step-up
	// freshness checks. Defaults to time.Now.
	Clock func() time.Time
}

Config describes one OIDC provider registration.

type Provider

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

Provider is a generic OIDC implementation of credbound.SSOProvider. It is stateless across ceremonies: everything a Finish needs travels inside the opaque Session bytes that credbound seals into its continuation.

func New

func New(config Config) (*Provider, error)

New validates the configuration and returns a Provider ready to register in credbound.Config.SSOProviders. Discovery is performed lazily on first use so construction does not require the issuer to be reachable.

func (*Provider) Begin

Begin implements credbound.SSOProvider. It generates fresh state, nonce, and PKCE S256 verifier material, builds the authorization code URL, and returns the material as opaque Session bytes for credbound to seal into its continuation. When ForceReauthentication is set the URL additionally carries prompt=login and max_age=0 so the issuer re-runs its own authentication (and MFA) policy.

func (*Provider) ConfigurationID

func (p *Provider) ConfigurationID() credbound.UUID

ConfigurationID implements credbound.SSOProvider.

func (*Provider) Finish

func (p *Provider) Finish(ctx context.Context, sessionBytes, response []byte) (credbound.SSOClaims, error)

Finish implements credbound.SSOProvider. sessionBytes is the Session issued by Begin (returned by credbound from its sealed continuation) and response is the raw callback payload from the host: the full callback URL, the bare query string, or a JSON object with code and state fields.

Finish verifies the state with a constant-time comparison, exchanges the code with the PKCE verifier, verifies the ID token (issuer, audience, expiry, RS256/ES256 allowlist — "none" and HMAC algorithms are rejected by construction), and requires a constant-time nonce match. Replay of a callback is bounded by credbound's continuation TTL and by the issuer's single-use authorization code; the adapter itself keeps no state.

func (*Provider) Kind

Kind implements credbound.SSOProvider.

Jump to

Keyboard shortcuts

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