Documentation
¶
Overview ¶
Package oauth2 wires a go-oauth2/oauth2/v4 server into a nexus app and bridges its access-token store to nexus.auth so handlers can gate themselves with auth.Required() / auth.Requires().
Minimum config — password grant against a user store:
nexus.Run(
nexus.Config{Server: nexus.ServerConfig{Addr: ":8080"}},
oauth2.Module(oauth2.Config{
Authenticator: func(ctx context.Context, clientID, username, password string) (string, error) {
user, err := users.Authenticate(ctx, username, password)
if err != nil { return "", err }
return strconv.Itoa(int(user.ID)), nil
},
}),
)
Defaults are conservative: in-memory token store, anonymous client only, no JTI, "Bearer" token type, 5-minute identity cache. Production apps replace TokenStore (Redis-backed via NewCacheTokenStore), ClientStore (DB-backed via NewLoaderClientStore), and frequently set IdentityResolver to populate Identity.Roles / .Extra.
The package owns:
- POST {Config.TokenPath} OAuth2 token endpoint
- POST {Config.RevokePath} token revocation (when set)
- bridge from token-store ↔ auth.Module
Three-legged authorization-code flow is reachable via Config.ServerCustomizer (set SetUserAuthorizationHandler and add the route yourself); the package doesn't mount it by default because most apps using this run password / client_credentials.
Index ¶
- Variables
- func DefaultErrorMapper(err error) *errors.Response
- func Module(cfg Config) nexus.Option
- func NewCacheTokenStore(cache Cache, prefix string) oauth2lib.TokenStore
- func NewLoadedClient(c StaticClient) oauth2lib.ClientInfo
- func NewLoaderClientStore(load LoaderFunc) oauth2lib.ClientStore
- func NewStaticClientStore(clients ...StaticClient) oauth2lib.ClientStore
- func SoftenStockMessages(re *errors.Response)
- func VerifyBcrypt(stored, input string) bool
- func VerifySpringPassword(stored, input string) (ok bool, scheme string)
- type Cache
- type Config
- type ErrorMapper
- type IdentityResolver
- type LoaderFunc
- type PasswordAuthenticator
- type Server
- type Session
- type StaticClient
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidCredentials = stderrors.New("oauth2: invalid credentials") ErrAccountDisabled = stderrors.New("oauth2: account disabled") ErrAccountLocked = stderrors.New("oauth2: account locked") )
Sentinel errors callers can return from PasswordAuthenticator to get the standard, user-friendly OAuth2 response without writing their own ErrorMapper. Domain code stays free of OAuth2 types — just return one of these and the mapper does the translation.
var DefaultErrorMessages = map[error]string{ ErrInvalidCredentials: "The username or password you entered is incorrect. Please try again.", ErrAccountDisabled: "Your account has been disabled. Please contact your administrator.", ErrAccountLocked: "Your account is locked due to too many failed sign-in attempts. Please contact your administrator.", ErrServiceUnavailable: "The sign-in service is temporarily unavailable. Please try again in a moment.", }
DefaultErrorMessages maps each well-known sentinel to the description string emitted in the OAuth2 error response. Exported so apps can clone, edit (i18n, brand voice), and pass the result to NewErrorMapper.
var ErrCacheMiss = errors.New("oauth2: cache miss")
ErrCacheMiss is the sentinel implementations may return from Get when the key is absent. Treated identically to a non-nil error other than this one (i.e. "no token here") — tokens.go converts any miss/error into a nil TokenInfo so a misconfigured cache fails closed instead of crashing.
Functions ¶
func DefaultErrorMapper ¶
DefaultErrorMapper translates the bundled sentinel errors into OAuth2 responses with DefaultErrorMessages. Returns nil for any other error so go-oauth2's stock translation runs.
func Module ¶
Module wires the OAuth2 server into a nexus app. Returns a nexus.Option to compose into your top-level Module(...) chain.
func NewCacheTokenStore ¶
func NewCacheTokenStore(cache Cache, prefix string) oauth2lib.TokenStore
NewCacheTokenStore returns an oauth2lib.TokenStore that persists tokens in the supplied Cache under keys prefixed with `prefix`.
Storage layout (4 keyspaces under prefix):
{prefix}basic:{uuid} → JSON-encoded TokenInfo (the source of truth)
{prefix}access:{access} → uuid pointing at the basic record
{prefix}refresh:{refresh} → uuid pointing at the basic record
{prefix}code:{code} → JSON-encoded TokenInfo (auth-code grant)
Refreshing rewrites access/refresh keys but reuses the basic record so a refresh-then-revoke-old-access doesn't strand the refresh token.
func NewLoadedClient ¶
func NewLoadedClient(c StaticClient) oauth2lib.ClientInfo
NewLoadedClient adapts a plain StaticClient into a ClientInfo — useful inside a LoaderFunc when you're reading rows from a DB and want the same secret-verification matrix as the static store.
func NewLoaderClientStore ¶
func NewLoaderClientStore(load LoaderFunc) oauth2lib.ClientStore
NewLoaderClientStore wraps a LoaderFunc as a ClientStore. The loader is called on every GetByID — add caching inside the loader if your backend can't take the load.
func NewStaticClientStore ¶
func NewStaticClientStore(clients ...StaticClient) oauth2lib.ClientStore
NewStaticClientStore returns a ClientStore over an in-memory list. Useful for tests and tiny apps; for production, prefer NewLoaderClientStore against your DB.
func SoftenStockMessages ¶
SoftenStockMessages returns a ResponseErrorRewriter that replaces the worst of go-oauth2's stock descriptions with friendlier strings. Mirrors the rewriter in portal_admin/services/server.go. Pass directly as Config.ResponseErrorRewriter.
func VerifyBcrypt ¶
VerifyBcrypt compares input against a bcrypt hash. Accepts both raw bcrypt strings and {bcrypt}-prefixed values; returns false for any other format. Constant-time semantics from x/crypto.
func VerifySpringPassword ¶
VerifySpringPassword tries every password format Spring Security's DelegatingPasswordEncoder emits in the wild, in priority order:
- {bcrypt}$2a$… — modern Spring default
- {noop}plain — disabled-encoding marker (test fixtures)
- $2a$ / $2b$ / $2y$ — raw bcrypt without the {scheme} prefix
- 40-char hex with a 10-char salt prefix — legacy salted-sha1 (Spring's StandardPasswordEncoder pre-5.0)
Returns (ok, scheme) — scheme is the matched bucket or "" on miss, useful for logging which path resolved the credential. Never logs the input.
Types ¶
type Cache ¶
type Cache interface {
Get(ctx context.Context, key string) (string, error)
Set(ctx context.Context, key, value string, ttl time.Duration) error
Delete(ctx context.Context, key string) error
}
Cache is the small surface NewCacheTokenStore needs from a caller's cache layer. Any Redis / Memcached / in-memory wrapper that exposes string Get/Set/Delete with TTL fits — the package doesn't import a specific cache library.
type Config ¶
type Config struct {
// Authenticator validates credentials for the password grant.
// Required for password-grant flows. Optional for pure
// client_credentials apps — leave nil and only client_credentials
// will issue tokens.
Authenticator PasswordAuthenticator
// ClientStore loads OAuth2 clients by ID. Defaults to a single
// public anonymous client (id="anonymous", no secret) — fine for
// trusted-network deploys and tests, wrong for the public
// internet. Provide NewLoaderClientStore or NewStaticClientStore
// for real workloads.
ClientStore oauth2lib.ClientStore
// TokenStore persists access/refresh/code tokens. Defaults to
// go-oauth2's in-memory store (single-process only). Use
// NewCacheTokenStore for horizontally-scaled apps.
TokenStore oauth2lib.TokenStore
// IdentityResolver enriches the auth.Identity built from each
// resolved access token. Defaults to {ID: ti.GetUserID()} with
// the raw token + TokenInfo on .Extra (a *Session) so logout
// handlers can revoke without re-extracting.
IdentityResolver IdentityResolver
// ErrorMapper translates Authenticator errors → OAuth2 responses.
// Defaults to DefaultErrorMapper (handles ErrInvalidCredentials,
// ErrAccountDisabled, ErrAccountLocked, ErrServiceUnavailable).
// Override or wrap to translate domain errors.
ErrorMapper ErrorMapper
// ResponseErrorRewriter runs after go-oauth2 builds an OAuth2
// error response — use it to soften descriptions, translate
// strings, or log. Optional.
ResponseErrorRewriter func(*errors.Response)
// TokenType is the value emitted as token_type in the response.
// Defaults to "Bearer". Set to "bearer" (lowercase) when
// migrating clients written against Spring's DefaultTokenServices.
TokenType string
// IncludeJTI adds a unique "jti" extension field to every
// issued token, useful for revocation lists / replay tracking.
// Off by default.
IncludeJTI bool
// AllowGetAccessRequest mirrors server.SetAllowGetAccessRequest.
// Off by default — the OAuth2 spec recommends POST.
AllowGetAccessRequest bool
// TokenPath is the mount path for the token endpoint. Defaults
// to "/oauth/token".
TokenPath string
// RevokePath, when non-empty, mounts a POST handler that
// removes the access token from the TokenStore. Empty by default.
RevokePath string
// IdentityCache bounds how long a resolved auth.Identity stays
// in the auth.Manager cache. Defaults to 5 minutes; set to
// negative to disable caching.
IdentityCache time.Duration
// Manager is an escape hatch — when non-nil, the package uses
// it verbatim and ignores ClientStore / TokenStore. Use for
// exotic config (custom token generator, code-expiry policy)
// the rest of Config doesn't expose.
Manager *manage.Manager
// ServerCustomizer runs after the *server.Server is built and
// before Mount, giving callers access to any go-oauth2 setter
// Config doesn't surface (UserAuthorizationHandler, ScopeHandler,
// custom AccessTokenExpHandler, etc).
ServerCustomizer func(*server.Server)
// AuthExtra are auth.Config fields the caller wants to thread
// through (OnResolve, OnFail, Permissions, error rewriters).
// Resolve and Cache are owned by this package and ignored if set.
AuthExtra auth.Config
}
Config drives oauth2.Module. The only required field is Authenticator (for password grant); everything else has a default.
type ErrorMapper ¶
ErrorMapper translates an internal error (returned by Authenticator or any custom handler) into an OAuth2 *errors.Response. Return nil to fall through to go-oauth2's stock translation.
func NewErrorMapper ¶
func NewErrorMapper(messages map[error]string) ErrorMapper
NewErrorMapper builds an ErrorMapper from a custom message table — use to override the bundled descriptions (i18n) without rewriting the switch logic.
type IdentityResolver ¶
IdentityResolver turns a verified OAuth2 TokenInfo into an auth.Identity. Defaults to {ID: ti.GetUserID()} — override to populate Roles / Scopes / Extra from a user-profile lookup.
type LoaderFunc ¶
LoaderFunc loads a client record by ID. Implementations typically hit a DB or cache; the result must satisfy oauth2lib.ClientInfo. Wrap a pure-data record with NewLoadedClient when you only have the fields, not the interface.
type PasswordAuthenticator ¶
type PasswordAuthenticator func(ctx context.Context, clientID, username, password string) (userID string, err error)
PasswordAuthenticator validates a (username, password) pair against the app's user store and returns the user ID that becomes TokenInfo.GetUserID() (and downstream auth.Identity.ID). Return a typed error and ErrorMapper translates it into the OAuth2 response; return ErrServiceUnavailable / ErrAccountLocked / etc. from the package-level vars to get the standard messages for free.
type Server ¶
type Server struct {
*nexus.Service
Srv *server.Server
Manager *manage.Manager
// contains filtered or unexported fields
}
Server is the wired OAuth2 server. Exported so handlers (revoke, custom userinfo, etc.) can reach the underlying *server.Server and *manage.Manager when ServerCustomizer isn't enough.
func (*Server) HandleRevoke ¶
HandleRevoke removes the bearer token from the TokenStore. Mounted under RevokePath when set. Returns 204 on success / unknown token (idempotent) so clients can safely retry.
func (*Server) HandleToken ¶
HandleToken is the OAuth2 token endpoint. Mounted under TokenPath.
type Session ¶
Session is the default Identity.Extra payload — carries the raw token + TokenInfo so a logout handler can revoke both the token store entry and the auth cache without re-extracting headers.
type StaticClient ¶
StaticClient is the data shape used by NewStaticClientStore.