feat(security): full OAuth 2.1 / OpenID Connect server and OIDC relying-party client

Authorization server: consent and scopes, OIDC (nonce, auth_time, acr, sid,
at_hash, signed userinfo, RP-initiated and back-channel logout), managed
refresh tokens with rotation and reuse detection, RFC 9068 JWT access tokens,
DPoP, PAR, device grant, token exchange, private_key_jwt, RFC 7591/7592
registration, RFC 9207 iss, signing keyring with rotation.

State is DB-backed through a new lookup.OAuthGrantStore (procedure and direct
backends, four dialect DDLs, conformance cases).

Client side: WithOIDC discovery, PKCE, nonce, id_token validation, OAuth2LogoutURL.

PeekRefresh now returns already rotated tokens so RotateRefresh can detect reuse.

Docs: OAUTH2_SERVER.md, oauth2_full_example.go, breaking_changes.md step 8.
This commit is contained in:
Hein
2026-10-01 14:42:12 +02:00
parent f54b707040
commit 640faeeeaf
51 changed files with 8682 additions and 1125 deletions
+23
View File
@@ -2,6 +2,8 @@ package security
import (
"context"
"github.com/bitechdev/ResolveSpec/pkg/security/lookup"
)
// OAuthRegisterClient persists an OAuth2 client registration.
@@ -33,3 +35,24 @@ func (a *DatabaseAuthenticator) OAuthIntrospectToken(ctx context.Context, token
func (a *DatabaseAuthenticator) OAuthRevokeToken(ctx context.Context, token string) error {
return a.src.get().OAuthClient.Revoke(ctx, token)
}
// OAuthGrants returns the store holding consents, managed refresh tokens, device codes,
// pushed authorization requests and the replay cache.
func (a *DatabaseAuthenticator) OAuthGrants() lookup.OAuthGrantStore {
return a.src.get().OAuthGrant
}
// OAuthUpdateClient replaces the registered metadata of a client (RFC 7592).
func (a *DatabaseAuthenticator) OAuthUpdateClient(ctx context.Context, client *OAuthServerClient) error {
return a.src.get().OAuthClient.UpdateClient(ctx, client)
}
// OAuthDeleteClient deactivates a registered client (RFC 7592).
func (a *DatabaseAuthenticator) OAuthDeleteClient(ctx context.Context, clientID string) error {
return a.src.get().OAuthClient.DeleteClient(ctx, clientID)
}
// OAuthGetUser returns the active user with the given id.
func (a *DatabaseAuthenticator) OAuthGetUser(ctx context.Context, userID int) (*UserContext, error) {
return a.src.get().OAuthUser.GetUser(ctx, userID)
}