rodauth-oauth 0.10.3 → 1.0.0.pre.beta1
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/MIGRATION-GUIDE-v1.md +286 -0
- data/README.md +22 -30
- data/doc/release_notes/0_10_3.md +1 -1
- data/doc/release_notes/0_10_4.md +11 -0
- data/doc/release_notes/1_0_0_beta1.md +38 -0
- data/lib/generators/rodauth/oauth/install_generator.rb +0 -1
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +4 -6
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +1 -1
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +2 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +1 -6
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +0 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_grants.html.erb +41 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +2 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_grants.html.erb +37 -0
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +18 -29
- data/lib/rodauth/features/oauth_application_management.rb +59 -72
- data/lib/rodauth/features/oauth_assertion_base.rb +19 -23
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +35 -88
- data/lib/rodauth/features/oauth_authorize_base.rb +103 -20
- data/lib/rodauth/features/oauth_base.rb +365 -302
- data/lib/rodauth/features/oauth_client_credentials_grant.rb +20 -18
- data/lib/rodauth/features/{oauth_device_grant.rb → oauth_device_code_grant.rb} +62 -73
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +46 -28
- data/lib/rodauth/features/oauth_grant_management.rb +70 -0
- data/lib/rodauth/features/oauth_implicit_grant.rb +25 -24
- data/lib/rodauth/features/oauth_jwt.rb +52 -678
- data/lib/rodauth/features/oauth_jwt_base.rb +435 -0
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +45 -17
- data/lib/rodauth/features/oauth_jwt_jwks.rb +47 -0
- data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +62 -0
- data/lib/rodauth/features/oauth_management_base.rb +2 -0
- data/lib/rodauth/features/oauth_pkce.rb +22 -26
- data/lib/rodauth/features/oauth_resource_indicators.rb +39 -22
- data/lib/rodauth/features/oauth_resource_server.rb +38 -0
- data/lib/rodauth/features/oauth_saml_bearer_grant.rb +5 -1
- data/lib/rodauth/features/oauth_token_introspection.rb +76 -45
- data/lib/rodauth/features/oauth_token_revocation.rb +46 -31
- data/lib/rodauth/features/oidc.rb +188 -95
- data/lib/rodauth/features/oidc_dynamic_client_registration.rb +89 -53
- data/lib/rodauth/oauth/database_extensions.rb +8 -6
- data/lib/rodauth/oauth/http_extensions.rb +61 -0
- data/lib/rodauth/oauth/railtie.rb +20 -0
- data/lib/rodauth/oauth/version.rb +1 -1
- data/lib/rodauth/oauth.rb +29 -1
- data/locales/en.yml +32 -22
- data/locales/pt.yml +32 -22
- data/templates/authorize.str +19 -24
- data/templates/device_search.str +1 -1
- data/templates/device_verification.str +2 -2
- data/templates/jwks_field.str +1 -0
- data/templates/new_oauth_application.str +1 -2
- data/templates/oauth_application.str +2 -2
- data/templates/oauth_application_oauth_grants.str +54 -0
- data/templates/oauth_applications.str +2 -2
- data/templates/oauth_grants.str +52 -0
- metadata +21 -17
- data/lib/generators/rodauth/oauth/templates/app/models/oauth_token.rb +0 -4
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +0 -39
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +0 -35
- data/lib/rodauth/features/oauth.rb +0 -9
- data/lib/rodauth/features/oauth_authorization_server.rb +0 -0
- data/lib/rodauth/features/oauth_http_mac.rb +0 -86
- data/lib/rodauth/features/oauth_token_management.rb +0 -81
- data/lib/rodauth/oauth/refinements.rb +0 -48
- data/templates/jwt_public_key_field.str +0 -4
- data/templates/oauth_application_oauth_tokens.str +0 -52
- data/templates/oauth_tokens.str +0 -50
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            <% oauth_grants = rodauth.scope.instance_variable_get(:@oauth_grants) %>
         | 
| 2 | 
            +
            <% grants_count = oauth_grants.count %>
         | 
| 3 | 
            +
            <% if grants_count.zero? %>
         | 
| 4 | 
            +
              <p><%= rodauth.oauth_no_grants_text %></p>
         | 
| 5 | 
            +
            <% else %>
         | 
| 6 | 
            +
              <table class="table">
         | 
| 7 | 
            +
                <thead>
         | 
| 8 | 
            +
                  <tr>
         | 
| 9 | 
            +
                    <th scope="col"><=% rodauth.oauth_applications_name_label %></th>
         | 
| 10 | 
            +
                    <th scope="col"><=% rodauth.oauth_grants_type_label %></th>
         | 
| 11 | 
            +
                    <th scope="col"><=% rodauth.oauth_grants_token_label %></th>
         | 
| 12 | 
            +
                    <th scope="col"><=% rodauth.oauth_grants_refresh_token_label %></th>
         | 
| 13 | 
            +
                    <th scope="col"><=% rodauth.oauth_grants_expires_in_label %></th>
         | 
| 14 | 
            +
                    <th scope="col"><=% rodauth.oauth_grants_scopes_label %></th>
         | 
| 15 | 
            +
                    <th scope="col"><span class="badge badge-pill badge-dark"><%= grants_count %></span>
         | 
| 16 | 
            +
                  </tr>
         | 
| 17 | 
            +
                </thead>
         | 
| 18 | 
            +
                <tbody>
         | 
| 19 | 
            +
                  <% oauth_grants.each do |oauth_grant| %>
         | 
| 20 | 
            +
                    <tr>
         | 
| 21 | 
            +
                      <td><%= oauth_grant[rodauth.oauth_applications_name_column] %></td>
         | 
| 22 | 
            +
                      <td><%= oauth_grant[rodauth.oauth_grants_type_column] %></td>
         | 
| 23 | 
            +
                      <td><code class="token"><%= oauth_grant[rodauth.oauth_grants_token_column] %></code></td>
         | 
| 24 | 
            +
                      <td><code class="token"><%= oauth_grant[rodauth.oauth_grants_refresh_token_column] %></code></td>
         | 
| 25 | 
            +
                      <td><%= oauth_grant[rodauth.oauth_grants_expires_in_column] %></td>
         | 
| 26 | 
            +
                      <td><%= oauth_grant[rodauth.oauth_grants_scopes_column] %></td>
         | 
| 27 | 
            +
                      <td>
         | 
| 28 | 
            +
                        <%= form_tag rodauth.oauth_grant_path(oauth_grant[rodauth.oauth_grants_id_column]), method: :post do %>
         | 
| 29 | 
            +
                          <%= submit_tag rodauth.oauth_grant_revoke_button, class: "btn btn-danger" %>
         | 
| 30 | 
            +
                        <% end %>
         | 
| 31 | 
            +
                      </td>
         | 
| 32 | 
            +
                    </tr>
         | 
| 33 | 
            +
                  <% end %>
         | 
| 34 | 
            +
                </tbody>
         | 
| 35 | 
            +
              </table>
         | 
| 36 | 
            +
              <%= rodauth.oauth_management_pagination_links(oauth_grants) %>
         | 
| 37 | 
            +
            <% end %>
         | 
| @@ -24,6 +24,19 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %> | |
| 24 24 | 
             
                  # t.string :contacts, null: true
         | 
| 25 25 | 
             
                  # t.string :software_id, null: true
         | 
| 26 26 | 
             
                  # t.string :software_version, null: true
         | 
| 27 | 
            +
                  # oidc extra params
         | 
| 28 | 
            +
                  # t.string :sector_identifier_uri, null: true
         | 
| 29 | 
            +
                  # t.string :application_type, null: true
         | 
| 30 | 
            +
                  # t.string :subject_type, null: true
         | 
| 31 | 
            +
                  # t.string :id_token_signed_response_alg, null: true
         | 
| 32 | 
            +
                  # t.string :id_token_encrypted_response_alg, null: true
         | 
| 33 | 
            +
                  # t.string :id_token_encrypted_response_enc, null: true
         | 
| 34 | 
            +
                  # t.string :userinfo_signed_response_alg, null: true
         | 
| 35 | 
            +
                  # t.string :userinfo_encrypted_response_alg, null: true
         | 
| 36 | 
            +
                  # t.string :userinfo_encrypted_response_enc, null: true
         | 
| 37 | 
            +
                  # t.string :request_object_signing_alg, null: true
         | 
| 38 | 
            +
                  # t.string :request_object_encryption_alg, null: true
         | 
| 39 | 
            +
                  # t.string :request_object_encryption_enc, null: true
         | 
| 27 40 | 
             
                  # JWT/OIDC per application signing verification
         | 
| 28 41 | 
             
                  # t.text :jwt_public_key, null: true
         | 
| 29 42 | 
             
                  # RP-initiated logout
         | 
| @@ -35,8 +48,11 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %> | |
| 35 48 | 
             
                  t.foreign_key :accounts, column: :account_id
         | 
| 36 49 | 
             
                  t.integer :oauth_application_id
         | 
| 37 50 | 
             
                  t.foreign_key :oauth_applications, column: :oauth_application_id
         | 
| 38 | 
            -
                  t.string : | 
| 51 | 
            +
                  t.string :type, null: true
         | 
| 52 | 
            +
                  t.string :code, null: true
         | 
| 39 53 | 
             
                  t.index(%i[oauth_application_id code], unique: true)
         | 
| 54 | 
            +
                  t.string :token, unique: true
         | 
| 55 | 
            +
                  t.string :refresh_token, unique: true
         | 
| 40 56 | 
             
                  t.datetime :expires_in, null: false
         | 
| 41 57 | 
             
                  t.string :redirect_uri
         | 
| 42 58 | 
             
                  t.datetime :revoked_at
         | 
| @@ -47,41 +63,14 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %> | |
| 47 63 | 
             
                  # uncomment to enable PKCE
         | 
| 48 64 | 
             
                  # t.string :code_challenge
         | 
| 49 65 | 
             
                  # t.string :code_challenge_method
         | 
| 50 | 
            -
                  # uncomment to use OIDC nonce
         | 
| 51 | 
            -
                  # t.string :nonce
         | 
| 52 66 | 
             
                  # device code grant
         | 
| 53 67 | 
             
                  # t.string :user_code, null: true, unique: true
         | 
| 54 68 | 
             
                  # t.datetime :last_polled_at, null: true
         | 
| 55 69 | 
             
                  # when using :oauth_resource_indicators feature
         | 
| 56 70 | 
             
                  # t.string :resource
         | 
| 57 | 
            -
                end
         | 
| 58 | 
            -
             | 
| 59 | 
            -
                create_table :oauth_tokens do |t|
         | 
| 60 | 
            -
                  t.integer :account_id
         | 
| 61 | 
            -
                  t.foreign_key :accounts, column: :account_id
         | 
| 62 | 
            -
                  t.integer :oauth_grant_id
         | 
| 63 | 
            -
                  t.foreign_key :oauth_grants, column: :oauth_grant_id
         | 
| 64 | 
            -
                  t.integer :oauth_token_id
         | 
| 65 | 
            -
                  t.foreign_key :oauth_tokens, column: :oauth_token_id
         | 
| 66 | 
            -
                  t.integer :oauth_application_id
         | 
| 67 | 
            -
                  t.foreign_key :oauth_applications, column: :oauth_application_id
         | 
| 68 | 
            -
                  t.string :token, null: false, token: true, unique: true
         | 
| 69 | 
            -
                  # uncomment if setting oauth_tokens_token_hash_column
         | 
| 70 | 
            -
                  # and delete the token column
         | 
| 71 | 
            -
                  # t.string :token_hash, token: true, unique: true
         | 
| 72 | 
            -
                  t.string :refresh_token, unique: true
         | 
| 73 | 
            -
                  # uncomment if setting oauth_tokens_refresh_token_hash_column
         | 
| 74 | 
            -
                  # and delete the refresh_token column
         | 
| 75 | 
            -
                  # t.string :refresh_token_hash, token: true, unique: true
         | 
| 76 | 
            -
                  t.datetime :expires_in, null: false
         | 
| 77 | 
            -
                  t.datetime :revoked_at
         | 
| 78 | 
            -
                  t.string :scopes, null: false
         | 
| 79 | 
            -
                  t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
         | 
| 80 71 | 
             
                  # uncomment to use OIDC nonce
         | 
| 81 72 | 
             
                  # t.string :nonce
         | 
| 82 | 
            -
                  # t. | 
| 83 | 
            -
                  # when using :oauth_resource_indicators feature
         | 
| 84 | 
            -
                  # t.string :resource
         | 
| 73 | 
            +
                  # t.string :acr
         | 
| 85 74 | 
             
                end
         | 
| 86 75 | 
             
              end
         | 
| 87 76 | 
             
            end
         | 
| @@ -1,8 +1,10 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "rodauth/oauth"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Rodauth
         | 
| 4 6 | 
             
              Feature.define(:oauth_application_management, :OauthApplicationManagement) do
         | 
| 5 | 
            -
                depends :oauth_management_base
         | 
| 7 | 
            +
                depends :oauth_management_base, :oauth_token_revocation
         | 
| 6 8 |  | 
| 7 9 | 
             
                before "create_oauth_application"
         | 
| 8 10 | 
             
                after "create_oauth_application"
         | 
| @@ -13,7 +15,7 @@ module Rodauth | |
| 13 15 | 
             
                view "oauth_applications", "Oauth Applications", "oauth_applications"
         | 
| 14 16 | 
             
                view "oauth_application", "Oauth Application", "oauth_application"
         | 
| 15 17 | 
             
                view "new_oauth_application", "New Oauth Application", "new_oauth_application"
         | 
| 16 | 
            -
                view " | 
| 18 | 
            +
                view "oauth_application_oauth_grants", "Oauth Application Grants", "oauth_application_oauth_grants"
         | 
| 17 19 |  | 
| 18 20 | 
             
                # Application
         | 
| 19 21 | 
             
                APPLICATION_REQUIRED_PARAMS = %w[name scopes homepage_url redirect_uri client_secret].freeze
         | 
| @@ -21,12 +23,6 @@ module Rodauth | |
| 21 23 |  | 
| 22 24 | 
             
                (APPLICATION_REQUIRED_PARAMS + %w[description client_id]).each do |param|
         | 
| 23 25 | 
             
                  auth_value_method :"oauth_application_#{param}_param", param
         | 
| 24 | 
            -
                  configuration_module_eval do
         | 
| 25 | 
            -
                    define_method :"#{param}_label" do
         | 
| 26 | 
            -
                      warn "#{__method__} is deprecated, switch to oauth_applications_#{__method__}_label"
         | 
| 27 | 
            -
                      __send__(:"oauth_applications_#{param}_label")
         | 
| 28 | 
            -
                    end
         | 
| 29 | 
            -
                  end
         | 
| 30 26 | 
             
                end
         | 
| 31 27 |  | 
| 32 28 | 
             
                translatable_method :oauth_applications_name_label, "Name"
         | 
| @@ -41,36 +37,42 @@ module Rodauth | |
| 41 37 | 
             
                translatable_method :oauth_applications_redirect_uri_label, "Redirect URI"
         | 
| 42 38 | 
             
                translatable_method :oauth_applications_client_secret_label, "Client Secret"
         | 
| 43 39 | 
             
                translatable_method :oauth_applications_client_id_label, "Client ID"
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                %w[type token refresh_token expires_in revoked_at].each do |param|
         | 
| 42 | 
            +
                  translatable_method :"oauth_grants_#{param}_label", param.gsub("_", " ").capitalize
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 44 45 | 
             
                button "Register", "oauth_application"
         | 
| 45 | 
            -
                button "Revoke", " | 
| 46 | 
            +
                button "Revoke", "oauth_grant_revoke"
         | 
| 46 47 |  | 
| 47 | 
            -
                auth_value_method : | 
| 48 | 
            +
                auth_value_method :oauth_applications_oauth_grants_path, "oauth-grants"
         | 
| 48 49 | 
             
                auth_value_method :oauth_applications_route, "oauth-applications"
         | 
| 49 50 | 
             
                auth_value_method :oauth_applications_per_page, 20
         | 
| 50 51 | 
             
                auth_value_method :oauth_applications_id_pattern, Integer
         | 
| 51 | 
            -
                auth_value_method : | 
| 52 | 
            +
                auth_value_method :oauth_grants_per_page, 20
         | 
| 52 53 |  | 
| 53 54 | 
             
                translatable_method :invalid_url_message, "Invalid URL"
         | 
| 54 55 | 
             
                translatable_method :null_error_message, "is not filled"
         | 
| 55 56 |  | 
| 56 | 
            -
                 | 
| 57 | 
            -
             | 
| 58 | 
            -
                end
         | 
| 57 | 
            +
                translatable_method :oauth_no_applications_text, "No oauth applications yet!"
         | 
| 58 | 
            +
                translatable_method :oauth_no_grants_text, "No oauth grants yet!"
         | 
| 59 59 |  | 
| 60 | 
            -
                def oauth_applications_url(opts = {})
         | 
| 61 | 
            -
                  route_url(oauth_applications_route, opts)
         | 
| 62 | 
            -
                end
         | 
| 63 60 | 
             
                auth_value_methods(
         | 
| 64 61 | 
             
                  :oauth_application_path
         | 
| 65 62 | 
             
                )
         | 
| 66 63 |  | 
| 64 | 
            +
                def oauth_applications_path(opts = {})
         | 
| 65 | 
            +
                  route_path(oauth_applications_route, opts)
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 67 68 | 
             
                def oauth_application_path(id)
         | 
| 68 69 | 
             
                  "#{oauth_applications_path}/#{id}"
         | 
| 69 70 | 
             
                end
         | 
| 70 71 |  | 
| 71 72 | 
             
                # /oauth-applications routes
         | 
| 72 | 
            -
                def  | 
| 73 | 
            +
                def load_oauth_application_management_routes
         | 
| 73 74 | 
             
                  request.on(oauth_applications_route) do
         | 
| 75 | 
            +
                    check_csrf if check_csrf?
         | 
| 74 76 | 
             
                    require_account
         | 
| 75 77 |  | 
| 76 78 | 
             
                    request.get "new" do
         | 
| @@ -92,57 +94,52 @@ module Rodauth | |
| 92 94 | 
             
                        end
         | 
| 93 95 | 
             
                      end
         | 
| 94 96 |  | 
| 95 | 
            -
                      request.on( | 
| 97 | 
            +
                      request.on(oauth_applications_oauth_grants_path) do
         | 
| 96 98 | 
             
                        page = Integer(param_or_nil("page") || 1)
         | 
| 97 | 
            -
                        per_page = per_page_param( | 
| 98 | 
            -
                         | 
| 99 | 
            -
                                       .where( | 
| 100 | 
            -
                                       .order(Sequel.desc( | 
| 101 | 
            -
                        scope.instance_variable_set(:@ | 
| 102 | 
            -
                        request. | 
| 103 | 
            -
                           | 
| 99 | 
            +
                        per_page = per_page_param(oauth_grants_per_page)
         | 
| 100 | 
            +
                        oauth_grants = db[oauth_grants_table]
         | 
| 101 | 
            +
                                       .where(oauth_grants_oauth_application_id_column => id)
         | 
| 102 | 
            +
                                       .order(Sequel.desc(oauth_grants_id_column))
         | 
| 103 | 
            +
                        scope.instance_variable_set(:@oauth_grants, oauth_grants.paginate(page, per_page))
         | 
| 104 | 
            +
                        request.is do
         | 
| 105 | 
            +
                          request.get do
         | 
| 106 | 
            +
                            oauth_application_oauth_grants_view
         | 
| 107 | 
            +
                          end
         | 
| 104 108 | 
             
                        end
         | 
| 105 109 | 
             
                      end
         | 
| 106 110 | 
             
                    end
         | 
| 107 111 |  | 
| 108 | 
            -
                    request. | 
| 109 | 
            -
                       | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
                        . | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
                      oauth_applications_view
         | 
| 117 | 
            -
                    end
         | 
| 112 | 
            +
                    request.is do
         | 
| 113 | 
            +
                      request.get do
         | 
| 114 | 
            +
                        page = Integer(param_or_nil("page") || 1)
         | 
| 115 | 
            +
                        per_page = per_page_param(oauth_applications_per_page)
         | 
| 116 | 
            +
                        scope.instance_variable_set(:@oauth_applications, db[oauth_applications_table]
         | 
| 117 | 
            +
                          .where(oauth_applications_account_id_column => account_id)
         | 
| 118 | 
            +
                          .order(Sequel.desc(oauth_applications_id_column))
         | 
| 119 | 
            +
                          .paginate(page, per_page))
         | 
| 118 120 |  | 
| 119 | 
            -
             | 
| 120 | 
            -
                       | 
| 121 | 
            -
                        validate_oauth_application_params
         | 
| 121 | 
            +
                        oauth_applications_view
         | 
| 122 | 
            +
                      end
         | 
| 122 123 |  | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
                           | 
| 126 | 
            -
             | 
| 127 | 
            -
                           | 
| 128 | 
            -
             | 
| 124 | 
            +
                      request.post do
         | 
| 125 | 
            +
                        catch_error do
         | 
| 126 | 
            +
                          validate_oauth_application_params
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                          transaction do
         | 
| 129 | 
            +
                            before_create_oauth_application
         | 
| 130 | 
            +
                            id = create_oauth_application
         | 
| 131 | 
            +
                            after_create_oauth_application
         | 
| 132 | 
            +
                            set_notice_flash create_oauth_application_notice_flash
         | 
| 133 | 
            +
                            redirect "#{request.path}/#{id}"
         | 
| 134 | 
            +
                          end
         | 
| 129 135 | 
             
                        end
         | 
| 136 | 
            +
                        set_error_flash create_oauth_application_error_flash
         | 
| 137 | 
            +
                        new_oauth_application_view
         | 
| 130 138 | 
             
                      end
         | 
| 131 | 
            -
                      set_error_flash create_oauth_application_error_flash
         | 
| 132 | 
            -
                      new_oauth_application_view
         | 
| 133 139 | 
             
                    end
         | 
| 134 140 | 
             
                  end
         | 
| 135 141 | 
             
                end
         | 
| 136 142 |  | 
| 137 | 
            -
                def check_csrf?
         | 
| 138 | 
            -
                  case request.path
         | 
| 139 | 
            -
                  when oauth_applications_path
         | 
| 140 | 
            -
                    only_json? ? false : super
         | 
| 141 | 
            -
                  else
         | 
| 142 | 
            -
                    super
         | 
| 143 | 
            -
                  end
         | 
| 144 | 
            -
                end
         | 
| 145 | 
            -
             | 
| 146 143 | 
             
                private
         | 
| 147 144 |  | 
| 148 145 | 
             
                def oauth_application_params
         | 
| @@ -176,7 +173,7 @@ module Rodauth | |
| 176 173 | 
             
                    elsif key == oauth_application_scopes_param
         | 
| 177 174 |  | 
| 178 175 | 
             
                      value.each do |scope|
         | 
| 179 | 
            -
                        set_field_error(key,  | 
| 176 | 
            +
                        set_field_error(key, oauth_invalid_scope_message) unless oauth_application_scopes.include?(scope)
         | 
| 180 177 | 
             
                      end
         | 
| 181 178 | 
             
                    end
         | 
| 182 179 | 
             
                  end
         | 
| @@ -196,28 +193,18 @@ module Rodauth | |
| 196 193 | 
             
                  redirect_uris = oauth_application_params[oauth_application_redirect_uri_param]
         | 
| 197 194 | 
             
                  redirect_uris = redirect_uris.to_a.reject(&:empty?).join(" ") if redirect_uris.respond_to?(:each)
         | 
| 198 195 | 
             
                  create_params[oauth_applications_redirect_uri_column] = redirect_uris unless redirect_uris.empty?
         | 
| 199 | 
            -
                  # set client ID/secret pairs
         | 
| 200 196 |  | 
| 201 | 
            -
                   | 
| 202 | 
            -
             | 
| 203 | 
            -
                      secret_hash(oauth_application_params[oauth_application_client_secret_param])
         | 
| 197 | 
            +
                  # set client ID/secret pairs
         | 
| 198 | 
            +
                  set_client_secret(create_params, oauth_application_params[oauth_application_client_secret_param])
         | 
| 204 199 |  | 
| 205 | 
            -
                   | 
| 206 | 
            -
             | 
| 207 | 
            -
             | 
| 208 | 
            -
                                                                      oauth_application_default_scope
         | 
| 209 | 
            -
                                                                    end
         | 
| 200 | 
            +
                  if create_params[oauth_applications_scopes_column]
         | 
| 201 | 
            +
                    create_params[oauth_applications_scopes_column] = create_params[oauth_applications_scopes_column].join(oauth_scope_separator)
         | 
| 202 | 
            +
                  end
         | 
| 210 203 |  | 
| 211 204 | 
             
                  rescue_from_uniqueness_error do
         | 
| 212 205 | 
             
                    create_params[oauth_applications_client_id_column] = oauth_unique_id_generator
         | 
| 213 206 | 
             
                    db[oauth_applications_table].insert(create_params)
         | 
| 214 207 | 
             
                  end
         | 
| 215 208 | 
             
                end
         | 
| 216 | 
            -
             | 
| 217 | 
            -
                def oauth_server_metadata_body(*)
         | 
| 218 | 
            -
                  super.tap do |data|
         | 
| 219 | 
            -
                    data[:registration_endpoint] = oauth_applications_url
         | 
| 220 | 
            -
                  end
         | 
| 221 | 
            -
                end
         | 
| 222 209 | 
             
              end
         | 
| 223 210 | 
             
            end
         | 
| @@ -1,11 +1,9 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "rodauth/oauth | 
| 3 | 
            +
            require "rodauth/oauth"
         | 
| 4 4 |  | 
| 5 5 | 
             
            module Rodauth
         | 
| 6 6 | 
             
              Feature.define(:oauth_assertion_base, :OauthAssertionBase) do
         | 
| 7 | 
            -
                using PrefixExtensions
         | 
| 8 | 
            -
             | 
| 9 7 | 
             
                depends :oauth_base
         | 
| 10 8 |  | 
| 11 9 | 
             
                auth_value_methods(
         | 
| @@ -17,7 +15,7 @@ module Rodauth | |
| 17 15 |  | 
| 18 16 | 
             
                private
         | 
| 19 17 |  | 
| 20 | 
            -
                def  | 
| 18 | 
            +
                def validate_token_params
         | 
| 21 19 | 
             
                  return super unless assertion_grant_type?
         | 
| 22 20 |  | 
| 23 21 | 
             
                  redirect_response_error("invalid_grant") unless param_or_nil("assertion")
         | 
| @@ -29,19 +27,16 @@ module Rodauth | |
| 29 27 | 
             
                  elsif client_assertion_type?
         | 
| 30 28 | 
             
                    @oauth_application = __send__(:"require_oauth_application_from_#{client_assertion_type}_assertion_subject",
         | 
| 31 29 | 
             
                                                  param("client_assertion"))
         | 
| 32 | 
            -
                  else
         | 
| 33 | 
            -
                    return super
         | 
| 34 | 
            -
                  end
         | 
| 35 30 |  | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
                     | 
| 43 | 
            -
             | 
| 44 | 
            -
                     | 
| 31 | 
            +
                    if (client_id = param_or_nil("client_id")) &&
         | 
| 32 | 
            +
                       client_id != @oauth_application[oauth_applications_client_id_column]
         | 
| 33 | 
            +
                      # If present, the value of the
         | 
| 34 | 
            +
                      # "client_id" parameter MUST identify the same client as is
         | 
| 35 | 
            +
                      # identified by the client assertion.
         | 
| 36 | 
            +
                      redirect_response_error("invalid_grant")
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  else
         | 
| 39 | 
            +
                    super
         | 
| 45 40 | 
             
                  end
         | 
| 46 41 | 
             
                end
         | 
| 47 42 |  | 
| @@ -54,7 +49,7 @@ module Rodauth | |
| 54 49 | 
             
                  )
         | 
| 55 50 | 
             
                end
         | 
| 56 51 |  | 
| 57 | 
            -
                def  | 
| 52 | 
            +
                def create_token(grant_type)
         | 
| 58 53 | 
             
                  return super unless assertion_grant_type?(grant_type) && supported_grant_type?(grant_type)
         | 
| 59 54 |  | 
| 60 55 | 
             
                  account = __send__(:"account_from_#{assertion_grant_type}_assertion", param("assertion"))
         | 
| @@ -62,19 +57,20 @@ module Rodauth | |
| 62 57 | 
             
                  redirect_response_error("invalid_grant") unless account
         | 
| 63 58 |  | 
| 64 59 | 
             
                  grant_scopes = if param_or_nil("scope")
         | 
| 65 | 
            -
                                   redirect_response_error(" | 
| 60 | 
            +
                                   redirect_response_error("invalid_scope") unless check_valid_scopes?
         | 
| 66 61 | 
             
                                   scopes
         | 
| 67 62 | 
             
                                 else
         | 
| 68 63 | 
             
                                   @oauth_application[oauth_applications_scopes_column]
         | 
| 69 64 | 
             
                                 end
         | 
| 70 65 |  | 
| 71 | 
            -
                   | 
| 72 | 
            -
                     | 
| 73 | 
            -
                     | 
| 74 | 
            -
                     | 
| 66 | 
            +
                  grant_params = {
         | 
| 67 | 
            +
                    oauth_grants_type_column => grant_type,
         | 
| 68 | 
            +
                    oauth_grants_account_id_column => account[account_id_column],
         | 
| 69 | 
            +
                    oauth_grants_oauth_application_id_column => @oauth_application[oauth_applications_id_column],
         | 
| 70 | 
            +
                    oauth_grants_scopes_column => grant_scopes
         | 
| 75 71 | 
             
                  }
         | 
| 76 72 |  | 
| 77 | 
            -
                   | 
| 73 | 
            +
                  generate_token(grant_params, false)
         | 
| 78 74 | 
             
                end
         | 
| 79 75 |  | 
| 80 76 | 
             
                def assertion_grant_type?(grant_type = param("grant_type"))
         | 
| @@ -1,67 +1,53 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "rodauth/oauth"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Rodauth
         | 
| 4 6 | 
             
              Feature.define(:oauth_authorization_code_grant, :OauthAuthorizationCodeGrant) do
         | 
| 5 7 | 
             
                depends :oauth_authorize_base
         | 
| 6 8 |  | 
| 7 | 
            -
                auth_value_method : | 
| 9 | 
            +
                auth_value_method :oauth_response_mode, "form_post"
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def oauth_grant_types_supported
         | 
| 12 | 
            +
                  super | %w[authorization_code]
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def oauth_response_types_supported
         | 
| 16 | 
            +
                  super | %w[code]
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def oauth_response_modes_supported
         | 
| 20 | 
            +
                  super | %w[query form_post]
         | 
| 21 | 
            +
                end
         | 
| 8 22 |  | 
| 9 23 | 
             
                private
         | 
| 10 24 |  | 
| 11 25 | 
             
                def validate_authorize_params
         | 
| 12 26 | 
             
                  super
         | 
| 13 27 |  | 
| 14 | 
            -
                   | 
| 28 | 
            +
                  return unless (response_mode = param_or_nil("response_mode")) && !oauth_response_modes_supported.include?(response_mode)
         | 
| 15 29 |  | 
| 16 | 
            -
                  redirect_response_error("invalid_request") | 
| 17 | 
            -
             | 
| 18 | 
            -
                  try_approval_prompt if use_oauth_access_type? && request.get?
         | 
| 30 | 
            +
                  redirect_response_error("invalid_request")
         | 
| 19 31 | 
             
                end
         | 
| 20 32 |  | 
| 21 | 
            -
                def  | 
| 33 | 
            +
                def validate_token_params
         | 
| 22 34 | 
             
                  redirect_response_error("invalid_request") if param_or_nil("grant_type") == "authorization_code" && !param_or_nil("code")
         | 
| 23 35 | 
             
                  super
         | 
| 24 36 | 
             
                end
         | 
| 25 37 |  | 
| 26 | 
            -
                def  | 
| 27 | 
            -
                   | 
| 28 | 
            -
             | 
| 29 | 
            -
                  return unless approval_prompt && approval_prompt == "auto"
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                  return if db[oauth_grants_table].where(
         | 
| 32 | 
            -
                    oauth_grants_account_id_column => account_id,
         | 
| 33 | 
            -
                    oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
         | 
| 34 | 
            -
                    oauth_grants_redirect_uri_column => redirect_uri,
         | 
| 35 | 
            -
                    oauth_grants_scopes_column => scopes.join(oauth_scope_separator),
         | 
| 36 | 
            -
                    oauth_grants_access_type_column => "online"
         | 
| 37 | 
            -
                  ).count.zero?
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                  # if there's a previous oauth grant for the params combo, it means that this user has approved before.
         | 
| 40 | 
            -
                  request.env["REQUEST_METHOD"] = "POST"
         | 
| 41 | 
            -
                end
         | 
| 38 | 
            +
                def do_authorize(response_params = {}, response_mode = param_or_nil("response_mode"))
         | 
| 39 | 
            +
                  response_mode ||= oauth_response_mode
         | 
| 42 40 |  | 
| 43 | 
            -
             | 
| 44 | 
            -
                  # Access Type flow
         | 
| 45 | 
            -
                  if use_oauth_access_type? && (access_type = param_or_nil("access_type"))
         | 
| 46 | 
            -
                    create_params[oauth_grants_access_type_column] = access_type
         | 
| 47 | 
            -
                  end
         | 
| 41 | 
            +
                  redirect_response_error("invalid_request") unless response_mode.nil? || supported_response_mode?(response_mode)
         | 
| 48 42 |  | 
| 49 | 
            -
                   | 
| 50 | 
            -
                end
         | 
| 43 | 
            +
                  response_type = param_or_nil("response_type")
         | 
| 51 44 |  | 
| 52 | 
            -
             | 
| 53 | 
            -
                  case param("response_type")
         | 
| 45 | 
            +
                  redirect_response_error("invalid_request") unless response_type.nil? || supported_response_type?(response_type)
         | 
| 54 46 |  | 
| 55 | 
            -
                   | 
| 56 | 
            -
             | 
| 57 | 
            -
                    response_params.replace(_do_authorize_code)
         | 
| 58 | 
            -
                  when "none"
         | 
| 59 | 
            -
                    response_mode ||= "none"
         | 
| 60 | 
            -
                  when "", nil
         | 
| 47 | 
            +
                  case response_type
         | 
| 48 | 
            +
                  when "code", nil
         | 
| 61 49 | 
             
                    response_mode ||= oauth_response_mode
         | 
| 62 50 | 
             
                    response_params.replace(_do_authorize_code)
         | 
| 63 | 
            -
                  else
         | 
| 64 | 
            -
                    return super if response_params.empty?
         | 
| 65 51 | 
             
                  end
         | 
| 66 52 |  | 
| 67 53 | 
             
                  response_params["state"] = param("state") if param_or_nil("state")
         | 
| @@ -70,11 +56,11 @@ module Rodauth | |
| 70 56 | 
             
                end
         | 
| 71 57 |  | 
| 72 58 | 
             
                def _do_authorize_code
         | 
| 73 | 
            -
                  create_params = { | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 59 | 
            +
                  create_params = {
         | 
| 60 | 
            +
                    oauth_grants_type_column => "authorization_code",
         | 
| 61 | 
            +
                    oauth_grants_account_id_column => account_id
         | 
| 62 | 
            +
                  }
         | 
| 63 | 
            +
             | 
| 78 64 | 
             
                  { "code" => create_oauth_grant(create_params) }
         | 
| 79 65 | 
             
                end
         | 
| 80 66 |  | 
| @@ -102,53 +88,20 @@ module Rodauth | |
| 102 88 | 
             
                        </body>
         | 
| 103 89 | 
             
                      </html>
         | 
| 104 90 | 
             
                    FORM
         | 
| 105 | 
            -
                  when "none"
         | 
| 106 | 
            -
                    redirect(redirect_url.to_s)
         | 
| 107 | 
            -
                  else
         | 
| 108 | 
            -
                    super
         | 
| 109 91 | 
             
                  end
         | 
| 110 92 | 
             
                end
         | 
| 111 93 |  | 
| 112 | 
            -
                def  | 
| 94 | 
            +
                def create_token(grant_type)
         | 
| 113 95 | 
             
                  return super unless supported_grant_type?(grant_type, "authorization_code")
         | 
| 114 96 |  | 
| 115 | 
            -
                   | 
| 116 | 
            -
             | 
| 97 | 
            +
                  grant_params = {
         | 
| 98 | 
            +
                    oauth_grants_type_column => grant_type,
         | 
| 117 99 | 
             
                    oauth_grants_code_column => param("code"),
         | 
| 118 100 | 
             
                    oauth_grants_redirect_uri_column => param("redirect_uri"),
         | 
| 119 | 
            -
                    oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column] | 
| 120 | 
            -
                    oauth_grants_revoked_at_column => nil
         | 
| 121 | 
            -
                  ).where(Sequel[oauth_grants_expires_in_column] >= Sequel::CURRENT_TIMESTAMP)
         | 
| 122 | 
            -
                                                      .for_update
         | 
| 123 | 
            -
                                                      .first
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                  redirect_response_error("invalid_grant") unless oauth_grant
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                  create_params = {
         | 
| 128 | 
            -
                    oauth_tokens_account_id_column => oauth_grant[oauth_grants_account_id_column],
         | 
| 129 | 
            -
                    oauth_tokens_oauth_application_id_column => oauth_grant[oauth_grants_oauth_application_id_column],
         | 
| 130 | 
            -
                    oauth_tokens_oauth_grant_id_column => oauth_grant[oauth_grants_id_column],
         | 
| 131 | 
            -
                    oauth_tokens_scopes_column => oauth_grant[oauth_grants_scopes_column]
         | 
| 101 | 
            +
                    oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column]
         | 
| 132 102 | 
             
                  }
         | 
| 133 | 
            -
                  create_oauth_token_from_authorization_code(oauth_grant, create_params, !use_oauth_access_type?)
         | 
| 134 | 
            -
                end
         | 
| 135 | 
            -
             | 
| 136 | 
            -
                ACCESS_TYPES = %w[offline online].freeze
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                def check_valid_access_type?
         | 
| 139 | 
            -
                  return true unless use_oauth_access_type?
         | 
| 140 103 |  | 
| 141 | 
            -
                   | 
| 142 | 
            -
                  !access_type || ACCESS_TYPES.include?(access_type)
         | 
| 143 | 
            -
                end
         | 
| 144 | 
            -
             | 
| 145 | 
            -
                APPROVAL_PROMPTS = %w[force auto].freeze
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                def check_valid_approval_prompt?
         | 
| 148 | 
            -
                  return true unless use_oauth_access_type?
         | 
| 149 | 
            -
             | 
| 150 | 
            -
                  approval_prompt = param_or_nil("approval_prompt")
         | 
| 151 | 
            -
                  !approval_prompt || APPROVAL_PROMPTS.include?(approval_prompt)
         | 
| 104 | 
            +
                  create_token_from_authorization_code(grant_params)
         | 
| 152 105 | 
             
                end
         | 
| 153 106 |  | 
| 154 107 | 
             
                def check_valid_response_type?
         | 
| @@ -160,12 +113,6 @@ module Rodauth | |
| 160 113 | 
             
                def oauth_server_metadata_body(*)
         | 
| 161 114 | 
             
                  super.tap do |data|
         | 
| 162 115 | 
             
                    data[:authorization_endpoint] = authorize_url
         | 
| 163 | 
            -
                    data[:response_types_supported] << "code"
         | 
| 164 | 
            -
             | 
| 165 | 
            -
                    data[:response_modes_supported] << "query"
         | 
| 166 | 
            -
                    data[:response_modes_supported] << "form_post"
         | 
| 167 | 
            -
             | 
| 168 | 
            -
                    data[:grant_types_supported] << "authorization_code"
         | 
| 169 116 | 
             
                  end
         | 
| 170 117 | 
             
                end
         | 
| 171 118 | 
             
              end
         |