feat(oauth): add dynamic client registration endpoint (RFC 7591)#103
Merged
feat(oauth): add dynamic client registration endpoint (RFC 7591)#103
Conversation
- Add POST /oauth/register for programmatic OAuth client registration - Gate behind ENABLE_DYNAMIC_CLIENT_REGISTRATION env var (default: false) - Registered clients start in "pending" status requiring admin approval - Support grant_types: authorization_code, device_code - Support token_endpoint_auth_method: none, client_secret_basic, client_secret_post - Restrict scopes to user-safe set: email, profile, openid, offline_access - Add rate limiting for registration endpoint (default: 5 req/min) - Add EventClientRegistered audit event type - Add 13 handler tests covering success and error paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Contributor
There was a problem hiding this comment.
Pull request overview
Adds an OAuth 2.0 Dynamic Client Registration endpoint (POST /oauth/register) intended to follow RFC 7591, wiring it into routing, configuration, rate limiting, and adding handler-level tests.
Changes:
- Introduces
RegistrationHandlerwith request validation and RFC 7591-style JSON responses. - Wires
/oauth/registerinto the router and rate-limiting middleware, plus new config knobs. - Adds handler tests covering success and error cases; adds an audit log event constant.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/models/audit_log.go | Adds a new audit event constant for dynamic registration. |
| internal/handlers/registration.go | Implements POST /oauth/register handler and response shaping. |
| internal/handlers/registration_test.go | Adds test coverage for registration handler behaviors. |
| internal/config/config.go | Adds env-backed config fields to enable registration and configure rate limit. |
| internal/bootstrap/router.go | Registers the new /oauth/register route with rate limiting. |
| internal/bootstrap/ratelimit.go | Adds a dedicated rate limiter middleware slot for registration. |
| internal/bootstrap/handlers.go | Instantiates and exposes the new registration handler. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
internal/handlers/registration.go
Outdated
Comment on lines
+107
to
+120
| // 5. Determine auth method → client type | ||
| var clientType string | ||
| switch req.TokenEPAuth { | ||
| case "", "none": | ||
| clientType = services.ClientTypePublic | ||
| case "client_secret_basic", "client_secret_post": | ||
| clientType = services.ClientTypeConfidential | ||
| default: | ||
| c.JSON(http.StatusBadRequest, gin.H{ | ||
| "error": "invalid_client_metadata", | ||
| "error_description": "Unsupported token_endpoint_auth_method: " + req.TokenEPAuth + ". Supported: none, client_secret_basic, client_secret_post", | ||
| }) | ||
| return | ||
| } |
internal/handlers/registration.go
Outdated
Comment on lines
+160
to
+175
| // 8. Build RFC 7591 §3.2.1 response | ||
| app := resp.OAuthApplication | ||
| grantTypes := buildResponseGrantTypes(app) | ||
| authMethod := "none" | ||
| if app.ClientType == services.ClientTypeConfidential { | ||
| authMethod = "client_secret_basic" | ||
| } | ||
|
|
||
| c.JSON(http.StatusCreated, gin.H{ | ||
| "client_id": app.ClientID, | ||
| "client_secret": resp.ClientSecretPlain, | ||
| "client_name": app.ClientName, | ||
| "redirect_uris": app.RedirectURIs, | ||
| "grant_types": grantTypes, | ||
| "token_endpoint_auth_method": authMethod, | ||
| "scope": app.Scopes, |
Comment on lines
+151
to
+158
| resp, err := h.clientService.CreateClient(c.Request.Context(), createReq) | ||
| if err != nil { | ||
| c.JSON(http.StatusBadRequest, gin.H{ | ||
| "error": "invalid_client_metadata", | ||
| "error_description": err.Error(), | ||
| }) | ||
| return | ||
| } |
- Wire AuditService into RegistrationHandler - Log EventClientRegistered with client metadata and source IP on successful registration - Distinguishes API dynamic registration from UI/Admin client creation in audit trail Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tion - Default token_endpoint_auth_method to "client_secret_basic" per RFC 7591 §2 - Echo back the actual requested auth method instead of hardcoding - Distinguish validation errors (400) from internal errors (500) - Add tests for client_secret_post, explicit public client, and default auth method Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…C 7591 §1.1) - Add DYNAMIC_CLIENT_REGISTRATION_TOKEN config for initial access token - When configured, require Authorization: Bearer <token> header - Use constant-time comparison to prevent timing attacks - Empty token config preserves open registration behavior - Add 4 new tests covering valid/missing/wrong token and open mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
POST /oauth/registerendpoint implementing RFC 7591 Dynamic Client RegistrationENABLE_DYNAMIC_CLIENT_REGISTRATION=trueenv var (disabled by default for security)grant_types:authorization_code,device_code(including URN form)token_endpoint_auth_method:none(public),client_secret_basic,client_secret_post(confidential)email,profile,openid,offline_accessDYNAMIC_CLIENT_REGISTRATION_RATE_LIMIT)client_id,client_secret,grant_types, etc.Configuration
Example Usage
Test plan
go test ./...)make lint— 0 issues)curlagainst running server🤖 Generated with Claude Code