Documentation ¶
Overview ¶
Package tokens creates tokenmanager, responsible for signing, issuing, and validating tokens
Index ¶
- Constants
- Variables
- func ExpiresAt(tks string) (_ time.Time, err error)
- func GetAlgorithms() (algs []string)
- func IsExpired(tks string) (bool, error)
- func NotBefore(tks string) (_ time.Time, err error)
- func ParseUnverified(tks string) (claims *jwt.RegisteredClaims, err error)
- func RegisterSigningMethod(alg string, f func() SigningMethod)
- type CachedJWKSValidator
- type Claims
- type Config
- type DiscoveryJSON
- type JWKSValidator
- type Keyfunc
- type MockValidator
- type OrgInviteToken
- type ParseError
- type ResetToken
- type SigningInfo
- type SigningMethod
- type Token
- type TokenManager
- func (tm *TokenManager) Config() Config
- func (tm *TokenManager) CreateAccessToken(claims *Claims) (_ *jwt.Token, err error)
- func (tm *TokenManager) CreateRefreshToken(accessToken *jwt.Token) (refreshToken *jwt.Token, err error)
- func (tm *TokenManager) CreateToken(claims *Claims) *jwt.Token
- func (tm *TokenManager) CreateTokenPair(claims *Claims) (accessToken, refreshToken string, err error)
- func (tm *TokenManager) CurrentKey() ulid.ULID
- func (tm *TokenManager) Keys() (keys jwk.Set, err error)
- func (v *TokenManager) Parse(tks string) (claims *Claims, err error)
- func (tm *TokenManager) RefreshAudience() string
- func (tm *TokenManager) Sign(token *jwt.Token) (string, error)
- func (v *TokenManager) Verify(tks string) (claims *Claims, err error)
- type TokenResponse
- type ValidationError
- type Validator
- type VerificationToken
Constants ¶
const ( // ValidationErrorMalformed is returned when the token is malformed ValidationErrorMalformed uint32 = 1 << iota // ValidationErrorUnverifiableToken is returned when the token could not be verified because of signing problems ValidationErrorUnverifiable // ValidationErrorSignatureInvalid is returned when the signature is invalid ValidationErrorSignatureInvalid // ValidationErrorAudience is returned when AUD validation failed ValidationErrorAudience // ValidationErrorExpired is returned when EXP validation failed ValidationErrorExpired // ValidationErrorIssuedAt is returned when IAT validation failed ValidationErrorIssuedAt // ValidationErrorIssuer is returned when ISS validation failed ValidationErrorIssuer // ValidationErrorNotValidYet is returned when NBF validation failed ValidationErrorNotValidYet // ValidationErrorID is returned when JTI validation failed ValidationErrorID // ValidationErrorClaimsInvalid is returned when there is a generic claims validation failure ValidationErrorClaimsInvalid )
The errors that might occur when parsing and validating a token
const DefaultRefreshAudience = "https://auth.datum.net/v1/refresh"
Variables ¶
var ( // ErrTokenManagerFailedInit returns when the token manager was not correctly provided signing keys ErrTokenManagerFailedInit = errors.New("token manager not initialized with signing keys") // ErrFailedRetrieveClaimsFromToken returns when claims can not be retrieved from an access token ErrFailedRetrieveClaimsFromToken = errors.New("could not retrieve claims from access token") // ErrTokenMissingKid returns when the kid cannot be found in the header of the token ErrTokenMissingKid = errors.New("token does not have kid in header") // ErrFailedParsingKid returns when the kid could not be parsed ErrFailedParsingKid = errors.New("could not parse kid: %s") // ErrUnknownSigningKey returns when the signing key fetched does not match the loaded managed keys ErrUnknownSigningKey = errors.New("unknown signing key") )
Error constants
var ( // ErrTokenMalformed returns when a token is malformed ErrTokenMalformed = errors.New("token is malformed") // ErrTokenUnverifiable is returned when the token could not be verified because of signing problems ErrTokenUnverifiable = errors.New("token is unverifiable") // ErrTokenSignatureInvalid is returned when the signature is invalid ErrTokenSignatureInvalid = errors.New("token signature is invalid") // ErrTokenInvalidAudience is returned when AUD validation failed ErrTokenInvalidAudience = errors.New("token has invalid audience") // ErrTokenExpired is returned when EXP validation failed ErrTokenExpired = errors.New("token is expired") // ErrTokenUsedBeforeIssued is returned when the token is used before issued ErrTokenUsedBeforeIssued = errors.New("token used before issued") // ErrTokenInvalidIssuer is returned when ISS validation failed ErrTokenInvalidIssuer = errors.New("token has invalid issuer") // ErrTokenNotValidYet is returned when NBF validation failed ErrTokenNotValidYet = errors.New("token is not valid yet") // ErrTokenNotValid is returned when the token is invalid ErrTokenNotValid = errors.New("token is invalid") // ErrTokenInvalidID is returned when the token has an invalid id ErrTokenInvalidID = errors.New("token has invalid id") // ErrTokenInvalidClaims is returned when the token has invalid claims ErrTokenInvalidClaims = errors.New("token has invalid claims") // ErrMissingEmail is returned when the token is attempted to be verified but the email is missing ErrMissingEmail = errors.New("unable to create verification token, email is missing") // ErrTokenMissingEmail is returned when the verification is missing an email address ErrTokenMissingEmail = errors.New("email verification token is missing email address") // ErrInvalidSecret is returned when the verification contains of secret of invalid length ErrInvalidSecret = errors.New("email verification token contains an invalid secret") // ErrMissingUserID is returned when a reset token is trying to be created but no user id is provided ErrMissingUserID = errors.New("unable to create reset token, user id is required") // ErrTokenMissingUserID is returned when the reset token is missing the required user id ErrTokenMissingUserID = errors.New("reset token is missing user id") // ErrInviteTokenMissingOrgID is returned when the invite token is missing the org owner ID match ErrInviteTokenMissingOrgID = errors.New("invite token is missing org id") // ErrInvitetokenMissingEmail ErrInvitetokenMissingEmail = errors.New("invite token is missing email") // ErrExpirationIsRequired is returned when signing info is provided a zero-value expiration ErrExpirationIsRequired = errors.New("signing info requires a non-zero expiration") // ErrFailedSigning is returned when an error occurs when trying to generate signing info with expiration ErrFailedSigning = errors.New("error occurred when attempting to signing info") // ErrTokenInvalid is returned when unable to verify the token with the signature and secret provided ErrTokenInvalid = errors.New("unable to verify token") )
var TimeFunc = time.Now
Functions ¶
func GetAlgorithms ¶
func GetAlgorithms() (algs []string)
GetAlgorithms returns a list of registered "alg" names
func ParseUnverified ¶
func ParseUnverified(tks string) (claims *jwt.RegisteredClaims, err error)
ParseUnverified parses a string of tokens and returns the claims and any error encountered
func RegisterSigningMethod ¶
func RegisterSigningMethod(alg string, f func() SigningMethod)
RegisterSigningMethod registers the "alg" name and a factory function for signing method
Types ¶
type CachedJWKSValidator ¶
type CachedJWKSValidator struct { JWKSValidator // contains filtered or unexported fields }
CachedJWKSValidator struct is a type that extends the functionality of the `JWKSValidator` struct. It adds caching capabilities to the JWKS validation process. It includes a `cache` field of type `*jwk.Cache` to store and retrieve the JWKS, an `endpoint` field to specify the endpoint from which to fetch the JWKS, and embeds the `JWKSValidator` struct to inherit its methods and fields. The `CachedJWKSValidator` struct also includes additional methods `Refresh` and`keyFunc` to handle the caching logic
func NewCachedJWKSValidator ¶
func NewCachedJWKSValidator(ctx context.Context, cache *jwk.Cache, endpoint, audience, issuer string) (validator *CachedJWKSValidator, err error)
NewCachedJWKSValidator function is a constructor for creating a new instance of the `CachedJWKSValidator` struct. It takes in a `context.Context`, a `*jwk.Cache`, an endpoint string, an audience string, and an issuer string
func (*CachedJWKSValidator) Parse ¶
Parse an access or refresh token verifying its signature but without verifying its claims. This ensures that valid JWT tokens are still accepted but claims can be handled on a case-by-case basis; for example by validating an expired access token during reauthentication
func (*CachedJWKSValidator) Refresh ¶
func (v *CachedJWKSValidator) Refresh(ctx context.Context) (err error)
Refresh method in the `CachedJWKSValidator` struct is responsible for refreshing the JWKS (JSON Web Key Set) cache. It takes in a `context.Context` as a parameter and returns an error if the refresh process fails
type Claims ¶
type Claims struct { jwt.RegisteredClaims // UserID is the internal generated ID for the user UserID string `json:"user_id,omitempty"` // Email associated with the user Email string `json:"email,omitempty"` // OrgID the JWT token is valid for OrgID string `json:"org,omitempty"` // ParentOrgID of the parent organization, if a child ParentOrgID string `json:"parentorg,omitempty"` // Tier the token is valid for Tier string `json:"tier,omitempty"` // DisplayName of the user DisplayName string `json:"displayName,omitempty"` // AvatarURL of the user AvatarURL string `json:"avatarURL,omitempty"` }
Claims implements custom claims and extends the `jwt.RegisteredClaims` struct; we will store user-related elements here (and thus in the JWT Token) for reference / validation
func ParseUnverifiedTokenClaims ¶
ParseUnverifiedTokenClaims parses token claims from an access token
func (Claims) ParseEmail ¶
func (c Claims) ParseEmail() ulid.ULID
ParseEmail is used to parse and return the email from the `Email` field of the claims.
func (Claims) ParseOrgID ¶
func (c Claims) ParseOrgID() ulid.ULID
ParseOrgID parses and return the organization ID from the `OrgID` field of the claims
func (Claims) ParseParentOrgID ¶
func (c Claims) ParseParentOrgID() ulid.ULID
ParseParentOrgID parses and returns the parent organization ID from the ParentOrgID field of the claims
func (Claims) ParseUserID ¶
func (c Claims) ParseUserID() ulid.ULID
ParseUserID returns the ID of the user from the Subject of the claims
type Config ¶
type Config struct { // KID represents the Key ID used in the configuration. KID string `json:"kid" koanf:"kid" jsonschema:"required"` // Audience represents the target audience for the tokens. Audience string `json:"audience" koanf:"audience" jsonschema:"required" default:"https://datum.net"` // RefreshAudience represents the audience for refreshing tokens. RefreshAudience string `json:"refresh_audience" koanf:"refresh_audience"` // Issuer represents the issuer of the tokens Issuer string `json:"issuer" koanf:"issuer" jsonschema:"required" default:"https://auth.datum.net" ` // AccessDuration represents the duration of the access token is valid for AccessDuration time.Duration `json:"access_duration" koanf:"access_duration" default:"1h"` // RefreshDuration represents the duration of the refresh token is valid for RefreshDuration time.Duration `json:"refresh_duration" koanf:"refresh_duration" default:"2h"` // RefreshOverlap represents the overlap time for a refresh and access token RefreshOverlap time.Duration `json:"refresh_overlap" koanf:"refresh_overlap" default:"-15m" ` // JWKSEndpoint represents the endpoint for the JSON Web Key Set JWKSEndpoint string `json:"jwks_endpoint" koanf:"jwks_endpoint" default:"https://api.datum.net/.well-known/jwks.json"` // Keys represents the key pairs used for signing the tokens Keys map[string]string `json:"keys" koanf:"keys" jsonschema:"required"` }
Config defines the configuration settings for authentication tokens used in the server
type DiscoveryJSON ¶ added in v0.3.0
type DiscoveryJSON struct { Issuer string `json:"issuer"` AuthorizationEP string `json:"authorization_endpoint"` TokenEP string `json:"token_endpoint"` DeviceAuthorizationEP string `json:"device_authorization_endpoint"` UserInfoEP string `json:"userinfo_endpoint"` MFAChallengeEP string `json:"mfa_challenge_endpoint"` JWKSURI string `json:"jwks_uri"` RegistrationEP string `json:"registration_endpoint"` RevocationEP string `json:"revocation_endpoint"` ScopesSupported []string `json:"scopes_supported"` ResponseTypesSupported []string `json:"response_types_supported"` CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"` ResponseModesSupported []string `json:"response_modes_supported"` SubjectTypesSupported []string `json:"subject_types_supported"` IDTokenSigningAlgValues []string `json:"id_token_signing_alg_values_supported"` TokenEndpointAuthMethods []string `json:"token_endpoint_auth_methods_supported"` ClaimsSupported []string `json:"claims_supported"` RequestURIParameterSupported bool `json:"request_uri_parameter_supported"` }
DiscoveryJSON is the response from the OIDC discovery endpoint and contains supported scopes and claims, public keys used to sign the tokens, issuer, and other information. Clients can use this information to construct a request to the OpenID server. The field names and values are defined in the OpenID Connect Discovery Specification https://openid.net/specs/openid-connect-discovery-1_0.html
type JWKSValidator ¶
type JWKSValidator struct {
// contains filtered or unexported fields
}
JWKSValidator provides public verification that JWT tokens have been issued by the Datum authentication service by checking that the tokens have been signed using public keys from a JSON Web Key Set (JWKS). The validator then returns Datum specific claims if the token is in fact valid.
func NewJWKSValidator ¶
func NewJWKSValidator(keys jwk.Set, audience, issuer string) *JWKSValidator
NewJWKSValidator is a constructor for creating a new instance of the `JWKSValidator` struct. It takes in a `jwk.Set` containing the JSON Web Key Set (JWKS), as well as the audience and issuer strings. It initializes a new `JWKSValidator` with the provided JWKS, audience, and issuer
type Keyfunc ¶
Keyfunc will be used by the Parse methods as a callback function to supply the key for verification
type MockValidator ¶
type OrgInviteToken ¶ added in v0.2.6
type OrgInviteToken struct { Email string `msgpack:"email"` OrgID ulid.ULID `msgpack:"organization_id"` SigningInfo }
OrgInviteToken packages an email address with random data and an expiration time so that it can be serialized and hashed into a token which can be sent to users
func NewOrgInvitationToken ¶ added in v0.2.6
func NewOrgInvitationToken(email string, orgID ulid.ULID) (token *OrgInviteToken, err error)
NewOrgInvitationToken creates a token struct from an email address that expires in 14 days
func (*OrgInviteToken) Sign ¶ added in v0.2.6
func (t *OrgInviteToken) Sign() (string, []byte, error)
Sign creates a base64 encoded string from the token data so that it can be sent to users as part of a URL. The returned secret should be stored in the database so that the string can be recomputed when verifying a user provided token.
type ParseError ¶
ParseError is defining a custom error type called `ParseError`
func (*ParseError) Error ¶
func (e *ParseError) Error() string
Error returns the ParseError in string format
type ResetToken ¶
type ResetToken struct { UserID ulid.ULID `msgpack:"user_id"` SigningInfo }
ResetToken packages a user ID with random data and an expiration time so that it can be serialized and hashed into a token which can be sent to users
func NewResetToken ¶
func NewResetToken(id ulid.ULID) (token *ResetToken, err error)
NewResetToken creates a token struct from a user ID that expires in 15 minutes
func (*ResetToken) Sign ¶
func (t *ResetToken) Sign() (string, []byte, error)
Sign creates a base64 encoded string from the token data so that it can be sent to users as part of a URL. The returned secret should be stored in the database so that the string can be recomputed when verifying a user provided token
type SigningInfo ¶
type SigningInfo struct { ExpiresAt time.Time `msgpack:"expires_at"` Nonce []byte `msgpack:"nonce"` }
SigningInfo contains an expiration time and a nonce that is used to sign the token
func NewSigningInfo ¶
func NewSigningInfo(expires time.Duration) (info SigningInfo, err error)
NewSigningInfo creates new signing info with a time expiration
func (SigningInfo) IsExpired ¶
func (d SigningInfo) IsExpired() bool
type SigningMethod ¶
type SigningMethod interface { // Verify returns nil if signature is valid Verify(signingString, signature string, key interface{}) error // Sign returns encoded signature or error Sign(signingString string, key interface{}) (string, error) // Alg returns the alg identifier for this method (example: 'HS256') Alg() string }
SigningMethod can be used add new methods for signing or verifying tokens
func GetSigningMethod ¶
func GetSigningMethod(alg string) (method SigningMethod)
GetSigningMethod retrieves a signing method from an "alg" string
type Token ¶
type Token struct { // Raw is the raw token; populated when you parse a token Raw string // Method is the signing metehod of the token Method SigningMethod // Header is the first segment of the token Header map[string]interface{} // Claims is the second segment of the token Claims Claims ClaimBytes []byte ToBeSignedString string // Signature is the third segment of the token; populated when you parse a token Signature string // Valid is a bool determining if the token is valid; populated when you parse or verify a toekn Valid bool }
Token represents a JWT Token. Different fields will be used depending on whether you're creating or parsing/verifying a token
type TokenManager ¶
type TokenManager struct {
// contains filtered or unexported fields
}
func New ¶
func New(conf Config) (tm *TokenManager, err error)
New creates a TokenManager with the specified keys which should be a mapping of ULID strings to paths to files that contain PEM encoded RSA private keys. This input is specifically designed for the config environment variable so that keys can be loaded from k8s or vault secrets that are mounted as files on disk
func NewWithKey ¶
func NewWithKey(key *rsa.PrivateKey, conf Config) (tm *TokenManager, err error)
NewWithKey is a constructor function that creates a new instance of the TokenManager struct with a specified RSA private key. It takes in the private key as a parameter and initializes the TokenManager with the provided key, along with other configuration settings from the TokenConfig struct. It returns the created TokenManager instance or an error if there was a problem initializing the TokenManager.
func (*TokenManager) Config ¶ added in v0.3.0
func (tm *TokenManager) Config() Config
Config returns the token manager config
func (*TokenManager) CreateAccessToken ¶
func (tm *TokenManager) CreateAccessToken(claims *Claims) (_ *jwt.Token, err error)
CreateAccessToken from the credential payload or from an previous token if the access token is being reauthorized from previous credentials or an already issued access token
func (*TokenManager) CreateRefreshToken ¶
func (tm *TokenManager) CreateRefreshToken(accessToken *jwt.Token) (refreshToken *jwt.Token, err error)
CreateRefreshToken from the Access token claims with predefined expiration
func (*TokenManager) CreateToken ¶
func (tm *TokenManager) CreateToken(claims *Claims) *jwt.Token
CreateToken from the claims payload without modifying the claims unless the claims are missing required fields that need to be updated
func (*TokenManager) CreateTokenPair ¶
func (tm *TokenManager) CreateTokenPair(claims *Claims) (accessToken, refreshToken string, err error)
CreateTokenPair returns signed access and refresh tokens for the specified claims in one step since usually you want both access and refresh tokens at the same time
func (*TokenManager) CurrentKey ¶
func (tm *TokenManager) CurrentKey() ulid.ULID
CurrentKey returns the ulid of the current key being used to sign tokens - this is just the identifier of the key, not the key itself
func (*TokenManager) Keys ¶
func (tm *TokenManager) Keys() (keys jwk.Set, err error)
Keys returns the JWKS with public keys for use externally
func (*TokenManager) Parse ¶
Parse an access or refresh token verifying its signature but without verifying its claims. This ensures that valid JWT tokens are still accepted but claims can be handled on a case-by-case basis; for example by validating an expired access token during reauthentication
func (*TokenManager) RefreshAudience ¶
func (tm *TokenManager) RefreshAudience() string
RefreshAudience returns the refresh audience for the token manager; The refresh audience in plain-human-speak is the URL where the refresh token should be sent for validation (which is our datum endpoint)
type TokenResponse ¶ added in v0.3.1
type TokenResponse struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` TokenType string `json:"token_type"` ExpiresIn int64 `json:"expires_in"` }
TokenResponse is the request response when exchanging an oauth token from one provider to a Datum issued token
type ValidationError ¶
ValidationError represents an error from Parse if token is not valid
func NewValidationError ¶
func NewValidationError(errorText string, errorFlags uint32) *ValidationError
NewValidationError is a helper for constructing a ValidationError with a string error message
func (ValidationError) Error ¶
func (e ValidationError) Error() string
Error is the implementation of the err interface for ValidationError
func (*ValidationError) Is ¶
func (e *ValidationError) Is(err error) bool
Is checks if this ValidationError is of the supplied error. We are first checking for the exact error message by comparing the inner error message. If that fails, we compare using the error flags. This way we can use custom error messages and leverage errors.Is using the global error variables, plus I just learned how to use errors.Is today so this is pretty sweet
func (*ValidationError) Unwrap ¶
func (e *ValidationError) Unwrap() error
Unwrap gives errors.Is and errors.As access to the inner errors defined above
type Validator ¶
type Validator interface { // Verify an access or a refresh token after parsing and return its claims Verify(tks string) (claims *Claims, err error) // Parse an access or refresh token without verifying claims (e.g. to check an expired token) Parse(tks string) (claims *Claims, err error) }
Validator are able to verify that access and refresh tokens were issued by Datum and that their claims are valid (e.g. not expired).
type VerificationToken ¶
type VerificationToken struct { Email string `msgpack:"email"` SigningInfo }
VerificationToken packages an email address with random data and an expiration time so that it can be serialized and hashed into a token which can be sent to users
func NewVerificationToken ¶
func NewVerificationToken(email string) (token *VerificationToken, err error)
NewVerificationToken creates a token struct from an email address that expires in 7 days
func (*VerificationToken) Sign ¶
func (t *VerificationToken) Sign() (string, []byte, error)
Sign creates a base64 encoded string from the token data so that it can be sent to users as part of a URL. The returned secret should be stored in the database so that the string can be recomputed when verifying a user provided token.