Documentation
¶
Overview ¶
Package oidc verifies Google and Apple ID tokens for social sign-in and talks to the providers' OAuth token endpoints. It is deliberately not a general OIDC client: no discovery, no provider SDKs — just RS256 JWS verification against a cached remote JWKS plus the two token-endpoint calls moth needs (Apple code exchange/revoke, Google web code exchange).
moth's own tokens are ES256 and live in internal/jwt; this package only ever verifies third-party provider tokens, which are RS256.
Index ¶
Constants ¶
const ( GoogleIssuer = "https://accounts.google.com" GoogleJWKSURL = "https://www.googleapis.com/oauth2/v3/certs" GoogleTokenURL = "https://oauth2.googleapis.com/token" AppleBaseURL = "https://appleid.apple.com" )
Default endpoint locations, overridable on the clients for tests.
Variables ¶
var ( ErrMalformed = errors.New("malformed token") ErrInvalidSignature = errors.New("invalid signature") ErrExpired = errors.New("token expired") ErrIssuerMismatch = errors.New("issuer mismatch") ErrAudienceMismatch = errors.New("audience mismatch") ErrNonceMismatch = errors.New("nonce mismatch") ErrUnknownKey = errors.New("unknown key id") )
Verification errors.
Functions ¶
Types ¶
type AppleClient ¶
type AppleClient struct {
// contains filtered or unexported fields
}
AppleClient talks to Apple's OAuth token endpoints.
func NewAppleClient ¶
func NewAppleClient(baseURL, clientID string, secrets *AppleSecrets, httpc Doer) *AppleClient
NewAppleClient returns a client for Apple's token endpoints under baseURL (defaults to AppleBaseURL when empty; overridable for tests). httpc defaults to a timeout-bounded client when nil.
func (*AppleClient) ExchangeCode ¶
func (c *AppleClient) ExchangeCode(ctx context.Context, code, redirectURI string) (TokenResponse, error)
ExchangeCode trades an authorization code for tokens at {base}/auth/token. redirectURI must match the one used to obtain the code; native-app codes have none, so it may be empty.
type AppleSecretConfig ¶
type AppleSecretConfig struct {
TeamID string // iss
KeyID string // JWS kid header (Apple Key ID, from the developer portal)
ClientID string // sub: the Services ID (web) or bundle ID (native)
Key *ecdsa.PrivateKey
// Audience defaults to AppleBaseURL; overridable for tests.
Audience string
// Lifetime defaults to defaultAppleSecretLifetime.
Lifetime time.Duration
}
AppleSecretConfig identifies the Apple developer key that signs client secrets and the client they authenticate.
type AppleSecrets ¶
type AppleSecrets struct {
// contains filtered or unexported fields
}
AppleSecrets generates Apple client secrets (ES256 JWTs signed with the developer .p8) and caches each one until ~80% of its lifetime has elapsed.
func NewAppleSecrets ¶
func NewAppleSecrets(cfg AppleSecretConfig, now func() time.Time) *AppleSecrets
NewAppleSecrets returns a cached client-secret generator. now defaults to time.Now when nil.
func (*AppleSecrets) ClientSecret ¶
func (s *AppleSecrets) ClientSecret() (string, error)
ClientSecret returns a currently valid client secret, minting a fresh one when the cached secret is past 80% of its lifetime.
type Doer ¶
Doer is the subset of *http.Client the package needs; injectable so tests and the server package can point it at doubles.
type GoogleClient ¶
type GoogleClient struct {
// contains filtered or unexported fields
}
GoogleClient exchanges web-redirect-flow authorization codes at Google's token endpoint.
func NewGoogleClient ¶
func NewGoogleClient(tokenURL, clientID, clientSecret string, httpc Doer) *GoogleClient
NewGoogleClient returns a client for Google's token endpoint at tokenURL (defaults to GoogleTokenURL when empty; overridable for tests). httpc defaults to a timeout-bounded client when nil.
func (*GoogleClient) ExchangeCode ¶
func (c *GoogleClient) ExchangeCode(ctx context.Context, code, redirectURI string) (TokenResponse, error)
ExchangeCode trades an authorization code for tokens (including the id_token the caller then verifies). redirectURI must match the one used in the authorization request.
type Identity ¶
type Identity struct {
Issuer string
Subject string
Email string
// EmailVerified is true only when the provider asserts it in the token
// (Google sends a bool, Apple a bool or the string "true").
EmailVerified bool
Name string
GivenName string
FamilyName string
// ExpiresAt is the token's exp claim; callers use it to bound how long
// a consumed token's hash must be remembered for replay rejection.
ExpiresAt time.Time
}
Identity is the normalized result of a verified ID token. All fields come from the verified token, never from client-asserted request fields.
type KeySet ¶
type KeySet struct {
// contains filtered or unexported fields
}
KeySet fetches and caches a provider's RSA JWKS, resolving kids to public keys. Refreshes are single-flight: concurrent lookups share one fetch.
type NonceMode ¶
type NonceMode int
NonceMode selects how a token's nonce claim relates to the raw per-attempt nonce the client sent in the RPC.
const ( // NonceRaw: the token's nonce claim is the raw nonce itself. Google // echoes the nonce request parameter verbatim. NonceRaw NonceMode = iota // NonceSHA256Hex: the token's nonce claim is hex(SHA-256(raw)). Apple // echoes whatever the client sent it, and the SDKs send Apple the // SHA-256 hex digest (per Apple's scheme) while passing the raw value // in the RPC, so the server hashes before comparing. NonceSHA256Hex )
type Provider ¶
type Provider struct {
Name string
Issuers []string // exact-match set for the iss claim
JWKSURL string
NonceMode NonceMode
}
Provider describes one identity provider: which issuer values its tokens carry, where its JWKS lives, and its nonce convention. Fields are plain so tests and the server package can point them at test doubles.
type TokenError ¶
TokenError is a non-2xx token-endpoint response, carrying the OAuth error code (e.g. "invalid_grant") when the provider sent one.
func (*TokenError) Error ¶
func (e *TokenError) Error() string
type TokenResponse ¶
type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int64 `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
IDToken string `json:"id_token"`
}
TokenResponse is the successful body of an OAuth token-endpoint call.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier checks provider ID tokens for one provider against its JWKS.
func NewVerifier ¶
NewVerifier returns a verifier for p. httpc and now default to a timeout-bounded client and time.Now when nil.
func (*Verifier) Verify ¶
func (v *Verifier) Verify(ctx context.Context, idToken string, audiences []string, rawNonce string) (Identity, error)
Verify checks an ID token's signature (RS256 only — any other alg, including "none" and HS256, is rejected before key material is touched), issuer, audience membership, exp/iat and nonce, and returns the normalized identity. rawNonce is the per-attempt nonce the client sent in the RPC; the comparison against the token's nonce claim follows the provider's NonceMode. An empty rawNonce skips the nonce check — only the web-redirect flow, where the signed state parameter binds the attempt, may pass it empty.