feat: implement OAuth2 dynamic client registration (RFC 7591/7592) (#18645)

# Implement OAuth2 Dynamic Client Registration (RFC 7591/7592)

This PR implements OAuth2 Dynamic Client Registration according to RFC 7591 and Client Configuration Management according to RFC 7592. These standards allow OAuth2 clients to register themselves programmatically with Coder as an authorization server.

Key changes include:

1. Added database schema extensions to support RFC 7591/7592 fields in the `oauth2_provider_apps` table
2. Implemented `/oauth2/register` endpoint for dynamic client registration (RFC 7591)
3. Added client configuration management endpoints (RFC 7592):
   - GET/PUT/DELETE `/oauth2/clients/{client_id}`
   - Registration access token validation middleware

4. Added comprehensive validation for OAuth2 client metadata:
   - URI validation with support for custom schemes for native apps
   - Grant type and response type validation
   - Token endpoint authentication method validation

5. Enhanced developer documentation with:
   - RFC compliance guidelines
   - Testing best practices to avoid race conditions
   - Systematic debugging approaches for OAuth2 implementations

The implementation follows security best practices from the RFCs, including proper token handling, secure defaults, and appropriate error responses. This enables third-party applications to integrate with Coder's OAuth2 provider capabilities programmatically.
This commit is contained in:
Thomas Kosiewski
2025-07-03 18:33:47 +02:00
committed by GitHub
parent 699dd8e554
commit 74e1d5c4b6
30 changed files with 5802 additions and 133 deletions

View File

@ -2989,6 +2989,40 @@ type OAuth2ProviderApp struct {
ClientType sql.NullString `db:"client_type" json:"client_type"`
// Whether this app was created via dynamic client registration
DynamicallyRegistered sql.NullBool `db:"dynamically_registered" json:"dynamically_registered"`
// RFC 7591: Timestamp when client_id was issued
ClientIDIssuedAt sql.NullTime `db:"client_id_issued_at" json:"client_id_issued_at"`
// RFC 7591: Timestamp when client_secret expires (null for non-expiring)
ClientSecretExpiresAt sql.NullTime `db:"client_secret_expires_at" json:"client_secret_expires_at"`
// RFC 7591: Array of grant types the client is allowed to use
GrantTypes []string `db:"grant_types" json:"grant_types"`
// RFC 7591: Array of response types the client supports
ResponseTypes []string `db:"response_types" json:"response_types"`
// RFC 7591: Authentication method for token endpoint
TokenEndpointAuthMethod sql.NullString `db:"token_endpoint_auth_method" json:"token_endpoint_auth_method"`
// RFC 7591: Space-delimited scope values the client can request
Scope sql.NullString `db:"scope" json:"scope"`
// RFC 7591: Array of email addresses for responsible parties
Contacts []string `db:"contacts" json:"contacts"`
// RFC 7591: URL of the client home page
ClientUri sql.NullString `db:"client_uri" json:"client_uri"`
// RFC 7591: URL of the client logo image
LogoUri sql.NullString `db:"logo_uri" json:"logo_uri"`
// RFC 7591: URL of the client terms of service
TosUri sql.NullString `db:"tos_uri" json:"tos_uri"`
// RFC 7591: URL of the client privacy policy
PolicyUri sql.NullString `db:"policy_uri" json:"policy_uri"`
// RFC 7591: URL of the client JSON Web Key Set
JwksUri sql.NullString `db:"jwks_uri" json:"jwks_uri"`
// RFC 7591: JSON Web Key Set document value
Jwks pqtype.NullRawMessage `db:"jwks" json:"jwks"`
// RFC 7591: Identifier for the client software
SoftwareID sql.NullString `db:"software_id" json:"software_id"`
// RFC 7591: Version of the client software
SoftwareVersion sql.NullString `db:"software_version" json:"software_version"`
// RFC 7592: Hashed registration access token for client management
RegistrationAccessToken sql.NullString `db:"registration_access_token" json:"registration_access_token"`
// RFC 7592: URI for client configuration endpoint
RegistrationClientUri sql.NullString `db:"registration_client_uri" json:"registration_client_uri"`
}
// Codes are meant to be exchanged for access tokens.