Documentation
¶
Overview ¶
Package oauth implements provider-side OAuth code-exchange flows for authenticating end users into the identity service.
Identity does OAuth FOR LOGIN ONLY: it accepts an authorization code from the frontend, swaps it with the provider for an ID token (or userinfo response), verifies the user's identity, and then mints OUR own JWT. Provider access/refresh tokens are NEVER stored — that's a separate "connections" service concern.
The Exchanger interface decouples the auth flow from any specific provider; a Registry holds the per-provider implementations the service layer dispatches to via the "provider" string in the RPC.
Currently supported providers:
- "google": OIDC. ID token verified via JWKS (RS256).
- "microsoft": OIDC via Azure AD common endpoint. ID token verified via JWKS; per-tenant issuer accepted.
- "github": NOT OIDC. We exchange the code, then call /user and /user/emails to discover the canonical (verified, primary) email.
Index ¶
- Variables
- func CodeChallengeS256(verifier string) string
- func GenerateCodeVerifier() (string, error)
- func GenerateState() (string, error)
- func IssueStateToken(ctx context.Context, signer identityjwt.Signer, ...) (string, error)
- func WithCodeVerifier(ctx context.Context, codeVerifier string) context.Context
- type Authorizer
- type Exchanger
- type GitHubConfig
- type GoogleConfig
- type Identity
- type MicrosoftConfig
- type Registry
- type StateClaims
Constants ¶
This section is empty.
Variables ¶
var ( // ErrCodeExchangeFailed indicates the provider rejected our // token-endpoint POST or returned an unparseable response. ErrCodeExchangeFailed = errors.New("oauth: code exchange failed") // ErrIdentityVerification indicates the ID token signature, issuer, // audience, or expiry could not be validated. ErrIdentityVerification = errors.New("oauth: identity verification failed") // ErrEmailNotVerified indicates the provider returned an unverified // email; we refuse to log such users in. ErrEmailNotVerified = errors.New("oauth: provider reported email is not verified") // ErrStateValidation indicates the OAuth callback state could not be // validated against the server-minted state token. ErrStateValidation = errors.New("oauth: state validation failed") )
Common error sentinels. Callers (e.g. the service layer) can errors.Is against these to map to RPC error codes.
Functions ¶
func CodeChallengeS256 ¶
CodeChallengeS256 returns the PKCE S256 code challenge for verifier.
func GenerateCodeVerifier ¶
GenerateCodeVerifier returns a PKCE code verifier.
func GenerateState ¶
GenerateState returns a high-entropy OAuth state string.
func IssueStateToken ¶
Types ¶
type Authorizer ¶
type Authorizer interface {
AuthorizationURL(ctx context.Context, redirectURI, state, codeChallenge string) (string, error)
}
Authorizer builds the provider authorization URL for the first half of the OAuth authorization-code flow.
type Exchanger ¶
type Exchanger interface {
Exchange(ctx context.Context, code, redirectURI string) (*Identity, error)
}
Exchanger swaps an OAuth authorization code for a verified user identity. Implementations are responsible for:
- POSTing to the provider's token endpoint with client_id / client_secret / code / redirect_uri.
- Verifying the resulting ID token (OIDC providers) OR fetching userinfo (non-OIDC providers like GitHub).
- Returning a canonical Identity with EmailVerified guaranteed true.
Errors returned to callers are intentionally generic — they do not leak provider response bodies. Callers that need provider-specific debugging should inspect logs.
func NewGitHub ¶
func NewGitHub(cfg GitHubConfig) Exchanger
NewGitHub returns an Exchanger for GitHub. Note that GitHub does not implement OIDC; this exchanger calls the user/userEmails APIs directly using the access token returned by the token endpoint.
func NewGoogle ¶
func NewGoogle(cfg GoogleConfig) Exchanger
NewGoogle returns an Exchanger for Google OIDC.
func NewMicrosoft ¶
func NewMicrosoft(cfg MicrosoftConfig) Exchanger
NewMicrosoft returns an Exchanger for Microsoft Azure AD.
type GitHubConfig ¶
type GitHubConfig struct {
ClientID string
ClientSecret string
HTTPClient *http.Client
AuthorizationURL string
TokenURL string
UserURL string
UserMailURL string
}
GitHubConfig configures a GitHub Exchanger.
type GoogleConfig ¶
type GoogleConfig struct {
ClientID string
ClientSecret string
// HTTPClient overrides the http.Client used for token + JWKS
// requests. Optional; defaults to a 10s-timeout client.
HTTPClient *http.Client
// TokenURL overrides the token endpoint. Optional; defaults to
// googleTokenURL.
TokenURL string
// AuthorizationURL overrides the provider authorization endpoint.
// Optional; defaults to googleAuthorizationURL or the discovery
// document's authorization_endpoint when DiscoveryURL is set.
AuthorizationURL string
// JWKSURL overrides the JWKS endpoint. Optional; defaults to
// googleJWKSURL.
JWKSURL string
// DiscoveryURL overrides the OIDC discovery endpoint. When set,
// Exchange resolves token / JWKS / userinfo endpoints from it.
DiscoveryURL string
// UserinfoURL overrides the OIDC userinfo endpoint. Optional.
UserinfoURL string
// Issuer overrides the expected `iss` claim. Optional; defaults
// to googleIssuer.
Issuer string
// JWKSCacheTTL overrides the JWKS cache TTL. Optional; defaults
// to 1h.
JWKSCacheTTL time.Duration
// Now overrides the clock used for ID token expiry validation.
// Optional; defaults to time.Now.
Now func() time.Time
}
GoogleConfig configures a Google Exchanger. ClientID and ClientSecret are required; the rest default to the live Google endpoints and a 1h JWKS cache.
type Identity ¶
type Identity struct {
// ProviderUserID is the stable per-provider user identifier.
// For OIDC providers this is the "sub" claim. For GitHub this is
// the numeric user id (rendered as a string).
ProviderUserID string
// Email is the user's primary email address. Always lowercased.
Email string
// EmailVerified is true if the provider asserts the email is
// verified. Implementations MUST refuse to return an Identity if
// the provider says the email is not verified.
EmailVerified bool
// Name is the user's display name. May be empty.
Name string
// AvatarURL is a URL to the user's profile picture. May be empty.
AvatarURL string
// Provider is the provider key — "google", "microsoft", "github".
Provider string
}
Identity is the canonical, verified user identity returned by an Exchanger after a successful code exchange. Implementations MUST only return an Identity for verified users — i.e. the caller can rely on Email/ProviderUserID being authoritative.
type MicrosoftConfig ¶
type MicrosoftConfig struct {
ClientID string
ClientSecret string
HTTPClient *http.Client
// AuthorizationURL overrides the authorization endpoint. Optional.
AuthorizationURL string
// TokenURL overrides the token endpoint. Optional.
TokenURL string
// JWKSURL overrides the JWKS endpoint. Optional.
JWKSURL string
// TenantID controls the default tenant segment in the authorization
// endpoint when AuthorizationURL is not set. Optional; defaults to
// "common".
TenantID string
// IssuerFormat is a fmt.Sprintf format string into which the
// token's `tid` (tenant id) claim is interpolated to derive the
// expected issuer. Optional; defaults to the Microsoft format.
// In tests, set this to e.g. "%s" plus a fixed test issuer.
IssuerFormat string
JWKSCacheTTL time.Duration
Now func() time.Time
}
MicrosoftConfig configures a Microsoft Azure AD Exchanger.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps provider keys ("google", "microsoft", "github") to their Exchanger implementations. The service layer looks up the Exchanger for the provider named in the OAuthLoginRequest.
A nil *Registry is valid and reports every provider as missing. This lets the service treat "OAuth login disabled" as a registry without any registered providers (or no registry at all).
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry returns an empty registry. Callers populate it with Register.
func (*Registry) Get ¶
Get returns the Exchanger for the given provider key. The second return value is false if no Exchanger is registered.
func (*Registry) Providers ¶
Providers returns the sorted list of currently-registered provider keys. Useful for startup logging.
type StateClaims ¶
type StateClaims struct {
Provider string
RedirectURI string
State string
CodeVerifier string
IssuedAt int64
ExpiresAt int64
}
func VerifyStateToken ¶
func VerifyStateToken( token string, kp identityjwt.KeyProvider, expectedProvider, expectedRedirectURI, returnedState, explicitCodeVerifier string, now time.Time, ) (*StateClaims, error)