Documentation
¶
Index ¶
- Constants
- Variables
- func ClockSkew(d time.Duration) *time.Duration
- func Match(value string) *string
- func SessionCookieName(projectID string, secure bool) string
- func Tolerance(seconds int) *int
- func VerifyWebhook(input WebhookInput) (bool, error)
- type Environment
- type ErrorCode
- type JWK
- type KeySource
- type Membership
- type PublishableKey
- type Query
- type RemoteKeySource
- type StaticKeySource
- type VerificationError
- type VerifiedToken
- type Verifier
- type WebhookInput
Constants ¶
const DefaultClockTolerance = 60 * time.Second
DefaultClockTolerance is the skew allowed on exp/nbf when none is configured.
const MaxClockTolerance = 300 * time.Second
MaxClockTolerance caps the configurable skew. A tolerance larger than this keeps revoked tokens alive too long to be called authorization.
Variables ¶
var ErrPublishableKeyRequired = errors.New("authowl: publishableKey is required")
ErrPublishableKeyRequired is returned for an empty key.
var ErrSecretKeySupplied = errors.New(
"authowl: a secret key was passed where a publishable key was expected; " +
"never embed secret keys in client code",
)
ErrSecretKeySupplied is returned when a secret key reaches a function that wants a publishable one. This check runs BEFORE any shape validation and is a hard rule across every AuthOwl SDK: a leaked `sk_` key is a full compromise of the project, so it must never be quietly accepted as a publishable key.
var ErrWebhookConfig = errors.New("authowl: invalid webhook verification config")
ErrWebhookConfig reports invalid LOCAL configuration - bad secrets or an out-of-range tolerance. It is deliberately an error rather than a false return: untrusted input should be rejected quietly, but a misconfigured endpoint silently dropping every delivery is a bug that must be loud.
Functions ¶
func Match ¶
Match marks a Query criterion as present. An empty value remains present and therefore fails closed instead of being treated as omitted.
func SessionCookieName ¶
SessionCookieName returns the exact session-cookie name the AuthOwl server sets for a project.
dev (http): p_<idNoDashes>.session_token prod (https): __Secure-p_<idNoDashes>.session_token
Note the DOT joining the prefix and the name, and the `__Secure-` (not `__Host-`) prefix - both are easy to get wrong by hand, and getting them wrong means reading a cookie the server never set. `secure` must reflect the SERVER's cookie mode: derive it from the API URL's scheme (https => true).
func VerifyWebhook ¶
func VerifyWebhook(input WebhookInput) (bool, error)
VerifyWebhook reports whether a delivery carries a valid signature.
Returns (false, nil) for anything wrong with the untrusted request, and a non-nil error only for invalid local configuration.
Types ¶
type Environment ¶
type Environment string
Environment is the deployment a publishable key belongs to.
const ( EnvironmentLive Environment = "live" EnvironmentTest Environment = "test" )
type ErrorCode ¶
type ErrorCode string
ErrorCode identifies why a token was refused. The codes are shared verbatim with every other AuthOwl SDK (see conformance/vectors/jwt-verify.json), so a log line from the Go SDK means the same thing as one from the TypeScript SDK.
const ( ErrTokenVerificationFailed ErrorCode = "TOKEN_VERIFICATION_FAILED" ErrTokenConfigInvalid ErrorCode = "TOKEN_CONFIG_INVALID" ErrTokenMalformed ErrorCode = "TOKEN_MALFORMED" ErrTokenAlgorithmUnsupported ErrorCode = "TOKEN_ALGORITHM_UNSUPPORTED" ErrTokenSignatureInvalid ErrorCode = "TOKEN_SIGNATURE_INVALID" ErrTokenClaimInvalid ErrorCode = "TOKEN_CLAIM_INVALID" ErrJWKSFetchFailed ErrorCode = "JWKS_FETCH_FAILED" ErrJWKSFetchTimeout ErrorCode = "JWKS_FETCH_TIMEOUT" ErrJWKSHTTPError ErrorCode = "JWKS_HTTP_ERROR" ErrJWKSResponseTooLarge ErrorCode = "JWKS_RESPONSE_TOO_LARGE" ErrJWKSDocumentInvalid ErrorCode = "JWKS_DOCUMENT_INVALID" ErrJWKSTooManyKeys ErrorCode = "JWKS_TOO_MANY_KEYS" ErrJWKSKeyInvalid ErrorCode = "JWKS_KEY_INVALID" ErrJWKSDuplicateKID ErrorCode = "JWKS_DUPLICATE_KID" ErrJWKSKeyNotFound ErrorCode = "JWKS_KEY_NOT_FOUND" )
type JWK ¶
type JWK struct {
Alg string
Crv string
Kid string
Kty string
Use string
X string
Y string
// contains filtered or unexported fields
}
JWK is a published ES256 verification key.
type KeySource ¶
type KeySource interface {
// ResolveKey returns the key for kid, or the first published key when kid is
// empty. It must return a *VerificationError with ErrJWKSKeyNotFound when no
// key matches.
ResolveKey(ctx context.Context, kid string) (*JWK, error)
}
KeySource resolves the verification key named by a token's `kid`.
type Membership ¶
type Membership struct {
// Role is the member's canonical role key (owner/admin/member or a project role).
Role string `json:"role"`
// Permissions holds the effective permission ids: `org:sys_*` system claims
// plus the operator's custom `org:<feature>:<action>` ids.
Permissions []string `json:"permissions"`
// Teams are the team ids held inside the ACTIVE organization.
//
// Nil (not empty) when the token carries no `teams` claim at all, which is
// what a token minted before teams shipped looks like. HasTeam then reports
// false rather than guessing: an absent claim is never read as "any team".
Teams []string `json:"teams,omitempty"`
}
Membership is the active-organization membership carried by a verified token.
func (*Membership) Has ¶
func (m *Membership) Has(query Query) bool
Has reports whether the membership satisfies EVERY criterion in the query. Returns false when there is no membership, and false for an empty query - asking nothing never grants anything.
func (*Membership) HasPermission ¶
func (m *Membership) HasPermission(permission string) bool
HasPermission reports whether the membership's permission claim includes permission. Exact match only - no prefix or substring matching.
func (*Membership) HasTeam ¶
func (m *Membership) HasTeam(teamID string) bool
HasTeam reports whether the membership's team claim includes teamID.
Teams are pure grouping: belonging to one grants nothing on its own, so this is for the application's own gating, never an authority check.
type PublishableKey ¶
type PublishableKey struct {
Prefix string
Env Environment
ProjectID string
}
PublishableKey is the decoded form of a pk_live_… / pk_test_… key.
func DecodePublishableKey ¶
func DecodePublishableKey(key string) (PublishableKey, error)
DecodePublishableKey validates a publishable key and extracts its project id.
type Query ¶
type Query struct {
Role *string `json:"role,omitempty"`
Permission *string `json:"permission,omitempty"`
TeamID *string `json:"teamId,omitempty"`
}
Query is a Clerk-style has() query. Every present field must hold (AND). Pointers preserve the difference between an omitted criterion and an explicit empty string, which must deny rather than silently disappearing.
type RemoteKeySource ¶
type RemoteKeySource struct {
URI string
Client *http.Client
// Now is injectable for deterministic tests. Nil means time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
RemoteKeySource fetches and caches a project's published JWKS.
An unknown `kid` may be a freshly rotated key, so it forces ONE cache-bypassing refetch to try to pick it up. That forced refetch is rate-limited: a flood of bogus-kid tokens must not turn into a flood of outbound JWKS requests, which would be a cheap amplification lever against the issuer. Legitimate rotation is unaffected - the server keeps signing with the old kid long enough for the normal TTL refresh to carry the new one.
func NewRemoteKeySource ¶
func NewRemoteKeySource(uri string) *RemoteKeySource
NewRemoteKeySource returns a key source that reads the project's JWKS URL.
func (*RemoteKeySource) ResolveKey ¶
ResolveKey implements KeySource.
type StaticKeySource ¶
type StaticKeySource struct{ Keys []*JWK }
StaticKeySource serves a fixed key set. Use it in tests, or when keys are provisioned out of band rather than fetched.
func NewStaticKeySource ¶
func NewStaticKeySource(document []byte) (*StaticKeySource, error)
NewStaticKeySource parses a JWKS document once and serves it forever.
func (*StaticKeySource) ResolveKey ¶
ResolveKey implements KeySource.
type VerificationError ¶
VerificationError is returned by Verify and the JWKS parser. Match on Code, never on Message: the codes are contractual, the messages are not.
func (*VerificationError) Error ¶
func (e *VerificationError) Error() string
type VerifiedToken ¶
type VerifiedToken struct {
// Subject is the signed-in user id, or "" when the token carries no `sub`.
Subject string
// Membership is the active-org membership, or nil when the token carries none.
Membership *Membership
// Claims is the full verified claim set, for callers reading extra claims.
Claims map[string]any
}
VerifiedToken is the result of a successful verification.
type Verifier ¶
type Verifier struct {
// Issuer is the expected `iss`: the project's AuthOwl auth base URL.
Issuer string
// Audience is the expected `aud`: the project id.
Audience string
// Keys resolves verification keys. Use NewRemoteKeySource in production.
Keys KeySource
// ClockTolerance bounds exp/nbf skew, from 0 through MaxClockTolerance.
//
// A POINTER so "unset" and "explicitly zero" stay distinct: nil means
// DefaultClockTolerance, while ClockSkew(0) demands an exact match. A plain
// duration would let Go's zero value turn the strictest setting into the
// default one.
ClockTolerance *time.Duration
// Now is injectable for deterministic tests. Nil means time.Now.
Now func() time.Time
}
Verifier performs stateless verification of AuthOwl project JWTs.
This is the REAL server-side authorization primitive. It verifies the ES256 signature against the project's published JWKS and checks issuer, audience, and expiry BEFORE reading any claim, so no permission is ever granted off an unverified claim.
func (*Verifier) Has ¶
Has verifies the token and evaluates the query against its membership.
Fails CLOSED: an invalid, tampered, expired, or wrong-audience token returns false rather than an error, so a caller that ignores errors still denies. Configuration mistakes are the exception - those return an error, because a misconfigured backend silently denying every request is far worse to debug.
func (*Verifier) HasPermission ¶
HasPermission verifies the token and reports whether it grants permission. Fails closed exactly like Has.
type WebhookInput ¶
type WebhookInput struct {
// RawBody must be the EXACT request bytes. Do not parse and re-serialize the
// JSON before verifying - re-serialization reorders keys and breaks the HMAC.
RawBody []byte
// Timestamp is the delivery's timestamp header, as a string.
Timestamp string
// SignatureHeader is the delivery's signature header (`v1=<hex>`, comma separated).
SignatureHeader string
// Secrets holds the current and, during rotation overlap, the previous secret.
Secrets []string
// Now is the current time in Unix seconds.
Now int64
// ToleranceSeconds bounds accepted clock skew, from 0 through 3600.
//
// A POINTER so that "unset" and "explicitly zero" stay distinct: nil means
// the 300-second default, while Tolerance(0) demands an exact-second match.
// With a plain int, Go's zero value would silently turn the strictest
// setting into the loosest one.
ToleranceSeconds *int
}
WebhookInput is one delivery to verify.