CreateDynamicClientRegistration returns an http.HandlerFunc that handles POST /oauth2/register
(db database.Store, accessURL *url.URL, auditor *audit.Auditor, logger slog.Logger)
| 27 | |
| 28 | // CreateDynamicClientRegistration returns an http.HandlerFunc that handles POST /oauth2/register |
| 29 | func CreateDynamicClientRegistration(db database.Store, accessURL *url.URL, auditor *audit.Auditor, logger slog.Logger) http.HandlerFunc { |
| 30 | return func(rw http.ResponseWriter, r *http.Request) { |
| 31 | ctx := r.Context() |
| 32 | aReq, commitAudit := audit.InitRequest[database.OAuth2ProviderApp](rw, &audit.RequestParams{ |
| 33 | Audit: *auditor, |
| 34 | Log: logger, |
| 35 | Request: r, |
| 36 | Action: database.AuditActionCreate, |
| 37 | }) |
| 38 | defer commitAudit() |
| 39 | |
| 40 | // Parse request |
| 41 | var req codersdk.OAuth2ClientRegistrationRequest |
| 42 | if !httpapi.Read(ctx, rw, r, &req) { |
| 43 | return |
| 44 | } |
| 45 | |
| 46 | // Validate request |
| 47 | if err := req.Validate(); err != nil { |
| 48 | writeOAuth2RegistrationError(ctx, rw, http.StatusBadRequest, |
| 49 | "invalid_client_metadata", err.Error()) |
| 50 | return |
| 51 | } |
| 52 | |
| 53 | // Apply defaults |
| 54 | req = req.ApplyDefaults() |
| 55 | |
| 56 | // Generate client credentials |
| 57 | clientID := uuid.New() |
| 58 | clientSecret, hashedSecret, err := generateClientCredentials() |
| 59 | if err != nil { |
| 60 | writeOAuth2RegistrationError(ctx, rw, http.StatusInternalServerError, |
| 61 | "server_error", "Failed to generate client credentials") |
| 62 | return |
| 63 | } |
| 64 | |
| 65 | // Generate registration access token for RFC 7592 management |
| 66 | registrationToken, hashedRegToken, err := generateRegistrationAccessToken() |
| 67 | if err != nil { |
| 68 | writeOAuth2RegistrationError(ctx, rw, http.StatusInternalServerError, |
| 69 | "server_error", "Failed to generate registration token") |
| 70 | return |
| 71 | } |
| 72 | |
| 73 | // Store in database - use system context since this is a public endpoint |
| 74 | now := dbtime.Now() |
| 75 | clientName := req.GenerateClientName() |
| 76 | //nolint:gocritic // OAuth2 system context — dynamic registration is a public endpoint |
| 77 | app, err := db.InsertOAuth2ProviderApp(dbauthz.AsSystemOAuth2(ctx), database.InsertOAuth2ProviderAppParams{ |
| 78 | ID: clientID, |
| 79 | CreatedAt: now, |
| 80 | UpdatedAt: now, |
| 81 | Name: clientName, |
| 82 | Icon: req.LogoURI, |
| 83 | CallbackURL: req.RedirectURIs[0], // Primary redirect URI |
| 84 | RedirectUris: req.RedirectURIs, |
| 85 | ClientType: sql.NullString{String: req.DetermineClientType(), Valid: true}, |
| 86 | DynamicallyRegistered: sql.NullBool{Bool: true, Valid: true}, |
no test coverage detected