Documentation
¶
Index ¶
- Constants
- func ConsentCovers(granted, requested []string) bool
- func CreateInteraction(app core.App, in *Interaction) (string, error)
- func DeleteInteraction(app core.App, id string) error
- func Deregister(app core.App)
- func DeregisterAt(app core.App, prefix string)
- func IsRegistered(app core.App) bool
- func IsRegisteredAt(app core.App, prefix string) bool
- func MustRegister(app core.App, config *Config)
- func NewClientFromRFC7591Metadata(app core.App, md *RFC7591ClientMetadataRequest) (*client.Client, string, error)
- func Register(app core.App, config *Config) error
- func RegisterProtectedResourceMetadata(app core.App, md *rfc9728.ProtectedResourceMetadata)
- func RegisterProtectedResourceMetadataAt(app core.App, prefix string, md *rfc9728.ProtectedResourceMetadata)
- func RequireScope(app core.App, requiredScopes ...string) *hook.Handler[*core.RequestEvent]
- func RequireScopeAt(app core.App, prefix string, requiredScopes ...string) *hook.Handler[*core.RequestEvent]
- func ResetGlobalStateForTests()
- func ResetStateForTests(app core.App)
- func RevokedTokenGuard(app core.App) *hook.Handler[*core.RequestEvent]
- type AccessTokenModel
- type AuthCodeModel
- type BaseConfig
- type BaseSessionModel
- func (m BaseSessionModel) GetClientID() string
- func (m BaseSessionModel) GetExpiresAt() *time.Time
- func (m BaseSessionModel) GetFormData() string
- func (m BaseSessionModel) GetGrantedAudience() []string
- func (m BaseSessionModel) GetGrantedScopes() []string
- func (m BaseSessionModel) GetID() string
- func (m BaseSessionModel) GetRequestID() string
- func (m BaseSessionModel) GetRequestedAt() time.Time
- func (m BaseSessionModel) GetRequestedAudience() []string
- func (m BaseSessionModel) GetScopes() []string
- func (m BaseSessionModel) GetSessionData() []byte
- func (m BaseSessionModel) GetSubject() string
- func (m BaseSessionModel) SetRequester(requester fosite.Requester, tokenType fosite.TokenType) error
- func (m BaseSessionModel) SetSignature(signature string)
- func (m BaseSessionModel) ToRequest(ctx context.Context, s *OAuth2Store, session fosite.Session) (*fosite.Request, error)
- type ClientModel
- type Config
- type Consent
- type DefaultUserInfoClaimStrategy
- type Instance
- type Interaction
- type JTIModel
- type MasterKeyProvider
- type MasterKeyringProvider
- type OAuth2Store
- func (s *OAuth2Store) ClientAssertionJWTValid(ctx context.Context, jti string) error
- func (s *OAuth2Store) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) (err error)
- func (s *OAuth2Store) CreateAuthorizeCodeSession(ctx context.Context, code string, request fosite.Requester) (err error)
- func (s *OAuth2Store) CreateOpenIDConnectSession(ctx context.Context, authorizeCode string, requester fosite.Requester) error
- func (s *OAuth2Store) CreatePKCERequestSession(ctx context.Context, signature string, requester fosite.Requester) error
- func (s *OAuth2Store) CreateRefreshTokenSession(ctx context.Context, signature string, accessSignature string, ...) (err error)
- func (s *OAuth2Store) DeleteAccessTokenSession(ctx context.Context, signature string) (err error)
- func (s *OAuth2Store) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode string) error
- func (s *OAuth2Store) DeletePKCERequestSession(ctx context.Context, signature string) error
- func (s *OAuth2Store) DeleteRefreshTokenSession(ctx context.Context, signature string) (err error)
- func (s *OAuth2Store) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error)
- func (s *OAuth2Store) GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (request fosite.Requester, err error)
- func (s *OAuth2Store) GetClient(ctx context.Context, id string) (fosite.Client, error)
- func (s *OAuth2Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, requester fosite.Requester) (fosite.Requester, error)
- func (s *OAuth2Store) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *OAuth2Store) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error)
- func (s *OAuth2Store) InvalidateAuthorizeCodeSession(ctx context.Context, code string) (err error)
- func (s *OAuth2Store) RegisterClient(ctx context.Context, client *RFC7591ClientMetadataRequest) (fosite.Client, string, error)
- func (s *OAuth2Store) RevokeAccessToken(ctx context.Context, requestID string) error
- func (s *OAuth2Store) RevokeRefreshToken(ctx context.Context, requestID string) error
- func (s *OAuth2Store) RotateRefreshToken(ctx context.Context, requestID string, refreshTokenSignature string) (err error)
- func (s *OAuth2Store) SetClientAssertionJWT(ctx context.Context, jti string, exp time.Time) error
- type OpenIDConnectSessionModel
- type PKCEModel
- type PocketBaseStrategy
- func (s *PocketBaseStrategy) AccessTokenSignature(ctx context.Context, token string) string
- func (s *PocketBaseStrategy) AuthorizeCodeSignature(ctx context.Context, token string) string
- func (s *PocketBaseStrategy) GenerateAccessToken(ctx context.Context, requester fosite.Requester) (token string, signature string, err error)
- func (s *PocketBaseStrategy) GenerateAuthorizeCode(ctx context.Context, requester fosite.Requester) (token string, signature string, err error)
- func (s *PocketBaseStrategy) GenerateRefreshToken(ctx context.Context, requester fosite.Requester) (token string, signature string, err error)
- func (s *PocketBaseStrategy) RefreshTokenSignature(ctx context.Context, token string) string
- func (s *PocketBaseStrategy) ValidateAccessToken(ctx context.Context, requester fosite.Requester, token string) error
- func (s *PocketBaseStrategy) ValidateAuthorizeCode(ctx context.Context, requester fosite.Requester, token string) (err error)
- func (s *PocketBaseStrategy) ValidateRefreshToken(ctx context.Context, requester fosite.Requester, token string) (err error)
- type RFC7591ClientMetadata
- type RFC7591ClientMetadataRequest
- type RFC7591ClientStorage
- type RefreshTokenModel
- func (p *RefreshTokenModel) GetCollectionName() string
- func (m *RefreshTokenModel) GetFamilyID() string
- func (m *RefreshTokenModel) GetParentRefreshID() string
- func (m *RefreshTokenModel) GetReusedAt() int64
- func (m *RefreshTokenModel) GetRotatedAt() int64
- func (m *RefreshTokenModel) GetStatus() string
- func (m *RefreshTokenModel) SetFamilyID(v string)
- func (m *RefreshTokenModel) SetParentRefreshID(v string)
- func (m *RefreshTokenModel) SetReusedAt(ts int64)
- func (m *RefreshTokenModel) SetRotatedAt(ts int64)
- func (m *RefreshTokenModel) SetStatus(v string)
- type Session
- type SessionModel
- type UserInfoAddressClaim
- type UserInfoClaimStrategy
- type UserInfoClaims
Constants ¶
const ( RefreshStatusActive = "active" RefreshStatusRotated = "rotated" RefreshStatusRevoked = "revoked" RefreshStatusReused = "reused" )
RefreshStatus values for the RefreshTokenModel.status column.
const DefaultPathPrefix = "/oauth2"
DefaultPathPrefix is the path prefix used when Config.PathPrefix is empty and when callers pass an empty prefix to the *At lookup helpers. Public constant so callers can reference the default consistently.
const ScopeContextKey = "oauth_granted_scopes"
ScopeContextKey is the key under which RequireScope stores the granted scopes slice on the RequestEvent for downstream handlers to read via e.Get(ScopeContextKey).
Variables ¶
This section is empty.
Functions ¶
func ConsentCovers ¶
ConsentCovers reports whether `granted` includes every scope in `requested`. Empty requested → true (no scopes to consent to).
func CreateInteraction ¶
func CreateInteraction(app core.App, in *Interaction) (string, error)
CreateInteraction persists a new pending authorization for later completion. Returns the generated interaction id.
func DeleteInteraction ¶
DeleteInteraction removes a completed (or denied) interaction row so it cannot be replayed.
func Deregister ¶
Deregister removes ALL of the plugin's per-app state from the given core.App and clears the registrationGuard entries for every prefix the app was registered at. Use this in long-running processes that create and destroy tenant apps dynamically — otherwise registrationGuard accumulates stale interface-value entries for the lifetime of the process.
After Deregister, Register may be called again on the same app value. HTTP handlers and cron jobs already bound to the app remain bound; this only releases the plugin's bookkeeping state. For full teardown, drop the core.App itself. Use DeregisterAt to release a single prefix.
func DeregisterAt ¶
DeregisterAt is the prefix-aware variant of Deregister. It releases just the (app, prefix) registration so other prefixes on the same app remain operational.
func IsRegistered ¶
func IsRegisteredAt ¶
IsRegisteredAt reports whether an OP is bound at the given prefix.
func MustRegister ¶
func RegisterProtectedResourceMetadata ¶
func RegisterProtectedResourceMetadata(app core.App, md *rfc9728.ProtectedResourceMetadata)
RegisterProtectedResourceMetadata registers a protected resource metadata entry for the OAuth2 instance at DefaultPathPrefix on the given app. Use RegisterProtectedResourceMetadataAt for non-default prefixes (e.g. when an app hosts multiple OPs).
func RegisterProtectedResourceMetadataAt ¶
func RegisterProtectedResourceMetadataAt(app core.App, prefix string, md *rfc9728.ProtectedResourceMetadata)
RegisterProtectedResourceMetadataAt is the prefix-aware variant.
func RequireScope ¶
RequireScope returns a router middleware that asserts the bearer token in the Authorization header has been granted ALL of the listed scopes. On success it sets the granted-scopes slice on e.Set(ScopeContextKey) for downstream handlers and calls Next. On failure it returns an RFC 6750 "insufficient_scope" 403 with the missing scopes listed in the WWW-Authenticate header.
Header-only by design: the token is read from "Authorization: Bearer <token>" only. Form-body and URL-query token parameters are deliberately rejected because URL-query tokens leak into access logs, browser history, and Referer headers (RFC 6750 §5.3 SHOULD-NOT) and form-body tokens trigger CORS preflight in browser clients. If you need form/query token support, use fosite.AccessTokenFromRequest directly in your own middleware.
This is opt-in: by default OAuth-issued access tokens are valid PocketBase auth tokens with no scope check on PB-native endpoints. Attach RequireScope to your own resource routes when you want OAuth scope to actually gate access. Example:
se.Router.GET("/api/widgets", listWidgetsHandler).
Bind(oauth2.RequireScope(app, "widgets:read"))
func RequireScopeAt ¶
func RequireScopeAt(app core.App, prefix string, requiredScopes ...string) *hook.Handler[*core.RequestEvent]
RequireScopeAt is the prefix-aware variant of RequireScope. Use it on resource routes that should be gated by an OP registered at a non- default path prefix (e.g. /oauth2/members).
func ResetGlobalStateForTests ¶
func ResetGlobalStateForTests()
ResetGlobalStateForTests is deprecated and no-op. State is now per-app; use ResetStateForTests(app) instead.
func ResetStateForTests ¶
ResetStateForTests removes the OAuth2 instance from the app's store. Use this in tests that need a clean slate.
func RevokedTokenGuard ¶
RevokedTokenGuard returns a router middleware that rejects bearer tokens which have no corresponding _oauth2Access row — i.e. tokens that were revoked via /oauth2/revoke or expired by the cleanup cron.
PocketBase native auth tokens are stateless JWTs validated against the PB signing secret; a revoked OAuth2 access token remains a syntactically valid PB token until its natural exp. Routes using apis.RequireAuth() alone will accept revoked tokens. Bind RevokedTokenGuard wherever you need OAuth-side revocation to take effect on a PB-native route:
se.Router.GET("/api/private", privateHandler).
Bind(apis.RequireAuth("users")).
Bind(oauth2.RevokedTokenGuard(app))
Tokens issued outside the OAuth flow (e.g. PB built-in auth via /api/collections/users/auth-with-password) have no _oauth2Access row and would also be rejected by this middleware — by design. Use it only on routes that should accept ONLY OAuth-issued tokens.
Types ¶
type AccessTokenModel ¶
type AccessTokenModel struct {
BaseSessionModel
}
func (*AccessTokenModel) GetCollectionName ¶
func (p *AccessTokenModel) GetCollectionName() string
type AuthCodeModel ¶
type AuthCodeModel struct {
BaseSessionModel
}
func (*AuthCodeModel) GetCollectionName ¶
func (p *AuthCodeModel) GetCollectionName() string
type BaseConfig ¶
type BaseSessionModel ¶
type BaseSessionModel struct {
core.BaseRecordProxy
}
func (BaseSessionModel) GetClientID ¶
func (m BaseSessionModel) GetClientID() string
func (BaseSessionModel) GetExpiresAt ¶
func (m BaseSessionModel) GetExpiresAt() *time.Time
func (BaseSessionModel) GetFormData ¶
func (m BaseSessionModel) GetFormData() string
func (BaseSessionModel) GetGrantedAudience ¶
func (m BaseSessionModel) GetGrantedAudience() []string
func (BaseSessionModel) GetGrantedScopes ¶
func (m BaseSessionModel) GetGrantedScopes() []string
func (BaseSessionModel) GetID ¶
func (m BaseSessionModel) GetID() string
func (BaseSessionModel) GetRequestID ¶
func (m BaseSessionModel) GetRequestID() string
func (BaseSessionModel) GetRequestedAt ¶
func (m BaseSessionModel) GetRequestedAt() time.Time
func (BaseSessionModel) GetRequestedAudience ¶
func (m BaseSessionModel) GetRequestedAudience() []string
func (BaseSessionModel) GetScopes ¶
func (m BaseSessionModel) GetScopes() []string
func (BaseSessionModel) GetSessionData ¶
func (m BaseSessionModel) GetSessionData() []byte
func (BaseSessionModel) GetSubject ¶
func (m BaseSessionModel) GetSubject() string
func (BaseSessionModel) SetRequester ¶
func (BaseSessionModel) SetSignature ¶
func (m BaseSessionModel) SetSignature(signature string)
type ClientModel ¶
type ClientModel struct {
core.BaseRecordProxy
}
func NewClientModel ¶
func NewClientModel(app core.App) *ClientModel
type Config ¶
type Config struct {
*BaseConfig
PathPrefix string
UserCollection string
UserInfoClaimStrategy UserInfoClaimStrategy
EnableRFC7591DynamicClientRegistration bool
EnableRFC9728ProtectedResourceMetadata bool
// MasterKeyProvider supplies the at-rest encryption master key for
// envelope-encrypting OAuth2 key material in _params. If nil, the
// DefaultMasterKeyProvider (reads OAUTH2_MASTER_KEY env) is used.
// When the provider returns a nil master, encryption is disabled
// and values are stored in legacy plaintext form (dev / back-compat).
MasterKeyProvider MasterKeyProvider
// DynamicClientRegistrationInitialAccessTokens, when EnableRFC7591…
// is true, gates the /oauth2/register endpoint behind RFC 7591 §3
// Initial Access Tokens. Each entry is a bearer token operators must
// hand out to legitimate registration callers. Requests without an
// Authorization: Bearer <token> header matching one of these values
// are rejected with 401.
//
// Nil OR empty disables the gate, BUT the plugin then refuses to
// register the /register route at all unless
// AllowUnauthenticatedDynamicClientRegistration is also set (a loud
// opt-in for development environments where rate-limit + network
// boundary already gate the endpoint).
DynamicClientRegistrationInitialAccessTokens []string
// AllowUnauthenticatedDynamicClientRegistration acknowledges that DCR
// is intentionally exposed with no Initial Access Token requirement.
// REQUIRED when EnableRFC7591DynamicClientRegistration is true and
// DynamicClientRegistrationInitialAccessTokens is empty; otherwise
// the /register route is silently NOT bound.
//
// Use only in development or in environments where the registration
// endpoint is reachable solely from a trusted network segment. Public
// deployments should always populate InitialAccessTokens instead.
AllowUnauthenticatedDynamicClientRegistration bool
// LoginRedirectURL — when non-empty, GET/POST /oauth2/login redirects
// (302) to this URL with the original query string (including
// interaction_id) appended. The destination page is the consumer's
// responsibility to render: authenticate the user against the right
// collection, then POST back to /oauth2/login/complete with the
// obtained pb_token + decision + consented_scopes.
//
// Empty (default) — the bundled plugin UI at /oauth2/login is served.
//
// This is the integration hook that lets a multi-tenant host re-use
// its own branded /members/login or /admin/login flow instead of
// presenting end users with two distinct login screens.
//
// Relative URLs ("/members/login") and absolute URLs
// ("https://other.host/login") are both honored. The plugin does NOT
// validate the destination — the consumer is trusted to point at a
// page it controls.
LoginRedirectURL string
}
func GetOAuth2Config ¶
GetOAuth2Config returns the Config for the OP registered at DefaultPathPrefix on the given app. Use GetOAuth2ConfigAt when the app hosts multiple OPs at different prefixes.
type Consent ¶
type Consent struct {
ID string
UserID string
UserCollection string
ClientID string
GrantedScopes []string
GrantedAt time.Time
}
Consent represents a single user's explicit grant of a scope set to a given client. Subsequent prompt=none flows from the same client can proceed silently only when the requested scopes are a subset of an existing Consent. (mci)
func FindConsent ¶
FindConsent returns the most recent consent row for (user, client) or (nil, nil) if none exists.
type DefaultUserInfoClaimStrategy ¶
type DefaultUserInfoClaimStrategy struct{}
func (*DefaultUserInfoClaimStrategy) GetUserInfoClaims ¶
func (d *DefaultUserInfoClaimStrategy) GetUserInfoClaims(e *core.RequestEvent, scopes []string) (interface{}, error)
GetUserInfoClaims implements UserInfoClaimStrategy.
type Instance ¶
type Instance struct {
// contains filtered or unexported fields
}
Instance holds all OAuth2 provider state for a single (app, prefix) pair. It replaces the upstream package-level globals to enable safe multi-tenant usage where multiple core.App instances run in the same process; with the per-prefix store key, a single app can also host multiple OPs at different path prefixes.
func (*Instance) RegisterProtectedResourceMetadata ¶
func (inst *Instance) RegisterProtectedResourceMetadata(md *rfc9728.ProtectedResourceMetadata)
RegisterProtectedResourceMetadata registers a protected resource metadata entry for this OAuth2 instance.
Safe to call before app bootstrap: the entry is buffered in inst.protected and the discovery metadata's ScopesSupported is merged later in loadParams once inst.metadata exists.
type Interaction ¶
type Interaction struct {
ID string
ClientID string
ClientName string
UserCollection string
RedirectURI string
RequestForm url.Values
RequestedScopes []string
Prompt string
RequestedAt time.Time
ExpiresAt time.Time
}
Interaction is the server-owned snapshot of a pending authorization request. It replaces the previous browser-controlled base64 JSON `state` parameter. The UI references each interaction by an opaque id, and the server reconstructs the original authorization request from request_form when completing the flow — so a malicious /login URL cannot smuggle in an attacker-chosen redirect_uri (lr7).
func FindInteraction ¶
func FindInteraction(app core.App, id string) (*Interaction, error)
FindInteraction loads + validates an interaction by id. Returns an error if the row is missing or expired; in both cases the UI should treat the interaction as gone and force the user back through /auth.
type MasterKeyProvider ¶
type MasterKeyProvider interface {
// Master returns the 32-byte raw master key. If encryption-at-rest is
// disabled, Master must return (nil, nil) -- callers treat that as
// "plaintext mode".
Master(ctx context.Context) ([]byte, error)
// Fingerprint returns a short stable hex identifier for the master
// key (first 8 bytes of its SHA-256). Returns ("", nil) when no
// master is configured.
Fingerprint(ctx context.Context) (string, error)
}
MasterKeyProvider abstracts the source of the at-rest encryption master key. The default implementation reads from the OAUTH2_MASTER_KEY env var, but a KMS-backed provider can be supplied via Config.MasterKeyProvider.
var DefaultMasterKeyProvider MasterKeyProvider = envMasterKeyProvider{}
DefaultMasterKeyProvider is the provider used when Config.MasterKeyProvider is nil. It reads OAUTH2_MASTER_KEY at call time (not at init), so tests can mutate the env between calls.
type MasterKeyringProvider ¶
MasterKeyringProvider is an optional extension implemented by providers that retain historical (decrypt-only) master keys during a rotation. The plugin uses Keyring to:
- decrypt envelopes whose embedded kid does not match the active master fingerprint (i.e. envelopes written before the rotation);
- re-encrypt those envelopes against the active master on the next read (lazy rewrap).
Implementations must include the active master in the returned map. The map is keyed by the per-master fingerprint (as returned by fingerprintOf).
type OAuth2Store ¶
type OAuth2Store struct {
// contains filtered or unexported fields
}
func GetOAuth2Store ¶
func GetOAuth2Store(app core.App) *OAuth2Store
func GetOAuth2StoreAt ¶
func GetOAuth2StoreAt(app core.App, prefix string) *OAuth2Store
GetOAuth2StoreAt is the prefix-aware variant of GetOAuth2Store.
func NewOAuth2Store ¶
func NewOAuth2Store(app core.App) *OAuth2Store
func (*OAuth2Store) ClientAssertionJWTValid ¶
func (s *OAuth2Store) ClientAssertionJWTValid(ctx context.Context, jti string) error
ClientAssertionJWTValid implements fosite.ClientManager.
func (*OAuth2Store) CreateAccessTokenSession ¶
func (s *OAuth2Store) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) (err error)
CreateAccessTokenSession implements oauth2.AccessTokenStorage.
func (*OAuth2Store) CreateAuthorizeCodeSession ¶
func (s *OAuth2Store) CreateAuthorizeCodeSession(ctx context.Context, code string, request fosite.Requester) (err error)
CreateAuthorizeCodeSession implements oauth2.AuthorizeCodeStorage.
func (*OAuth2Store) CreateOpenIDConnectSession ¶
func (s *OAuth2Store) CreateOpenIDConnectSession(ctx context.Context, authorizeCode string, requester fosite.Requester) error
CreateOpenIDConnectSession implements openid.OpenIDConnectRequestStorage.
func (*OAuth2Store) CreatePKCERequestSession ¶
func (s *OAuth2Store) CreatePKCERequestSession(ctx context.Context, signature string, requester fosite.Requester) error
CreatePKCERequestSession implements [pkce.PKCERequestStorage].
func (*OAuth2Store) CreateRefreshTokenSession ¶
func (s *OAuth2Store) CreateRefreshTokenSession(ctx context.Context, signature string, accessSignature string, request fosite.Requester) (err error)
CreateRefreshTokenSession implements oauth2.RefreshTokenStorage.
Family-tracking behavior:
- On an authorization_code-grant issuance (chain root), a fresh family_id is generated and parent_refresh_id is left empty.
- On a refresh_token-grant rotation, the predecessor row (already marked status=rotated by RotateRefreshToken) is located by request_id and the new row inherits its family_id with parent_refresh_id set to the predecessor's record id.
- status defaults to active.
func (*OAuth2Store) DeleteAccessTokenSession ¶
func (s *OAuth2Store) DeleteAccessTokenSession(ctx context.Context, signature string) (err error)
DeleteAccessTokenSession implements oauth2.AccessTokenStorage.
func (*OAuth2Store) DeleteOpenIDConnectSession ¶
func (s *OAuth2Store) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode string) error
DeleteOpenIDConnectSession implements openid.OpenIDConnectRequestStorage.
func (*OAuth2Store) DeletePKCERequestSession ¶
func (s *OAuth2Store) DeletePKCERequestSession(ctx context.Context, signature string) error
DeletePKCERequestSession implements [pkce.PKCERequestStorage].
func (*OAuth2Store) DeleteRefreshTokenSession ¶
func (s *OAuth2Store) DeleteRefreshTokenSession(ctx context.Context, signature string) (err error)
DeleteRefreshTokenSession implements oauth2.RefreshTokenStorage.
func (*OAuth2Store) GetAccessTokenSession ¶
func (s *OAuth2Store) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error)
GetAccessTokenSession implements oauth2.AccessTokenStorage.
func (*OAuth2Store) GetAuthorizeCodeSession ¶
func (s *OAuth2Store) GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (request fosite.Requester, err error)
GetAuthorizeCodeSession implements oauth2.AuthorizeCodeStorage.
func (*OAuth2Store) GetClient ¶
GetClient implements fosite.ClientManager.
func (*OAuth2Store) GetOpenIDConnectSession ¶
func (s *OAuth2Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, requester fosite.Requester) (fosite.Requester, error)
GetOpenIDConnectSession implements openid.OpenIDConnectRequestStorage.
func (*OAuth2Store) GetPKCERequestSession ¶
func (s *OAuth2Store) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetPKCERequestSession implements [pkce.PKCERequestStorage].
func (*OAuth2Store) GetRefreshTokenSession ¶
func (s *OAuth2Store) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (request fosite.Requester, err error)
GetRefreshTokenSession implements oauth2.RefreshTokenStorage.
Reuse detection: if the row exists but its status is not "active" the token is being replayed. We invalidate every row in the same family (status=reused, reused_at=now) and delete every access token issued against any row in the family, then return fosite.ErrInactiveToken so the upstream refresh-grant handler treats it as reuse per RFC 6819 section 5.2.2.3.
func (*OAuth2Store) InvalidateAuthorizeCodeSession ¶
func (s *OAuth2Store) InvalidateAuthorizeCodeSession(ctx context.Context, code string) (err error)
InvalidateAuthorizeCodeSession implements oauth2.AuthorizeCodeStorage.
func (*OAuth2Store) RegisterClient ¶
func (s *OAuth2Store) RegisterClient(ctx context.Context, client *RFC7591ClientMetadataRequest) (fosite.Client, string, error)
RegisterClient implements RFC7591ClientStorage.
func (*OAuth2Store) RevokeAccessToken ¶
func (s *OAuth2Store) RevokeAccessToken(ctx context.Context, requestID string) error
RevokeAccessToken implements oauth2.AccessTokenStorage.
func (*OAuth2Store) RevokeRefreshToken ¶
func (s *OAuth2Store) RevokeRefreshToken(ctx context.Context, requestID string) error
RevokeRefreshToken implements oauth2.TokenRevocationStorage.
The row is marked status=revoked instead of being deleted so a later replay of the same signature is still caught as reuse against the family breadcrumb. The cleanup cron deletes the row eventually via expires_at.
func (*OAuth2Store) RotateRefreshToken ¶
func (s *OAuth2Store) RotateRefreshToken(ctx context.Context, requestID string, refreshTokenSignature string) (err error)
RotateRefreshToken implements oauth2.RefreshTokenStorage.
Marks the predecessor refresh row as rotated (keeping the family breadcrumb for reuse detection) and deletes the predecessor access token by request_id. The replacement refresh row is created by the upstream handler in a follow-up CreateRefreshTokenSession call.
func (*OAuth2Store) SetClientAssertionJWT ¶
SetClientAssertionJWT implements fosite.ClientManager.
type OpenIDConnectSessionModel ¶
type OpenIDConnectSessionModel struct {
BaseSessionModel
}
func (*OpenIDConnectSessionModel) GetCollectionName ¶
func (p *OpenIDConnectSessionModel) GetCollectionName() string
type PocketBaseStrategy ¶
type PocketBaseStrategy struct {
App core.App
Config interface {
fosite.AccessTokenIssuerProvider
fosite.AccessTokenLifespanProvider
fosite.JWTScopeFieldProvider
}
HMACSHAStrategy fositeoauth2.CoreStrategy
}
func NewPocketBaseStrategy ¶
func NewPocketBaseStrategy(app core.App, config fosite.Configurator) *PocketBaseStrategy
func (*PocketBaseStrategy) AccessTokenSignature ¶
func (s *PocketBaseStrategy) AccessTokenSignature(ctx context.Context, token string) string
AccessTokenSignature implements oauth2.CoreStrategy.
func (*PocketBaseStrategy) AuthorizeCodeSignature ¶
func (s *PocketBaseStrategy) AuthorizeCodeSignature(ctx context.Context, token string) string
AuthorizeCodeSignature implements oauth2.CoreStrategy.
func (*PocketBaseStrategy) GenerateAccessToken ¶
func (s *PocketBaseStrategy) GenerateAccessToken(ctx context.Context, requester fosite.Requester) (token string, signature string, err error)
GenerateAccessToken implements oauth2.CoreStrategy.
func (*PocketBaseStrategy) GenerateAuthorizeCode ¶
func (s *PocketBaseStrategy) GenerateAuthorizeCode(ctx context.Context, requester fosite.Requester) (token string, signature string, err error)
GenerateAuthorizeCode implements oauth2.CoreStrategy.
func (*PocketBaseStrategy) GenerateRefreshToken ¶
func (s *PocketBaseStrategy) GenerateRefreshToken(ctx context.Context, requester fosite.Requester) (token string, signature string, err error)
GenerateRefreshToken implements oauth2.CoreStrategy.
func (*PocketBaseStrategy) RefreshTokenSignature ¶
func (s *PocketBaseStrategy) RefreshTokenSignature(ctx context.Context, token string) string
RefreshTokenSignature implements oauth2.CoreStrategy.
func (*PocketBaseStrategy) ValidateAccessToken ¶
func (s *PocketBaseStrategy) ValidateAccessToken(ctx context.Context, requester fosite.Requester, token string) error
ValidateAccessToken implements oauth2.CoreStrategy.
Two checks: (1) the PB native JWT must validate (cryptographic + expiry), and (2) the corresponding _oauth2Access session row must still exist. The second check is what makes /oauth2/revoke actually effective for callers that reach this function (fosite introspection + the local RequireScope middleware). PB-native routes that use apis.RequireAuth() bypass this entirely — wire RevokedTokenGuard() on those routes when you need revocation to take effect there too.
func (*PocketBaseStrategy) ValidateAuthorizeCode ¶
func (s *PocketBaseStrategy) ValidateAuthorizeCode(ctx context.Context, requester fosite.Requester, token string) (err error)
ValidateAuthorizeCode implements oauth2.CoreStrategy.
func (*PocketBaseStrategy) ValidateRefreshToken ¶
func (s *PocketBaseStrategy) ValidateRefreshToken(ctx context.Context, requester fosite.Requester, token string) (err error)
ValidateRefreshToken implements oauth2.CoreStrategy.
type RFC7591ClientMetadata ¶
type RFC7591ClientMetadata struct {
RFC7591ClientMetadataRequest
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
ClientSecretExpiresAt int64 `json:"client_secret_expires_at"`
}
type RFC7591ClientMetadataRequest ¶
type RFC7591ClientMetadataRequest struct {
Scope string `json:"scope"`
RedirectURIs []string `json:"redirect_uris"`
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"`
GrantTypes []string `json:"grant_types"`
ResponseTypes []string `json:"response_types"`
Contacts []string `json:"contacts,omitempty"`
ClientName string `json:"client_name"`
ClientURI string `json:"client_uri,omitempty"`
LogoURI string `json:"logo_uri,omitempty"`
TermsOfServiceURI string `json:"tos_uri,omitempty"`
PolicyURI string `json:"policy_uri,omitempty"`
JwksURI string `json:"jwks_uri,omitempty"`
Jwks *jose.JSONWebKeySet `json:"jwks,omitempty"`
SoftwareID string `json:"software_id,omitempty"`
SoftwareVersion string `json:"software_version,omitempty"`
// The following fields are not part of the RFC7591 but are required for OpenID Connect client registration.
// @ref https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.6.2
RequestURIs []string `json:"request_uris,omitempty"`
}
type RFC7591ClientStorage ¶
type RefreshTokenModel ¶
type RefreshTokenModel struct {
BaseSessionModel
}
func (*RefreshTokenModel) GetCollectionName ¶
func (p *RefreshTokenModel) GetCollectionName() string
func (*RefreshTokenModel) GetFamilyID ¶
func (m *RefreshTokenModel) GetFamilyID() string
func (*RefreshTokenModel) GetParentRefreshID ¶
func (m *RefreshTokenModel) GetParentRefreshID() string
func (*RefreshTokenModel) GetReusedAt ¶
func (m *RefreshTokenModel) GetReusedAt() int64
func (*RefreshTokenModel) GetRotatedAt ¶
func (m *RefreshTokenModel) GetRotatedAt() int64
func (*RefreshTokenModel) GetStatus ¶
func (m *RefreshTokenModel) GetStatus() string
func (*RefreshTokenModel) SetFamilyID ¶
func (m *RefreshTokenModel) SetFamilyID(v string)
func (*RefreshTokenModel) SetParentRefreshID ¶
func (m *RefreshTokenModel) SetParentRefreshID(v string)
func (*RefreshTokenModel) SetReusedAt ¶
func (m *RefreshTokenModel) SetReusedAt(ts int64)
func (*RefreshTokenModel) SetRotatedAt ¶
func (m *RefreshTokenModel) SetRotatedAt(ts int64)
func (*RefreshTokenModel) SetStatus ¶
func (m *RefreshTokenModel) SetStatus(v string)
type Session ¶
type Session struct {
fositeopenid.DefaultSession
CollectionId string `json:"collection,omitempty"`
}
func NewSession ¶
NewSession constructs a Session with issuer + subject metadata populated.
Claims.ExpiresAt is intentionally left as the zero time.Time. fosite's openid/strategy_jwt.go computes the actual ID-token expiry from Config.GetIDTokenLifespan (or per-client overrides) when ExpiresAt is zero; setting an arbitrary 6-hour default here unconditionally over-rode that configuration. Callers that genuinely need a per-session override can still mutate Claims.ExpiresAt before passing the session to fosite.
func (*Session) Clone ¶
Clone returns a deep copy of the session. fosite calls Clone before mutating session state in introspection/refresh flows; sharing a single session pointer would let one request observe another's amr/acr/sub changes. We hand-clone instead of pulling in github.com/mohae/deepcopy (unmaintained since 2017) since the struct is small and fully known.
func (*Session) GetJWTClaims ¶
func (s *Session) GetJWTClaims() jwt.JWTClaimsContainer
type SessionModel ¶
type SessionModel interface {
core.RecordProxy
GetCollectionName() string
}
type UserInfoAddressClaim ¶
type UserInfoAddressClaim struct {
// Formatted
// Full mailing address, formatted for display or use on a mailing label. This field MAY
// contain multiple lines, separated by newlines. Newlines can be represented either as a
// carriage return/line feed pair ("\r\n") or as a single line feed character ("\n").
Formatted string `json:"formatted,omitempty"`
// Street Address
// Full street address component, which MAY include house number, street name, Post Office Box,
// and multi-line extended street address information. This field MAY contain multiple lines,
// separated by newlines. Newlines can be represented either as a carriage return/line feed
// pair ("\r\n") or as a single line feed character ("\n").
StreetAddress string `json:"street_address,omitempty"`
// Locality
// City or locality component.
Locality string `json:"locality,omitempty"`
// Region
// State, province, prefecture, or region component.
Region string `json:"region,omitempty"`
// Postal Code
// Zip code or postal code component.
PostalCode string `json:"postal_code,omitempty"`
// Country
// Country name component.
Country string `json:"country,omitempty"`
}
func (UserInfoAddressClaim) IsEmpty ¶
func (a UserInfoAddressClaim) IsEmpty() bool
type UserInfoClaimStrategy ¶
type UserInfoClaimStrategy interface {
GetUserInfoClaims(e *core.RequestEvent, scopes []string) (interface{}, error)
}
UserInfoClaimStrategy defines the interface for retrieving user info claims based on the request event and scopes. This allows for custom implementations to determine how user info claims are populated and returned in the /userinfo endpoint.
type UserInfoClaims ¶
type UserInfoClaims struct {
// Subject
// Identifier for the End-User at the Issuer.
Sub string `json:"sub"`
// Name
// End-User's full name in displayable form including all name parts,
// possibly including titles and suffixes, ordered according to the
// End-User's locale and preferences.
Name string `json:"name,omitempty"`
// Given Name
// Given name(s) or first name(s) of the End-User. Note that in some
// cultures, people can have multiple given names; all can be present,
// with the names being separated by space characters.
GivenName string `json:"given_name,omitempty"`
// Family Name
// Surname(s) or last name(s) of the End-User. Note that in some
// cultures, people can have multiple family names or no family name;
// all can be present, with the names being separated by space
// characters.
FamilyName string `json:"family_name,omitempty"`
// Middle Name
// Middle name(s) of the End-User. Note that in some cultures, people
// can have multiple middle names; all can be present, with the names
// being separated by space characters.
MiddleName string `json:"middle_name,omitempty"`
// Nickname
// Casual name of the End-User that may or may not be the same as the
// given_name. For instance, a nickname value of Mike might be returned
// alongside a given_name value of Michael.
Nickname string `json:"nickname,omitempty"`
// Preferred Username
// Shorthand name by which the End-User wishes to be referred to at the
// RP, such as janedoe or j.doe. This value MAY be any valid JSON string
// including special characters such as @, /, or whitespace. The RP MUST
// NOT rely upon this value being unique, as discussed in Section 5.7.
PreferredUsername string `json:"preferred_username,omitempty"`
// Profile
// URL of the End-User's profile page. The contents of this Web page SHOULD
// be about the End-User.
Profile string `json:"profile,omitempty"`
// Picture
// URL of the End-User's profile picture. This URL MUST refer to an image
// file (for example, a PNG, JPEG, or GIF image file), rather than to a
// Web page containing an image. Note that this URL SHOULD specifically
// reference a profile photo of the End-User suitable for displaying when
// describing the End-User, rather than an arbitrary photo taken by the End-User.
Picture string `json:"picture,omitempty"`
// Website
// URL of the End-User's Web page or blog. This Web page SHOULD contain
// information published by the End-User or an organization that the End-User
// is affiliated with.
Website string `json:"website,omitempty"`
// Email
// End-User's preferred e-mail address. Its value MUST conform to the RFC 5322
// addr-spec syntax. The RP MUST NOT rely upon this value being unique, as
// discussed in Section 5.7.
Email string `json:"email,omitempty"`
// Email Verified
// True if the End-User's e-mail address has been verified; otherwise false.
// When this Claim Value is true, this means that the OP took affirmative steps
// to ensure that this e-mail address was controlled by the End-User at the time
// the verification was performed. The means by which an e-mail address is
// verified is context specific, and dependent upon the trust framework or
// contractual agreements within which the parties are operating.
EmailVerified bool `json:"email_verified,omitempty"`
// Gender
// End-User's gender. Values defined by this specification are female and male.
// Other values MAY be used when neither of the defined values are applicable.
Gender string `json:"gender,omitempty"`
// Birthdate
// End-User's birthday, represented as an ISO 8601-1 [ISO8601‑1] YYYY-MM-DD format.
// The year MAY be 0000, indicating that it is omitted. To represent only the year,
// YYYY format is allowed. Note that depending on the underlying platform's date
// elated function, providing just year can result in varying month and day, so the
// implementers need to take this factor into account to correctly process the dates.
Birthdate string `json:"birthdate,omitempty"`
// ZoneInfo
// String from IANA Time Zone Database [IANA.time‑zones] representing the End-User's
// time zone. For example, Europe/Paris or America/Los_Angeles.
ZoneInfo string `json:"zoneinfo,omitempty"`
// Locale
// End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically
// an ISO 639 Alpha-2 [ISO639] language code in lowercase and an ISO 3166-1 Alpha-2
// [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or
// fr-CA. As a compatibility note, some implementations have used an underscore as
// the separator rather than a dash, for example, en_US; Relying Parties MAY choose
// to accept this locale syntax as well.
Locale string `json:"locale,omitempty"`
// Phone Number
// End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of
// this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number
// contains an extension, it is RECOMMENDED that the extension be represented using the
// RFC 3966 [RFC3966] extension syntax, for example, +1 (604) 555-1234;ext=5678.
PhoneNumber string `json:"phone_number,omitempty"`
// Phone Number Verified
// True if the End-User's phone number has been verified; otherwise false. When this Claim
// Value is true, this means that the OP took affirmative steps to ensure that this phone
// number was controlled by the End-User at the time the verification was performed. The
// means by which a phone number is verified is context specific, and dependent upon the
// trust framework or contractual agreements within which the parties are operating. When
// true, the phone_number Claim MUST be in E.164 format and any extensions MUST be
// represented in RFC 3966 format.
PhoneNumberVerified bool `json:"phone_number_verified,omitempty"`
// Address
// End-User's preferred postal address. The value of the address member is a JSON [RFC8259]
// structure containing some or all of the members defined in Section 5.1.1.
Address *UserInfoAddressClaim `json:"address,omitempty"`
// Updated At
// Time the End-User's information was last updated. Its value is a JSON number representing
// the number of seconds from 1970-01-01T00:00:00Z as measured in UTC until the date/time.
UpdatedAt int64 `json:"updated_at,omitempty"`
}