ProcessAuthorize handles POST /oauth2/authorize requests to process the user's authorization decision and generate an authorization code.
(db database.Store)
| 186 | // ProcessAuthorize handles POST /oauth2/authorize requests to process the user's authorization decision |
| 187 | // and generate an authorization code. |
| 188 | func ProcessAuthorize(db database.Store) http.HandlerFunc { |
| 189 | return func(rw http.ResponseWriter, r *http.Request) { |
| 190 | ctx := r.Context() |
| 191 | apiKey := httpmw.APIKey(r) |
| 192 | app := httpmw.OAuth2ProviderApp(r) |
| 193 | |
| 194 | callbackURL, err := url.Parse(app.CallbackURL) |
| 195 | if err != nil { |
| 196 | httpapi.WriteOAuth2Error(r.Context(), rw, http.StatusInternalServerError, codersdk.OAuth2ErrorCodeServerError, "Failed to validate query parameters") |
| 197 | return |
| 198 | } |
| 199 | |
| 200 | params, _, err := extractAuthorizeParams(r, callbackURL) |
| 201 | if err != nil { |
| 202 | httpapi.WriteOAuth2Error(ctx, rw, http.StatusBadRequest, codersdk.OAuth2ErrorCodeInvalidRequest, err.Error()) |
| 203 | return |
| 204 | } |
| 205 | |
| 206 | // OAuth 2.1 removes the implicit grant. Only |
| 207 | // authorization code flow is supported. |
| 208 | if params.responseType != codersdk.OAuth2ProviderResponseTypeCode { |
| 209 | httpapi.WriteOAuth2Error(ctx, rw, http.StatusBadRequest, |
| 210 | codersdk.OAuth2ErrorCodeUnsupportedResponseType, |
| 211 | "Only response_type=code is supported") |
| 212 | return |
| 213 | } |
| 214 | |
| 215 | // code_challenge is required (enforced by RequiredNotEmpty above), |
| 216 | // but default the method to S256 if omitted. |
| 217 | if params.codeChallengeMethod == "" { |
| 218 | params.codeChallengeMethod = string(codersdk.OAuth2PKCECodeChallengeMethodS256) |
| 219 | } |
| 220 | if err := codersdk.ValidatePKCECodeChallengeMethod(params.codeChallengeMethod); err != nil { |
| 221 | httpapi.WriteOAuth2Error(ctx, rw, http.StatusBadRequest, codersdk.OAuth2ErrorCodeInvalidRequest, err.Error()) |
| 222 | return |
| 223 | } |
| 224 | |
| 225 | // TODO: Ignoring scope for now, but should look into implementing. |
| 226 | code, err := GenerateSecret() |
| 227 | if err != nil { |
| 228 | httpapi.WriteOAuth2Error(r.Context(), rw, http.StatusInternalServerError, codersdk.OAuth2ErrorCodeServerError, "Failed to generate OAuth2 app authorization code") |
| 229 | return |
| 230 | } |
| 231 | err = db.InTx(func(tx database.Store) error { |
| 232 | // Delete any previous codes. |
| 233 | err = tx.DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx, database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams{ |
| 234 | AppID: app.ID, |
| 235 | UserID: apiKey.UserID, |
| 236 | }) |
| 237 | if err != nil && !errors.Is(err, sql.ErrNoRows) { |
| 238 | return xerrors.Errorf("delete oauth2 app codes: %w", err) |
| 239 | } |
| 240 | |
| 241 | // Insert the new code. |
| 242 | _, err = tx.InsertOAuth2ProviderAppCode(ctx, database.InsertOAuth2ProviderAppCodeParams{ |
| 243 | ID: uuid.New(), |
| 244 | CreatedAt: dbtime.Now(), |
| 245 | // TODO: Configurable expiration? Ten minutes matches GitHub. |
no test coverage detected