Class: Doorkeeper::OAuth::ClientAuthentication::PrivateKeyJwt
- Inherits:
-
Object
- Object
- Doorkeeper::OAuth::ClientAuthentication::PrivateKeyJwt
- Defined in:
- lib/doorkeeper/oauth/client_authentication/private_key_jwt.rb,
lib/doorkeeper/oauth/client_authentication/private_key_jwt/key_resolver.rb,
lib/doorkeeper/oauth/client_authentication/private_key_jwt/replay_guard.rb
Overview
"private_key_jwt" client authentication (RFC 7523 / OIDC Core §9): the client authenticates with a JWT assertion signed by its private key; the server verifies it against the client's published public keys (a jwks attribute on the application model, or keys fetched from its jwks_uri). No shared secret is involved.
The "jwt" gem is required only when an assertion is actually authenticated, so servers that don't enable this method don't need the dependency. Its constants are always referenced as ::JWT: doorkeeper-jwt defines Doorkeeper::JWT, which would otherwise shadow the gem everywhere inside this class.
Defined Under Namespace
Modules: KeyResolver Classes: ReplayGuard
Constant Summary collapse
- CLIENT_ASSERTION_TYPE =
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"- ALLOWED_ALGORITHMS =
Asymmetric signature algorithms only: HMAC family (and "none") are shared-secret/unauthenticated and must never verify an assertion.
%w[RS256 RS384 RS512 PS256 PS384 PS512 ES256 ES384 ES512].freeze
- REQUIRED_CLAIMS =
iss/sub identify the client, aud prevents cross-server replay, exp bounds the assertion lifetime and jti makes it single-use (OIDC Core §9 requires all of these for private_key_jwt).
%w[iss sub aud exp jti].freeze
- MAX_LIFETIME =
Upper bound on how far in the future an assertion may expire. This both rejects sloppily long-lived assertions and bounds the replay guard's memory.
3600
Class Method Summary collapse
- .authenticate(request) ⇒ Object
- .matches_request?(request) ⇒ Boolean
-
.uses_shared_secret? ⇒ Boolean
Assertions are verified against the client's published public keys; no shared secret is involved.
Class Method Details
.authenticate(request) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/doorkeeper/oauth/client_authentication/private_key_jwt.rb', line 53 def self.authenticate(request) require_jwt! params = request.request_parameters.with_indifferent_access assertion = params[:client_assertion].to_s client_id = unverified_client_id(assertion) return if client_id.blank? # RFC 7521 §4.2: a client_id parameter sent alongside the assertion # must agree with the assertion's issuer. return if params[:client_id].present? && params[:client_id] != client_id application = OAuth::Client.find(client_id)&.application return unless application jwk_set = KeyResolver.jwk_set_for(application) return unless jwk_set claims = verified_claims(assertion, client_id, jwk_set, request) return unless claims return unless replay_guard.first_use?( "#{client_id}:#{claims["jti"]}", expires_at: claims["exp"].to_i, ) Doorkeeper::ClientAuthentication::VerifiedCredentials.new(client_id) end |
.matches_request?(request) ⇒ Boolean
45 46 47 48 49 50 51 |
# File 'lib/doorkeeper/oauth/client_authentication/private_key_jwt.rb', line 45 def self.matches_request?(request) params = request.request_parameters.with_indifferent_access request.post? && params[:client_assertion].present? && params[:client_assertion_type] == CLIENT_ASSERTION_TYPE end |
.uses_shared_secret? ⇒ Boolean
Assertions are verified against the client's published public keys; no shared secret is involved.
27 28 29 |
# File 'lib/doorkeeper/oauth/client_authentication/private_key_jwt.rb', line 27 def self.uses_shared_secret? false end |