federated

package
v1.27.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package federated is the contract a browser-redirect identity provider implements — OIDC, SAML, anything where the user leaves for an identity provider and comes back — and nothing else.

It is a second contract rather than a wider first one because the shapes genuinely differ. backend.Backend answers "do these credentials belong to a real user": one call, no browser, no state. A federated flow has no credentials to hand over at all. The application sends the browser away with a request it must remember, the provider answers to a different URL later, and only then is there an identity. Squeezing that into Authenticate(ctx, username, password) would have meant a password parameter nobody fills in and a stateful handshake hidden behind a stateless signature.

What the framework keeps

The provider does NOT own the anti-forgery state. The framework generates it, stores it, and refuses a callback that does not carry it back — before the provider is called at all. That division is the reason this package exists rather than a bare interface: the state parameter is the part of a redirect flow that is easy to leave out and impossible to notice missing, because a flow with no CSRF protection works perfectly well until somebody attacks it. An author who forgets it here cannot forget it: they are never handed the choice.

The provider is given a Nonce it may bind into its own protocol (an OIDC id_token nonce, a SAML RelayState) and must not confuse with the state: the state is the framework's, checked before Complete runs.

What the provider owns

Where to send the browser, and what a valid answer from its own identity provider looks like. Everything protocol-specific — signature validation, token exchange, claim mapping — belongs to the provider, because that is what differs between OIDC and SAML and what a third party knows better than this framework does.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrIdentityRejected is a CERTAIN no: the identity provider answered,
	// and the answer was that this person does not get in.
	ErrIdentityRejected = errors.New("auth: the identity provider rejected the sign-in")

	// ErrProviderUnavailable means the provider could not reach its
	// identity provider, or the answer was unusable. It is not a
	// rejection: an operator with a local account must still be able to
	// sign in on the morning the identity provider is down.
	ErrProviderUnavailable = errors.New("auth: identity provider unavailable")
)

Errors a provider returns. They mirror backend.ErrInvalidCredentials and backend.ErrBackendUnavailable deliberately: the reasons the distinction matters do not change because the protocol did.

Functions

func Register

func Register(name string, factory Factory) error

Register makes a federated provider TYPE selectable from configuration.

The name registered here is the protocol — "oidc", "saml" — not the identity provider an operator points it at. That separation is the difference from the credential-backend registry, and it is deliberate: having two identity providers of the same type is ordinary (a corporate tenant and a partner one), so the name an operator writes in auth_federated is an INSTANCE name they choose, and `provider:` names the type registered here. A registry keyed by type would have made the second tenant impossible to express without a second module.

Call it from an init function in the implementing package, then import that package for its side effects:

func init() {
    federated.Register("oidc", New)
}

A name already taken is an error rather than a silent replacement: two packages claiming "oidc" would make the effective provider depend on import order, which only ever shows up in somebody else's deployment.

func Registered

func Registered() []string

Registered returns every selectable provider type, sorted.

func Unregister

func Unregister(name string)

Unregister removes a registered provider type. It exists for tests that register a fake and must not leak it into the next one.

Types

type BeginRequest

type BeginRequest struct {
	// CallbackURL is the absolute URL the identity provider must send the
	// browser back to. The framework owns it — it is the route it wired
	// and the one it will be listening on — so a provider registers this
	// value with its identity provider rather than inventing one.
	CallbackURL string

	// Nonce is a single-use value the provider may bind into its own
	// protocol so the answer can be tied to this request. It is NOT the
	// anti-forgery state: that one never reaches the provider.
	Nonce string
}

BeginRequest is what the framework hands a provider to start a flow.

type CompleteRequest

type CompleteRequest struct {
	// Query and Form are the values the identity provider sent back. A
	// provider reads its own protocol's parameters from them; it is given
	// values rather than the request so that reaching for a header, a
	// cookie or the session is not possible by accident.
	Query url.Values
	Form  url.Values

	// State is exactly what Begin returned in Redirect.State.
	State map[string]string

	// Nonce is the one Begin was given, for a provider that must check it
	// against what its identity provider echoed back.
	Nonce string
}

CompleteRequest is the callback, parsed and vouched for.

type Factory

type Factory func(cfg backend.Config) (Provider, error)

Factory builds a configured provider. cfg carries the instance name and the `auth.<instance>.*` subtree, the same channel a credential backend receives — an operator configures a federated provider the way they configure any other, and a provider reads it with cfg.Bind.

func Lookup

func Lookup(name string) (Factory, bool)

Lookup returns the factory registered under a provider type.

type Provider

type Provider interface {
	// Name is the INSTANCE name the operator configured this provider
	// under — "corp", not "oidc". It appears in its URLs and its logs.
	Name() string

	// Begin starts a sign-in and says where to send the browser.
	Begin(ctx context.Context, req BeginRequest) (Redirect, error)

	// Complete validates what came back and returns the identity.
	//
	// The framework has already verified that the callback carries the
	// state it issued; a provider does not re-check it and must not
	// accept a callback that skips it, because it never sees one.
	Complete(ctx context.Context, req CompleteRequest) (*User, error)
}

Provider is one configured identity provider.

An implementation is built by a Factory and used concurrently: Begin and Complete may run for many sign-ins at once, so an implementation holds configuration, not per-flow state. The per-flow state is what Begin returns and what the framework carries.

type Redirect

type Redirect struct {
	// URL is the absolute URL to redirect to.
	URL string

	// State is whatever the provider needs back when the flow completes —
	// a PKCE verifier, a SAML request ID. The framework stores it with
	// the pending flow and returns it in CompleteRequest, and never
	// interprets it. It is not sent to the browser.
	State map[string]string
}

Redirect is where to send the browser to start a sign-in.

type User

type User = backend.User

User is the identity a completed flow yields. It is backend.User — the same type, not a parallel one — so an application that already knows how to receive an authenticated user does not learn a second shape.

Jump to

Keyboard shortcuts

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