feat(08-05): add wristband consent issue/deny operations

Server.PendingRequest/IssueCode/DenyPending port PHP
OAuthConsentController::pendingFor/OAuthCodeManager::issueCode as
app-agnostic protocol operations (08-CONTEXT.md D-08): every missing,
foreign-owner, used, expired, or already-issued pending row collapses to
the identical ErrPendingNotFound (T-08-CROSS-USER/T-08-REQUEST-LEAK).
IssueCode trusts the caller's already-computed granted scopes/collection
ids and returns the ordered redirect_to URL built through the existing
RFC 3986 encoder.

AuthCodeStore.MarkIssued gains scopes/collectionIDs/expiresAt parameters
(PHP's issueCode overwrites all three, not just code_hash/user_id) and
ClientStore gains MarkConsented, both required for D-08's consented_at
stamping and server-derived grant persistence. Options gains CodeTTL
(600s PHP-parity default) following the established Options-extension
pattern.
This commit is contained in:
Jakub Zych
2026-09-23 20:59:24 +02:00
parent a459f74897
commit a1fa9c6f44
5 changed files with 341 additions and 2 deletions

View File

@@ -107,12 +107,28 @@ func (t *memoryTx) ByCodeHashForUpdate(ctx context.Context, codeHash string) (*A
return nil, nil
}
func (t *memoryTx) MarkIssued(ctx context.Context, id uint, codeHash string, userID uint) error {
func (t *memoryTx) MarkIssued(ctx context.Context, id uint, codeHash string, userID uint, scopes []string, collectionIDs []uint, expiresAt time.Time) error {
for _, c := range t.b.codes {
if c.ID == id {
c.RequestID = nil
c.CodeHash = &codeHash
c.UserID = &userID
c.Scopes = scopes
c.CollectionIDs = collectionIDs
c.ExpiresAt = expiresAt
return nil
}
}
return nil
}
func (t *memoryTx) MarkConsented(ctx context.Context, clientID string) error {
for _, c := range t.b.clients {
if c.ClientID == clientID {
if c.ConsentedAt == nil {
now := time.Now()
c.ConsentedAt = &now
}
return nil
}
}