(ctx context.Context, db database.Store, token string, appID uuid.UUID)
| 161 | } |
| 162 | |
| 163 | func revokeAPIKeyInTx(ctx context.Context, db database.Store, token string, appID uuid.UUID) error { |
| 164 | keyID, secret, err := httpmw.SplitAPIToken(token) |
| 165 | if err != nil { |
| 166 | return ErrInvalidTokenFormat |
| 167 | } |
| 168 | |
| 169 | // Get the API key |
| 170 | //nolint:gocritic // Using AsSystemOAuth2 for OAuth2 public token revocation endpoint |
| 171 | apiKey, err := db.GetAPIKeyByID(dbauthz.AsSystemOAuth2(ctx), keyID) |
| 172 | if err != nil { |
| 173 | if errors.Is(err, sql.ErrNoRows) { |
| 174 | // API key not found - return success per RFC 7009 (don't reveal token existence) |
| 175 | return nil |
| 176 | } |
| 177 | return xerrors.Errorf("get api key by id: %w", err) |
| 178 | } |
| 179 | |
| 180 | // Checking to see if the provided secret matches the stored hashed secret |
| 181 | hashedSecret := sha256.Sum256([]byte(secret)) |
| 182 | if subtle.ConstantTimeCompare(apiKey.HashedSecret, hashedSecret[:]) != 1 { |
| 183 | return xerrors.Errorf("invalid api key") |
| 184 | } |
| 185 | |
| 186 | // Verify the API key was created by OAuth2 |
| 187 | if apiKey.LoginType != database.LoginTypeOAuth2ProviderApp { |
| 188 | return xerrors.New("api key is not an oauth2 token") |
| 189 | } |
| 190 | |
| 191 | // Find the associated OAuth2 token to verify ownership |
| 192 | //nolint:gocritic // Using AsSystemOAuth2 for OAuth2 public token revocation endpoint |
| 193 | dbToken, err := db.GetOAuth2ProviderAppTokenByAPIKeyID(dbauthz.AsSystemOAuth2(ctx), apiKey.ID) |
| 194 | if err != nil { |
| 195 | if errors.Is(err, sql.ErrNoRows) { |
| 196 | // No associated OAuth2 token - return success per RFC 7009 |
| 197 | return nil |
| 198 | } |
| 199 | return xerrors.Errorf("get oauth2 provider app token by api key id: %w", err) |
| 200 | } |
| 201 | |
| 202 | // Verify the token belongs to the requesting app |
| 203 | //nolint:gocritic // Using AsSystemOAuth2 for OAuth2 public token revocation endpoint |
| 204 | appSecret, err := db.GetOAuth2ProviderAppSecretByID(dbauthz.AsSystemOAuth2(ctx), dbToken.AppSecretID) |
| 205 | if err != nil { |
| 206 | return xerrors.Errorf("get oauth2 provider app secret for api key verification: %w", err) |
| 207 | } |
| 208 | |
| 209 | if appSecret.AppID != appID { |
| 210 | return ErrTokenNotBelongsToClient |
| 211 | } |
| 212 | |
| 213 | // Delete the API key |
| 214 | //nolint:gocritic // Using AsSystemOAuth2 for OAuth2 public token revocation endpoint |
| 215 | err = db.DeleteAPIKeyByID(dbauthz.AsSystemOAuth2(ctx), apiKey.ID) |
| 216 | if err != nil && !errors.Is(err, sql.ErrNoRows) { |
| 217 | return xerrors.Errorf("delete api key for revocation: %w", err) |
| 218 | } |
| 219 | |
| 220 | return nil |
no test coverage detected