(c *echo.Context)
| 20 | } |
| 21 | |
| 22 | func (s *Service) handleRevoke(c *echo.Context) error { |
| 23 | ctx := c.Request().Context() |
| 24 | |
| 25 | var req revokeRequest |
| 26 | if err := c.Bind(&req); err != nil { |
| 27 | return oauth2Error(c, http.StatusBadRequest, "invalid_request", "failed to parse request") |
| 28 | } |
| 29 | |
| 30 | // Authenticate client |
| 31 | clientID, clientSecret := extractRevokeClientCredentials(c, &req) |
| 32 | if clientID == "" { |
| 33 | return oauth2Error(c, http.StatusUnauthorized, "invalid_client", "client authentication required") |
| 34 | } |
| 35 | |
| 36 | client, err := s.store.GetOAuth2Client(ctx, clientID) |
| 37 | if err != nil { |
| 38 | return oauth2Error(c, http.StatusInternalServerError, "server_error", "failed to lookup client") |
| 39 | } |
| 40 | if client == nil { |
| 41 | return oauth2Error(c, http.StatusUnauthorized, "invalid_client", "client not found") |
| 42 | } |
| 43 | |
| 44 | // Verify client credentials based on token_endpoint_auth_method |
| 45 | // Public clients (token_endpoint_auth_method: none) don't have secrets |
| 46 | if client.Config.TokenEndpointAuthMethod != "none" { |
| 47 | if !verifySecret(client.ClientSecretHash, clientSecret) { |
| 48 | return oauth2Error(c, http.StatusUnauthorized, "invalid_client", "invalid client credentials") |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | // Validate token |
| 53 | if req.Token == "" { |
| 54 | return oauth2Error(c, http.StatusBadRequest, "invalid_request", "token is required") |
| 55 | } |
| 56 | |
| 57 | // Try to revoke as refresh token |
| 58 | // RFC 7009 says to return 200 even if token is invalid, but we log errors for debugging |
| 59 | tokenHash := auth.HashToken(req.Token) |
| 60 | if err := s.store.DeleteOAuth2RefreshToken(ctx, client.ClientID, tokenHash); err != nil { |
| 61 | slog.Warn("failed to delete OAuth2 refresh token during revocation", log.BBError(err)) |
| 62 | } |
| 63 | |
| 64 | // Return success (RFC 7009: always return 200) |
| 65 | return c.NoContent(http.StatusOK) |
| 66 | } |
| 67 | |
| 68 | func extractRevokeClientCredentials(c *echo.Context, req *revokeRequest) (clientID, clientSecret string) { |
| 69 | // Try Basic auth first |
nothing calls this directly
no test coverage detected