rodauth-oauth 1.0.0.pre.beta1 → 1.0.0.pre.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -5
- data/doc/release_notes/1_0_0_beta2.md +34 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +19 -7
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +54 -43
- data/lib/rodauth/features/oauth_application_management.rb +2 -2
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +31 -6
- data/lib/rodauth/features/oauth_authorize_base.rb +16 -6
- data/lib/rodauth/features/oauth_base.rb +35 -16
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +7 -4
- data/lib/rodauth/features/oauth_implicit_grant.rb +6 -5
- data/lib/rodauth/features/oauth_jwt.rb +3 -3
- data/lib/rodauth/features/oauth_jwt_base.rb +29 -6
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +7 -4
- data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +64 -10
- data/lib/rodauth/features/oauth_resource_indicators.rb +0 -4
- data/lib/rodauth/features/oauth_resource_server.rb +3 -3
- data/lib/rodauth/features/oauth_saml_bearer_grant.rb +2 -0
- data/lib/rodauth/features/oidc.rb +231 -183
- data/lib/rodauth/features/oidc_dynamic_client_registration.rb +65 -25
- data/lib/rodauth/features/oidc_rp_initiated_logout.rb +115 -0
- data/lib/rodauth/oauth/http_extensions.rb +15 -2
- data/lib/rodauth/oauth/ttl_store.rb +2 -0
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +3 -1
- data/locales/pt.yml +3 -1
- data/templates/authorize.str +17 -10
- metadata +5 -2
| @@ -6,6 +6,8 @@ module Rodauth | |
| 6 6 | 
             
              Feature.define(:oauth_jwt_bearer_grant, :OauthJwtBearerGrant) do
         | 
| 7 7 | 
             
                depends :oauth_assertion_base, :oauth_jwt
         | 
| 8 8 |  | 
| 9 | 
            +
                auth_value_method :max_param_bytesize, nil if Rodauth::VERSION >= "2.26.0"
         | 
| 10 | 
            +
             | 
| 9 11 | 
             
                auth_value_methods(
         | 
| 10 12 | 
             
                  :require_oauth_application_from_jwt_bearer_assertion_issuer,
         | 
| 11 13 | 
             
                  :require_oauth_application_from_jwt_bearer_assertion_subject,
         | 
| @@ -54,7 +56,7 @@ module Rodauth | |
| 54 56 |  | 
| 55 57 | 
             
                def require_oauth_application_from_client_secret_jwt(client_id, assertion, alg)
         | 
| 56 58 | 
             
                  oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first
         | 
| 57 | 
            -
                  authorization_required unless supports_auth_method?(oauth_application, "client_secret_jwt")
         | 
| 59 | 
            +
                  authorization_required unless oauth_application && supports_auth_method?(oauth_application, "client_secret_jwt")
         | 
| 58 60 | 
             
                  client_secret = oauth_application[oauth_applications_client_secret_column]
         | 
| 59 61 | 
             
                  claims = jwt_assertion(assertion, jws_key: client_secret, jws_algorithm: alg)
         | 
| 60 62 | 
             
                  authorization_required unless claims && claims["iss"] == client_id
         | 
| @@ -63,7 +65,7 @@ module Rodauth | |
| 63 65 |  | 
| 64 66 | 
             
                def require_oauth_application_from_private_key_jwt(client_id, assertion)
         | 
| 65 67 | 
             
                  oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first
         | 
| 66 | 
            -
                  authorization_required unless supports_auth_method?(oauth_application, "private_key_jwt")
         | 
| 68 | 
            +
                  authorization_required unless oauth_application && supports_auth_method?(oauth_application, "private_key_jwt")
         | 
| 67 69 | 
             
                  jwks = oauth_application_jwks(oauth_application)
         | 
| 68 70 | 
             
                  claims = jwt_assertion(assertion, jwks: jwks)
         | 
| 69 71 | 
             
                  authorization_required unless claims
         | 
| @@ -79,8 +81,9 @@ module Rodauth | |
| 79 81 | 
             
                end
         | 
| 80 82 |  | 
| 81 83 | 
             
                def jwt_assertion(assertion, **kwargs)
         | 
| 82 | 
            -
                  claims = jwt_decode(assertion, verify_iss: false, verify_aud: false, **kwargs)
         | 
| 83 | 
            -
             | 
| 84 | 
            +
                  claims = jwt_decode(assertion, verify_iss: false, verify_aud: false, verify_jti: false, **kwargs)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  return unless claims && verify_aud(request.url, claims["aud"])
         | 
| 84 87 |  | 
| 85 88 | 
             
                  claims
         | 
| 86 89 | 
             
                end
         | 
| @@ -4,31 +4,46 @@ require "rodauth/oauth" | |
| 4 4 |  | 
| 5 5 | 
             
            module Rodauth
         | 
| 6 6 | 
             
              Feature.define(:oauth_jwt_secured_authorization_request, :OauthJwtSecuredAuthorizationRequest) do
         | 
| 7 | 
            +
                ALLOWED_REQUEST_URI_CONTENT_TYPES = %w[application/jose application/oauth-authz-req+jwt].freeze
         | 
| 8 | 
            +
             | 
| 7 9 | 
             
                depends :oauth_authorize_base, :oauth_jwt_base
         | 
| 8 10 |  | 
| 11 | 
            +
                auth_value_method :oauth_require_request_uri_registration, false
         | 
| 12 | 
            +
                auth_value_method :oauth_request_object_signing_alg_allow_none, false
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                auth_value_method :oauth_applications_request_uris_column, :request_uris
         | 
| 15 | 
            +
             | 
| 9 16 | 
             
                auth_value_method :oauth_applications_request_object_signing_alg_column, :request_object_signing_alg
         | 
| 10 17 | 
             
                auth_value_method :oauth_applications_request_object_encryption_alg_column, :request_object_encryption_alg
         | 
| 11 18 | 
             
                auth_value_method :oauth_applications_request_object_encryption_enc_column, :request_object_encryption_enc
         | 
| 12 19 |  | 
| 13 | 
            -
                translatable_method :oauth_request_uri_not_supported_message, "request uri is unsupported"
         | 
| 14 20 | 
             
                translatable_method :oauth_invalid_request_object_message, "request object is invalid"
         | 
| 15 21 |  | 
| 22 | 
            +
                auth_value_method :max_param_bytesize, nil if Rodauth::VERSION >= "2.26.0"
         | 
| 23 | 
            +
             | 
| 16 24 | 
             
                private
         | 
| 17 25 |  | 
| 18 26 | 
             
                # /authorize
         | 
| 19 27 |  | 
| 20 28 | 
             
                def validate_authorize_params
         | 
| 21 | 
            -
                  # TODO: add support for requst_uri
         | 
| 22 | 
            -
                  redirect_response_error("request_uri_not_supported") if param_or_nil("request_uri")
         | 
| 23 | 
            -
             | 
| 24 29 | 
             
                  request_object = param_or_nil("request")
         | 
| 25 30 |  | 
| 26 | 
            -
                   | 
| 31 | 
            +
                  request_uri = param_or_nil("request_uri")
         | 
| 27 32 |  | 
| 28 | 
            -
                   | 
| 29 | 
            -
             | 
| 30 | 
            -
                   | 
| 31 | 
            -
                     | 
| 33 | 
            +
                  return super unless (request_object || request_uri) && oauth_application
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  if request_uri
         | 
| 36 | 
            +
                    request_uri = CGI.unescape(request_uri)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    redirect_response_error("invalid_request_uri") unless supported_request_uri?(request_uri, oauth_application)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    response = http_request(request_uri)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    unless response.code.to_i == 200 && ALLOWED_REQUEST_URI_CONTENT_TYPES.include?(response["content-type"])
         | 
| 43 | 
            +
                      redirect_response_error("invalid_request_uri")
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    request_object = response.body
         | 
| 32 47 | 
             
                  end
         | 
| 33 48 |  | 
| 34 49 | 
             
                  request_sig_enc_opts = {
         | 
| @@ -37,10 +52,33 @@ module Rodauth | |
| 37 52 | 
             
                    jws_encryption_method: oauth_application[oauth_applications_request_object_encryption_enc_column]
         | 
| 38 53 | 
             
                  }.compact
         | 
| 39 54 |  | 
| 40 | 
            -
                   | 
| 55 | 
            +
                  request_sig_enc_opts[:jws_algorithm] ||= "none" if oauth_request_object_signing_alg_allow_none
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  if request_sig_enc_opts[:jws_algorithm] == "none"
         | 
| 58 | 
            +
                    jwks = nil
         | 
| 59 | 
            +
                  elsif (jwks = oauth_application_jwks(oauth_application))
         | 
| 60 | 
            +
                    jwks = JSON.parse(jwks, symbolize_names: true) if jwks.is_a?(String)
         | 
| 61 | 
            +
                  else
         | 
| 62 | 
            +
                    redirect_response_error("invalid_request_object")
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  claims = jwt_decode(request_object,
         | 
| 66 | 
            +
                                      jwks: jwks,
         | 
| 67 | 
            +
                                      verify_jti: false,
         | 
| 68 | 
            +
                                      verify_iss: false,
         | 
| 69 | 
            +
                                      verify_aud: false,
         | 
| 70 | 
            +
                                      **request_sig_enc_opts)
         | 
| 41 71 |  | 
| 42 72 | 
             
                  redirect_response_error("invalid_request_object") unless claims
         | 
| 43 73 |  | 
| 74 | 
            +
                  if (iss = claims["iss"]) && (iss != oauth_application[oauth_applications_client_id_column])
         | 
| 75 | 
            +
                    redirect_response_error("invalid_request_object")
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  if (aud = claims["aud"]) && !verify_aud(aud, oauth_jwt_issuer)
         | 
| 79 | 
            +
                    redirect_response_error("invalid_request_object")
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 44 82 | 
             
                  # If signed, the Authorization Request
         | 
| 45 83 | 
             
                  # Object SHOULD contain the Claims "iss" (issuer) and "aud" (audience)
         | 
| 46 84 | 
             
                  # as members, with their semantics being the same as defined in the JWT
         | 
| @@ -58,5 +96,21 @@ module Rodauth | |
| 58 96 |  | 
| 59 97 | 
             
                  super
         | 
| 60 98 | 
             
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def supported_request_uri?(request_uri, oauth_application)
         | 
| 101 | 
            +
                  return false unless check_valid_uri?(request_uri)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  request_uris = oauth_application[oauth_applications_request_uris_column]
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  request_uris.nil? || request_uris.split(oauth_scope_separator).one? { |uri| request_uri.start_with?(uri) }
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                def oauth_server_metadata_body(*)
         | 
| 109 | 
            +
                  super.tap do |data|
         | 
| 110 | 
            +
                    data[:request_parameter_supported] = true
         | 
| 111 | 
            +
                    data[:request_uri_parameter_supported] = true
         | 
| 112 | 
            +
                    data[:require_request_uri_registration] = oauth_require_request_uri_registration
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
                end
         | 
| 61 115 | 
             
              end
         | 
| 62 116 | 
             
            end
         | 
| @@ -70,10 +70,6 @@ module Rodauth | |
| 70 70 | 
             
                  super(oauth_grant, update_params.merge(oauth_grants_resource_column => resource_indicators))
         | 
| 71 71 | 
             
                end
         | 
| 72 72 |  | 
| 73 | 
            -
                def check_valid_no_fragment_uri?(uri)
         | 
| 74 | 
            -
                  check_valid_uri?(uri) && URI.parse(uri).fragment.nil?
         | 
| 75 | 
            -
                end
         | 
| 76 | 
            -
             | 
| 77 73 | 
             
                module IndicatorAuthorizationCodeGrant
         | 
| 78 74 | 
             
                  private
         | 
| 79 75 |  | 
| @@ -16,12 +16,12 @@ module Rodauth | |
| 16 16 | 
             
                  return @authorization_token if defined?(@authorization_token)
         | 
| 17 17 |  | 
| 18 18 | 
             
                  # check if there is a token
         | 
| 19 | 
            -
                   | 
| 19 | 
            +
                  access_token = fetch_access_token
         | 
| 20 20 |  | 
| 21 | 
            -
                  return unless  | 
| 21 | 
            +
                  return unless access_token
         | 
| 22 22 |  | 
| 23 23 | 
             
                  # where in resource server, NOT the authorization server.
         | 
| 24 | 
            -
                  payload = introspection_request("access_token",  | 
| 24 | 
            +
                  payload = introspection_request("access_token", access_token)
         | 
| 25 25 |  | 
| 26 26 | 
             
                  return unless payload["active"]
         | 
| 27 27 |  | 
| @@ -17,6 +17,8 @@ module Rodauth | |
| 17 17 | 
             
                auth_value_method :oauth_saml_security_digest_method, XMLSecurity::Document::SHA1
         | 
| 18 18 | 
             
                auth_value_method :oauth_saml_security_signature_method, XMLSecurity::Document::RSA_SHA1
         | 
| 19 19 |  | 
| 20 | 
            +
                auth_value_method :max_param_bytesize, nil if Rodauth::VERSION >= "2.26.0"
         | 
| 21 | 
            +
             | 
| 20 22 | 
             
                auth_value_methods(
         | 
| 21 23 | 
             
                  :require_oauth_application_from_saml2_bearer_assertion_issuer,
         | 
| 22 24 | 
             
                  :require_oauth_application_from_saml2_bearer_assertion_subject,
         |