Documentation ¶
Index ¶
- Constants
- Variables
- func NewAccessToken(privateKey *rsa.PrivateKey, jti uuid.UUID, ident Identity, expireAt time.Time) (signedToken string, err error)
- type Authenticator
- func (a *Authenticator) AuthenticateClientBySecret(ctx context.Context, clientID uuid.UUID, secret []byte, meta *RequestMetadata) (c *client.Client, session *Session, signedToken string, err error)
- func (a *Authenticator) AuthenticateUserByPassword(ctx context.Context, username string, rawpass []byte, meta *RequestMetadata) (u user.User, err error)
- func (a *Authenticator) AuthenticateUserByRefreshToken(ctx context.Context, c *client.Client, hash RefreshTokenHash, ...) (session *Session, tpair TokenPair, err error)
- func (a *Authenticator) CreateAuthorizationCode(ctx context.Context, challenge PKCEChallenge, tpair TokenPair) (code string, err error)
- func (a *Authenticator) CreateSession(ctx context.Context, traceID uuid.UUID, clnt *client.Client, ident Identity, ...) (session *Session, signedToken string, err error)
- func (a *Authenticator) CreateSessionWithRefreshToken(ctx context.Context, traceID uuid.UUID, oldToken *RefreshToken, ...) (session *Session, tpair TokenPair, err error)
- func (a *Authenticator) ExchangeAuthorizationCode(ctx context.Context, code string, verifier string) (tpair TokenPair, err error)
- func (a *Authenticator) GroupManager() *group.Manager
- func (a *Authenticator) Logger() *zap.Logger
- func (a *Authenticator) PasswordManager() password.Manager
- func (a *Authenticator) PrivateKey() (*rsa.PrivateKey, error)
- func (a *Authenticator) RefreshTokenByHash(ctx context.Context, hash RefreshTokenHash) (rt RefreshToken, err error)
- func (a *Authenticator) RevokeRefreshToken(ctx context.Context, hash RefreshTokenHash, reason string) (err error)
- func (a *Authenticator) RevokeSession(ctx context.Context, sessionID uuid.UUID, revoker uint32, reason string) (err error)
- func (a *Authenticator) RotateRefreshToken(ctx context.Context, tokenHash RefreshTokenHash) (newToken RefreshToken, err error)
- func (a *Authenticator) SessionByAccessToken(ctx context.Context, signedToken string) (session *Session, err error)
- func (a *Authenticator) SessionByID(ctx context.Context, jti uuid.UUID) (*Session, error)
- func (a *Authenticator) SetLogger(logger *zap.Logger) error
- func (a *Authenticator) TokenManager() *token.Manager
- func (a *Authenticator) UserManager() *user.Manager
- type AuthorizationCodePayload
- type Backend
- type Cache
- type Claims
- type ClientCredentials
- type Context
- type ContextKey
- type Credentials
- type DefaultBackend
- func (b *DefaultBackend) CreateAuthorizationCode(ctx context.Context, code string, challenge PKCEChallenge, tpair TokenPair) (err error)
- func (b *DefaultBackend) CreateRefreshToken(ctx context.Context, rt RefreshToken) error
- func (b *DefaultBackend) CreateSession(ctx context.Context, session *Session) error
- func (b *DefaultBackend) DeleteAuthorizationCode(ctx context.Context, code string) (err error)
- func (b *DefaultBackend) DeleteRefreshToken(ctx context.Context, t RefreshToken) error
- func (b *DefaultBackend) DeleteSession(ctx context.Context, s *Session) error
- func (b *DefaultBackend) GetAuthorizationPayloadByCode(ctx context.Context, code string) (payload AuthorizationCodePayload, err error)
- func (b *DefaultBackend) GetHeadRefreshTokenByTraceID(ctx context.Context, traceID uuid.UUID) (rtok RefreshToken, err error)
- func (b *DefaultBackend) GetHeadSessionByTraceID(ctx context.Context, traceID uuid.UUID) (_ *Session, err error)
- func (b *DefaultBackend) GetRefreshTokenByHash(ctx context.Context, hash RefreshTokenHash) (t RefreshToken, err error)
- func (b *DefaultBackend) GetSessionByID(ctx context.Context, jti uuid.UUID) (s *Session, err error)
- func (b *DefaultBackend) RotateRefreshToken(ctx context.Context, h RefreshTokenHash, newToken RefreshToken) (err error)
- func (b *DefaultBackend) UpdateRefreshToken(ctx context.Context, h RefreshTokenHash, ...) (rtok RefreshToken, err error)
- func (b *DefaultBackend) UpdateSession(ctx context.Context, id uuid.UUID, ...) (_ *Session, err error)
- type Domain
- type Identity
- type IdentityKind
- type Options
- type PKCEChallenge
- type RefreshToken
- type RefreshTokenHash
- type RequestMetadata
- type Session
- func (s *Session) IsCreatedByCredentials() bool
- func (s *Session) IsCreatedByRefToken() bool
- func (s *Session) IsExpired() bool
- func (s *Session) IsIdle() bool
- func (s *Session) IsRevoked() bool
- func (s *Session) IsRevokedByClient() bool
- func (s *Session) IsRevokedByExpiry() bool
- func (s *Session) IsRevokedByLogout() bool
- func (s *Session) IsRevokedBySystem() bool
- func (s *Session) IsValid() bool
- func (s *Session) LastActiveAt() time.Time
- func (s *Session) RevokeByClient() error
- func (s *Session) RevokeByExpiry() error
- func (s *Session) RevokeBySystem(reason string) error
- func (s *Session) TimeLeft() time.Duration
- func (s *Session) Touch()
- func (s *Session) Validate() error
- type TokenPair
- type UserCredentials
Constants ¶
const ( Length = 32 DefaultRefreshTokenTTL = 24 * time.Hour )
const ( SRevokedByLogout uint32 = 1 << iota SRevokedByExpiry SRevokedBySystem SRevokedByClient SCreatedByCreds SCreatedByRefToken )
const ( // DefaultIdleTimeoutSec is the time since the last session activity (in seconds) DefaultIdleTimeoutSec = 120 // DefaultSessionTTL is the default session lifetime DefaultSessionTTL = 10 * time.Minute )
const ( IKNone = iota IKUser IKApplication )
Variables ¶
var ( ErrNilUserManager = errors.New("user manager is nil") ErrNilPrivateKey = errors.New("private key is nil") ErrAuthenticationFailed = errors.New("authentication failed") ErrNilAuthenticator = errors.New("authenticator is nil") ErrEmptyUsername = errors.New("username is empty") ErrEmptyPassword = errors.New("password is empty") ErrUserSuspended = errors.New("user is suspended") ErrInvalidAccessToken = errors.New("invalid accesspolicy token") ErrInvalidRefreshToken = errors.New("invalid refresh token") ErrInvalidRefreshTokenID = errors.New("refresh token id is invalid") ErrRefreshTokenExpired = errors.New("refresh token has expired") ErrRefreshTokenNotActive = errors.New("refresh token is not active") ErrRefreshTokenRotated = errors.New("refresh token is rotated") ErrRefreshTokenRevoked = errors.New("refresh token is revoked") ErrIPAddrMismatch = errors.New("ip address mismatch") ErrIdentityMismatch = errors.New("identity mismatch") ErrUserAgentMismatch = errors.New("user agent mismatch") ErrNilSession = errors.New("session is nil") ErrSessionNotFound = errors.New("session not found") ErrSessionRevoked = errors.New("session is revoked") ErrSessionAlreadyRevoked = errors.New("session is already revoked") ErrInvalidRevocationFlag = errors.New("invalid revocation flag") ErrInvalidIdentityID = errors.New("invalid identity id") ErrRefreshTokenNotFound = errors.New("refresh token not found") ErrInvalidJTI = errors.New("invalid access token id") ErrInvalidSessionID = errors.New("invalid session id") ErrInvalidExpirationTime = errors.New("invalid expiration time") ErrZeroExpiration = errors.New("expiration time is zero") ErrRefreshTokenIsEmpty = errors.New("refresh token is empty") ErrNilPasswordManager = errors.New("password manager is nil") ErrAuthorizationCodeNotFound = errors.New("authorization code not found") ErrClientDisabled = errors.New("client is disabled") ErrClientExpired = errors.New("client is expired") ErrClientSecretMismatch = errors.New("client secret mismatch") ErrClientIsNonconfidential = errors.New("client is non-confidential") ErrEmptyCodeChallenge = errors.New("code challenge is empty") ErrEmptyCodeChallengeMethod = errors.New("code challenge method is empty") ErrInvalidCodeChallengeMethod = errors.New("code challenge method is invalid") ErrCodeChallengeVerificationFailed = errors.New("code challenge verification failed") ErrInvalidTraceID = errors.New("invalid trace id") ErrAuthorizationCodeEmpty = errors.New("authorization code is empty") ErrEntryNotFound = errors.New("entry not found") )
errors
var EmptyRefreshTokenHash = RefreshTokenHash{}
EmptyRefreshTokenHash is a predefined sample for comparison
var NilIdentity = Identity{ ID: uuid.UUID{}, Kind: 0, }
NilIdentity represents a "no identity"
Functions ¶
Types ¶
type Authenticator ¶
type Authenticator struct {
// contains filtered or unexported fields
}
Authenticator represents an authenticator which is responsible for the user authentication and authorization
func NewAuthenticator ¶
func NewAuthenticator( pk *rsa.PrivateKey, users *user.Manager, clients *client.Manager, b Backend, opts Options, ) (*Authenticator, error)
NewAuthenticator initializes a new authenticator NOTE: if private key is nil, then using an autogenerated key
func (*Authenticator) AuthenticateClientBySecret ¶
func (a *Authenticator) AuthenticateClientBySecret( ctx context.Context, clientID uuid.UUID, secret []byte, meta *RequestMetadata, ) ( c *client.Client, session *Session, signedToken string, err error, )
TODO take metadata into account when authenticating
func (*Authenticator) AuthenticateUserByPassword ¶
func (a *Authenticator) AuthenticateUserByPassword( ctx context.Context, username string, rawpass []byte, meta *RequestMetadata, ) (u user.User, err error)
AuthenticateByPassword authenticates a user by username and password. each successful authentication generates a new access token and spawns a new session identified by that token's JTI (JWT ID). NOTE: the number of active sessions should be limited by a sensible amount
func (*Authenticator) AuthenticateUserByRefreshToken ¶
func (a *Authenticator) AuthenticateUserByRefreshToken( ctx context.Context, c *client.Client, hash RefreshTokenHash, meta *RequestMetadata, ) ( session *Session, tpair TokenPair, err error, )
func (*Authenticator) CreateAuthorizationCode ¶
func (a *Authenticator) CreateAuthorizationCode( ctx context.Context, challenge PKCEChallenge, tpair TokenPair, ) (code string, err error)
func (*Authenticator) CreateSession ¶
func (a *Authenticator) CreateSession( ctx context.Context, traceID uuid.UUID, clnt *client.Client, ident Identity, meta *RequestMetadata, ) ( session *Session, signedToken string, err error, )
CreateSession creates a new session for a given owner within the specified realm
func (*Authenticator) CreateSessionWithRefreshToken ¶
func (a *Authenticator) CreateSessionWithRefreshToken( ctx context.Context, traceID uuid.UUID, oldToken *RefreshToken, clnt *client.Client, ident Identity, meta *RequestMetadata, ) ( session *Session, tpair TokenPair, err error, )
func (*Authenticator) ExchangeAuthorizationCode ¶
func (*Authenticator) GroupManager ¶
func (a *Authenticator) GroupManager() *group.Manager
func (*Authenticator) Logger ¶
func (a *Authenticator) Logger() *zap.Logger
Logger returns own logger
func (*Authenticator) PasswordManager ¶
func (a *Authenticator) PasswordManager() password.Manager
func (*Authenticator) PrivateKey ¶
func (a *Authenticator) PrivateKey() (*rsa.PrivateKey, error)
PrivateKey returns a private key (RSA) used by this manager
func (*Authenticator) RefreshTokenByHash ¶
func (a *Authenticator) RefreshTokenByHash( ctx context.Context, hash RefreshTokenHash, ) ( rt RefreshToken, err error, )
TODO: considered whether this is a redundant function
func (*Authenticator) RevokeRefreshToken ¶
func (a *Authenticator) RevokeRefreshToken( ctx context.Context, hash RefreshTokenHash, reason string, ) (err error)
func (*Authenticator) RevokeSession ¶
func (a *Authenticator) RevokeSession(ctx context.Context, sessionID uuid.UUID, revoker uint32, reason string) (err error)
RevokeSession revokes existing session instead of deleting, because previously issued access tokens are still out and can be used to re-generate a new session, thus it must be preserved until it eventually expires, and then deleted WARNING: session MUST NOT be deleted at least until it expires naturally
func (*Authenticator) RotateRefreshToken ¶
func (a *Authenticator) RotateRefreshToken(ctx context.Context, tokenHash RefreshTokenHash) (newToken RefreshToken, err error)
func (*Authenticator) SessionByAccessToken ¶
func (*Authenticator) SessionByID ¶
SessionByID returns a session if it's found in the backend by its token string
func (*Authenticator) SetLogger ¶
func (a *Authenticator) SetLogger(logger *zap.Logger) error
SetLogger sets an own logger
func (*Authenticator) TokenManager ¶
func (a *Authenticator) TokenManager() *token.Manager
func (*Authenticator) UserManager ¶
func (a *Authenticator) UserManager() *user.Manager
type AuthorizationCodePayload ¶
type AuthorizationCodePayload struct { PKCEChallenge `json:"pkce"` TokenPair `json:"token_pair"` }
type Backend ¶
type Backend interface { CreateSession(ctx context.Context, s *Session) (err error) GetHeadSessionByTraceID(ctx context.Context, traceID uuid.UUID) (session *Session, err error) UpdateSession(ctx context.Context, id uuid.UUID, fn func(ctx context.Context, session *Session) (*Session, error)) (session *Session, err error) DeleteSession(ctx context.Context, s *Session) (err error) CreateRefreshToken(ctx context.Context, rtok RefreshToken) (err error) GetRefreshTokenByHash(ctx context.Context, hash RefreshTokenHash) (rtok RefreshToken, err error) GetHeadRefreshTokenByTraceID(ctx context.Context, traceID uuid.UUID) (rtok RefreshToken, err error) UpdateRefreshToken(ctx context.Context, h RefreshTokenHash, fn func(ctx context.Context, rtok RefreshToken) (RefreshToken, error)) (rtok RefreshToken, err error) DeleteRefreshToken(ctx context.Context, rtok RefreshToken) (err error) CreateAuthorizationCode(ctx context.Context, code string, challenge PKCEChallenge, tpair TokenPair) (err error) GetAuthorizationPayloadByCode(ctx context.Context, code string) (payload AuthorizationCodePayload, err error) DeleteAuthorizationCode(ctx context.Context, code string) (err error) GetSessionByID(ctx context.Context, jti uuid.UUID) (*Session, error) }
Backend is an interface contract for an auth backend
type Cache ¶
type Claims ¶
type Claims struct { Identity Identity `json:"identity"` jwt.StandardClaims }
Claims holds required JWT claims
type ClientCredentials ¶
type ClientCredentials struct { ClientID uuid.UUID `json:"client_id"` Secret token.Hash `json:"secret"` }
ClientCredentials represents client authentication credentials
type ContextKey ¶
type ContextKey uint8
ContextKey is a named context key type for this package
const ( CKAuthenticator ContextKey = iota CKSession CKUserManager CKUser CKClient )
context keys
type Credentials ¶
type Credentials struct { Client *ClientCredentials User *UserCredentials }
type DefaultBackend ¶
DefaultBackend is a default in-memory implementation
func NewDefaultRegistryBackend ¶
func NewDefaultRegistryBackend() *DefaultBackend
NewDefaultRegistryBackend initializes a default in-memory registry
func (*DefaultBackend) CreateAuthorizationCode ¶
func (b *DefaultBackend) CreateAuthorizationCode(ctx context.Context, code string, challenge PKCEChallenge, tpair TokenPair) (err error)
func (*DefaultBackend) CreateRefreshToken ¶
func (b *DefaultBackend) CreateRefreshToken(ctx context.Context, rt RefreshToken) error
func (*DefaultBackend) CreateSession ¶
func (b *DefaultBackend) CreateSession(ctx context.Context, session *Session) error
UpsertSession stores a given session to a temporary registry backend
func (*DefaultBackend) DeleteAuthorizationCode ¶
func (b *DefaultBackend) DeleteAuthorizationCode(ctx context.Context, code string) (err error)
func (*DefaultBackend) DeleteRefreshToken ¶
func (b *DefaultBackend) DeleteRefreshToken(ctx context.Context, t RefreshToken) error
DeleteSession deletes a given session from the backend registry
func (*DefaultBackend) DeleteSession ¶
func (b *DefaultBackend) DeleteSession(ctx context.Context, s *Session) error
DeleteSession deletes a given session from the backend registry
func (*DefaultBackend) GetAuthorizationPayloadByCode ¶
func (b *DefaultBackend) GetAuthorizationPayloadByCode(ctx context.Context, code string) (payload AuthorizationCodePayload, err error)
func (*DefaultBackend) GetHeadRefreshTokenByTraceID ¶
func (b *DefaultBackend) GetHeadRefreshTokenByTraceID(ctx context.Context, traceID uuid.UUID) (rtok RefreshToken, err error)
func (*DefaultBackend) GetHeadSessionByTraceID ¶
func (*DefaultBackend) GetRefreshTokenByHash ¶
func (b *DefaultBackend) GetRefreshTokenByHash(ctx context.Context, hash RefreshTokenHash) (t RefreshToken, err error)
GetSessionByRefreshToken retrieves session by a refresh token
func (*DefaultBackend) GetSessionByID ¶
GetSession fetches a session by a given token hash from the registry backend
func (*DefaultBackend) RotateRefreshToken ¶
func (b *DefaultBackend) RotateRefreshToken(ctx context.Context, h RefreshTokenHash, newToken RefreshToken) (err error)
func (*DefaultBackend) UpdateRefreshToken ¶
func (b *DefaultBackend) UpdateRefreshToken( ctx context.Context, h RefreshTokenHash, fn func(ctx context.Context, rtok RefreshToken) (RefreshToken, error), ) (rtok RefreshToken, err error)
type Identity ¶
type Identity struct { ID uuid.UUID `json:"id"` Kind IdentityKind `json:"kind"` }
Identity represents a session owner
func UserIdentity ¶
type IdentityKind ¶
type IdentityKind uint8
IdentityKind represents an identity kind (i.e.: user/device/etc...)
func (IdentityKind) String ¶
func (k IdentityKind) String() string
type Options ¶
type Options struct { AccessTokenTTL time.Duration RefreshTokenTTL time.Duration CompareIP bool CompareUserAgent bool }
func DefaultOptions ¶
func DefaultOptions() Options
type PKCEChallenge ¶
func NewPKCEChallenge ¶
func NewPKCEChallenge(challenge string, method string) (c PKCEChallenge, err error)
func (*PKCEChallenge) Validate ¶
func (c *PKCEChallenge) Validate() error
func (*PKCEChallenge) Verify ¶
func (c *PKCEChallenge) Verify(verifier string) bool
type RefreshToken ¶
type RefreshToken struct { ID uuid.UUID `db:"id" json:"id"` TraceID uuid.UUID `db:"trace_id" json:"trace_id"` ParentID uuid.UUID `db:"parent_id" json:"parent_id"` RotatedID uuid.UUID `db:"rotated_id" json:"rotated_id"` LastSessionID uuid.UUID `db:"last_session_id" json:"last_session_id"` PreviousRefreshTokenID uuid.UUID `db:"previous_refresh_token_id" json:"previous_refresh_token_id"` ClientID uuid.UUID `db:"client_id" json:"client_id"` Identity Identity `db:"identity" json:"identity"` Hash RefreshTokenHash `db:"hash" json:"hash"` CreatedAt time.Time `db:"created_at" json:"created_at"` RotatedAt time.Time `db:"rotated_at" json:"rotated_at"` RevokedAt time.Time `db:"revoked_at" json:"revoked_at"` ExpireAt time.Time `db:"expire_at" json:"expire_at"` Flags uint8 `db:"flags" json:"flags"` // contains filtered or unexported fields }
func NewRefreshToken ¶
func NewRotatedRefreshToken ¶
func NewRotatedRefreshToken(currentToken RefreshToken) (newToken RefreshToken, err error)
func (*RefreshToken) IsActive ¶
func (rtok *RefreshToken) IsActive() (bool, error)
func (*RefreshToken) IsExpired ¶
func (rtok *RefreshToken) IsExpired() bool
func (*RefreshToken) IsRevoked ¶
func (rtok *RefreshToken) IsRevoked() bool
func (*RefreshToken) IsRotated ¶
func (rtok *RefreshToken) IsRotated() bool
func (*RefreshToken) Validate ¶
func (rtok *RefreshToken) Validate() error
type RefreshTokenHash ¶
RefreshTokenHash represents the very secret essence of a refresh token TODO: implement hash encryption for the store TODO: implement hash signature and verification
func NewTokenHash ¶
func NewTokenHash() (hash RefreshTokenHash)
func (RefreshTokenHash) DecodeBinary ¶
func (h RefreshTokenHash) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error
func (RefreshTokenHash) EncodeBinary ¶
func (RefreshTokenHash) String ¶
func (h RefreshTokenHash) String() string
func (RefreshTokenHash) Validate ¶
func (h RefreshTokenHash) Validate() error
type RequestMetadata ¶
RequestMetadata holds request information
func NewRequestMetadata ¶
func NewRequestMetadata(r *http.Request) (meta *RequestMetadata)
NewRequestMetadata initializes RequestMetadata from a given http.Request
type Session ¶
type Session struct { // ID is also used as JTI (JWT ID) ID uuid.UUID `db:"id" json:"id"` TraceID uuid.UUID `db:"trace_id" json:"trace_id"` // ClientID is an ID of a client that originally initiated this session ClientID uuid.UUID `db:"client_id" json:"client_id"` // Identity is the owner of this session Identity Identity `db:"identity" json:"identity"` // IP is the IP address from which this session has been initiated IP net.IP `db:"ip" json:"ip"` // Flags describes metadata like whether it's idling, revoked, // revoked by its owner, expiry, client or some external system Flags uint32 `db:"flags" json:"flags"` // times CreatedAt time.Time `db:"created_at" json:"created_at"` RefreshedAt time.Time `db:"refreshed_at" json:"refreshed_at"` RevokedAt time.Time `db:"revoked_at" json:"revoked_at"` ExpireAt time.Time `db:"expire_at" json:"expire_at"` // revocation reason RevokeReason string `db:"revoke_reason" json:"revoke_reason"` sync.RWMutex // contains filtered or unexported fields }
Session represents an authenticated session
func NewSession ¶
func (*Session) IsCreatedByCredentials ¶
func (*Session) IsCreatedByRefToken ¶
func (*Session) IsRevokedByClient ¶
func (*Session) IsRevokedByExpiry ¶
func (*Session) IsRevokedByLogout ¶
func (*Session) IsRevokedBySystem ¶
func (*Session) LastActiveAt ¶
LastActiveAt returns the time when this client was active last time
func (*Session) RevokeByClient ¶
func (*Session) RevokeByExpiry ¶
func (*Session) RevokeBySystem ¶
type TokenPair ¶
type TokenPair struct { AccessToken string `json:"access_token"` RefreshToken RefreshTokenHash `json:"refresh_token,omitempty"` }
TokenPair contains access and refresh tokens which are returned back to the client upon successful authentication
type UserCredentials ¶
UserCredentials represents user authentication credentials
func (*UserCredentials) SanitizeAndValidate ¶
func (c *UserCredentials) SanitizeAndValidate() error
SanitizeAndValidate performs basic trimming and validations TODO: consider preserving original username but still change case to lower NOTE: trimming passwords whitespace to prevent problems when people copy+paste