oidc

package module
v0.3.10 Latest Latest
Warning

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

Go to latest
Published: Dec 30, 2025 License: MIT Imports: 42 Imported by: 0

README

OIDC: Next-Gen OpenID Connect Library

Go Report Card

中文 | English

oidc is a security-first, architecture-decoupled, and OAuth 2.1 ready OpenID Connect library for Go.

It goes beyond a simple protocol implementation to provide production-grade authentication infrastructure. It features a Tiered Storage Architecture that perfectly balances data durability (SQL) with high-concurrency performance (Redis), along with built-in defenses against DoS attacks, distributed consistency support, and native support for cutting-edge security standards like DPoP, PAR, and PKCE.

Core Features

  • Standard Compliance: Full implementation of OIDC Core 1.0 and RFC 6749 (OAuth 2.0).
  • Tiered Storage Architecture:
    • Persistence Layer (SQL): Stores "asset" data like Clients, Users, and Refresh Tokens.
    • Cache Layer (Redis): Handles high-frequency/ephemeral data like Auth Codes, Distributed Locks, Blacklists, and Replay Caches.
    • Hybrid Replay Protection: Manages Grace Period for Refresh Token rotation via Redis, ensuring both performance and consistency.
  • Cutting-Edge Security:
    • DPoP (RFC 9449): Application-layer proof-of-possession to prevent token theft and replay (Sender-Constrained Tokens).
    • PAR (RFC 9126): Pushed Authorization Requests to hide parameters and prevent frontend tampering.
    • PKCE (RFC 7636): Enforced by default to prevent authorization code interception.
  • Defensive Architecture:
    • Hybrid Refresh Tokens: Uses "Structured Token + DB Index" design. Signatures and expiration are verified via CPU before any database lookup, effectively mitigating DB DoS attacks.
    • Key Rotation: Supports automated asymmetric key rotation with Redis-based coordination for zero-downtime updates across multiple instances.
  • Observability: Deep integration with o11y for structured logging, distributed tracing, and key metrics.

Installation

go get github.com/oy3o/oidc

Quick Start

To build a production-grade OIDC server, you combine SQL (Persistence) and Redis (Cache).

1. Initialization
package main

import (
    "context"
    "net/http"
    "time"
    
    "github.com/redis/go-redis/v9"
    "github.com/oy3o/oidc"
    "github.com/oy3o/oidc/persist"
    "github.com/oy3o/oidc/cache"
)

func main() {
    ctx := context.Background()

    // 1. Init Persistence Layer (SQL) -> Assets (Client, User, RT)
    persistStore := persist.NewPgx(db, &MyHasher{})
    
    // 2. Init Cache Layer (Redis) -> Ephemeral (AuthCode, Lock, DPoP)
    cacheStore := cache.NewRedis(redisClient)

    // 3. [Crucial] Compose Tiered Storage
    // Requests are automatically routed to the correct layer
    storage := oidc.NewTieredStorage(persistStore, cacheStore)

    // 4. Init Key Manager (Auto L1+L2+L3 Caching)
    km := oidc.NewKeyManager(storage)
    // Generate initial key if none exists
    if _, _, err := km.GetSigningKey(ctx); err != nil {
        km.Generate(ctx, oidc.KEY_RSA, true)
    }

    // 5. Init HMAC Secret Manager (for Refresh Token signatures)
    sm := oidc.NewSecretManager()
    sm.AddKey("hmac-key-1", "your-32-byte-hex-secret...")

    // 6. Create Server
    server, err := oidc.NewServer(oidc.ServerConfig{
        Issuer:         "https://auth.example.com",
        Storage:        storage,
        Hasher:         &MyHasher{},
        SecretManager:  sm,
        AccessTokenTTL: 1 * time.Hour,
    })
    if err != nil {
        panic(err)
    }

    // 7. Register Routes (using httpx adapters)
    // Note: httpx adapters are in the 'oidc/httpx' package
    // mux.Handle("POST /token", oidc_httpx.TokenHandler(server))
    // ...
}

Advanced Security

DPoP (Sender-Constrained Tokens)

DPoP binds Access Tokens to the client's private key. Even if stolen, the token cannot be used without the corresponding private key signature.

// Enable DPoP Middleware (replayCache is usually Redis)
mux.Handle("POST /token", oidc.DPoPRequiredMiddleware(cacheStore)(
    httpx.NewHandler(oidc_httpx.TokenHandler(server))
))
PAR (Pushed Authorization Requests)

PAR requires clients to push parameters to the backend first to exchange for a request_uri.

// Register PAR Endpoint (Data stored in Redis, TTL 60s)
mux.Handle("POST /par", oidc_httpx.PARHandler(server))

Supported Standards (RFCs)

Spec Description Status
OIDC Core 1.0 OpenID Connect Core ✅ Full
RFC 6749 OAuth 2.0 Framework ✅ Full
RFC 7636 PKCE ✅ Enforced
RFC 7009 Token Revocation ✅ Supported
RFC 7662 Token Introspection ✅ Supported
RFC 8628 Device Flow ✅ Supported
RFC 9126 PAR (Pushed Authorization Requests) ✅ Supported
RFC 9449 DPoP ✅ Supported

> **Note:** This library relies on `github.com/oy3o/o11y` for logging and metrics. Please ensure that the o11y configuration is initialized correctly when the application starts.

Documentation

Index

Constants

View Source
const (
	DeviceCodeStatusPending = "pending"
	DeviceCodeStatusAllowed = "allowed"
	DeviceCodeStatusDenied  = "denied"
)
View Source
const (
	// Response Types
	ResponseTypeCode = "code"

	// Grant Types
	GrantTypeAuthorizationCode = "authorization_code"
	GrantTypeRefreshToken      = "refresh_token"
	GrantTypeClientCredentials = "client_credentials"
	GrantTypePassword          = "password"
	GrantTypeDeviceCode        = "urn:ietf:params:oauth:grant-type:device_code"

	// Subject Types
	SubjectTypePublic = "public"

	// Auth Methods
	AuthMethodClientSecretBasic = "client_secret_basic"
	AuthMethodClientSecretPost  = "client_secret_post"

	// Scopes
	ScopeOpenID        = "openid"
	ScopeProfile       = "profile"
	ScopeEmail         = "email"
	ScopePhone         = "phone"
	ScopeOfflineAccess = "offline_access"
)
View Source
const (
	// CodeChallengeMethodS256 是推荐的 PKCE 转换方法
	CodeChallengeMethodS256 = "S256"
	// CodeChallengeMethodPlain 是不推荐的方法,仅用于兼容性
	CodeChallengeMethodPlain = "plain"
)
View Source
const (
	// DefaultKeyCacheTTL 是本地缓存签名 Key ID 的默认有效期
	// 在分布式环境中,这决定了轮换后其他实例感知到的最大延迟
	DefaultKeyCacheTTL = 30 * time.Second
)

Variables

View Source
var (
	// ErrInvalidRequest 请求缺少必需的参数、包含无效的参数值、包含多个同名参数,或者格式不正确。
	ErrInvalidRequest = errors.New("invalid_request")

	// ErrInvalidClient 客户端认证失败(例如:未知的客户端、未包含客户端认证信息、不支持的认证方法)。
	ErrInvalidClient = errors.New("invalid_client")

	// ErrInvalidGrant 提供的授权许可(例如:授权码、资源所有者凭据)或刷新令牌无效、过期、已撤销、与重定向 URI 不匹配,或不属于该客户端。
	ErrInvalidGrant = errors.New("invalid_grant")

	// ErrUnauthorizedClient 经过认证的客户端无权使用此授权许可类型。
	ErrUnauthorizedClient = errors.New("unauthorized_client")

	// ErrUnsupportedGrantType 授权服务器不支持该授权许可类型。
	ErrUnsupportedGrantType = errors.New("unsupported_grant_type")

	// ErrInvalidScope 请求的范围无效、未知、格式不正确,或超出了资源所有者授予的范围。
	ErrInvalidScope = errors.New("invalid_scope")

	// ErrAccessDenied 资源所有者或授权服务器拒绝了请求。
	ErrAccessDenied = errors.New("access_denied")

	// ErrServerError 授权服务器遇到意外情况,无法完成请求。
	ErrServerError = errors.New("server_error")

	// ErrTemporarilyUnavailable 授权服务器目前因过载或维护而无法处理请求。
	ErrTemporarilyUnavailable = errors.New("temporarily_unavailable")
)
View Source
var (
	// ErrAuthorizationPending 设备授权请求待处理,客户端应继续轮询。
	ErrAuthorizationPending = errors.New("authorization_pending")

	// ErrSlowDown 客户端轮询过于频繁,应减慢轮询速率。
	ErrSlowDown = errors.New("slow_down")

	// ErrExpiredToken 设备码已过期。
	ErrExpiredToken = errors.New("expired_token")
)
View Source
var (
	ErrTokenExpired          = errors.New("token is expired")
	ErrTokenSignatureInvalid = errors.New("token signature is invalid")
	ErrInvalidIssuer         = errors.New("invalid issuer")
	ErrInvalidAudience       = errors.New("invalid audience")
	ErrInvalidNonce          = errors.New("invalid nonce")
	ErrNotFound              = errors.New("resource not found")
	ErrTokenFormatInvalid    = errors.New("token format is invalid")
	ErrTokenForged           = errors.New("token forged")
)
View Source
var (
	ErrClientNotFound = errors.New("client not found")
	ErrTokenNotFound  = errors.New("token not found or expired")
	ErrCodeNotFound   = errors.New("authorization code not found or consumed")
	ErrUserNotFound   = errors.New("user not found")
)
View Source
var (
	// ErrKeyNil 密钥不能为 nil
	ErrKeyNil = errors.New("key cannot be nil")

	// ErrInvalidRSAKey 无效的 RSA 私钥
	ErrInvalidRSAKey = errors.New("invalid RSA private key")

	// ErrRSAKeyTooSmall RSA 私钥必须至少 2048 位
	ErrRSAKeyTooSmall = errors.New("RSA private key must be at least 2048 bits")

	// ErrInvalidEd25519KeySize 无效的 Ed25519 私钥大小
	ErrInvalidEd25519KeySize = errors.New("invalid Ed25519 private key size")

	// ErrNoSigningKey 未配置签名密钥
	ErrNoSigningKey = errors.New("no signing key configured")

	// ErrKeyNotFound 密钥未找到
	ErrKeyNotFound = errors.New("key not found")

	// ErrTokenRevoked 表示 Access Token 已被撤销 (例如用户登出或安全事件)
	ErrTokenRevoked = errors.New("token has been revoked")

	// ErrCannotRemoveSigningKey 不能删除当前的签名密钥
	ErrCannotRemoveSigningKey = errors.New("cannot remove current signing key")

	// ErrUnsupportedKeyType 不支持的密钥类型
	ErrUnsupportedKeyType = errors.New("unsupported key type")

	// ErrCircuitBreakerOpen 断路器打开
	ErrCircuitBreakerOpen = errors.New("remote JWKS unavailable (circuit breaker open)")

	// ErrKeyExpired 密钥已过期
	ErrKeyExpired = errors.New("key expired")

	// ErrNoActiveKey 未设置活跃密钥
	ErrNoActiveKey = errors.New("no active key set")

	// ErrKIDEmpty KID 不能为空
	ErrKIDEmpty = errors.New("kid cannot be empty")

	// ErrKeyTooShort 密钥长度不足
	ErrKeyTooShort = errors.New("key must be at least 32 bytes")

	// ErrCannotRemoveActiveKey 不能删除活跃密钥
	ErrCannotRemoveActiveKey = errors.New("cannot remove active key")

	// ErrUnsupportedECDSACurve 不支持的 ECDSA 曲线
	ErrUnsupportedECDSACurve = errors.New("unsupported ECDSA curve")

	// ErrUnsupportedPrivateKeyType 不支持的私钥类型
	ErrUnsupportedPrivateKeyType = errors.New("unsupported private key type")

	// ErrSigningKeyMissing 当前签名密钥丢失
	ErrSigningKeyMissing = errors.New("current signing key is missing")

	// ErrKeyInterfaceNotImplemented 存储的密钥未实现 Key 接口
	ErrKeyInterfaceNotImplemented = errors.New("stored key does not implement Key interface")

	// ErrNoActiveRefreshTokenKey 没有用于刷新令牌的活跃密钥
	ErrNoActiveRefreshTokenKey = errors.New("hmac key check failed: no active key for refresh tokens")

	// ErrSchedulerAlreadyStarted 调度器已启动
	ErrSchedulerAlreadyStarted = errors.New("scheduler already started")

	// ErrRotationInProgress 另一个实例正在进行轮换
	ErrRotationInProgress = errors.New("rotation already in progress by another instance")

	// ErrMemoryProviderOnly 操作仅支持 MemoryKeyProvider
	ErrMemoryProviderOnly = errors.New("operation only supported for MemoryKeyProvider")
)
View Source
var (
	// ErrPKCEVerifierEmpty PKCE verifier 不能为空
	ErrPKCEVerifierEmpty = errors.New("pkce verifier cannot be empty")

	// ErrPKCEVerifierInvalidLength PKCE verifier 长度无效
	ErrPKCEVerifierInvalidLength = errors.New("invalid pkce verifier length")

	// ErrPKCEVerifierInvalidChars PKCE verifier 包含无效字符
	ErrPKCEVerifierInvalidChars = errors.New("invalid characters in pkce verifier")

	// ErrPKCEVerificationFailed PKCE 验证失败
	ErrPKCEVerificationFailed = errors.New("pkce verification failed")

	// ErrPKCERandomnessGenerationFailed PKCE 随机数生成失败
	ErrPKCERandomnessGenerationFailed = errors.New("failed to generate randomness for pkce")

	// ErrUnsupportedPKCEChallengeMethod 不支持的 PKCE challenge 方法
	ErrUnsupportedPKCEChallengeMethod = errors.New("unsupported pkce challenge method")
)
View Source
var (
	// ErrKeyNotEC 密钥不是 EC 类型
	ErrKeyNotEC = errors.New("key is not EC")

	// ErrKeyNotRSA 密钥不是 RSA 类型
	ErrKeyNotRSA = errors.New("key is not RSA")

	// ErrKeyNotEd25519 密钥不是 Ed25519 类型
	ErrKeyNotEd25519 = errors.New("key is not Ed25519")

	// ErrInvalidEd25519PublicKeySize 无效的 Ed25519 公钥大小
	ErrInvalidEd25519PublicKeySize = errors.New("invalid Ed25519 public key size")

	// ErrInvalidJWKType 无效的 JWK 类型
	ErrInvalidJWKType = errors.New("invalid JWK type")

	// ErrMissingJWKFields 缺少 JWK 字段
	ErrMissingJWKFields = errors.New("missing JWK fields")

	// ErrUnsupportedJWKPublicKeyType 不支持的 JWK 公钥类型
	ErrUnsupportedJWKPublicKeyType = errors.New("jwks: unsupported key type")

	// ErrUnsupportedKtyForThumbprint 不支持的 kty 用于指纹计算
	ErrUnsupportedKtyForThumbprint = errors.New("unsupported kty for thumbprint")

	// ErrUnsupportedCurve 不支持的曲线
	ErrUnsupportedCurve = errors.New("jwks: unsupported curve")
)
View Source
var (
	// ErrIssuerEmpty 发行者不能为空
	ErrIssuerEmpty = errors.New("issuer cannot be empty")

	// ErrInvalidIssuerURL 发行者必须是有效的 URL
	ErrInvalidIssuerURL = errors.New("issuer must be a valid URL")

	// ErrInvalidTTL Token TTL 必须大于 0
	ErrInvalidTTL = errors.New("token TTL must be greater than 0")

	// ErrKeyManagerNil KeyManager 不能为 nil
	ErrKeyManagerNil = errors.New("keyManager cannot be nil")

	// ErrSecretManagerNil SecretManager 不能为 nil
	ErrSecretManagerNil = errors.New("secretManager cannot be nil")

	// ErrFailedToGenerateUUID 生成 UUID 失败
	ErrFailedToGenerateUUID = errors.New("failed to generate UUIDv7")

	// ErrUnsupportedSigningKeyType 不支持的签名密钥类型
	ErrUnsupportedSigningKeyType = errors.New("unsupported signing key type")

	// ErrUnsupportedAlgForHash 不支持的哈希计算算法
	ErrUnsupportedAlgForHash = errors.New("unsupported alg for hash calculation")
)
View Source
var (
	// ErrHash256InvalidLength Hash256 必须为 32 字节
	ErrHash256InvalidLength = errors.New("Hash256 must be exactly 32 bytes")

	// ErrHash256ScanInvalidLength 扫描失败: Hash256 应为 32 字节
	ErrHash256ScanInvalidLength = errors.New("scan failed: expected 32 bytes for Hash256")

	// ErrHash256UnsupportedType 扫描失败: Hash256 不支持的类型
	ErrHash256UnsupportedType = errors.New("scan failed: unsupported type for Hash256")

	// ErrInvalidHexStringLength 无效的十六进制字符串长度
	ErrInvalidHexStringLength = errors.New("invalid hex string length")

	// ErrBinaryUUIDUnsupportedType BinaryUUID 无法扫描的类型
	ErrBinaryUUIDUnsupportedType = errors.New("BinaryUUID: cannot scan type")
)
View Source
var (
	// ErrUserIDRequired 生成授权码需要 user_id
	ErrUserIDRequired = errors.New("user_id is required to generate authorization code")

	// ErrTokenIsInvalid Token 无效
	ErrTokenIsInvalid = errors.New("token is invalid")

	// ErrExpClaimRequired exp 声明是必需的
	ErrExpClaimRequired = errors.New("exp claim is required")

	// ErrAZPRequired 当存在多个受众时需要 azp 声明
	ErrAZPRequired = errors.New("azp claim is required when multiple audiences are present")

	// ErrAZPMismatch azp 与 client_id 不匹配
	ErrAZPMismatch = errors.New("azp does not match client_id")

	// ErrAZPRequiredForTrust 受信任的客户端验证需要 azp 声明
	ErrAZPRequiredForTrust = errors.New("azp claim is required for trusted client validation")

	// ErrAZPNotAuthorized azp 未被授权访问资源
	ErrAZPNotAuthorized = errors.New("azp is not authorized to access resource")

	// ErrUnexpectedSigningMethod 意外的签名方法
	ErrUnexpectedSigningMethod = errors.New("unexpected signing method")

	// ErrHasherNotConfigured Hasher 未配置
	ErrHasherNotConfigured = errors.New("hasher not configured")

	// ErrInvalidIdentifier 无效的标识符
	ErrInvalidIdentifier = errors.New("invalid identifier")

	// ErrUserNotConfirmed 用户未确认
	ErrUserNotConfirmed = errors.New("user not confirmed")

	// ErrUserForbidden 用户被禁用
	ErrUserForbidden = errors.New("user forbidden")

	// ErrDefaultPassword 使用默认密码
	ErrDefaultPassword = errors.New("default password")

	// ErrIssuerURLRequired issuer URL 是必需的
	ErrIssuerURLRequired = errors.New("issuer url is required")

	// ErrStorageRequired storage 实现是必需的
	ErrStorageRequired = errors.New("storage implementation is required")

	// ErrHasherRequired hasher 实现是必需的
	ErrHasherRequired = errors.New("hasher implementation is required")

	// ErrSecretHasherRequired 机密客户端需要 secret hasher
	ErrSecretHasherRequired = errors.New("secret hasher is required for confidential clients")

	// ErrEnvTokenSecretRequired 环境变量 OIDC_TOKEN_SECRET 是必需的
	ErrEnvTokenSecretRequired = errors.New("env OIDC_TOKEN_SECRET is required")
)
View Source
var (
	// ErrPARNotSupported 服务器不支持 PAR
	ErrPARNotSupported = errors.New("server does not support PAR")

	// ErrDeviceFlowNotSupported 服务器不支持设备流
	ErrDeviceFlowNotSupported = errors.New("server does not support device flow")

	// ErrRevocationNotSupported 服务器不支持撤销
	ErrRevocationNotSupported = errors.New("server does not support revocation")

	// ErrIntrospectionNotSupported 服务器不支持内省
	ErrIntrospectionNotSupported = errors.New("server does not support introspection")

	// ErrNonceMismatch nonce 不匹配
	ErrNonceMismatch = errors.New("nonce mismatch")

	// ErrUnparseableError 请求失败且错误无法解析
	ErrUnparseableError = errors.New("request failed with unparseable error")
)
View Source
var AllowedSchemes = map[string]struct{}{
	"http":  {},
	"https": {},
}
View Source
var DefaultSupportedSigningAlgs = []string{"RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"}

DefaultSupportedSigningAlgs 默认支持的签名算法

View Source
var ErrInvalidURNFormat = errors.New("invalid urn format")

ErrInvalidURNFormat 无效的 URN 格式

Functions

func BuildDPoPBoundAccessTokenURI

func BuildDPoPBoundAccessTokenURI(fullURL string) (string, error)

BuildDPoPBoundAccessTokenURI 构建资源请求 URI (去除查询参数和片段) RFC 9449 要求 htu 与实际请求的 URI 匹配(不含查询参数)

func BuildRedirectURL

func BuildRedirectURL(baseURI, code, state string) (string, error)

BuildRedirectURL 拼接 URL 参数

func BuildRequestURI

func BuildRequestURI(r *http.Request) string

BuildRequestURI 构建 HTTP 请求的完整 URI (不含 query 和 fragment) RFC 9449 Section 4.2: htu 必须是 scheme + host + path,不包含 query 和 fragment

func ComputeJKT

func ComputeJKT(jwk map[string]interface{}) (string, error)

ComputeJKT 计算 JWK Thumbprint (RFC 7638) 用于生成 cnf.jkt claim

func ComputePKCEChallenge

func ComputePKCEChallenge(method, verifier string) (string, error)

ComputePKCEChallenge 根据给定的 Verifier 和 Method 计算 Challenge。 目前主要支持 S256。

func DPoPMiddleware

func DPoPMiddleware(cache ReplayCache, required bool) func(http.Handler) http.Handler

DPoPMiddleware 创建一个中间件来验证 DPoP proof 如果验证成功,将 DPoPClaims 存入 context 如果验证失败,返回 401 错误

使用方式:

middleware := oidc.DPoPMiddleware(server, replayCache, true)
handler := middleware(yourHandler)

func DPoPOptionalMiddleware

func DPoPOptionalMiddleware(cache ReplayCache) func(http.Handler) http.Handler

DPoPOptionalMiddleware 创建一个可选的 DPoP 中间件 如果提供了 DPoP header 则验证,否则继续处理

func DPoPProtectedEndpoints

func DPoPProtectedEndpoints() []string

DPoPProtectedEndpoints 返回需要 DPoP 保护的端点列表 通常包括:/token, /userinfo, /introspect

func DPoPRequiredMiddleware

func DPoPRequiredMiddleware(cache ReplayCache) func(http.Handler) http.Handler

DPoPRequiredMiddleware 创建一个必需的 DPoP 中间件 如果未提供 DPoP header 则返回 401 错误

func DecodeJSON

func DecodeJSON(r io.Reader, v any) error

DecodeJSON 是一个安全的 JSON 解码辅助函数。 它启用 UseNumber() 选项,防止大整数(如 expires_in 或 ID)被错误解析为 float64 导致精度丢失。

func DeviceAuthorized

func DeviceAuthorized(ctx context.Context, storage Storage, req *DeviceAuthorizedRequest) error

DeviceAuthorized 处理设备授权确认 (用户在前端点击同意后调用)

func DeviceDenied

func DeviceDenied(ctx context.Context, storage Storage, userCode string) error

补充:处理用户拒绝授权的逻辑 (建议添加)

func EndSession

func EndSession(ctx context.Context, storage Storage, verifier TokenVerifier, req *EndSessionRequest) (string, error)

EndSession 处理用户登出请求

func ExtractDPoPJKT

func ExtractDPoPJKT(ctx context.Context) string

ExtractDPoPJKT 是一个辅助函数,用于从 context 中提取 JKT 如果不存在则返回空字符串

func GeneratePKCEVerifier

func GeneratePKCEVerifier() (string, error)

GeneratePKCEVerifier 生成一个符合 RFC 7636 标准的高熵随机字符串。 长度默认为 43 字符(32 字节熵)。

func GetSigningMethod

func GetSigningMethod(key crypto.PrivateKey) jwt.SigningMethod

GetSigningMethod 根据私钥类型返回对应的 JWT 签名方法

func Hash

func Hash(method jwt.SigningMethod, str string) (string, error)

Hash 根据签名算法计算字符串的哈希值 OIDC 规范:使用 ID 令牌头部指定的哈希算法对令牌 ASCII 表示的字节进行哈希处理。取哈希值的左半部分并进行 base64url 编码。

func IsConfidentialClient added in v0.3.9

func IsConfidentialClient(method string) bool

IsConfidentialClient 判断客户端类型

func LoadTokenSecret

func LoadTokenSecret() ([]byte, error)

LoadTokenSecret 从环境变量加载 Token Secret

func ParseECDSAPublicKeyFromJWK

func ParseECDSAPublicKeyFromJWK(jwk *JSONWebKey) (*ecdsa.PublicKey, error)

ParseECDSAPublicKeyFromJWK 是一个辅助函数,用于将 JWK 转换回 *ecdsa.PublicKey (主要用于 Client 端验证)。

func ParseEd25519PublicKeyFromJWK

func ParseEd25519PublicKeyFromJWK(jwk *JSONWebKey) (ed25519.PublicKey, error)

ParseEd25519PublicKeyFromJWK 解析 Ed25519 JWK

func ParseRSAPublicKeyFromJWK

func ParseRSAPublicKeyFromJWK(jwk *JSONWebKey) (*rsa.PublicKey, error)

ParseRSAPublicKeyFromJWK 是一个辅助函数,用于将 JWK 转换回 *rsa.PublicKey。

func RandomString

func RandomString(n int) (string, error)

RandomString 生成指定长度的随机字符串 (URL Safe Base64)

func RefreshTokenRevoke added in v0.3.0

func RefreshTokenRevoke(ctx context.Context, storage TokenStorage, secretManager *SecretManager, tokenStr string, client RegisteredClient) error

RefreshTokenRevoke 处理 Opaque Refresh Token 的撤销 (物理删除/标记)

func RegisterMetrics

func RegisterMetrics()

RegisterMetrics 注册 OIDC 相关的 metrics 到 o11y registry 这个函数应该在初始化 OIDC Server 之后,启动服务之前调用 通常在 main.go 中调用一次即可

示例用法:

shutdown := o11y.Init(cfg.O11y)
defer shutdown(context.Background())
oidc.RegisterMetrics()  // 注册 OIDC 指标

func ResponseAuthorized

func ResponseAuthorized(ctx context.Context, storage AuthCodeStorage, req *AuthorizeRequest, codeTTL time.Duration) (string, error)

ResponseAuthorized 在用户通过身份验证并同意授权后调用。 它生成 Authorization Code,保存到存储层,并返回包含 code 和 state 的重定向 URL。

func RevokeAccessToken

func RevokeAccessToken(ctx context.Context, storage RevocationStorage, verifier TokenVerifier, tokenStr string, client RegisteredClient) error

RevokeAccessToken 处理 JWT Access Token 的撤销 (加入黑名单)

func RevokeToken

func RevokeToken(ctx context.Context, storage Storage, secretManager *SecretManager, hasher Hasher, verifier TokenVerifier, req *RevocationRequest) error

RevokeToken 是一个无状态函数,直接由 Server 调用

func SaveKey

func SaveKey(path string, key Key, password []byte) error

SaveKey 生成一个指定类型的新私钥,并将其以 PEM 格式保存到路径。 如果提供了密码,私钥将被安全地加密。

func SetServerTimeHeader

func SetServerTimeHeader(w http.ResponseWriter)

SetServerTimeHeader 设置服务器时间响应头 帮助客户端计算时间偏差

func ShouldUseDPoP

func ShouldUseDPoP(path string) bool

ShouldUseDPoP 检查请求路径是否应该使用 DPoP

func UnregisterClient

func UnregisterClient(ctx context.Context, storage ClientStorage, clientIDStr string) error

UnregisterClient 注销客户端 注意:实际业务中通常需要验证是否有权删除(如验证 Registration Access Token 或 OwnerID)

func ValidateKeySecurity

func ValidateKeySecurity(key Key) error

ValidateKeySecurity 验证密钥安全性

func ValidateRegistrationRequest

func ValidateRegistrationRequest(req *ClientRegistrationRequest, allowedSchemes map[string]struct{}) error

ValidateRegistrationRequest 集中处理校验逻辑

func ValidateScopes

func ValidateScopes(allowedScopeStr, requestedScopeStr string) error

ValidateScopes 检查 requestedScope 是否是 allowedScope 的子集 支持通配符匹配: - "scope:*" 匹配 "scope:read", "scope:write" - "scope:read:*" 匹配 "scope:read:user" - "*" 匹配所有

func ValidateStructuredRefreshToken

func ValidateStructuredRefreshToken(ctx context.Context, sm *SecretManager, token RefreshToken) error

验证 Token (在查库之前)

func ValidateURN

func ValidateURN(uri string) error

ValidateURN 验证 URN 格式

func VerifyDPoPProof

func VerifyDPoPProof(
	ctx context.Context,
	req *http.Request,
	w http.ResponseWriter,
	cache ReplayCache,
	httpMethod, httpURI string,
) (jkt string, err error)

VerifyDPoPProof 验证 HTTP 请求中的 DPoP Proof 返回 JWK Thumbprint (jkt) 用于绑定到 Access Token

RFC 9449 验证步骤: 1. 解析 DPoP header 中的 JWT 2. 验证 JWT 签名 (公钥在 header 的 jwk 字段) 3. 验证 htm 和 htu 与请求匹配 4. 验证 iat 时间窗口 (推荐 ±60秒) 5. 验证 jti 防重放 (使用 ReplayCache) 6. 计算并返回 JKT (JWK Thumbprint)

w: 可选的 ResponseWriter,用于在验证失败时设置服务器时间响应头

func VerifyPKCE

func VerifyPKCE(challenge, method, verifier string) error

VerifyPKCE 验证前端传来的 Verifier 是否与存储的 Challenge 匹配。

Types

type AccessToken

type AccessToken SecretString

func (AccessToken) Hash

func (ac AccessToken) Hash(alg jwt.SigningMethod) (string, error)

Hash 根据签名算法计算 Access Token 的哈希值 规范要求:Hash 算法必须匹配 ID Token 的签名算法

type AccessTokenClaims

type AccessTokenClaims struct {
	jwt.RegisteredClaims

	// --- OAuth2 协议核心字段 ---
	Scope string `json:"scope,omitempty"` // 核心权限字段!例如 "read:orders write:profile"

	// --- 扩展字段 ---
	AuthorizedParty string `json:"azp,omitempty"` // 哪个 Client 发起的请求?(用于限流、审计)

	// --- DPoP (RFC 9449) ---
	// Confirmation claim: 用于 DPoP sender-constrained tokens
	// 格式: {"jkt": "<JWK Thumbprint>"}
	Confirmation map[string]interface{} `json:"cnf,omitempty"`
}

AccessTokenClaims 表示自定义的 Access Token 载荷。 虽然 OAuth2 没有严格规定 Access Token 格式,但使用 JWT 是常见做法。

func (*AccessTokenClaims) SignedString

func (ac *AccessTokenClaims) SignedString(method jwt.SigningMethod, privateKey crypto.PrivateKey) (AccessToken, error)

type AuthCodeSession

type AuthCodeSession struct {
	// Code: 授权码本身作为主键,必须是唯一的
	Code string `db:"code"`

	// 关联索引:Token Exchange 时需验证 ClientID,且包含 UserID
	ClientID BinaryUUID `db:"client_id"`
	UserID   BinaryUUID `db:"user_id"`

	AuthTime time.Time
	// ExpiresAt: 必须加索引,用于定期清理过期数据 (GC)
	ExpiresAt time.Time `db:"expires_at"`

	// ACR/AMR: 认证上下文 (可选)
	ACR string   `db:"acr"`
	AMR []string `db:"amr"`

	// 原始请求参数校验
	RedirectURI string `db:"redirect_uri"`
	Scope       string `db:"scope"`
	Nonce       string `db:"nonce"`

	// PKCE (RFC 7636): 必须字段
	CodeChallenge       string `db:"code_challenge"`
	CodeChallengeMethod string `db:"code_challenge_method"`

	// DPoP JKT (RFC 9449): 绑定指纹,防止 Code 窃取
	DPoPJKT string `db:"d_pop_jkt"`
}

AuthCodeSession 授权码会话 (临时数据), 这些信息需要在 Exchange 阶段被恢复。 存活时间极短 (通常 < 10分钟),读写极高

func (AuthCodeSession) TableName added in v0.3.0

func (AuthCodeSession) TableName() string

type AuthCodeStorage

type AuthCodeStorage interface {
	// AuthCodeSave 存储生成的授权码及其上下文。
	AuthCodeSave(ctx context.Context, session *AuthCodeSession) error

	// AuthCodeConsume 查找并标记为已使用(防止重放)。
	// 这是一个原子操作:读取的同时必须确保下次读取失败或标记为已消耗。
	// 如果未找到或已过期/已消耗,应返回 ErrCodeNotFound。
	AuthCodeConsume(ctx context.Context, code string) (*AuthCodeSession, error)
}

type AuthorizeRequest

type AuthorizeRequest struct {
	// 必需参数
	ClientID     string `form:"client_id" json:"client_id"`
	RedirectURI  string `form:"redirect_uri" json:"redirect_uri"`
	ResponseType string `form:"response_type" json:"response_type"` // 目前仅支持 "code"

	// 可选参数
	Scope               string `form:"scope" json:"scope"`
	State               string `form:"state" json:"state"`
	Nonce               string `form:"nonce" json:"nonce"`
	CodeChallenge       string `form:"code_challenge" json:"code_challenge"`
	CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method"`

	// PAR (RFC 9126): 推送授权请求 URI
	// 如果存在,忽略其他参数,从 PARStorage 加载完整请求
	RequestURI string `form:"request_uri" json:"request_uri"`

	// DPoP (RFC 9449): JWK Thumbprint from DPoP proof
	// 由 HTTP Handler 验证 DPoP Proof 后提取并传入
	// 将被绑定到 Auth Code,在 Token Exchange 时验证
	DPoPJKT string `form:"-" json:"-"`

	// 上下文数据 (由调用者在用户登录/确认后填充)
	UserID   string    `form:"-" json:"-"`
	AuthTime time.Time `form:"-" json:"-"`
	// FinalScope 允许调用者在业务层修改最终授予的 Scope (例如移除用户无权的 scope)
	// 如果为空,将默认使用请求的 Scope
	FinalScope string `form:"-" json:"-"`
}

AuthorizeRequest 封装授权端点的请求参数

func LoadPARSession

func LoadPARSession(ctx context.Context, storage PARStorage, requestURI string) (*AuthorizeRequest, error)

LoadPARSession 从 request_uri 加载授权请求参数 仅在 /authorize 端点内部使用

type BinaryUUID

type BinaryUUID uuid.UUID

BinaryUUID 包装标准 UUID,强制数据库交互使用二进制

func NewBinaryUUID added in v0.3.0

func NewBinaryUUID() (BinaryUUID, error)

NewBinaryUUID 创建一个 uuid v7

func ParseUUID

func ParseUUID(s string) (BinaryUUID, error)

ParseUUID 辅助函数

func (BinaryUUID) MarshalJSON

func (b BinaryUUID) MarshalJSON() ([]byte, error)

MarshalJSON 必须重写!否则 Go 会把底层 []byte 转成 Base64 字符串

func (*BinaryUUID) Scan

func (b *BinaryUUID) Scan(value interface{}) error

Scan 实现 sql.Scanner (从数据库读取)

func (BinaryUUID) String

func (b BinaryUUID) String() string

String 实现 fmt.Stringer

func (*BinaryUUID) UnmarshalJSON

func (b *BinaryUUID) UnmarshalJSON(data []byte) error

UnmarshalJSON 从字符串解析

func (BinaryUUID) Value

func (b BinaryUUID) Value() (driver.Value, error)

Value 实现 driver.Valuer (写入数据库)

type Cache

type Cache interface {
	AuthCodeStorage   // 极短 TTL
	DeviceCodeStorage // 短 TTL
	DistributedLock   // 分布式锁
	PARStorage        // 极短 TTL
	ReplayCache       // DPoP JTI 防重放
	RevocationStorage // Access Token 黑名单 (高频读取)

	// 用于多级缓存, “缓存穿透”和“回写”的逻辑
	KeyStorage // JWK (基础设施)
	ClientCache
	TokenCache
}

Cache 负责临时、高频、需要自动过期的数据 建议实现:Redis

type ClientCache

type ClientCache interface {
	// ClientGetByID 从缓存获取客户端
	ClientGetByID(ctx context.Context, clientID BinaryUUID) (RegisteredClient, error)

	// ClientSave 将客户端存入缓存
	ClientSave(ctx context.Context, client RegisteredClient, ttl time.Duration) error

	// ClientInvalidate 从缓存中移除客户端
	ClientInvalidate(ctx context.Context, clientID BinaryUUID) error
}

ClientCache 定义了客户端信息的缓存接口。

type ClientFactory

type ClientFactory interface {
	New() RegisteredClient
}

ClientFactory 定义了创建客户端的接口。

type ClientMetadata

type ClientMetadata struct {
	// ID: 主键,使用 UUID
	ID BinaryUUID `db:"id"`

	// OwnerID: 用于查询“我创建的应用”,需要索引
	OwnerID BinaryUUID `db:"owner_id"`

	// Secret: 客户端密钥,经过哈希,预留足够长度
	Secret SecretString `db:"secret"`

	// Name: 应用名称
	Name string `db:"name"`

	// 数组类型处理:
	RedirectURIs StringSlice `db:"redirect_uris"`
	GrantTypes   StringSlice `db:"grant_types"`

	// Scope: 空格分隔的字符串,或者也可以用 type:text
	Scope string `db:"scope"`

	LogoURI                 string `db:"logo_uri"`
	TokenEndpointAuthMethod string `db:"token_endpoint_auth_method"`

	// IsConfidentialClient: 区分公开/机密客户端
	IsConfidentialClient bool `db:"is_confidential_client"`

	CreatedAt time.Time `db:"created_at"`
	UpdatedAt time.Time `db:"updated_at"`
}

ClientMetadata 客户端注册信息 对应 OAuth 2.0 Dynamic Client Registration Protocol

func (*ClientMetadata) Deserialize added in v0.3.0

func (c *ClientMetadata) Deserialize(data string) error

func (*ClientMetadata) GetGrantTypes added in v0.3.0

func (c *ClientMetadata) GetGrantTypes() []string

func (*ClientMetadata) GetID added in v0.3.0

func (c *ClientMetadata) GetID() BinaryUUID

func (*ClientMetadata) GetRedirectURIs added in v0.3.0

func (c *ClientMetadata) GetRedirectURIs() []string

func (*ClientMetadata) GetScope added in v0.3.0

func (c *ClientMetadata) GetScope() string

func (*ClientMetadata) IsConfidential

func (c *ClientMetadata) IsConfidential() bool

func (*ClientMetadata) Metadata added in v0.3.2

func (c *ClientMetadata) Metadata() *ClientMetadata

func (*ClientMetadata) Serialize added in v0.3.0

func (c *ClientMetadata) Serialize() (string, error)

func (ClientMetadata) TableName added in v0.3.0

func (ClientMetadata) TableName() string

func (*ClientMetadata) ValidateSecret added in v0.3.0

func (c *ClientMetadata) ValidateSecret(ctx context.Context, hasher Hasher, secret string) error

ValidateSecret 需要 hasher 协助,这里只提供数据,逻辑在 Server 层或 Storage 方法中 为了满足接口,我们在 Storage 实现中处理,这里仅作占位

type ClientRegistrationRequest

type ClientRegistrationRequest struct {
	RedirectURIs  []string `json:"redirect_uris"`
	GrantTypes    []string `json:"grant_types"`
	ResponseTypes []string `json:"response_types"`
	Scope         string   `json:"scope"`
	ClientName    string   `json:"client_name"`
	LogoURI       string   `json:"logo_uri,omitempty"`
	ClientURI     string   `json:"client_uri,omitempty"`

	// auth_method 决定了是否需要生成 secret
	// options: client_secret_basic, client_secret_post, none, private_key_jwt
	TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"`

	// 扩展字段:绑定所有者
	OwnerID string `json:"-"`
}

ClientRegistrationRequest RFC 7591 Client Registration Request

type ClientRegistrationResponse

type ClientRegistrationResponse struct {
	ClientID              string `json:"client_id"`
	ClientSecret          string `json:"client_secret,omitempty"` // 仅在创建或重置时返回明文
	ClientSecretExpiresAt int64  `json:"client_secret_expires_at,omitempty"`

	RedirectURIs []string `json:"redirect_uris"`
	GrantTypes   []string `json:"grant_types"`
	Scope        string   `json:"scope"`
	ClientName   string   `json:"client_name"`
	LogoURI      string   `json:"logo_uri,omitempty"`

	TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"`
	RegistrationAccessToken string `json:"registration_access_token,omitempty"`
	RegistrationClientURI   string `json:"registration_client_uri,omitempty"`
}

ClientRegistrationResponse RFC 7591 Response

func ClientUpdate added in v0.3.0

ClientUpdate 更新客户端信息 RFC 7592: Update Request

func RegisterClient

func RegisterClient(ctx context.Context, storage ClientStorage, hasher Hasher, req *ClientRegistrationRequest) (*ClientRegistrationResponse, error)

RegisterClient 处理新客户端注册 hasher: 必填,用于对生成的 Secret 进行哈希处理后再存入 DB

type ClientStorage

type ClientStorage interface {
	// ClientGetByID 根据 ID 获取客户端详情。
	// 如果未找到,应返回 ErrClientNotFound。
	ClientGetByID(ctx context.Context, clientID BinaryUUID) (RegisteredClient, error)

	// ClientCreate 注册新客户端
	// 注意:metadata.Secret 必须已经通过 Hasher.Hash() 哈希处理
	ClientCreate(ctx context.Context, metadata *ClientMetadata) (RegisteredClient, error)

	// ClientUpdate 更新客户端元数据
	ClientUpdate(ctx context.Context, metadata *ClientMetadata) (RegisteredClient, error)

	// ClientDeleteByID 删除客户端
	ClientDeleteByID(ctx context.Context, clientID BinaryUUID) error

	// ClientListByOwner 根据所有者查询客户端 (可选)
	ClientListByOwner(ctx context.Context, ownerID BinaryUUID, query ListQuery) ([]RegisteredClient, error)

	// ClientListAll 列出所有客户端
	ClientListAll(ctx context.Context, query ListQuery) ([]RegisteredClient, error)
}

ClientStorage 定义了获取客户端信息的接口。

type ClientUpdateRequest

type ClientUpdateRequest struct {
	ClientID string `json:"client_id"`
	*ClientRegistrationRequest
}

ClientUpdateRequest RFC 7591 Client Update Request

type ClientVerifier

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

ClientVerifier 用于客户端验证 OIDC ID Token。

func NewClientVerifier

func NewClientVerifier(issuer, clientID string, keySet KeySource) *ClientVerifier

NewClientVerifier 创建一个新的客户端验证器。 issuer: 必须完全匹配 Token 中的 iss。 clientID: 必须包含在 Token 的 aud 中。

func (*ClientVerifier) SetSupportedSigningAlgs

func (v *ClientVerifier) SetSupportedSigningAlgs(algs []string)

SetSupportedSigningAlgs 设置支持的签名算法

func (*ClientVerifier) Verify

func (v *ClientVerifier) Verify(ctx context.Context, rawToken string) (*IDTokenClaims, error)

Verify 解析并验证原始 ID Token 字符串。

type Code

type Code SecretString

func (Code) Hash

func (c Code) Hash(alg jwt.SigningMethod) (string, error)

Hash 根据签名算法计算 Code 的哈希值 规范要求:Hash 算法必须匹配 ID Token 的签名算法

type DPoPClaims

type DPoPClaims struct {
	JKT string // JWK Thumbprint,用于绑定到 Access Token
}

DPoPClaims 存储 DPoP 验证后的信息

func DPoPFromContext

func DPoPFromContext(ctx context.Context) (*DPoPClaims, bool)

DPoPFromContext 从 context 中提取 DPoP 验证结果 返回 (claims, ok),如果 ok == false 表示未使用 DPoP

type DPoPProof

type DPoPProof struct {
	jwt.RegisteredClaims

	// htm: HTTP 方法 (必需)
	HTM string `json:"htm"`

	// htu: HTTP URI (必需,不包含查询参数和片段)
	HTU string `json:"htu"`

	// Nonce: 可选,服务器可以要求客户端包含 nonce 以防重放
	Nonce string `json:"nonce,omitempty"`
}

DPoPProof 表示 DPoP Proof JWT 的 Claims RFC 9449 Section 4.2

type DPoPTimeSkewError

type DPoPTimeSkewError struct {
	Info DPoPTimeSkewInfo
	Err  error
}

DPoPTimeSkewError DPoP 时间偏差错误 包含详细的时间信息以便客户端调试

func (*DPoPTimeSkewError) Error

func (e *DPoPTimeSkewError) Error() string

func (*DPoPTimeSkewError) Unwrap

func (e *DPoPTimeSkewError) Unwrap() error

type DPoPTimeSkewInfo

type DPoPTimeSkewInfo struct {
	ServerTime time.Time
	ClientTime time.Time
	Skew       time.Duration
}

DPoPTimeSkewInfo 记录 DPoP 时间偏差信息

func (DPoPTimeSkewInfo) String

func (d DPoPTimeSkewInfo) String() string

String 返回人类可读的时间偏差信息

type DeviceAuthorizationRequest

type DeviceAuthorizationRequest struct {
	ClientID string `form:"client_id" json:"client_id"`
	Scope    string `form:"scope" json:"scope"`
}

type DeviceAuthorizationResponse

type DeviceAuthorizationResponse struct {
	DeviceCode      string `json:"device_code"`
	UserCode        string `json:"user_code"`
	VerificationURI string `json:"verification_uri"`
	ExpiresIn       int    `json:"expires_in"`
	Interval        int    `json:"interval,omitempty"` // Polling interval in seconds
}

func DeviceAuthorization

func DeviceAuthorization(ctx context.Context, storage Storage, issuer string, req *DeviceAuthorizationRequest) (*DeviceAuthorizationResponse, error)

DeviceAuthorization 处理设备授权请求

type DeviceAuthorizedRequest

type DeviceAuthorizedRequest struct {
	ClientID   string `form:"client_id" json:"client_id"`
	UserID     string `form:"user_id" json:"user_id"`
	UserCode   string `form:"user_code" json:"user_code"`
	FinalScope string `form:"final_scope" json:"final_scope"`
}

type DeviceCodeSession

type DeviceCodeSession struct {
	// DeviceCode: 设备换取 Token 的凭证,主键
	DeviceCode string `db:"device_code"`

	// UserCode: 用户在浏览器输入的短码,必须唯一且有索引
	UserCode string `db:"user_code"`

	ClientID BinaryUUID `db:"client_id"`
	UserID   BinaryUUID `db:"user_id"` // 初始为空,用户授权后填充

	Scope      string    `db:"scope"`
	ExpiresAt  time.Time `db:"expires_at"` // 用于清理
	LastPolled time.Time // 用于频率限制 (Rate Limiting)检查

	// Status: pending, allowed, denied
	Status string `db:"status"`

	AuthTime        time.Time
	AuthorizedScope string `db:"authorized_scope"` // 用户实际同意的 Scope
}

DeviceCodeSession 设备流会话 (RFC 8628)

func (DeviceCodeSession) TableName added in v0.3.0

func (DeviceCodeSession) TableName() string

type DeviceCodeStorage

type DeviceCodeStorage interface {
	// DeviceCodeSave 存储设备码和用户码
	DeviceCodeSave(ctx context.Context, session *DeviceCodeSession) error

	// DeviceCodeGet 根据设备码获取会话
	DeviceCodeGet(ctx context.Context, deviceCode string) (*DeviceCodeSession, error)

	// DeviceCodeGetByUserCode 根据用户码获取会话 (用于用户授权页面)
	DeviceCodeGetByUserCode(ctx context.Context, userCode string) (*DeviceCodeSession, error)

	// DeviceCodeUpdate 更新会话状态 (例如用户同意后)
	DeviceCodeUpdate(ctx context.Context, deviceCode string, session *DeviceCodeSession) error

	// DeviceCodeDelete 删除设备码会话及其关联索引
	DeviceCodeDelete(ctx context.Context, deviceCode string) error
}

type Discovery

type Discovery struct {
	Issuer                             string `json:"issuer"`
	AuthorizationEndpoint              string `json:"authorization_endpoint"`
	TokenEndpoint                      string `json:"token_endpoint"`
	JWKSURI                            string `json:"jwks_uri"`
	UserInfoEndpoint                   string `json:"userinfo_endpoint,omitempty"`
	RevocationEndpoint                 string `json:"revocation_endpoint,omitempty"`
	IntrospectionEndpoint              string `json:"introspection_endpoint,omitempty"`
	EndSessionEndpoint                 string `json:"end_session_endpoint,omitempty"`
	RegistrationEndpoint               string `json:"registration_endpoint,omitempty"`
	DeviceAuthorizationEndpoint        string `json:"device_authorization_endpoint,omitempty"`
	PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint,omitempty"` // RFC 9126

	// 关键能力标识
	ScopesSupported                   []string `json:"scopes_supported,omitempty"`
	ResponseTypesSupported            []string `json:"response_types_supported"`
	ResponseModesSupported            []string `json:"response_modes_supported,omitempty"`
	GrantTypesSupported               []string `json:"grant_types_supported,omitempty"`
	SubjectTypesSupported             []string `json:"subject_types_supported"`
	IDTokenSigningAlgValuesSupported  []string `json:"id_token_signing_alg_values_supported"`
	TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
	ClaimsSupported                   []string `json:"claims_supported,omitempty"`

	// PKCE 支持
	CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
}

Discovery 定义了完整的 OpenID Provider 元数据 参见: RFC 8414, OIDC Discovery 1.0

func Discover

func Discover(ctx context.Context, issuer string, httpClient *http.Client) (*Discovery, error)

Discover 从给定的 Issuer URL 获取 OIDC 配置信息。 它会自动追加 /.well-known/openid-configuration。

type DistributedLock

type DistributedLock interface {
	// Lock 尝试获取锁
	// ttl: 锁的自动过期时间
	// 返回: true 如果获取成功, false 如果已被占用
	Lock(ctx context.Context, key string, ttl time.Duration) (bool, error)

	// Unlock 释放锁
	Unlock(ctx context.Context, key string) error
}

DistributedLock 定义了分布式锁接口 用于密钥轮换等需要互斥的操作

type DpopContextKey

type DpopContextKey struct{}

DPoPContext 是用于在 context 中传递 DPoP 验证结果的 key

type EndSessionRequest

type EndSessionRequest struct {
	IDTokenHint           string
	PostLogoutRedirectURI string
	State                 string
	// AccessToken 当前有效的 Access Token (可选)
	// 如果提供,将被加入黑名单以实现即时登出
	AccessToken string
}

EndSessionRequest RP-Initiated Logout Request

type Error

type Error struct {
	Code        string `json:"error"`             // e.g. "invalid_request"
	Description string `json:"error_description"` // e.g. "Missing client_id"
	StatusCode  int    `json:"-"`                 // HTTP 状态码 (仅用于 Token 端点)
}

Error 代表一个标准的 OAuth2 错误

func AccessDeniedError

func AccessDeniedError(description string) *Error

AccessDeniedError 创建 access_denied 错误 (HTTP 403)

func AuthorizationPendingError

func AuthorizationPendingError(description string) *Error

AuthorizationPendingError 创建 authorization_pending 错误 (HTTP 400) 用于 Device Flow

func ExpiredTokenError

func ExpiredTokenError(description string) *Error

ExpiredTokenError 创建 expired_token 错误 (HTTP 400) 用于 Device Flow

func InvalidClientError

func InvalidClientError(description string) *Error

InvalidClientError 创建 invalid_client 错误 (HTTP 401)

func InvalidGrantError

func InvalidGrantError(description string) *Error

InvalidGrantError 创建 invalid_grant 错误 (HTTP 400)

func InvalidRequestError

func InvalidRequestError(description string) *Error

InvalidRequestError 创建 invalid_request 错误 (HTTP 400)

func InvalidScopeError

func InvalidScopeError(description string) *Error

InvalidScopeError 创建 invalid_scope 错误 (HTTP 400)

func NewError

func NewError(code string, description string, statusCode int) *Error

NewError 创建一个新的 OAuth2 错误

func ServerError added in v0.3.0

func ServerError(description string) *Error

ServerError 创建 server_error 错误 (HTTP 500)

func ServerErrorWithDescription

func ServerErrorWithDescription(description string) *Error

ServerError 创建 server_error 错误 (HTTP 500)

func SlowDownError

func SlowDownError(description string) *Error

SlowDownError 创建 slow_down 错误 (HTTP 400) 用于 Device Flow

func TemporarilyUnavailableError

func TemporarilyUnavailableError(description string) *Error

TemporarilyUnavailableError 创建 temporarily_unavailable 错误 (HTTP 503)

func UnauthorizedClientError

func UnauthorizedClientError(description string) *Error

UnauthorizedClientError 创建 unauthorized_client 错误 (HTTP 400)

func UnsupportedGrantTypeError

func UnsupportedGrantTypeError(description string) *Error

UnsupportedGrantTypeError 创建 unsupported_grant_type 错误 (HTTP 400)

func (*Error) BizStatus

func (e *Error) BizStatus() string

BizStatus 实现 httpx.BizCoder 接口 (隐式)

func (*Error) Error

func (e *Error) Error() string

Error 实现 error 接口

func (*Error) HTTPStatus

func (e *Error) HTTPStatus() int

HTTPStatus 实现 httpx.ErrorCoder 接口 (隐式)

func (*Error) PublicMessage

func (e *Error) PublicMessage() string

PublicMessage 实现 httpx.PublicError 接口 (隐式) OAuth2 错误(如 invalid_grant)需要返回给客户端以供调试或处理,因此视为 safe。

type GCWorker

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

GCWorker 垃圾回收 Worker 定期调用 Persistence.Cleanup() 清理过期数据

func NewGCWorker

func NewGCWorker(persistence Persistence, interval time.Duration) *GCWorker

NewGCWorker 创建 GC Worker interval: 清理间隔,建议 1 小时

func (*GCWorker) Start

func (w *GCWorker) Start(ctx context.Context)

Start 启动 GC Worker (非阻塞)

func (*GCWorker) Stop

func (w *GCWorker) Stop()

Stop 停止 GC Worker

type Hash256

type Hash256 []byte

Hash256 自定义类型,零依赖

func (Hash256) MarshalJSON

func (h Hash256) MarshalJSON() ([]byte, error)

func (*Hash256) Scan

func (h *Hash256) Scan(value interface{}) error

Scan 实现 sql.Scanner 接口 (从数据库读取)

func (Hash256) String

func (h Hash256) String() string

func (*Hash256) UnmarshalJSON

func (h *Hash256) UnmarshalJSON(data []byte) error

func (Hash256) Value

func (h Hash256) Value() (driver.Value, error)

Value 实现 driver.Valuer 接口 (写入数据库)

type Hasher

type Hasher interface {
	// Hash 对给定的明文密码进行哈希处理。
	// 返回哈希后的字节切片或错误。
	Hash(ctx context.Context, password []byte) ([]byte, error)

	// Compare 将明文密码与已有的哈希值进行比较。
	// 如果匹配,则返回nil;否则返回错误。
	Compare(ctx context.Context, hashedPassword []byte, password []byte) error
}

Hasher 定义了密码哈希和验证的接口。

type IDToken

type IDToken SecretString

type IDTokenClaims

type IDTokenClaims struct {
	jwt.RegisteredClaims

	// OIDC 特定声明
	Nonce           string `json:"nonce,omitempty"`     // 关联客户端会话的字符串值,用于缓解重放攻击
	AuthTime        int64  `json:"auth_time,omitempty"` // 终端用户认证发生的时间
	AuthorizedParty string `json:"azp,omitempty"`       // 授权方 (Authorized Party),当 aud 包含多个值时必须存在
	AtHash          string `json:"at_hash,omitempty"`   // Access Token 的哈希值,用于验证 Access Token
	CHash           string `json:"c_hash,omitempty"`    // Code 的哈希值

	// Profile 声明 (标准 Scope: profile, email, phone)
	Name              *string `json:"name,omitempty"`
	PreferredUsername *string `json:"preferred_username,omitempty"`
	Picture           *string `json:"picture,omitempty"`

	Email         *string `json:"email,omitempty"`
	EmailVerified *bool   `json:"email_verified,omitempty"` // 指针用于区分 false 和 null

	PhoneNumber         *string `json:"phone_number,omitempty"`
	PhoneNumberVerified *bool   `json:"phone_number_verified,omitempty"`
}

IDTokenClaims 表示 OIDC ID Token 的标准载荷。 参见: OIDC Core 1.0, Section 2.

func (*IDTokenClaims) SignedString

func (ic *IDTokenClaims) SignedString(method jwt.SigningMethod, privateKey crypto.PrivateKey) (IDToken, error)

type IntrospectionRequest added in v0.3.3

type IntrospectionRequest struct {
	Token         string `form:"token" json:"token"`                     // 必填
	TokenTypeHint string `form:"token_type_hint" json:"token_type_hint"` // 选填

	// Client 认证信息 (自动通过 ClientAuthBinder 注入)
	ClientID     string `form:"client_id" json:"client_id"`
	ClientSecret string `form:"client_secret" json:"client_secret"`
}

IntrospectionRequest 定义 RFC 7662 请求体 必须支持 Form 表单和 Basic Auth

type IntrospectionResponse

type IntrospectionResponse struct {
	Active    bool   `json:"active"`
	Scope     string `json:"scope,omitempty"`
	ClientID  string `json:"client_id,omitempty"`
	Username  string `json:"username,omitempty"`
	TokenType string `json:"token_type,omitempty"`
	Exp       int64  `json:"exp,omitempty"`
	Iat       int64  `json:"iat,omitempty"`
	Nbf       int64  `json:"nbf,omitempty"`
	Sub       string `json:"sub,omitempty"`
	Aud       string `json:"aud,omitempty"`
	Iss       string `json:"iss,omitempty"`
	Jti       string `json:"jti,omitempty"`

	// DPoP (RFC 9449): Confirmation claim
	Cnf map[string]interface{} `json:"cnf,omitempty"`
}

IntrospectionResponse RFC 7662 Introspection Response

func Introspect

func Introspect(ctx context.Context, storage Storage, verifier TokenVerifier, tokenStr, clientIDStr, clientSecret string, hasher Hasher) (*IntrospectionResponse, error)

Introspect 验证 Token 状态 RFC 7662: OAuth 2.0 Token Introspection

type Issuer

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

Issuer 负责生成符合 OIDC/OAuth2 标准的 Token

func NewIssuer

func NewIssuer(cfg IssuerConfig, keyManager *KeyManager) (*Issuer, error)

NewIssuer 创建一个新的 Token 发行者

func (*Issuer) IssueClientCredentialsToken

func (g *Issuer) IssueClientCredentialsToken(ctx context.Context, req *IssuerRequest) (*IssuerResponse, error)

IssueClientCredentialsToken 专门用于客户端凭证模式 只生成 Access Token,不生成 Refresh Token,不生成 ID Token

func (*Issuer) IssueOAuthTokens

func (g *Issuer) IssueOAuthTokens(ctx context.Context, req *IssuerRequest) (*IssuerResponse, error)

func (*Issuer) IssueOIDCTokens

func (g *Issuer) IssueOIDCTokens(ctx context.Context, req *IssuerRequest) (*IssuerResponse, error)

IssueOIDCTokens 生成 OIDC 套件 (ID Token + Access Token + Refresh Token)

func (*Issuer) IssuePasswordResetAccessToken added in v0.3.0

func (g *Issuer) IssuePasswordResetAccessToken(ctx context.Context, req *IssuerRequest) (*IssuerResponse, error)

IssuePasswordResetAccessToken 生成密码修改 AccessToken

func (*Issuer) Issuer

func (i *Issuer) Issuer() string

func (*Issuer) RefreshOAuthTokens

func (g *Issuer) RefreshOAuthTokens(ctx context.Context, req *IssuerRequest) (*IssuerResponse, error)

RefreshOAuthTokens 刷新 OAuth2 Token 注意:调用者负责验证旧 Refresh Token 的合法性,并将旧 Token 关联的信息填入 req

func (*Issuer) RefreshOIDCTokens

func (g *Issuer) RefreshOIDCTokens(ctx context.Context, req *IssuerRequest) (*IssuerResponse, error)

RefreshOIDCTokens 刷新 OIDC Token 区别:ID Token 在刷新时不应包含 Nonce (OIDC Core 1.0 Section 12.1)

func (*Issuer) SecretManager

func (i *Issuer) SecretManager() *SecretManager

type IssuerConfig

type IssuerConfig struct {
	Issuer        string
	SecretManager *SecretManager

	// 默认 TTL (兜底策略)
	AccessTokenTTL          time.Duration
	IDTokenTTL              time.Duration
	RefreshTokenTTL         time.Duration
	RefreshTokenGracePeriod time.Duration
}

IssuerConfig 配置签发的默认参数

type IssuerRequest

type IssuerRequest struct {
	ClientID BinaryUUID
	UserID   BinaryUUID // Subject (sub)
	Scopes   string     // Space delimited scopes
	Audience []string   // Resource Server URIs

	Nonce    string    // 仅用于 Implicit/AuthCode Flow 的 Issue 阶段
	Code     Code      // 关联的 Authorization Code (如果适用,用于计算 c_hash)
	AuthTime time.Time // 用户完成认证的时间

	// --- TTL 覆盖策略 ---
	// 如果设置了以下字段(大于0),则使用该时间;否则使用 Config 中的默认时间。
	AccessTokenDuration  time.Duration
	RefreshTokenDuration time.Duration
	IDTokenDuration      time.Duration

	// --- User Profile ---
	Name                *string
	PreferredUsername   *string
	Picture             *string
	Email               *string
	EmailVerified       *bool
	PhoneNumber         *string
	PhoneNumberVerified *bool

	// DPoP (RFC 9449): JWK Thumbprint from DPoP proof
	// 如果不为空,将在 Access Token 中添加 cnf.jkt claim
	DPoPJKT string
}

IssuerRequest 包含生成 Token 所需的上下文信息

type IssuerResponse

type IssuerResponse struct {
	TokenType    string `json:"token_type"` // Bearer
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token,omitempty"`
	IDToken      string `json:"id_token,omitempty"` // 仅在 OIDC 流程中存在
	ExpiresIn    int64  `json:"expires_in"`
	Scope        string `json:"scope,omitempty"`
}

IssuerResponse OAuth2 /oauth/token 响应结构

func DeviceTokenExchange

func DeviceTokenExchange(ctx context.Context, storage Storage, issuer *Issuer, req *TokenRequest) (*IssuerResponse, error)

DeviceTokenExchange 处理设备码换取 Token

func ExchangeClientCredentials

func ExchangeClientCredentials(ctx context.Context, storage ClientStorage, hasher Hasher, issuer *Issuer, req *TokenRequest) (*IssuerResponse, error)

ExchangeClientCredentials 处理 client_credentials 流程 (M2M)

func ExchangeCode

func ExchangeCode(ctx context.Context, storage Storage, hasher Hasher, issuer *Issuer, req *TokenRequest) (*IssuerResponse, error)

ExchangeCode 用于处理 authorization_code 流程

func PasswordGrant

func PasswordGrant(ctx context.Context, storage Storage, hasher Hasher, issuer *Issuer, req *TokenRequest) (*IssuerResponse, error)

PasswordGrant 生产存根实现,总是返回 "unsupported grant type" 错误。 这个函数确保了在生产构建中调用 password grant 逻辑会安全地失败,并且二进制文件中不包含任何实际的处理代码。

func RefreshTokens

func RefreshTokens(ctx context.Context, storage Storage, secretManager *SecretManager, hasher Hasher, issuer *Issuer, req *TokenRequest) (*IssuerResponse, error)

RefreshTokens 用于处理 refresh_token 流程

type JSONWebKey

type JSONWebKey struct {
	Kty string `json:"kty"`           // Key Type (RSA, EC, OKP)
	Kid string `json:"kid,omitempty"` // Key ID
	Use string `json:"use,omitempty"` // Public Key Use (sig, enc)
	Alg string `json:"alg,omitempty"` // Algorithm (RS256, ES256, EdDSA...)

	// RSA 字段
	N string `json:"n,omitempty"` // Modulus (Base64URL)
	E string `json:"e,omitempty"` // Exponent (Base64URL)

	// ECDSA / Ed25519 字段
	Crv string `json:"crv,omitempty"` // Curve (P-256, P-384, P-521, Ed25519)
	X   string `json:"x,omitempty"`   // X Coordinate (Base64URL)
	Y   string `json:"y,omitempty"`   // Y Coordinate (Base64URL), Ed25519 不需要此字段
}

JSONWebKey 表示单个 JWK 的结构。

func PublicKeyToJWK

func PublicKeyToJWK(pub crypto.PublicKey, kid, alg string) (JSONWebKey, error)

PublicKeyToJWK 将标准库的 crypto.PublicKey 转换为 JWK 结构体。

func (*JSONWebKey) Thumbprint

func (jwk *JSONWebKey) Thumbprint() (string, error)

Thumbprint 根据 RFC 7638 计算 JWK Thumbprint (SHA-256) 常用于生成 kid

type JSONWebKeySet

type JSONWebKeySet struct {
	Keys []JSONWebKey `json:"keys"`
}

JSONWebKeySet 表示 JWKS 端点返回的顶级 JSON 结构。

type JWK added in v0.3.0

type JWK struct {
	// KID: Key ID,主键
	KID string `db:"kid"`

	// JWK: 包含私钥的大段 JSON 文本,使用 text 类型
	JWK SecretString `db:"jwk"`

	CreatedAt time.Time `db:"created_at"`
}

JWK 存储轮转的加密密钥

type Key

type Key interface {
	crypto.PrivateKey
	Public() crypto.PublicKey
	Equal(x crypto.PrivateKey) bool
	Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error)
}

Key 接口定义了一个可用于签名的私钥所需的方法。

func LoadKey

func LoadKey(path string, password []byte) (Key, error)

LoadKey 从指定路径加载一个 PEM 编码的私钥。 它使用 pemutil 库来支持加密和未加密的密钥。

func LoadOrGenerateKey

func LoadOrGenerateKey(path string, keyType KeyType, password []byte) (Key, error)

LoadOrGenerateKey 尝试从指定路径加载私钥。 如果文件存在,则加载它;如果不存在,则生成一个新密钥并保存。

func NewKey

func NewKey(keyType KeyType) (key Key, err error)

NewKey 根据指定的类型生成一个新的私钥

type KeyManager

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

KeyManager 负责管理服务端的私钥集合。 它支持密钥轮换、JWKS 导出以及查找当前签名密钥。 现在的实现是无状态的(依赖 KeyStorage),但保留了本地缓存以提高性能。

func NewKeyManager

func NewKeyManager(storage KeyStorage, cacheTTL time.Duration) *KeyManager

NewKeyManager 创建密钥管理器

func (*KeyManager) Add

func (km *KeyManager) Add(ctx context.Context, key Key) (string, error)

Add 从外部添加一个已有的私钥。 如果 key 没有 ID,会自动计算 Thumbprint 作为 ID。 返回计算出的 kid。

func (*KeyManager) ExportJWKS

func (km *KeyManager) ExportJWKS(ctx context.Context) (*JSONWebKeySet, error)

ExportJWKS 导出所有公钥为 JWKS 结构。

func (*KeyManager) Generate

func (km *KeyManager) Generate(ctx context.Context, kty KeyType, setAsSigning bool) (string, error)

Generate 生成一个新的密钥对,添加到管理器中,并选择是否立即将其设为签名密钥。

func (*KeyManager) GetKey

func (km *KeyManager) GetKey(ctx context.Context, kid string) (crypto.PublicKey, error)

GetKey 实现 KeySource 接口。

func (*KeyManager) GetKeyInternal

func (km *KeyManager) GetKeyInternal(ctx context.Context, kid string) (Key, error)

GetKeyInternal 获取私钥 (Key 接口)

func (*KeyManager) GetSigningKey

func (km *KeyManager) GetSigningKey(ctx context.Context) (string, Key, error)

GetSigningKey 获取当前用于签名的私钥和 kid。

func (*KeyManager) JWKGetSigning added in v0.3.0

func (km *KeyManager) JWKGetSigning(ctx context.Context) (string, error)

JWKGetSigning 获取当前签名密钥 ID

func (*KeyManager) ListKeys

func (km *KeyManager) ListKeys(ctx context.Context) ([]string, error)

ListKeys 返回所有密钥的 KID 列表

func (*KeyManager) RemoveKey

func (km *KeyManager) RemoveKey(ctx context.Context, kid string) error

RemoveKey 删除指定的密钥。

func (*KeyManager) SetSigningKeyID

func (km *KeyManager) SetSigningKeyID(ctx context.Context, kid string) error

SetSigningKeyID 指定哪个 kid 用于签名。

type KeyProvider

type KeyProvider interface {
	// GetKey 获取指定版本的密钥
	GetKey(ctx context.Context, kid string) ([]byte, error)

	// GetActiveKey 获取当前活跃的签名密钥
	GetActiveKey(ctx context.Context) (string, []byte, error)

	// ListKeys 列出所有可用密钥(用于验证)
	// 返回 map[kid]key,包括活跃密钥和处于宽限期的旧密钥
	ListKeys(ctx context.Context) (map[string][]byte, error)
}

KeyProvider 定义密钥获取接口,支持内存、KMS、HSM 等多种实现 这是依赖倒置原则的体现:核心逻辑依赖抽象接口,而非具体实现

type KeyRotationConfig

type KeyRotationConfig struct {
	// RotationInterval 密钥轮换间隔(例如 30 天)
	RotationInterval time.Duration

	// GracePeriod 旧密钥保留期,用于验证旧 Token(例如 7 天)
	GracePeriod time.Duration

	// KeyType 生成的密钥类型
	KeyType KeyType

	// EnableAutoRotate 是否启用自动定时轮换
	EnableAutoRotate bool

	// CleanupInterval 清理过期密钥的检查间隔
	// 如果为 0,默认为 1 分钟
	// 测试时可设置为较短时间(如 1 秒)
	CleanupInterval time.Duration
}

KeyRotationConfig 密钥轮换配置

type KeyRotationScheduler

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

KeyRotationScheduler 密钥轮换调度器 负责编排密钥的完整生命周期:生成 → 签名 → 宽限期 → 删除

func NewKeyRotationScheduler

func NewKeyRotationScheduler(manager *KeyManager, lock DistributedLock, config KeyRotationConfig) *KeyRotationScheduler

NewKeyRotationScheduler 创建密钥轮换调度器

func (*KeyRotationScheduler) GetCurrentKeyID

func (s *KeyRotationScheduler) GetCurrentKeyID() string

GetCurrentKeyID 获取当前签名密钥 ID

func (*KeyRotationScheduler) GetPendingDeletes

func (s *KeyRotationScheduler) GetPendingDeletes() map[string]time.Time

GetPendingDeletes 获取待删除密钥列表(用于监控)

func (*KeyRotationScheduler) RotateNow

func (s *KeyRotationScheduler) RotateNow(ctx context.Context) error

RotateNow 立即触发一次密钥轮换 适用于安全事件响应或手动干预

func (*KeyRotationScheduler) Start

func (s *KeyRotationScheduler) Start(ctx context.Context) error

Start 启动后台自动轮换任务 返回的 error 仅在严重初始化问题时非 nil

func (*KeyRotationScheduler) Stop

func (s *KeyRotationScheduler) Stop()

Stop 停止调度器

type KeySource

type KeySource interface {
	// GetKey 根据 Key ID (kid) 返回对应的公钥。
	// 如果 kid 为空,且源中只有一个 key,应返回该 key。
	GetKey(ctx context.Context, kid string) (crypto.PublicKey, error)
}

KeySource 定义了验证器如何获取公钥。 它可以是静态的 JWKS,也可以是远程 URL (OIDC Discovery)。

type KeyStorage

type KeyStorage interface {
	// JWKSave 存储一个 JWK (包含私钥)
	// 如果 key 已存在,应该覆盖或返回错误(取决于实现,通常是覆盖)
	JWKSave(ctx context.Context, key jwk.Key) error

	// JWKGet 获取指定 kid 的 JWK
	JWKGet(ctx context.Context, kid string) (jwk.Key, error)

	// JWKList 获取所有存储的 JWK
	JWKList(ctx context.Context) ([]jwk.Key, error)

	// JWKDelete 删除指定 kid 的 JWK
	JWKDelete(ctx context.Context, kid string) error

	// JWKMarkSigning 存储当前签名密钥 ID
	JWKMarkSigning(ctx context.Context, kid string) error

	// JWKGetSigning 获取当前签名密钥 ID
	JWKGetSigning(ctx context.Context) (string, error)
}

KeyStorage 定义了私钥的持久化存储接口 必须支持分布式环境下的并发访问

type KeyType

type KeyType string
const (
	KEY_RSA     KeyType = "RSA"
	KEY_ECDSA   KeyType = "ECDSA"
	KEY_Ed25519 KeyType = "Ed25519"

	KEY_Default = KEY_RSA
)

type ListQuery

type ListQuery struct {
	Offset int
	Limit  int
}

ListQuery 列表查询参数

type MemoryKeyProvider

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

MemoryKeyProvider 内存实现,保持现有行为 适用于开发环境和不需要 KMS 的简单部署

func NewMemoryKeyProvider

func NewMemoryKeyProvider() *MemoryKeyProvider

NewMemoryKeyProvider 创建内存密钥提供者

func (*MemoryKeyProvider) AddKey

func (m *MemoryKeyProvider) AddKey(kid string, key []byte) error

AddKey 添加新密钥

func (*MemoryKeyProvider) CleanupExpiredKeys

func (m *MemoryKeyProvider) CleanupExpiredKeys(ctx context.Context) int

CleanupExpiredKeys 清理已过期的 deprecated keys 应该定期调用(例如通过 cron)

func (*MemoryKeyProvider) GetActiveKey

func (m *MemoryKeyProvider) GetActiveKey(ctx context.Context) (string, []byte, error)

GetActiveKey 实现 KeyProvider 接口

func (*MemoryKeyProvider) GetKey

func (m *MemoryKeyProvider) GetKey(ctx context.Context, kid string) ([]byte, error)

GetKey 实现 KeyProvider 接口

func (*MemoryKeyProvider) ListKeys

func (m *MemoryKeyProvider) ListKeys(ctx context.Context) (map[string][]byte, error)

ListKeys 实现 KeyProvider 接口

func (*MemoryKeyProvider) RemoveKey

func (m *MemoryKeyProvider) RemoveKey(kid string) error

RemoveKey 删除密钥 注意:不能删除当前活跃的密钥

func (*MemoryKeyProvider) RotateKey

func (m *MemoryKeyProvider) RotateKey(newKID string, newKey []byte, gracePeriod time.Duration) error

RotateKey 轮换密钥(优雅轮换) 1. 添加新密钥 2. 切换活跃密钥 3. 将旧密钥标记为 deprecated(但保留验证能力)

func (*MemoryKeyProvider) SetActiveKey

func (m *MemoryKeyProvider) SetActiveKey(kid string) error

SetActiveKey 设置活跃密钥

type PARRequest

type PARRequest struct {
	// 客户端认证信息 (必需)
	ClientID     string `form:"client_id" json:"client_id"`
	ClientSecret string `form:"client_secret" json:"client_secret"` // 对于机密客户端

	// 授权请求参数 (与标准 /authorize 端点相同)
	RedirectURI  string `form:"redirect_uri" json:"redirect_uri"`
	ResponseType string `form:"response_type" json:"response_type"`
	Scope        string `form:"scope" json:"scope"`
	State        string `form:"state" json:"state"`
	Nonce        string `form:"nonce" json:"nonce"`

	// PKCE
	CodeChallenge       string `form:"code_challenge" json:"code_challenge"`
	CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method"`

	// DPoP (RFC 9449): JWK Thumbprint from DPoP proof
	// 由 HTTP Handler 验证 DPoP Proof 后提取并传入
	DPoPJKT string `form:"-" json:"-"`

	// 扩展参数
	AdditionalParams map[string]string `form:"-" json:"-"`
}

PARRequest 表示推送授权请求的参数 RFC 9126 Section 2

type PARResponse

type PARResponse struct {
	RequestURI string `json:"request_uri"` // 格式: urn:ietf:params:oauth:request_uri:...
	ExpiresIn  int    `json:"expires_in"`  // 秒数,典型值 60
}

PARResponse 表示 PAR 端点的响应 RFC 9126 Section 2.2

func PushedAuthorization

func PushedAuthorization(
	ctx context.Context,
	storage Storage,
	hasher Hasher,
	req *PARRequest,
) (*PARResponse, error)

PushedAuthorization 处理推送授权请求 RFC 9126: 客户端通过后端通道提交授权参数,获得 request_uri

安全要求: - 必须验证客户端身份 (Client Secret 或 mTLS) - 所有授权参数必须经过验证 - request_uri 只能使用一次 - TTL 应较短 (推荐 60 秒)

type PARStorage

type PARStorage interface {
	// PARSessionSave 保存 PAR 会话
	// requestURI: 格式为 "urn:ietf:params:oauth:request_uri:<唯一标识>"
	// req: 完整的授权请求参数
	// ttl: 会话有效期,RFC 建议 60 秒
	PARSessionSave(ctx context.Context, requestURI string, req *AuthorizeRequest, ttl time.Duration) error

	// PARSessionConsume 获取并删除 PAR 会话(原子操作)
	// PAR request_uri 只能使用一次
	PARSessionConsume(ctx context.Context, requestURI string) (*AuthorizeRequest, error)
}

PARStorage 管理推送授权请求 (Pushed Authorization Requests) 会话 RFC 9126: OAuth 2.0 Pushed Authorization Requests

type Persistence

type Persistence interface {
	ClientStorage
	KeyStorage     // JWK (基础设施)
	TokenStorage   // Refresh Token (长效)
	UserInfoGetter // 用户数据
	UserAuthenticator

	// Cleanup 清理过期的数据 (Garbage Collection)
	// 此方法应删除已过期的 RefreshToken, AuthCode, DeviceCode 等临时数据
	// 建议由后台 Worker 定期调用(例如每小时一次)
	// 返回被清理的记录数和错误
	Cleanup(ctx context.Context) (deleted int64, err error)
	Close()
}

Persistence 负责长久存储的数据 建议实现:GORM (PostgreSQL)

type RefreshToken

type RefreshToken SecretString

func IssueStructuredRefreshToken

func IssueStructuredRefreshToken(ctx context.Context, sm *SecretManager, userID string, ttl time.Duration) (RefreshToken, error)

IssueStructuredRefreshToken 生成一个结构化的 Refresh Token

func (RefreshToken) HashForDB

func (t RefreshToken) HashForDB() Hash256

HashForDB 刷新 token 使用 sha256 进行 hash 如果是结构化 Token (kid.meta.rand.sig),只 Hash 随机部分 (rand) 这配合签名验证可防止数据库 DoS,同时保证数据库索引的唯一性和安全性

type RefreshTokenSession

type RefreshTokenSession struct {
	// ID 默认为主键,无需标签
	// GORM 默认将 Hash256 映射为对应类型(需实现 Scanner/Valuer)
	ID Hash256

	// 默认映射为 client_id,但在 db 标签中指定 index
	ClientID BinaryUUID `db:"client_id"`

	// 默认映射为 user_id,指定 index
	UserID BinaryUUID `db:"user_id"`

	// 指定数据库类型为 text
	Scope string `db:"scope"`

	// 默认映射 auth_time
	AuthTime time.Time

	// 默认映射 expires_at,指定 index
	ExpiresAt time.Time `db:"expires_at"`

	// 上下文信息
	Nonce string      // 默认 varchar
	ACR   string      // 默认 varchar
	AMR   StringSlice `db:"amr"`
}

RefreshTokenSession 包含刷新令牌关联的上下文信息。

func (RefreshTokenSession) TableName added in v0.3.0

func (RefreshTokenSession) TableName() string

type RegisteredClient

type RegisteredClient interface {
	// GetID 返回客户端 ID
	GetID() BinaryUUID

	// GetRedirectURIs 返回注册的回调地址列表
	GetRedirectURIs() []string

	// GetGrantTypes 返回允许的授权类型 (e.g., "authorization_code", "refresh_token")
	GetGrantTypes() []string

	// GetScope 返回客户端注册的允许 Scopes 字符串 (空格分隔)
	// OIDC Core 逻辑会用此与请求的 Scope 取交集
	GetScope() string

	// IsConfidential 返回是否为机密客户端 (Confidential vs Public)
	IsConfidential() bool

	// ValidateSecret 验证输入的明文密钥。
	// 对于 Public Client,此方法应直接返回 nil。
	// 对于 Confidential Client,实现层应处理哈希比对 (如 bcrypt/argon2)。
	ValidateSecret(ctx context.Context, hasher Hasher, secret string) error

	// 提供给Redis一个序列化和反序列化的方法
	Serialize() (string, error)
	Deserialize(string) error

	// Metadata 获取客户端的元数据以便进行更新
	Metadata() *ClientMetadata
}

RegisteredClient 定义了 OIDC 协议流程中所需的客户端视图。

func AuthenticateClient

func AuthenticateClient(ctx context.Context, storage ClientStorage, clientIDStr, clientSecret string, hasher Hasher) (RegisteredClient, error)

AuthenticateClient 验证客户端身份

func ListClient

func ListClient(ctx context.Context, storage ClientStorage, query ListQuery) ([]RegisteredClient, error)

ListClient 列出所有客户端

func RequestAuthorize

func RequestAuthorize(ctx context.Context, storage Storage, req *AuthorizeRequest) (RegisteredClient, error)

RequestAuthorize 校验授权请求的基本参数。 这一步通常在显示登录页面或授权同意页面之前调用。 如果返回 error,调用者应直接向用户显示错误页,而不是重定向(因为 RedirectURI 可能尚未验证)。

PAR 支持 (RFC 9126): - 如果 req.RequestURI 存在,从 PARStorage 加载完整请求参数 - request_uri 只能使用一次

type RemoteKeySet

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

RemoteKeySet 实现了 KeySource 接口。 它能够从远程 JWKS URI 获取公钥,并支持 Stale-While-Revalidate 缓存机制。

func NewRemoteKeySet

func NewRemoteKeySet(ctx context.Context, jwksURI string, httpClient *http.Client, opts ...RemoteKeySetOption) *RemoteKeySet

NewRemoteKeySet 创建一个新的远程密钥集。 ctx: 用于控制 HTTP 请求的生命周期。 jwksURI: 远程 JWKS 地址。

func (*RemoteKeySet) GetKey

func (r *RemoteKeySet) GetKey(ctx context.Context, kid string) (crypto.PublicKey, error)

GetKey 实现了 KeySource 接口。 使用 Stale-While-Revalidate 策略:缓存过期时立即返回旧值,并触发后台刷新。

func (*RemoteKeySet) Stop

func (r *RemoteKeySet) Stop()

Stop 停止后台刷新

type RemoteKeySetOption

type RemoteKeySetOption func(*RemoteKeySet)

RemoteKeySetOption allows configuring RemoteKeySet

func WithCacheDuration

func WithCacheDuration(d time.Duration) RemoteKeySetOption

WithCacheDuration sets the default cache duration

type ReplayCache

type ReplayCache interface {
	// CheckAndStore 原子性地检查 JTI 是否已使用,若未使用则存储。
	// 返回 true 表示 JTI 已存在 (重放攻击),false 表示首次使用。
	// ttl 参数指定 JTI 在缓存中的有效期。
	CheckAndStore(ctx context.Context, jti string, ttl time.Duration) (bool, error)
}

ReplayCache 定义了防重放攻击的缓存接口。 用于 DPoP (RFC 9449) 的 JTI (JWT ID) 去重。

type ResourceVerifier

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

ResourceVerifier 用于资源服务器验证 OIDC ID Token。

func NewResourceVerifier

func NewResourceVerifier(issuer, resourceURI string, keySet KeySource, trustedClients []string) *ResourceVerifier

NewResourceVerifier 创建一个新的客户端验证器。 issuer: 必须完全匹配 Token 中的 iss。 resourceURI: 必须包含在 Token 的 aud 中。 trustedClients: 如果不为nil, 会限制使用本服务的请求客户端

func (*ResourceVerifier) SetSupportedSigningAlgs

func (v *ResourceVerifier) SetSupportedSigningAlgs(algs []string)

SetSupportedSigningAlgs 设置支持的签名算法

func (*ResourceVerifier) Verify

func (v *ResourceVerifier) Verify(ctx context.Context, rawToken string) (*AccessTokenClaims, error)

Verify 解析并验证原始 ID Token 字符串。

type RevocationRequest

type RevocationRequest struct {
	Token         string `form:"token" json:"token"`
	TokenTypeHint string `form:"token_type_hint" json:"token_type_hint"` // "access_token" 或 "refresh_token" (可选)
	ClientID      string `form:"client_id" json:"client_id"`
	ClientSecret  string `form:"client_secret" json:"client_secret"`
}

RevocationRequest 封装 RFC 7009 撤销请求参数

type RevocationStorage

type RevocationStorage interface {
	// AccessTokenRevoke 将 JTI (JWT ID) 加入黑名单,直到 expiration 时间。
	AccessTokenRevoke(ctx context.Context, jti string, expiration time.Time) error

	// AccessTokenIsRevoked 检查 JTI 是否在黑名单中。
	AccessTokenIsRevoked(ctx context.Context, jti string) (bool, error)
}

RevocationStorage 用于管理 Access Token 的黑名单 (Revocation JWKList)。 Access Token 通常是无状态 JWT,一旦签发无法修改,撤销只能通过黑名单 (JTI) 实现。

type SecretBytes added in v0.3.0

type SecretBytes []byte

SecretBytes 是一种自定义字节切片类型,用于防止敏感信息在日志中被意外打印。 注意:当用于存储密码或 Secret 的哈希值时,应确保存储的是哈希后的值。

func (SecretBytes) GoString added in v0.3.0

func (s SecretBytes) GoString() string

Go 语法格式化接口 (%#v) - 往常容易被忽略的泄露点

func (SecretBytes) MarshalJSON added in v0.3.0

func (s SecretBytes) MarshalJSON() ([]byte, error)

MarshalJSON 方法确保在将配置序列化为JSON时,敏感字段也被屏蔽。

func (*SecretBytes) Scan added in v0.3.0

func (s *SecretBytes) Scan(value interface{}) error

Scan 实现 sql.Scanner 接口, 告诉 Go 如何从数据库读取值到 Bytes

func (SecretBytes) String added in v0.3.0

func (s SecretBytes) String() string

Bytes 方法重写了默认行为,使其在被格式化打印时返回一个屏蔽后的字符串。

func (SecretBytes) Value added in v0.3.0

func (s SecretBytes) Value() (driver.Value, error)

Value 实现 driver.Valuer 接口, 告诉数据库如何写入 Bytes

type SecretManager

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

SecretManager 管理 HMAC 密钥,用于 Refresh Token 签名 现在使用 KeyProvider 抽象,支持内存、KMS、HSM 等多种实现

func NewSecretManager

func NewSecretManager() *SecretManager

NewSecretManager 创建新的密钥管理器(使用内存提供者)

func NewSecretManagerWithProvider

func NewSecretManagerWithProvider(provider KeyProvider) *SecretManager

NewSecretManagerWithProvider 创建密钥管理器(使用自定义提供者)

func (*SecretManager) AddKey

func (s *SecretManager) AddKey(id string, hexSecret string) error

AddKey 添加密钥(向后兼容的便捷方法) 注意:仅在 provider 是 MemoryKeyProvider 时可用

func (*SecretManager) CleanupExpiredKeys

func (s *SecretManager) CleanupExpiredKeys() (int, error)

CleanupExpiredKeys 清理已过期的密钥 返回清理的密钥数量

func (*SecretManager) GetSigningKey

func (s *SecretManager) GetSigningKey(ctx context.Context) ([]byte, string)

GetSigningKey 获取签名用的密钥 (HMAC Key) 返回 (key, kid)

func (*SecretManager) GetVerificationKey

func (s *SecretManager) GetVerificationKey(ctx context.Context, kid string) ([]byte, error)

GetVerificationKey 根据 Token 中的 kid 获取验证密钥

func (*SecretManager) RotateKey

func (s *SecretManager) RotateKey(newKID string, newHexSecret string, gracePeriod time.Duration) error

RotateKey 轮换密钥(优雅轮换) gracePeriod: 旧密钥保留用于验证的时间

func (*SecretManager) SetActiveKey

func (s *SecretManager) SetActiveKey(kid string) error

SetActiveKey 设置活跃密钥(向后兼容的便捷方法) 注意:仅在 provider 是 MemoryKeyProvider 时可用

type SecretString added in v0.3.0

type SecretString string

SecretString 是一种自定义字符串类型,用于防止敏感信息在日志中被意外打印。 注意:当用于存储 Client Secret 时,此字段应存储哈希后的值,而非明文。 在 ClientMetadata 中,Secret 字段通过 Hasher 接口哈希后再赋值给此类型。

func (SecretString) GoString added in v0.3.0

func (s SecretString) GoString() string

Go 语法格式化接口 (%#v) - 往常容易被忽略的泄露点

func (SecretString) MarshalJSON added in v0.3.0

func (s SecretString) MarshalJSON() ([]byte, error)

MarshalJSON 方法确保在将配置序列化为JSON时,敏感字段也被屏蔽。

func (*SecretString) Scan added in v0.3.0

func (s *SecretString) Scan(value interface{}) error

Scan 实现 sql.Scanner 接口, 告诉 Go 如何从数据库读取值到 String

func (SecretString) String added in v0.3.0

func (s SecretString) String() string

String 方法重写了默认行为,使其在被格式化打印时返回一个屏蔽后的字符串。

func (SecretString) Value added in v0.3.0

func (s SecretString) Value() (driver.Value, error)

Value 实现 driver.Valuer 接口, 告诉数据库如何写入 String

type Server

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

Server 是 OIDC 协议的核心控制器。 它是一个 Facade (门面),组合了 Authorizer, Issuer, TokenHandler 等组件。 调用者应通过 NewServer 创建实例,并将其挂载到 HTTP 路由上。

func NewServer

func NewServer(cfg ServerConfig) (*Server, error)

NewServer 初始化一个新的 OIDC 服务。 注意:初始化后,必须: 1. 调用 server.KeyManager().Add(...) 添加至少一个签名密钥 (RSA/EC) 2. 调用 server.SecretManager().AddKey(...) 添加至少一个 HMAC 密钥 (用于 Refresh Token) 3. 调用 server.ValidateKeys() 验证密钥配置 4. 启动 HTTP 服务

func (*Server) ClientUpdate added in v0.3.0

ClientUpdate 处理动态客户端更新

func (*Server) Config

func (s *Server) Config() *ServerConfig

func (*Server) DeviceAuthorization

func (s *Server) DeviceAuthorization(ctx context.Context, req *DeviceAuthorizationRequest) (*DeviceAuthorizationResponse, error)

DeviceAuthorization 处理设备授权请求

func (*Server) Discovery

func (s *Server) Discovery() *Discovery

Discovery 返回符合 OIDC Discovery 标准的元数据结构。 调用者应将其序列化为 JSON 并通过 /.well-known/openid-configuration 暴露。

func (*Server) EndSession

func (s *Server) EndSession(ctx context.Context, req *EndSessionRequest) (string, error)

EndSession 处理用户登出

func (*Server) Exchange

func (s *Server) Exchange(ctx context.Context, req *TokenRequest) (resp *IssuerResponse, err error)

Exchange 处理 Token 交换

func (*Server) GetUserInfo

func (s *Server) GetUserInfo(ctx context.Context, claims *AccessTokenClaims) (*UserInfo, error)

GetUserInfo 获取用户信息

func (*Server) Introspect

func (s *Server) Introspect(ctx context.Context, tokenStr, clientIDStr, clientSecret string) (*IntrospectionResponse, error)

Introspect 验证 Token 状态

func (*Server) Issuer

func (s *Server) Issuer() *Issuer

Issuer 返回 Issuer,用于生成 Token。

func (*Server) KeyManager

func (s *Server) KeyManager() *KeyManager

KeyManager 返回密钥管理器,用于添加、删除密钥或导出 JWKS。

func (*Server) ListClient

func (s *Server) ListClient(ctx context.Context, query ListQuery) ([]RegisteredClient, error)

ListClient 列出所有客户端

func (*Server) ParseAccessToken

func (s *Server) ParseAccessToken(ctx context.Context, tokenStr string) (*AccessTokenClaims, error)

ParseAccessToken 解析并验证 Token 签名和 Issuer,但不检查撤销状态。

func (*Server) PushedAuthorization

func (s *Server) PushedAuthorization(ctx context.Context, req *PARRequest) (*PARResponse, error)

PushedAuthorization 处理推送授权请求 (PAR - RFC 9126)

func (*Server) RegisterClient

RegisterClient 处理动态客户端注册

func (*Server) RequestAuthorize

func (s *Server) RequestAuthorize(ctx context.Context, req *AuthorizeRequest) (client RegisteredClient, err error)

RequestAuthorize 校验授权请求

func (*Server) ResponseAuthorized

func (s *Server) ResponseAuthorized(ctx context.Context, req *AuthorizeRequest) (redirectURL string, err error)

ResponseAuthorized 用户同意后生成重定向URL

func (*Server) RevokeToken

func (s *Server) RevokeToken(ctx context.Context, req *RevocationRequest) error

RevokeToken 处理 Token 撤销

func (*Server) SecretManager

func (s *Server) SecretManager() *SecretManager

SecretManager 返回 HMAC 密钥管理器,用于 Refresh Token 的签名管理。

func (*Server) StartBackgroundWorkers

func (s *Server) StartBackgroundWorkers(ctx context.Context)

StartBackgroundWorkers 启动所有后台 Worker 应在 HTTP 服务启动之前调用 ctx 用于传递取消信号和 tracing context

func (*Server) StopBackgroundWorkers

func (s *Server) StopBackgroundWorkers()

StopBackgroundWorkers 停止所有后台 Worker 应在 HTTP 服务关闭时调用

func (*Server) UnregisterClient

func (s *Server) UnregisterClient(ctx context.Context, clientIDStr string) error

UnregisterClient 处理动态客户端注销

func (*Server) ValidateKeys

func (s *Server) ValidateKeys(ctx context.Context) error

ValidateKeys 检查密钥管理器是否至少配置了一个签名密钥。 应在启动 HTTP 服务前调用,以尽早发现配置错误。

返回 ErrNoSigningKey 如果未配置任何签名密钥。

func (*Server) VerifyAccessToken

func (s *Server) VerifyAccessToken(ctx context.Context, tokenStr string) (*AccessTokenClaims, error)

VerifyAccessToken 用于验证 Bearer Token 的有效性。 此实现不校验 Audience,因为 UserInfo 端点信任本 Issuer 签发的任何包含 openid scope 的 Token。

type ServerConfig

type ServerConfig struct {
	// Issuer 是服务的唯一标识符 (URL),例如 "https://auth.example.com"
	Issuer string

	// Storage 是数据持久层接口实现
	Storage Storage

	// Hasher 是密码哈希器
	Hasher Hasher

	// SecretManager 用于管理 Refresh Token 签名的对称密钥 (HMAC)
	// 如果为 nil,NewServer 会初始化一个默认的 MemoryKeyProvider,但该 Provider 初始无密钥,
	// 需要通过 server.SecretManager() 获取实例后添加密钥,或者在 Config 中直接传入配置好的实例。
	SecretManager *SecretManager

	// 令牌有效期配置 (若为 0,NewServer 会设置默认值)
	CodeTTL                 time.Duration // 默认 5 分钟
	AccessTokenTTL          time.Duration // 默认 1 小时
	RefreshTokenTTL         time.Duration // 默认 30 天
	IDTokenTTL              time.Duration // 默认 1 小时
	RefreshTokenGracePeriod time.Duration // 默认 30 秒

	// SupportedSigningAlgs 支持的签名算法 (默认为 RS256, ES256)
	SupportedSigningAlgs []string

	// EnableGC 是否启用垃圾回收 Worker (默认为 true)
	EnableGC bool

	// GCInterval GC 执行间隔 (默认 1 小时)
	GCInterval time.Duration
}

ServerConfig 用于初始化 OIDC Server 的配置

type StaticKeySet

type StaticKeySet struct {
	Keys map[string]crypto.PublicKey
}

StaticKeySet 是 KeySource 的一个简单实现,用于基于本地 JWKS 验证 (单元测试或本地开发用)

func NewStaticKeySet

func NewStaticKeySet() *StaticKeySet

func (*StaticKeySet) Add

func (s *StaticKeySet) Add(kid string, pub crypto.PublicKey)

func (*StaticKeySet) GetKey

func (s *StaticKeySet) GetKey(ctx context.Context, kid string) (crypto.PublicKey, error)

type Storage

type Storage interface {
	Persistence
	Cache
}

Storage 是一个聚合接口,方便在 Server 初始化时传递。

type StringSlice added in v0.3.0

type StringSlice []string

StringSlice 用于在数据库中以 JSON 格式存储 []string

func (*StringSlice) Scan added in v0.3.0

func (ss *StringSlice) Scan(value interface{}) error

func (StringSlice) Value added in v0.3.0

func (ss StringSlice) Value() (driver.Value, error)

type TieredStorage

type TieredStorage struct {
	Persistence
	Cache
}

TieredStorage implements a storage strategy that uses a fast Cache layer backed by a persistent Database layer.

Strategy:

  • Reads: Read-Through (Cache -> Miss? -> DB -> Set Cache)
  • Writes: Cache-Aside (DB -> Invalidate Cache on success) After DB commit, we invalidate cache. If invalidation fails, we log the error but don't fail the request. This ensures eventual consistency and prevents stale cache data.
  • Deletes: DB -> Cache JWKDelete

func NewTieredStorage

func NewTieredStorage(db Persistence, cache Cache) *TieredStorage

NewTieredStorage creates a new TieredStorage instance.

func (*TieredStorage) ClientCreate added in v0.3.0

func (s *TieredStorage) ClientCreate(ctx context.Context, metadata *ClientMetadata) (RegisteredClient, error)

func (*TieredStorage) ClientDeleteByID added in v0.3.0

func (s *TieredStorage) ClientDeleteByID(ctx context.Context, clientID BinaryUUID) error

func (*TieredStorage) ClientGetByID added in v0.3.0

func (s *TieredStorage) ClientGetByID(ctx context.Context, clientID BinaryUUID) (RegisteredClient, error)

func (*TieredStorage) ClientListAll added in v0.3.0

func (s *TieredStorage) ClientListAll(ctx context.Context, query ListQuery) ([]RegisteredClient, error)

func (*TieredStorage) ClientListByOwner added in v0.3.0

func (s *TieredStorage) ClientListByOwner(ctx context.Context, ownerID BinaryUUID, query ListQuery) ([]RegisteredClient, error)

func (*TieredStorage) ClientUpdate added in v0.3.0

func (s *TieredStorage) ClientUpdate(ctx context.Context, metadata *ClientMetadata) (RegisteredClient, error)

func (*TieredStorage) JWKDelete added in v0.3.0

func (s *TieredStorage) JWKDelete(ctx context.Context, kid string) error

func (*TieredStorage) JWKGet added in v0.3.0

func (s *TieredStorage) JWKGet(ctx context.Context, kid string) (jwk.Key, error)

func (*TieredStorage) JWKGetSigning added in v0.3.0

func (s *TieredStorage) JWKGetSigning(ctx context.Context) (string, error)

func (*TieredStorage) JWKList added in v0.3.0

func (s *TieredStorage) JWKList(ctx context.Context) ([]jwk.Key, error)

func (*TieredStorage) JWKMarkSigning added in v0.3.0

func (s *TieredStorage) JWKMarkSigning(ctx context.Context, kid string) error

func (*TieredStorage) JWKSave added in v0.3.0

func (s *TieredStorage) JWKSave(ctx context.Context, key jwk.Key) error

func (*TieredStorage) RefreshTokenCreate added in v0.3.0

func (s *TieredStorage) RefreshTokenCreate(ctx context.Context, session *RefreshTokenSession) error

func (*TieredStorage) RefreshTokenGet added in v0.3.0

func (s *TieredStorage) RefreshTokenGet(ctx context.Context, tokenID Hash256) (*RefreshTokenSession, error)

func (*TieredStorage) RefreshTokenRevoke added in v0.3.0

func (s *TieredStorage) RefreshTokenRevoke(ctx context.Context, tokenID Hash256) error

func (*TieredStorage) RefreshTokenRevokeUser added in v0.3.0

func (s *TieredStorage) RefreshTokenRevokeUser(ctx context.Context, userID BinaryUUID) ([]Hash256, error)

func (*TieredStorage) RefreshTokenRotate added in v0.3.0

func (s *TieredStorage) RefreshTokenRotate(ctx context.Context, oldTokenID Hash256, newSession *RefreshTokenSession, gracePeriod time.Duration) error

type TokenCache

type TokenCache interface {
	// RefreshTokenGet 从缓存获取
	RefreshTokenGet(ctx context.Context, tokenID Hash256) (*RefreshTokenSession, error)

	// RefreshTokenSave 存入缓存
	RefreshTokenSave(ctx context.Context, session *RefreshTokenSession, ttl time.Duration) error

	// RefreshTokenRotate 令牌轮换:删除旧的,保存新的。如果保存新令牌失败,旧令牌也不应被删除(或者整个操作回滚)。
	RefreshTokenRotate(ctx context.Context, oldTokenID Hash256, newSession *RefreshTokenSession, gracePeriod time.Duration) error

	// RefreshTokenInvalidate 从缓存移除
	RefreshTokenInvalidate(ctx context.Context, tokenID Hash256) error

	// RefreshTokensInvalidate 批量从缓存移除
	RefreshTokensInvalidate(ctx context.Context, tokenIDs []Hash256) error
}

TokenCache 定义了 Refresh Token 的缓存接口

type TokenRequest

type TokenRequest struct {
	GrantType    string `form:"grant_type" json:"grant_type"`
	ClientID     string `form:"client_id" json:"client_id"`
	ClientSecret string `form:"client_secret" json:"client_secret"`
	RedirectURI  string `form:"redirect_uri" json:"redirect_uri"`

	// authorization_code 参数
	Code         string `form:"code" json:"code"`
	CodeVerifier string `form:"code_verifier" json:"code_verifier"` // PKCE

	// device_code 参数
	DeviceCode string `form:"device_code" json:"device_code"`

	// refresh_token 参数
	RefreshToken string `form:"refresh_token" json:"refresh_token"`

	// client_credentials 参数 (可选扩展)
	Scope string `form:"scope" json:"scope"`

	// password 参数 (可选扩展, 仅用于测试)
	Username string `form:"username" json:"username"`
	Password string `form:"password" json:"password"`

	// DPoP (RFC 9449): JWK Thumbprint from DPoP proof
	// 如果请求包含 DPoP header,验证后提取的 JKT
	DPoPJKT string `form:"-" json:"-"` // 不从 body 绑定
}

TokenRequest 封装了 /token 端点的标准参数 对应 RFC 6749 Section 4.1.3 & 6

type TokenStorage

type TokenStorage interface {
	// RefreshTokenCreate 存储新的刷新令牌。
	RefreshTokenCreate(ctx context.Context, session *RefreshTokenSession) error

	// RefreshTokenGet 根据令牌 ID (或哈希) 查找。
	// 如果未找到或过期,应返回 ErrTokenNotFound。
	RefreshTokenGet(ctx context.Context, tokenID Hash256) (*RefreshTokenSession, error)

	// RefreshTokenRotate 令牌轮换:删除旧的,保存新的。
	// 强烈建议在事务中执行:如果保存新令牌失败,旧令牌也不应被删除(或者整个操作回滚)。
	RefreshTokenRotate(ctx context.Context, oldTokenID Hash256, newSession *RefreshTokenSession, gracePeriod time.Duration) error

	// RefreshTokenRevoke 撤销指定的刷新令牌。
	RefreshTokenRevoke(ctx context.Context, tokenID Hash256) error

	// RefreshTokenRevokeUser 撤销指定用户的所有令牌 (例如用户登出或修改密码时)。
	RefreshTokenRevokeUser(ctx context.Context, userID BinaryUUID) ([]Hash256, error)

	// RefreshTokenListByUser 列出指定用户的所有活跃令牌。
	RefreshTokenListByUser(ctx context.Context, userID BinaryUUID) ([]*RefreshTokenSession, error)
}

TokenStorage 负责持久化刷新令牌 (Long-lived tokens)。

type TokenVerifier

type TokenVerifier interface {
	VerifyAccessToken(ctx context.Context, tokenStr string) (*AccessTokenClaims, error)
	// ParseAccessToken 解析并验证 Token 签名和 Issuer,但不检查撤销状态。
	// 用于撤销操作或需要自行处理撤销检查的场景。
	ParseAccessToken(ctx context.Context, tokenStr string) (*AccessTokenClaims, error)
}

TokenVerifier 定义了 UserInfoHandler 所需的令牌验证能力。 由 Server 层实现,通常包括验签、验证 Issuer、验证过期时间、验证撤销状态。 注意:该接口不应在内部强制验证 Audience,因为 UserInfo 的 Audience 可能是动态的 ClientID。

type UserAuthenticator

type UserAuthenticator interface {
	// AuthenticateByPassword 根据用户名和密码进行认证。
	// 成功时返回用户 ID,失败时应返回 ErrUserNotFound。
	// 注意:此方法应实现速率限制和防暴力破解机制。
	AuthenticateByPassword(ctx context.Context, username, password string) (BinaryUUID, string, error)
}

UserAuthenticator 定义用户认证接口。 此接口仅用于特殊场景(如负载测试的 Password Grant 流程),不应在生产环境的标准 OIDC 流程中使用。

type UserInfo

type UserInfo struct {
	Subject             string  `json:"sub"`
	Name                *string `json:"name,omitempty"`
	GivenName           *string `json:"given_name,omitempty"`
	FamilyName          *string `json:"family_name,omitempty"`
	Nickname            *string `json:"nickname,omitempty"`
	PreferredUsername   *string `json:"preferred_username,omitempty"`
	Profile             *string `json:"profile,omitempty"`
	Picture             *string `json:"picture,omitempty"`
	Website             *string `json:"website,omitempty"`
	Email               *string `json:"email,omitempty"`
	EmailVerified       *bool   `json:"email_verified,omitempty"`
	Gender              *string `json:"gender,omitempty"`
	Birthdate           *string `json:"birthdate,omitempty"`
	Zoneinfo            *string `json:"zoneinfo,omitempty"`
	Locale              *string `json:"locale,omitempty"`
	PhoneNumber         *string `json:"phone_number,omitempty"`
	PhoneNumberVerified *bool   `json:"phone_number_verified,omitempty"`
	UpdatedAt           int64   `json:"updated_at,omitempty"`

	// Metadata 存储扩展字段 (Custom Claims)。
	// json:"-" 表示标准库忽略该字段,我们将在 MarshalJSON 中手动将其合并到顶层。
	Metadata map[string]interface{} `json:"-"`
}

UserInfo 表示 OIDC UserInfo 端点的标准响应结构。 参见: OIDC Core 1.0, Section 5.1.

func GetUserInfo

func GetUserInfo(ctx context.Context, storage UserInfoGetter, verifier TokenVerifier, claims *AccessTokenClaims) (*UserInfo, error)

GetUserInfo 根据 claims 获取用户信息

func (*UserInfo) MarshalJSON added in v0.3.0

func (u *UserInfo) MarshalJSON() ([]byte, error)

MarshalJSON 自定义序列化逻辑

func (*UserInfo) UnmarshalJSON added in v0.3.0

func (u *UserInfo) UnmarshalJSON(data []byte) error

UnmarshalJSON 自定义反序列化逻辑

type UserInfoGetter

type UserInfoGetter interface {
	// 测试中需要添加可查询的用户信息
	UserCreateInfo(ctx context.Context, userInfo *UserInfo) error
	// UserGetInfoByID 根据 UserID 和请求的 Scopes 返回用户信息。
	// scope 参数允许实现层根据权限过滤返回字段 (例如:没有 'email' scope 就不查邮箱)。
	UserGetInfoByID(ctx context.Context, userID BinaryUUID, scopes []string) (*UserInfo, error)
}

UserInfoGetter 用于从业务系统获取用户信息,以填充 ID Token 或响应 UserInfo 端点。

Directories

Path Synopsis
sdk
go

Jump to

Keyboard shortcuts

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