test(08-02): add failing RFC 7591 registration RED test in wristband
- TestPhase8RedRegistration asserts the exact public-client DCR success contract and fails while Server.Register is a 501 stub - adds the Backend/Tx transaction-scoped store bundle (ClientStore, AuthCodeStore, RefreshTokenStore, AccessTokenIssuer) and wristband's own in-memory implementation for framework-level tests (D-07) - adds crypto.go's fixed-transform helpers (random base64url, sha256 hex, constant-time compare, S256) and Options/Server seams for the DCR lifetimes, cap, sweep age and 64 KiB body bound (D-03/D-21)
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Options configures a Server's advertised endpoints and metadata values.
|
||||
@@ -42,6 +43,20 @@ type Options struct {
|
||||
// AuthorizationResponseIssParameterSupported is the RFC 9207 metadata
|
||||
// capability flag. PHP default: true.
|
||||
AuthorizationResponseIssParameterSupported bool
|
||||
|
||||
// DCRClientCap is the maximum number of unrevoked OAuth clients RFC
|
||||
// 7591 registration allows (PHP OAuthRegisterController::MAX_CLIENTS).
|
||||
// PHP default: 200 (D-03).
|
||||
DCRClientCap int
|
||||
|
||||
// DCRUnconsentedSweepAge is how old an unconsented, dynamically
|
||||
// registered client (non-nil RegistrationIP) must be before
|
||||
// registration sweeps it. PHP default: 24h (D-03).
|
||||
DCRUnconsentedSweepAge time.Duration
|
||||
|
||||
// RegisterMaxBodyBytes bounds the RFC 7591 registration request body
|
||||
// before JSON decoding (D-21, T-08-DCR-FLOOD). PHP default: 65536 (64 KiB).
|
||||
RegisterMaxBodyBytes int64
|
||||
}
|
||||
|
||||
// DefaultOptions returns PHP-parity defaults for every metadata option
|
||||
@@ -52,18 +67,41 @@ func DefaultOptions() Options {
|
||||
ScopesSupported: []string{"read", "write", "ai", "offline_access"},
|
||||
TokenEndpointAuthMethodsSupported: []string{"none", "client_secret_post", "client_secret_basic"},
|
||||
AuthorizationResponseIssParameterSupported: true,
|
||||
DCRClientCap: 200,
|
||||
DCRUnconsentedSweepAge: 24 * time.Hour,
|
||||
RegisterMaxBodyBytes: 65536,
|
||||
}
|
||||
}
|
||||
|
||||
// Server is the app-agnostic wristband authorization-server surface. It is
|
||||
// constructed with Options and never imports an application package.
|
||||
type Server struct {
|
||||
opts Options
|
||||
opts Options
|
||||
backend Backend
|
||||
|
||||
// now and randomBytes are deterministic clock/entropy seams so tests
|
||||
// can control timestamps and generated secrets without depending on
|
||||
// wall-clock time or true randomness (08-02-PLAN.md Task 2).
|
||||
now func() time.Time
|
||||
randomBytes func(n int) (string, error)
|
||||
}
|
||||
|
||||
// NewServer constructs a Server from Options.
|
||||
// NewServer constructs a Server from Options. The backend is nil until
|
||||
// SetBackend is called (D-09: the metadata route needs no backend at all,
|
||||
// so plugin boot can construct a Server before a *gorm.DB is available).
|
||||
func NewServer(opts Options) *Server {
|
||||
return &Server{opts: opts}
|
||||
return &Server{
|
||||
opts: opts,
|
||||
now: time.Now,
|
||||
randomBytes: randomBase64URL,
|
||||
}
|
||||
}
|
||||
|
||||
// SetBackend attaches the app's transaction-scoped store bundle. Handlers
|
||||
// that need persistence (Register) return an opaque 500 until this is
|
||||
// called.
|
||||
func (s *Server) SetBackend(b Backend) {
|
||||
s.backend = b
|
||||
}
|
||||
|
||||
// metadataDocument is the exact unwrapped RFC 8414 body. Field order matches
|
||||
|
||||
Reference in New Issue
Block a user