authn

package
v0.1.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package authn cached.go — cache decorator for Authenticator + TokenService.

CachedClient wraps an Authenticator + TokenService pair with a shared cachex-backed cache. Both Authenticate and VerifyToken share the same cache key for a given token, so a token verified via VerifyToken will be served from cache on the next Authenticate call (and vice versa) — important when middleware authenticates via Authenticator.Authenticate but business code calls TokenService.VerifyToken explicitly.

Cache semantics:

  • Hit: return cached Principal without calling the inner provider.
  • Miss: call inner provider, cache result on success.
  • Invalidate(ctx, token): remove entry; call from RevokeToken, LogoutURL, and any path that invalidates a session.

Cache key: "authn:token:" + sha256_hex(token). We hash the token so cache dumps (Redis MONITOR, MEMORY USAGE, etc.) do not leak live bearer tokens.

TTL: min(cfg.ttl, token_exp - now). A token that expires in 30s is cached for at most 30s even if cfg.ttl is 5m. This prevents serving a cached principal for an already-expired token.

Best-effort: if cache.Get/Set/Del fails, the wrapper falls through to the inner provider. A cache outage never blocks authn.

Limitations:

  • The cache does NOT enforce revocation. If a token is revoked at the IdP but Invalidate is not called, the cache will keep returning the cached Principal until the TTL expires. Callers that need revocation semantics must maintain a separate blacklist AND call Invalidate on revoke.
  • The cache assumes the inner provider's verification is deterministic for a given token within the TTL window. This is true for JWT-based providers (Casdoor, OIDC) but may not be true for introspection-based providers that check a server-side session table. For those, set TTL=0 or do not wrap.

Package authn defines Kernel's authentication and identity-management boundary.

The package is intentionally provider-neutral. A Casdoor adapter should map OAuth code exchange, JWT parsing, users, organizations, applications and group trees into these contracts, but business code should only see Principal.

Typical flow:

  1. HTTP/gRPC middleware extracts a bearer token.
  2. Authenticator verifies it and returns Principal.
  3. Principal is stored in context.
  4. Business code uses authz.Authorizer for permissions.

Identity provisioning flow:

  1. Kernel IAM DB creates the platform organization/application/group.
  2. IdentityAdmin syncs the identity-provider projection, e.g. Casdoor.
  3. authz.RelationshipWriter projects owner/member relationships to SpiceDB.

Package authn defines Kernel's provider-neutral authentication contracts.

authn owns the question "who is the caller?". It deliberately does not decide whether the caller can access a business resource; that belongs to authz. Casdoor, OIDC, JWT, mTLS and API-key implementations should all adapt into these interfaces.

Index

Constants

View Source
const (
	CodeMissingCredential       = errorx.Code("AUTHN_MISSING_CREDENTIAL")
	CodeInvalidCredential       = errorx.Code("AUTHN_INVALID_CREDENTIAL")
	CodeUnauthenticated         = errorx.Code("AUTHN_UNAUTHENTICATED")
	CodeInvalidTokenRequest     = errorx.Code("AUTHN_INVALID_TOKEN_REQUEST")
	CodeIdentityBackendFailed   = errorx.Code("AUTHN_IDENTITY_BACKEND_FAILED")
	CodeIdentityObjectNotFound  = errorx.Code("AUTHN_IDENTITY_OBJECT_NOT_FOUND")
	CodeIdentityObjectConflict  = errorx.Code("AUTHN_IDENTITY_OBJECT_CONFLICT")
	CodeUnsupportedIdentityFlow = errorx.Code("AUTHN_UNSUPPORTED_IDENTITY_FLOW")
)
View Source
const (
	SubjectTypeUser      = "user"
	SubjectTypeService   = "service"
	SubjectTypeAgent     = "agent"
	SubjectTypeWorkflow  = "workflow"
	SubjectTypeWorkload  = "workload"
	SubjectTypeAnonymous = "anonymous"
)
View Source
const (
	AuthMethodOAuth    = "oauth"
	AuthMethodOIDC     = "oidc"
	AuthMethodJWT      = "jwt"
	AuthMethodAPIKey   = "api_key"
	AuthMethodMTLS     = "mtls"
	AuthMethodSession  = "session"
	AuthMethodDevToken = "dev_token"
	AuthMethodNone     = "none"
)
View Source
const (
	CredentialBearer = "bearer"
	CredentialBasic  = "basic"
	CredentialAPIKey = "api_key"
	CredentialMTLS   = "mtls"
	CredentialCode   = "authorization_code"
)
View Source
const (
	// Casdoor group types. A user can belong to one Physical group and multiple Virtual groups.
	GroupTypePhysical = "Physical"
	GroupTypeVirtual  = "Virtual"
)

Variables

This section is empty.

Functions

func ContextWithPrincipal

func ContextWithPrincipal(ctx context.Context, principal Principal) context.Context

func ErrIdentityBackendFailed

func ErrIdentityBackendFailed(message string, cause error) error

func ErrInvalidCredential

func ErrInvalidCredential(message string) error

func ErrInvalidTokenRequest

func ErrInvalidTokenRequest(message string) error

func ErrMissingCredential

func ErrMissingCredential(message string) error

func ErrUnauthenticated

func ErrUnauthenticated(message string) error

func ValidateApplication

func ValidateApplication(app Application) error

func ValidateAuthCodeExchangeRequest

func ValidateAuthCodeExchangeRequest(req AuthCodeExchangeRequest) error

func ValidateGroup

func ValidateGroup(group Group) error

func ValidateOrganization

func ValidateOrganization(org Organization) error

Types

type Application

type Application struct {
	ID             string
	ExternalID     string
	OrgID          string
	Name           string
	DisplayName    string
	ClientID       string
	ClientSecret   string
	RedirectURIs   []string
	GrantTypes     []string
	Scopes         []string
	Providers      []string
	EnablePassword bool
	EnableSignup   bool
	Attributes     AttributeSet
}

Application represents a login client/application in the identity provider.

type ApplicationAdmin

type ApplicationAdmin interface {
	CreateApplication(ctx context.Context, req CreateApplicationRequest) (Application, error)
	GetApplication(ctx context.Context, orgID, appID string) (Application, error)
	UpdateApplication(ctx context.Context, req UpdateApplicationRequest) (Application, error)
	DeleteApplication(ctx context.Context, req DeleteApplicationRequest) error
}

ApplicationAdmin manages identity-provider applications / OAuth clients.

type AssignUserToGroupRequest

type AssignUserToGroupRequest struct {
	OrgID    string
	GroupID  string
	UserID   string
	Metadata map[string]string
}

type AttributeSet

type AttributeSet map[string]any

AttributeSet carries provider-specific claims or admin metadata. Values should be JSON-serializable when they are persisted or sent to remote systems.

type AuthCodeExchangeRequest

type AuthCodeExchangeRequest struct {
	Code         string
	State        string
	RedirectURI  string
	CodeVerifier string
	OrgID        string
	AppID        string
	Metadata     map[string]string
}

AuthCodeExchangeRequest is the provider-neutral shape for OAuth code exchange.

type Authenticator

type Authenticator interface {
	Authenticate(ctx context.Context, credential Credential) (Principal, error)
}

Authenticator verifies a transport credential and returns a normalized Principal.

type CachedClient added in v0.0.3

type CachedClient struct {
	// contains filtered or unexported fields
}

CachedClient wraps an Authenticator + TokenService with a shared cache.

Construction:

client := casdoor.New(cfg)              // implements both interfaces
cached := authn.NewCachedClient(client, client, cache,
    authn.WithCachedTTL(5*time.Minute))
_ = authn.Authenticator(cached)         // usable as Authenticator
_ = authn.TokenService(cached)          // usable as TokenService

Both `auth` and `tokens` may point to the same object (typical for the casdoor Client which implements both). They are accepted separately so callers can mix providers (e.g. JWT verifier for Authenticate, introspection endpoint for TokenService) if needed.

func NewCachedClient added in v0.0.3

func NewCachedClient(auth Authenticator, tokens TokenService, cache cachex.Cache, opts ...CachedOption) *CachedClient

NewCachedClient wraps an Authenticator + TokenService with a cache.

If cache is nil, the returned CachedClient delegates every call to the inner providers without any caching. This lets callers always go through CachedClient (no nil checks) even when caching is disabled.

func (*CachedClient) Authenticate added in v0.0.3

func (c *CachedClient) Authenticate(ctx context.Context, credential Credential) (Principal, error)

Authenticate verifies the credential and returns the cached Principal on hit, or delegates to the inner Authenticator on miss.

func (*CachedClient) ExchangeCode added in v0.0.3

ExchangeCode is a one-shot operation — never cached. Delegates directly.

func (*CachedClient) Invalidate added in v0.0.3

func (c *CachedClient) Invalidate(ctx context.Context, token string) error

Invalidate removes a token's cached Principal. Call this from revocation flows that bypass RevokeToken (e.g. hub's local-blacklist fallback when the IdP's RevokeToken is UNIMPLEMENTED) so the next Authenticate/VerifyToken re-queries the inner provider (or, more usefully, the hub blacklist layer rejects the call before it reaches the cache).

func (*CachedClient) RefreshToken added in v0.0.3

func (c *CachedClient) RefreshToken(ctx context.Context, req RefreshTokenRequest) (TokenSet, error)

RefreshToken is a one-shot operation — never cached. Delegates directly.

func (*CachedClient) RevokeToken added in v0.0.3

func (c *CachedClient) RevokeToken(ctx context.Context, req RevokeTokenRequest) error

RevokeToken delegates to the inner TokenService and, on success, removes the token from the cache. On failure, the cache entry is left untouched (the inner provider may have rejected the request, in which case there is nothing to invalidate; or the inner provider is UNIMPLEMENTED, in which case the caller — typically a hub-level repo — should call Invalidate explicitly after updating its own blacklist).

func (*CachedClient) VerifyToken added in v0.0.3

func (c *CachedClient) VerifyToken(ctx context.Context, req VerifyTokenRequest) (Principal, error)

VerifyToken verifies the token and returns the cached Principal on hit, or delegates to the inner TokenService on miss.

type CachedOption added in v0.0.3

type CachedOption func(*CachedClient)

CachedOption configures a CachedClient.

func WithCachedTTL added in v0.0.3

func WithCachedTTL(ttl time.Duration) CachedOption

WithCachedTTL sets the maximum cache TTL. The effective TTL for a given token is min(this, token_exp - now). Default is 5 minutes. Set to 0 to effectively disable caching (every call goes to the inner provider).

type CallbackRequest

type CallbackRequest struct {
	Code          string
	State         string
	ExpectedState string
	RedirectURI   string
	OrgID         string
	AppID         string
	Metadata      map[string]string
}

CallbackRequest is the provider-neutral OAuth/OIDC callback payload.

type CallbackResult

type CallbackResult struct {
	Tokens    TokenSet
	Principal Principal
	State     string
	Provider  string
}

CallbackResult is the result of a successful browser callback flow.

type CreateApplicationRequest

type CreateApplicationRequest struct {
	Application    Application
	IdempotencyKey string
	Metadata       map[string]string
}

type CreateGroupRequest

type CreateGroupRequest struct {
	Group          Group
	IdempotencyKey string
	Metadata       map[string]string
}

type CreateOrganizationRequest

type CreateOrganizationRequest struct {
	Organization   Organization
	IdempotencyKey string
	Metadata       map[string]string
}

type Credential

type Credential struct {
	Scheme   string
	Token    string
	Metadata map[string]string
}

Credential is authentication input extracted by HTTP/gRPC middleware.

func BearerCredential

func BearerCredential(header string) (Credential, bool)

type DeleteApplicationRequest

type DeleteApplicationRequest struct {
	AppID    string
	OrgID    string
	Hard     bool
	Metadata map[string]string
}

type DeleteGroupRequest

type DeleteGroupRequest struct {
	OrgID     string
	GroupID   string
	Recursive bool
	Metadata  map[string]string
}

type DeleteOrganizationRequest

type DeleteOrganizationRequest struct {
	OrgID    string
	Hard     bool
	Metadata map[string]string
}

type Group

type Group struct {
	ID          string
	ExternalID  string
	OrgID       string
	ParentID    string
	Name        string
	DisplayName string
	Type        string
	Path        string
	Users       []string
	Attributes  AttributeSet
}

Group models Casdoor's organization tree/groups without coupling Kernel to Casdoor names. Group.Type can be physical, virtual, team, etc.

type GroupAdmin

type GroupAdmin interface {
	CreateGroup(ctx context.Context, req CreateGroupRequest) (Group, error)
	GetGroup(ctx context.Context, orgID, groupID string) (Group, error)
	ListGroups(ctx context.Context, filter GroupFilter) ([]Group, error)
	UpdateGroup(ctx context.Context, req UpdateGroupRequest) (Group, error)
	DeleteGroup(ctx context.Context, req DeleteGroupRequest) error
	AssignUserToGroup(ctx context.Context, req AssignUserToGroupRequest) error
	RemoveUserFromGroup(ctx context.Context, req AssignUserToGroupRequest) error
}

GroupAdmin manages app/org group trees. Casdoor maps this naturally to organization groups with ParentGroup.

type GroupFilter

type GroupFilter struct {
	OrgID    string
	ParentID string
	Type     string
	UserID   string
	Limit    int
	Offset   int
}

type IdentityAdmin

IdentityAdmin is the full identity management surface expected from a Casdoor adapter. Keep it out of normal business services; use it in IAM provisioning.

func NoopAdmin

func NoopAdmin() IdentityAdmin

NoopAdmin returns an IdentityAdmin that reports unsupported operations. Use it as a safe default when identity provisioning is explicitly disabled.

type IdentityProfile

type IdentityProfile struct {
	Principal Principal

	// User is the canonical user projection from the identity provider when it
	// can be read. It may be zero when only token claims were available.
	User User

	// Groups contains expanded group objects. Principal.Groups and User.Groups
	// are usually only group IDs/names; this slice carries display names, parent
	// IDs and group type when available.
	Groups []Group

	// CurrentApplication is the login/OIDC application used by the current flow.
	// A user does not usually "belong" to an application in the same way they
	// belong to a group; the application here means the OAuth client that issued
	// the token or handled the login.
	CurrentApplication Application

	Warnings   []string
	Attributes AttributeSet
}

IdentityProfile is the normalized post-login identity snapshot used by handlers, middleware and Kernel IAM sync logic.

func (IdentityProfile) GroupIDs

func (p IdentityProfile) GroupIDs() []string

func (IdentityProfile) HasFullUser

func (p IdentityProfile) HasFullUser() bool

func (IdentityProfile) IsPartial

func (p IdentityProfile) IsPartial() bool

type IdentityProfileRequest

type IdentityProfileRequest struct {
	Principal  Principal
	Token      string
	Credential Credential

	OrgID string
	AppID string

	IncludeUser               bool
	IncludeGroups             bool
	IncludeCurrentApplication bool

	// AllowPartial returns Principal plus whatever profile data can be read when
	// admin/read APIs fail. This is useful for normal request paths where token
	// authentication must not fail only because optional profile enrichment is
	// unavailable.
	AllowPartial bool

	Metadata map[string]string
}

IdentityProfileRequest controls how much identity-provider data should be loaded after login or token verification.

For request handling, pass Principal when you already have one from Authenticate/VerifyToken. For test tools and middleware, pass Token or Credential and the implementation will authenticate first.

type LoginService

type LoginService interface {
	BuildLoginURL(ctx context.Context, req LoginURLRequest) (LoginURL, error)
	HandleCallback(ctx context.Context, req CallbackRequest) (CallbackResult, error)
}

LoginService is the provider-neutral browser login contract used by HTTP handlers, examples and application boot code. Implementations can use Casdoor/OIDC SDKs internally, but callers only depend on Kernel authn types.

type LoginURL

type LoginURL struct {
	URL         string
	RedirectURI string
	State       string
	Scope       string
	Provider    string
	OrgID       string
	AppID       string
}

LoginURL is the safe redirect target returned by LoginService.

type LoginURLRequest

type LoginURLRequest struct {
	RedirectURI string
	State       string
	Scope       string
	OrgID       string
	AppID       string
	Metadata    map[string]string
}

LoginURLRequest asks an identity provider for a hosted login URL.

type Organization

type Organization struct {
	ID          string
	ExternalID  string
	Name        string
	DisplayName string
	OwnerID     string
	ParentID    string
	Tags        []string
	Enabled     bool
	Attributes  AttributeSet
}

Organization is Kernel's identity-side organization projection. Kernel IAM DB can store richer business org metadata; this type is for authn provider sync.

type OrganizationAdmin

type OrganizationAdmin interface {
	CreateOrganization(ctx context.Context, req CreateOrganizationRequest) (Organization, error)
	GetOrganization(ctx context.Context, orgID string) (Organization, error)
	UpdateOrganization(ctx context.Context, req UpdateOrganizationRequest) (Organization, error)
	DeleteOrganization(ctx context.Context, req DeleteOrganizationRequest) error
}

OrganizationAdmin manages identity-provider organizations.

type Principal

type Principal struct {
	SubjectID   string
	SubjectType string

	Provider   string
	ExternalID string
	Issuer     string
	Audience   []string

	TenantID  string
	OrgID     string
	AppID     string
	ProjectID string

	Username string
	Name     string
	Email    string
	Phone    string

	Roles  []string
	Groups []string
	Scopes []string

	// AuthMethod records how the subject authenticated. It is intentionally a
	// string instead of an enum type so adapters can preserve provider-specific
	// methods while still using the AuthMethod* constants above.
	AuthMethod string

	Attributes AttributeSet
	IssuedAt   time.Time
	ExpiresAt  time.Time
}

Principal is the normalized identity passed to business code.

SubjectID must be stable inside Kernel. For Casdoor this should normally map from the Casdoor user's stable UUID when available, not only owner/name.

func Anonymous

func Anonymous() Principal

func MustPrincipal

func MustPrincipal(ctx context.Context) (Principal, error)

func PrincipalFromContext

func PrincipalFromContext(ctx context.Context) (Principal, bool)

func (Principal) Attribute

func (p Principal) Attribute(key string) any

func (Principal) Expired

func (p Principal) Expired(now time.Time) bool

func (Principal) HasGroup

func (p Principal) HasGroup(group string) bool

func (Principal) HasRole

func (p Principal) HasRole(role string) bool

func (Principal) HasScope

func (p Principal) HasScope(scope string) bool

func (Principal) IsAnonymous

func (p Principal) IsAnonymous() bool

func (Principal) IsAuthenticated

func (p Principal) IsAuthenticated() bool

func (Principal) Normalize

func (p Principal) Normalize() Principal

type ProfileService

type ProfileService interface {
	GetIdentityProfile(ctx context.Context, req IdentityProfileRequest) (IdentityProfile, error)
}

ProfileService enriches an authenticated Principal with identity-provider profile data. It is intentionally provider-neutral: callers should not have to know whether the backing system is Casdoor, OIDC, LDAP, SCIM, etc.

type RefreshTokenRequest

type RefreshTokenRequest struct {
	RefreshToken string
	Scope        string
	OrgID        string
	AppID        string
	Metadata     map[string]string
}

type RevokeTokenRequest

type RevokeTokenRequest struct {
	Token     string
	TokenType string
	OrgID     string
	AppID     string
	Metadata  map[string]string
}

type TokenAuthenticator

type TokenAuthenticator struct {
	Verifier TokenVerifier
	Issuer   string
	Audience []string
	OrgID    string
	AppID    string
}

TokenAuthenticator adapts a TokenService/TokenVerifier to Authenticator.

func (TokenAuthenticator) Authenticate

func (a TokenAuthenticator) Authenticate(ctx context.Context, credential Credential) (Principal, error)

type TokenService

type TokenService interface {
	ExchangeCode(ctx context.Context, req AuthCodeExchangeRequest) (TokenSet, Principal, error)
	RefreshToken(ctx context.Context, req RefreshTokenRequest) (TokenSet, error)
	VerifyToken(ctx context.Context, req VerifyTokenRequest) (Principal, error)
	RevokeToken(ctx context.Context, req RevokeTokenRequest) error
}

TokenService covers OAuth/OIDC token flows such as Casdoor code exchange, refresh, validation and revocation.

type TokenSet

type TokenSet struct {
	AccessToken  string
	RefreshToken string
	IDToken      string
	TokenType    string
	Scope        string
	ExpiresAt    time.Time
	Raw          AttributeSet
}

TokenSet is the result of OAuth/OIDC token operations.

type TokenVerifier

type TokenVerifier func(ctx context.Context, req VerifyTokenRequest) (Principal, error)

TokenVerifier adapts JWT/OIDC libraries into Authenticator.

type UpdateApplicationRequest

type UpdateApplicationRequest struct {
	Application Application
	Metadata    map[string]string
}

type UpdateGroupRequest

type UpdateGroupRequest struct {
	Group    Group
	Metadata map[string]string
}

type UpdateOrganizationRequest

type UpdateOrganizationRequest struct {
	Organization Organization
	Metadata     map[string]string
}

type User

type User struct {
	ID          string
	ExternalID  string
	Provider    string
	OrgID       string
	Username    string
	DisplayName string
	Email       string
	Phone       string
	Roles       []string
	Groups      []string
	Enabled     bool
	Attributes  AttributeSet
}

User mirrors identity-provider user data without importing a provider SDK.

type UserDirectory

type UserDirectory interface {
	GetUser(ctx context.Context, orgID, userID string) (User, error)
	FindUsers(ctx context.Context, filter UserFilter) ([]User, error)
	UpsertUser(ctx context.Context, user User) (User, error)
	DisableUser(ctx context.Context, orgID, userID string) error
}

UserDirectory reads and provisions identity-provider users. Business modules should usually go through Kernel IAM service, not a provider SDK directly.

type UserFilter

type UserFilter struct {
	ID         string
	ExternalID string
	OrgID      string
	Username   string
	Email      string
	Phone      string
	GroupID    string
	Role       string
	Limit      int
	Offset     int
}

type VerifyTokenRequest

type VerifyTokenRequest struct {
	Token     string
	TokenType string
	Issuer    string
	Audience  []string
	OrgID     string
	AppID     string
	Leeway    time.Duration
	Metadata  map[string]string
}

Directories

Path Synopsis
Package callback contains reusable HTTP helpers for browser based authn callback flows.
Package callback contains reusable HTTP helpers for browser based authn callback flows.
Package casdoor adapts the official Casdoor Go SDK to Kernel authn contracts.
Package casdoor adapts the official Casdoor Go SDK to Kernel authn contracts.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL