lib/rodauth/features/oauth_jwt.rb in rodauth-oauth-0.7.4 vs lib/rodauth/features/oauth_jwt.rb in rodauth-oauth-0.8.0
- old
+ new
@@ -13,12 +13,18 @@
auth_value_method :oauth_jwt_subject_type, "public" # public, pairwise
auth_value_method :oauth_jwt_subject_secret, nil # salt for pairwise generation
auth_value_method :oauth_jwt_token_issuer, nil
- auth_value_method :oauth_application_jws_jwk_column, nil
+ auth_value_method :oauth_applications_jws_jwk_column, :jws_jwk
+ auth_value_method :oauth_applications_jwt_public_key_column, :jwt_public_key
+ translatable_method :oauth_applications_jws_jwk_label, "JSON Web Keys"
+ translatable_method :oauth_applications_jwt_public_key_label, "Public key"
+ auth_value_method :oauth_application_jws_jwk_param, :jws_jwk
+ auth_value_method :oauth_application_jwt_public_key_param, :jwt_public_key
+
auth_value_method :oauth_jwt_key, nil
auth_value_method :oauth_jwt_public_key, nil
auth_value_method :oauth_jwt_algorithm, "HS256"
auth_value_method :oauth_jwt_jwe_key, nil
@@ -111,13 +117,11 @@
request_object = param_or_nil("request")
return super unless request_object && oauth_application
- jws_jwk = if oauth_application[oauth_application_jws_jwk_column]
- jwk = oauth_application[oauth_application_jws_jwk_column]
-
+ jws_jwk = if (jwk = oauth_application[oauth_applications_jws_jwk_column])
jwk = JSON.parse(jwk, symbolize_names: true) if jwk && jwk.is_a?(String)
else
redirect_response_error("invalid_request_object")
end
@@ -143,55 +147,10 @@
super
end
# /token
- def require_oauth_application
- # requset authentication optional for assertions
- return super unless param("grant_type") == "urn:ietf:params:oauth:grant-type:jwt-bearer"
-
- claims = jwt_decode(param("assertion"))
-
- redirect_response_error("invalid_grant") unless claims
-
- @oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
-
- authorization_required unless @oauth_application
- end
-
- def validate_oauth_token_params
- if param("grant_type") == "urn:ietf:params:oauth:grant-type:jwt-bearer"
- redirect_response_error("invalid_client") unless param_or_nil("assertion")
- else
- super
- end
- end
-
- def create_oauth_token
- if param("grant_type") == "urn:ietf:params:oauth:grant-type:jwt-bearer"
- create_oauth_token_from_assertion
- else
- super
- end
- end
-
- def create_oauth_token_from_assertion
- claims = jwt_decode(param("assertion"))
-
- account = account_ds(claims["sub"]).first
-
- redirect_response_error("invalid_client") unless oauth_application && account
-
- create_params = {
- oauth_tokens_account_id_column => claims["sub"],
- oauth_tokens_oauth_application_id_column => oauth_application[oauth_applications_id_column],
- oauth_tokens_scopes_column => claims["scope"]
- }
-
- generate_oauth_token(create_params, false)
- end
-
def generate_oauth_token(params = {}, should_generate_refresh_token = true)
create_params = {
oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_token_expires_in)
}.merge(params)
@@ -293,11 +252,21 @@
token_endpoint_auth_signing_alg_values_supported: [oauth_jwt_algorithm]
metadata
end
def _jwt_key
- @_jwt_key ||= oauth_jwt_key || (oauth_application[oauth_applications_client_secret_column] if oauth_application)
+ @_jwt_key ||= oauth_jwt_key || begin
+ if oauth_application
+
+ if (jwk = oauth_application[oauth_applications_jws_jwk_column])
+ jwk = JSON.parse(jwk, symbolize_names: true) if jwk && jwk.is_a?(String)
+ jwk
+ else
+ oauth_application[oauth_applications_jwt_public_key_column]
+ end
+ end
+ end
end
# Resource Server only!
#
# returns the jwks set from the authorization server.
@@ -344,12 +313,12 @@
def verify_jti(jti, claims)
generate_jti(claims) == jti
end
- def verify_aud(aud, claims)
- aud == (oauth_jwt_audience || claims["client_id"])
+ def verify_aud(expected_aud, aud)
+ expected_aud == aud
end
if defined?(JSON::JWT)
def jwk_import(data)
@@ -377,10 +346,12 @@
def jwt_decode(
token,
jws_key: oauth_jwt_public_key || _jwt_key,
verify_claims: true,
verify_jti: true,
+ verify_iss: true,
+ verify_aud: false,
**
)
token = JSON::JWT.decode(token, oauth_jwt_jwe_key).plain_text if oauth_jwt_jwe_key
claims = if is_authorization_server?
@@ -391,15 +362,19 @@
end
elsif (jwks = auth_server_jwks_set)
JSON::JWT.decode(token, JSON::JWK::Set.new(jwks))
end
- if verify_claims && !(claims[:iss] == issuer &&
- verify_aud(claims[:aud], claims) &&
- (!claims[:iat] || Time.at(claims[:iat]) > (Time.now - oauth_token_expires_in)) &&
- (!claims[:exp] || Time.at(claims[:exp]) > Time.now) &&
- (!verify_jti || verify_jti(claims[:jti], claims)))
+ now = Time.now
+ if verify_claims && (
+ (!claims[:exp] || Time.at(claims[:exp]) < now) &&
+ (claims[:nbf] && Time.at(claims[:nbf]) < now) &&
+ (claims[:iat] && Time.at(claims[:iat]) < now) &&
+ (verify_iss && claims[:iss] != issuer) &&
+ (verify_aud && !verify_aud(claims[:aud], claims[:client_id])) &&
+ (verify_jti && !verify_jti(claims[:jti], claims))
+ )
return
end
claims
rescue JSON::JWT::Exception
@@ -454,11 +429,13 @@
def jwt_decode(
token,
jws_key: oauth_jwt_public_key || _jwt_key,
jws_algorithm: oauth_jwt_algorithm,
verify_claims: true,
- verify_jti: true
+ verify_jti: true,
+ verify_iss: true,
+ verify_aud: false
)
# decrypt jwe
token = JWE.decrypt(token, oauth_jwt_jwe_key) if oauth_jwt_jwe_key
# verifying the JWT implies verifying:
@@ -470,11 +447,11 @@
# subject can't be verified automatically without having access to the account id,
# which we don't because that's the whole point.
#
verify_claims_params = if verify_claims
{
- verify_iss: true,
+ verify_iss: verify_iss,
iss: issuer,
# can't use stock aud verification, as it's dependent on the client application id
verify_aud: false,
verify_jti: (verify_jti ? method(:verify_jti) : false),
verify_iat: true
@@ -494,10 +471,10 @@
elsif (jwks = auth_server_jwks_set)
algorithms = jwks[:keys].select { |k| k[:use] == "sig" }.map { |k| k[:alg] }
JWT.decode(token, nil, true, jwks: jwks, algorithms: algorithms, **verify_claims_params).first
end
- return if verify_claims && !verify_aud(claims["aud"], claims)
+ return if verify_claims && verify_aud && !verify_aud(claims["aud"], claims["client_id"])
claims
rescue JWT::DecodeError, JWT::JWKError
nil
end