| name | security-review |
| description | Verify that code changes do not introduce OAuth security vulnerabilities. Use when modifying token handling, client authentication, scope validation, redirect URI checks, secret comparison, or grant flows in Doorkeeper. |
Security Review
When implementing or reviewing changes to Doorkeeper, use this skill to verify that changes do not introduce security vulnerabilities. Doorkeeper is an OAuth 2 provider library — security is the primary constraint.
Constant-Time Comparisons
All comparisons of secrets MUST use ActiveSupport::SecurityUtils.secure_compare or equivalent, never ==:
- Token values
- Client secrets
- PKCE code challenge verification
- Refresh tokens
- Any cryptographic digest comparison
Check: grep for == near variables named token, secret, code_challenge, refresh_token. If found, replace with secure_compare.
Reference: The codebase already uses secure_compare in lib/doorkeeper/secret_storing/base.rb. Follow that pattern.
Token Leakage Prevention
Tokens and secrets must never appear in:
- Log output (Rails parameter filtering should exclude them)
- Error response bodies (beyond what RFC 6749 requires)
- URL query parameters for sensitive values (except where RFC requires, e.g., authorization codes)
- Exception messages that might be captured by error reporters
Check: Doorkeeper registers Doorkeeper.setup_filter_parameters (in lib/doorkeeper.rb) which adds client_secret, authentication_token, access_token, and refresh_token to Rails.application.config.filter_parameters, plus code when the authorization_code grant flow is enabled. Verify any newly-introduced sensitive parameter is covered by that list.
Redirect URI Validation (RFC 6749 §3.1.2, RFC 8252 §7)
- Redirect URIs must be compared using exact string matching
- Exception: loopback redirects (127.0.0.1, [::1]) allow port variation per RFC 8252 §7.3
- Fragment components (
#) are forbidden
- Relative URIs are forbidden
- The
urn:ietf:wg:oauth:2.0:oob redirect is special-cased
Reference: lib/doorkeeper/oauth/helpers/uri_checker.rb and lib/doorkeeper/orm/active_record/redirect_uri_validator.rb
Client Authentication (RFC 6749 §2.3)
- A client MUST NOT use more than one authentication method per request
- The codebase enforces this via
Doorkeeper::Request.validate_client_authentication! — ensure new auth methods participate in this check
- Confidential clients must always authenticate; public clients use
none
by_uid_and_secret must be the resolution path — don't bypass it
Reference: lib/doorkeeper/client_authentication/ and lib/doorkeeper/request.rb
Grant Code Single-Use (RFC 6749 §4.1.2)
- Authorization codes MUST be single-use
- If a code is presented twice, all tokens issued from that code must be revoked
- Race conditions are handled via database locking (
lock!)
Reference: lib/doorkeeper/oauth/authorization_code_request.rb — InvalidGrantReuse handling
Scope Validation
- Requested scopes must be a subset of configured server scopes
- Per-application scopes further restrict what a client can request
scopes_by_grant_type can restrict scopes per flow
- Dynamic scopes (if enabled) must still match the configured delimiter pattern
Reference: lib/doorkeeper/oauth/helpers/scope_checker.rb
PKCE (RFC 7636)
- When
force_pkce is true, code_challenge is mandatory
- S256 is the recommended (and often required) challenge method
- Code verifier comparison must use the correct transform (SHA256 + base64url for S256, plain for plain)
- Note: the codebase currently uses
== for PKCE comparison — this is a known issue
Input Validation
- All user-supplied parameters must be validated before use
grant_type, response_type, response_mode must match registered values
- Token endpoint enforces
application/x-www-form-urlencoded content type (when enforce_content_type is enabled)
- Multiple values for
scope in the same request must be rejected at the authorization endpoint
Revocation (RFC 7009)
- Token revocation should return 200 even for invalid tokens (to avoid information leakage)
- Revoking a refresh token should also revoke associated access tokens
- Client authentication is required for revocation (default behavior)
Patterns to Avoid
token == stored_token
ActiveSupport::SecurityUtils.secure_compare(token, stored_token)
raise "Invalid token: #{token}"
raise Doorkeeper::Errors::InvalidToken
uri.start_with?(registered_uri)
Doorkeeper::OAuth::Helpers::URIChecker.valid_for_authorization?(uri, registered_uris)
Doorkeeper::Request.client_authentication_method(request)
After Making Changes
- Run security-related specs:
bundle exec rspec spec/requests/flows/ spec/lib/oauth/
- Check if Brakeman would flag the change:
gem install brakeman && brakeman --no-pager
- Verify no token values appear in test output or logs