ruby_oidc_client 0.0.1
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 +7 -0
 - data/.rspec +3 -0
 - data/.rubocop.yml +13 -0
 - data/CHANGELOG.md +23 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +52 -0
 - data/Rakefile +12 -0
 - data/examples/README.md +3 -0
 - data/examples/client_secret_basic/Gemfile +6 -0
 - data/examples/client_secret_basic/Gemfile.lock +26 -0
 - data/examples/client_secret_basic/README.md +24 -0
 - data/examples/client_secret_basic/app.rb +45 -0
 - data/examples/client_secret_basic/config.json +7 -0
 - data/examples/client_secret_basic/public/stylesheets/style.css +8 -0
 - data/examples/client_secret_basic/views/index.erb +11 -0
 - data/lib/id_partner.rb +260 -0
 - data/lib/ruby_oidc_client/version.rb +5 -0
 - data/ruby_oidc_client.gemspec +47 -0
 - data/sig/ruby_oidc_client.rbs +40 -0
 - metadata +155 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 5b5f37ec00ec2675db46405e2ae66bbb0575039bdf0de00ac5f2a9f9420360de
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 86093df21b39985900be9a4fcba7c831bdcb64331759c63f2acd13a8f93787a4
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 5eb4f9337b6f5dfde2a008c544fd2a706ab58d81ee23ab2618cc36cd5376ec4840c94c2169ec32ce37a85a5c9efec742749a575b2f75e5a0cc23b5ad9d7d9f88
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 137967b68fddf32d29a0bcd69923297aec3eb5ef8ff89e347db096ce10b49ea116004a5380038023e273eec25df9b04bf55c5c0f145d20cb249bff3b9d713d76
         
     | 
    
        data/.rspec
    ADDED
    
    
    
        data/.rubocop.yml
    ADDED
    
    
    
        data/CHANGELOG.md
    ADDED
    
    | 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            0.1.0
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            - Flexible Configuration:
         
     | 
| 
      
 4 
     | 
    
         
            +
              - Supports various authentication methods including client_secret_basic and private_key_jwt.
         
     | 
| 
      
 5 
     | 
    
         
            +
              - Configurable token endpoint auth method.
         
     | 
| 
      
 6 
     | 
    
         
            +
              - Configurable JWKS for signing and encryption.
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Authorization:
         
     | 
| 
      
 8 
     | 
    
         
            +
              - Generation of authorization URL with support for different scopes, and parameters like scope and claims.
         
     | 
| 
      
 9 
     | 
    
         
            +
              - Support for both standard and pushed authorization requests.
         
     | 
| 
      
 10 
     | 
    
         
            +
              - Generation of proofs including state, nonce, and code verifier for PKCE.
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Token Acquisition:
         
     | 
| 
      
 12 
     | 
    
         
            +
              - Token exchange functionality for obtaining tokens from an authorization code.
         
     | 
| 
      
 13 
     | 
    
         
            +
            - User Information Retrieval:
         
     | 
| 
      
 14 
     | 
    
         
            +
              - Userinfo endpoint interaction to retrieve user information using an access token.
         
     | 
| 
      
 15 
     | 
    
         
            +
            - JWT Handling:
         
     | 
| 
      
 16 
     | 
    
         
            +
              - JWT decoding support including handling of encrypted JWTs.
         
     | 
| 
      
 17 
     | 
    
         
            +
              - JWT signing and encryption support.
         
     | 
| 
      
 18 
     | 
    
         
            +
            - Utilities:
         
     | 
| 
      
 19 
     | 
    
         
            +
              - Fetching provider configuration from the well-known configuration endpoint.
         
     | 
| 
      
 20 
     | 
    
         
            +
            - Error Handling:
         
     | 
| 
      
 21 
     | 
    
         
            +
              - Comprehensive error handling for various steps in the OIDC interaction process.
         
     | 
| 
      
 22 
     | 
    
         
            +
            - Example:
         
     | 
| 
      
 23 
     | 
    
         
            +
              - Sinatra example usage provided in the examples folder demonstrating how to use the IDPartner class for OIDC interactions.
         
     | 
    
        data/LICENSE.txt
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            The MIT License (MIT)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright (c) 2023 IDPartner
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         
     | 
| 
      
 6 
     | 
    
         
            +
            of this software and associated documentation files (the "Software"), to deal
         
     | 
| 
      
 7 
     | 
    
         
            +
            in the Software without restriction, including without limitation the rights
         
     | 
| 
      
 8 
     | 
    
         
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         
     | 
| 
      
 9 
     | 
    
         
            +
            copies of the Software, and to permit persons to whom the Software is
         
     | 
| 
      
 10 
     | 
    
         
            +
            furnished to do so, subject to the following conditions:
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be included in
         
     | 
| 
      
 13 
     | 
    
         
            +
            all copies or substantial portions of the Software.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         
     | 
| 
      
 16 
     | 
    
         
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         
     | 
| 
      
 17 
     | 
    
         
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         
     | 
| 
      
 18 
     | 
    
         
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         
     | 
| 
      
 19 
     | 
    
         
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         
     | 
| 
      
 20 
     | 
    
         
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         
     | 
| 
      
 21 
     | 
    
         
            +
            THE SOFTWARE.
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # RubyOidcClient
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RubyOidcClient is a Ruby gem that simplifies OpenID Connect client operations. This gem provides a set of functionalities to interact with OIDC providers, facilitating authentication and authorization processes.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ## Installation
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            You can install the `ruby_oidc_client` gem by executing the following command:
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 10 
     | 
    
         
            +
            $ gem install ruby_oidc_client
         
     | 
| 
      
 11 
     | 
    
         
            +
            ```
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            Alternatively, if you are using Bundler, add this line to your application's Gemfile:
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 16 
     | 
    
         
            +
            gem 'ruby_oidc_client'
         
     | 
| 
      
 17 
     | 
    
         
            +
            ```
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            And then execute:
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 22 
     | 
    
         
            +
            $ bundle install
         
     | 
| 
      
 23 
     | 
    
         
            +
            ```
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            ## Usage
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            Please check the [examples folder](./examples/)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            ### Running Tests
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            After checking out the repo, install the necessary dependencies and run the tests by executing:
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 34 
     | 
    
         
            +
            $ rspec
         
     | 
| 
      
 35 
     | 
    
         
            +
            ```
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            ### Running RuboCop
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            To check the code for styling issues according to the RuboCop guidelines, run:
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 42 
     | 
    
         
            +
            $ rubocop
         
     | 
| 
      
 43 
     | 
    
         
            +
            ```
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            ## Contributing
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            Bug reports and pull requests are welcome on GitHub at [https://github.com/idpartner-app/ruby_oidc_client](https://github.com/idpartner-app/ruby_oidc_client).
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            ## License
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
         
     | 
| 
      
 52 
     | 
    
         
            +
            ```
         
     | 
    
        data/Rakefile
    ADDED
    
    
    
        data/examples/README.md
    ADDED
    
    | 
         @@ -0,0 +1,3 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # IDPartner examples
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            This contains the an example with the [ruby_oidc_client](https://github.com/idpartner-app/ruby_oidc_client) library, called [client-secret-basic](./client-secret-basic/README.md), which is a sinatra project using the client_secret_basic authentication method to obtain user info
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            GEM
         
     | 
| 
      
 2 
     | 
    
         
            +
              remote: https://rubygems.org/
         
     | 
| 
      
 3 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 4 
     | 
    
         
            +
                mustermann (3.0.0)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  ruby2_keywords (~> 0.0.1)
         
     | 
| 
      
 6 
     | 
    
         
            +
                rack (2.2.8)
         
     | 
| 
      
 7 
     | 
    
         
            +
                rack-protection (3.1.0)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  rack (~> 2.2, >= 2.2.4)
         
     | 
| 
      
 9 
     | 
    
         
            +
                ruby2_keywords (0.0.5)
         
     | 
| 
      
 10 
     | 
    
         
            +
                sinatra (3.1.0)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  mustermann (~> 3.0)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  rack (~> 2.2, >= 2.2.4)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  rack-protection (= 3.1.0)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  tilt (~> 2.0)
         
     | 
| 
      
 15 
     | 
    
         
            +
                tilt (2.3.0)
         
     | 
| 
      
 16 
     | 
    
         
            +
                webrick (1.8.1)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            PLATFORMS
         
     | 
| 
      
 19 
     | 
    
         
            +
              x86_64-darwin-23
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            DEPENDENCIES
         
     | 
| 
      
 22 
     | 
    
         
            +
              sinatra
         
     | 
| 
      
 23 
     | 
    
         
            +
              webrick
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            BUNDLED WITH
         
     | 
| 
      
 26 
     | 
    
         
            +
               2.4.21
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # client_secret_basic flow using ruby_oidc_client lib
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            ## Prerequisites
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            1. [Create an IDPartner Account](https://console.idpartner.com).
         
     | 
| 
      
 6 
     | 
    
         
            +
            1. [Create an Application (Client Secret)](https://docs.idpartner.com/documentation/relying-party-user-guide/registering-your-app#create-an-application).
         
     | 
| 
      
 7 
     | 
    
         
            +
            1. Ensure the following properties are set:
         
     | 
| 
      
 8 
     | 
    
         
            +
               - Origin URL: http://localhost:3001/button/oauth
         
     | 
| 
      
 9 
     | 
    
         
            +
               - Redirect URL: http://localhost:3001/button/oauth/callback
         
     | 
| 
      
 10 
     | 
    
         
            +
            1. Grab the "Client ID" and the "Client Secret" to update the following parts in your code:
         
     | 
| 
      
 11 
     | 
    
         
            +
               1. [Update CHANGE_ME_CLIENT_ID in the configuration file](./config.json)
         
     | 
| 
      
 12 
     | 
    
         
            +
               1. [Update CHANGE_ME_CLIENT_SECRET in the configuration file](./config.json)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
               Aditionally you optionally can configure the next steps:
         
     | 
| 
      
 15 
     | 
    
         
            +
               1. [Update the "redirect_uri" in the configuration file](./config.json)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            ## Running the project
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            1. Run: `bundle`
         
     | 
| 
      
 20 
     | 
    
         
            +
            1. Run: `ruby app.rb`
         
     | 
| 
      
 21 
     | 
    
         
            +
            1. Access http://localhost:3001
         
     | 
| 
      
 22 
     | 
    
         
            +
            1. Click the "Choose your ID Partner" button
         
     | 
| 
      
 23 
     | 
    
         
            +
            1. Search for "Mikomo Bank"
         
     | 
| 
      
 24 
     | 
    
         
            +
            1. Use these test credentials `mikomo_10/mikomo_10`
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "sinatra"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative "../../lib/id_partner"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class App < Sinatra::Base
         
     | 
| 
      
 7 
     | 
    
         
            +
              config = JSON.parse(File.read("config.json"))
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              set :bind, "0.0.0.0"
         
     | 
| 
      
 10 
     | 
    
         
            +
              set :port, config["port"]
         
     | 
| 
      
 11 
     | 
    
         
            +
              set :public_folder, "#{File.dirname(__FILE__)}/public"
         
     | 
| 
      
 12 
     | 
    
         
            +
              enable :sessions
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              id_partner = RubyOidcClient::IDPartner.new({
         
     | 
| 
      
 15 
     | 
    
         
            +
                                                           client_id: config["client_id"],
         
     | 
| 
      
 16 
     | 
    
         
            +
                                                           client_secret: config["client_secret"],
         
     | 
| 
      
 17 
     | 
    
         
            +
                                                           redirect_uri: config["redirect_uri"]
         
     | 
| 
      
 18 
     | 
    
         
            +
                                                         })
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              get "/" do
         
     | 
| 
      
 21 
     | 
    
         
            +
                erb :index, locals: { title: "RP Client Secret Example", config: config }
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              get "/button/oauth" do
         
     | 
| 
      
 25 
     | 
    
         
            +
                scope = config["scope"]
         
     | 
| 
      
 26 
     | 
    
         
            +
                proofs = id_partner.generate_proofs
         
     | 
| 
      
 27 
     | 
    
         
            +
                session[:proofs] = proofs
         
     | 
| 
      
 28 
     | 
    
         
            +
                session[:issuer] = params[:iss]
         
     | 
| 
      
 29 
     | 
    
         
            +
                redirect id_partner.get_authorization_url(params, proofs, scope, { prompt: "consent" })
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              get "/button/oauth/callback" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                token = id_partner.token(params, session[:proofs])
         
     | 
| 
      
 34 
     | 
    
         
            +
                userinfo = id_partner.userinfo(token["access_token"])
         
     | 
| 
      
 35 
     | 
    
         
            +
                content_type :json
         
     | 
| 
      
 36 
     | 
    
         
            +
                userinfo.to_json
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              get "/jwks" do
         
     | 
| 
      
 40 
     | 
    
         
            +
                content_type :json
         
     | 
| 
      
 41 
     | 
    
         
            +
                id_partner.public_jwks.to_json
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              run! if app_file == $PROGRAM_NAME
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!DOCTYPE html>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <html>
         
     | 
| 
      
 3 
     | 
    
         
            +
              <head>
         
     | 
| 
      
 4 
     | 
    
         
            +
                <script src="https://install.idpartner.com/script.js"></script>
         
     | 
| 
      
 5 
     | 
    
         
            +
                <title><%= title %></title>
         
     | 
| 
      
 6 
     | 
    
         
            +
                <link rel='stylesheet' href='/stylesheets/style.css' />
         
     | 
| 
      
 7 
     | 
    
         
            +
              </head>
         
     | 
| 
      
 8 
     | 
    
         
            +
              <body>
         
     | 
| 
      
 9 
     | 
    
         
            +
                <id-partner id="<%= config["client_id"] %>"></id-partner>
         
     | 
| 
      
 10 
     | 
    
         
            +
              </body>
         
     | 
| 
      
 11 
     | 
    
         
            +
            </html>
         
     | 
    
        data/lib/id_partner.rb
    ADDED
    
    | 
         @@ -0,0 +1,260 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative "ruby_oidc_client/version"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "json/jwt"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "net/http"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "uri"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "base64"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module RubyOidcClient
         
     | 
| 
      
 10 
     | 
    
         
            +
              class IDPartner
         
     | 
| 
      
 11 
     | 
    
         
            +
                SUPPORTED_AUTH_METHODS = [
         
     | 
| 
      
 12 
     | 
    
         
            +
                  "client_secret_basic",
         
     | 
| 
      
 13 
     | 
    
         
            +
                  "tls_client_auth",
         
     | 
| 
      
 14 
     | 
    
         
            +
                  "private_key_jwt" # For backward compatibility
         
     | 
| 
      
 15 
     | 
    
         
            +
                ].freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
                SIGNING_ALG = "PS256"
         
     | 
| 
      
 17 
     | 
    
         
            +
                ENCRYPTION_ALG = "RSA-OAEP"
         
     | 
| 
      
 18 
     | 
    
         
            +
                ENCRYPTION_ENC = "A256CBC-HS512"
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                attr_reader :config, :endpoints, :provider_keys
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def initialize(config)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  raise ArgumentError, "Config missing." unless config
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  default_config = {
         
     | 
| 
      
 26 
     | 
    
         
            +
                    account_selector_service_url: "https://auth-api.idpartner.com/oidc-proxy",
         
     | 
| 
      
 27 
     | 
    
         
            +
                    token_endpoint_auth_method: "client_secret_basic",
         
     | 
| 
      
 28 
     | 
    
         
            +
                    jwks: nil,
         
     | 
| 
      
 29 
     | 
    
         
            +
                    client_secret: nil
         
     | 
| 
      
 30 
     | 
    
         
            +
                  }
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  @config = default_config.merge(config)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  unless SUPPORTED_AUTH_METHODS.include?(@config[:token_endpoint_auth_method])
         
     | 
| 
      
 35 
     | 
    
         
            +
                    raise ArgumentError,
         
     | 
| 
      
 36 
     | 
    
         
            +
                          "Unsupported token_endpoint_auth_method '#{config[:token_endpoint_auth_method]}'. It must be one of (#{SUPPORTED_AUTH_METHODS.join(", ")})"
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  client_secret_config = @config[:token_endpoint_auth_method] == "client_secret_basic" ? { client_secret: @config[:client_secret] } : {}
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  jwks_config = if @config[:jwks]
         
     | 
| 
      
 42 
     | 
    
         
            +
                                  {
         
     | 
| 
      
 43 
     | 
    
         
            +
                                    authorization_encrypted_response_alg: ENCRYPTION_ALG,
         
     | 
| 
      
 44 
     | 
    
         
            +
                                    authorization_encrypted_response_enc: ENCRYPTION_ENC,
         
     | 
| 
      
 45 
     | 
    
         
            +
                                    id_token_encrypted_response_alg: ENCRYPTION_ALG,
         
     | 
| 
      
 46 
     | 
    
         
            +
                                    id_token_encrypted_response_enc: ENCRYPTION_ENC,
         
     | 
| 
      
 47 
     | 
    
         
            +
                                    request_object_signing_alg: SIGNING_ALG
         
     | 
| 
      
 48 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 49 
     | 
    
         
            +
                                else
         
     | 
| 
      
 50 
     | 
    
         
            +
                                  {}
         
     | 
| 
      
 51 
     | 
    
         
            +
                                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  @config = @config.merge({
         
     | 
| 
      
 54 
     | 
    
         
            +
                                            authorization_signed_response_alg: SIGNING_ALG,
         
     | 
| 
      
 55 
     | 
    
         
            +
                                            id_token_signed_response_alg: SIGNING_ALG
         
     | 
| 
      
 56 
     | 
    
         
            +
                                          }).merge(client_secret_config).merge(jwks_config)
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def generate_proofs
         
     | 
| 
      
 60 
     | 
    
         
            +
                  {
         
     | 
| 
      
 61 
     | 
    
         
            +
                    state: SecureRandom.urlsafe_base64(64),
         
     | 
| 
      
 62 
     | 
    
         
            +
                    nonce: SecureRandom.urlsafe_base64(64),
         
     | 
| 
      
 63 
     | 
    
         
            +
                    code_verifier: SecureRandom.urlsafe_base64(64)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  }
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                def get_authorization_url(query, proofs, scope, extra_authorization_params = {})
         
     | 
| 
      
 68 
     | 
    
         
            +
                  raise ArgumentError, "The URL query parameter is required." unless query
         
     | 
| 
      
 69 
     | 
    
         
            +
                  raise ArgumentError, "The scope parameter is required." unless scope
         
     | 
| 
      
 70 
     | 
    
         
            +
                  raise ArgumentError, "The proofs parameter is required." unless proofs
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  if query[:iss].nil?
         
     | 
| 
      
 73 
     | 
    
         
            +
                    return "#{config[:account_selector_service_url]}/auth/select-accounts?client_id=#{config[:client_id]}&visitor_id=#{query[:visitor_id]}&scope=#{scope}&claims=#{extract_claims(extra_authorization_params[:claims]).join("+")}"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  @config[:iss] = query[:iss]
         
     | 
| 
      
 77 
     | 
    
         
            +
                  obtain_well_known_config_endpoints
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  extra_authorization_params[:claims] = extra_authorization_params[:claims]&.to_json
         
     | 
| 
      
 80 
     | 
    
         
            +
                  extended_authorization_params = {
         
     | 
| 
      
 81 
     | 
    
         
            +
                    redirect_uri: config[:redirect_uri],
         
     | 
| 
      
 82 
     | 
    
         
            +
                    code_challenge_method: "S256",
         
     | 
| 
      
 83 
     | 
    
         
            +
                    code_challenge: generate_code_challenge(proofs[:code_verifier]),
         
     | 
| 
      
 84 
     | 
    
         
            +
                    state: proofs[:state],
         
     | 
| 
      
 85 
     | 
    
         
            +
                    nonce: proofs[:nonce],
         
     | 
| 
      
 86 
     | 
    
         
            +
                    scope: scope,
         
     | 
| 
      
 87 
     | 
    
         
            +
                    response_type: "code",
         
     | 
| 
      
 88 
     | 
    
         
            +
                    client_id: config[:client_id],
         
     | 
| 
      
 89 
     | 
    
         
            +
                    'x-fapi-interaction-id': SecureRandom.uuid,
         
     | 
| 
      
 90 
     | 
    
         
            +
                    identity_provider_id: query[:idp_id],
         
     | 
| 
      
 91 
     | 
    
         
            +
                    idpartner_token: query[:idpartner_token],
         
     | 
| 
      
 92 
     | 
    
         
            +
                    response_mode: "jwt"
         
     | 
| 
      
 93 
     | 
    
         
            +
                  }.merge(extra_authorization_params).compact
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  pushed_authorization_request_params = extended_authorization_params
         
     | 
| 
      
 96 
     | 
    
         
            +
                  pushed_authorization_request_params = { request: create_request_object(extended_authorization_params) } if config[:jwks]
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  request_uri = push_authorization_request(pushed_authorization_request_params)["request_uri"]
         
     | 
| 
      
 99 
     | 
    
         
            +
                  query_params = URI.encode_www_form(request_uri: request_uri)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  "#{endpoints[:authorization_endpoint]}?#{query_params}"
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                def public_jwks
         
     | 
| 
      
 104 
     | 
    
         
            +
                  return {} unless config[:jwks]
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  jwk_set = JSON::JWK::Set.new(JSON.parse(config[:jwks]))
         
     | 
| 
      
 107 
     | 
    
         
            +
                  public_jwks = jwk_set.collect do |jwk|
         
     | 
| 
      
 108 
     | 
    
         
            +
                    public_jwk = jwk.to_key.public_key.to_jwk
         
     | 
| 
      
 109 
     | 
    
         
            +
                    public_jwk.merge("alg" => jwk["alg"], "use" => jwk["use"]).compact
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                  { "keys" => public_jwks }
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                def token(query, proofs)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  decoded_jwt = decode_jwt(query[:response])
         
     | 
| 
      
 117 
     | 
    
         
            +
                  basic_auth_credentials = Base64.strict_encode64("#{config[:client_id]}:#{config[:client_secret]}")
         
     | 
| 
      
 118 
     | 
    
         
            +
                  payload = {
         
     | 
| 
      
 119 
     | 
    
         
            +
                    code: decoded_jwt["code"],
         
     | 
| 
      
 120 
     | 
    
         
            +
                    code_verifier: proofs[:code_verifier],
         
     | 
| 
      
 121 
     | 
    
         
            +
                    grant_type: "authorization_code",
         
     | 
| 
      
 122 
     | 
    
         
            +
                    redirect_uri: config[:redirect_uri]
         
     | 
| 
      
 123 
     | 
    
         
            +
                  }
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                  uri = URI(endpoints[:token_endpoint])
         
     | 
| 
      
 126 
     | 
    
         
            +
                  http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  headers = {
         
     | 
| 
      
 128 
     | 
    
         
            +
                    "Authorization" => "Basic #{basic_auth_credentials}",
         
     | 
| 
      
 129 
     | 
    
         
            +
                    "Content-Type" => "application/x-www-form-urlencoded",
         
     | 
| 
      
 130 
     | 
    
         
            +
                    "Accept" => "application/json"
         
     | 
| 
      
 131 
     | 
    
         
            +
                  }
         
     | 
| 
      
 132 
     | 
    
         
            +
                  token_request = Net::HTTP::Post.new(uri.request_uri, headers)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  token_request.set_form_data(payload)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  token_response = http.request(token_request)
         
     | 
| 
      
 135 
     | 
    
         
            +
                  raise "Failed to exchange token: #{token_response.body}" unless token_response.is_a?(Net::HTTPSuccess)
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                  JSON.parse(token_response.body)
         
     | 
| 
      
 138 
     | 
    
         
            +
                end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                def userinfo(access_token)
         
     | 
| 
      
 141 
     | 
    
         
            +
                  uri = URI(endpoints[:userinfo_endpoint])
         
     | 
| 
      
 142 
     | 
    
         
            +
                  http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
      
 143 
     | 
    
         
            +
                  headers = {
         
     | 
| 
      
 144 
     | 
    
         
            +
                    "Authorization" => "Bearer #{access_token}",
         
     | 
| 
      
 145 
     | 
    
         
            +
                    "Accept" => "application/json"
         
     | 
| 
      
 146 
     | 
    
         
            +
                  }
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                  userinfo_request = Net::HTTP::Get.new(uri.request_uri, headers)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  userinfo_response = http.request(userinfo_request)
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                  raise "Failed to retrieve userinfo: #{userinfo_response.body}" unless userinfo_response.is_a?(Net::HTTPSuccess)
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                  JSON.parse(userinfo_response.body)
         
     | 
| 
      
 154 
     | 
    
         
            +
                rescue StandardError => e
         
     | 
| 
      
 155 
     | 
    
         
            +
                  raise "Failed to fetch well-known config: #{e.message}"
         
     | 
| 
      
 156 
     | 
    
         
            +
                end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                private
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                def extract_claims(claims_object)
         
     | 
| 
      
 161 
     | 
    
         
            +
                  return [] unless claims_object.is_a?(Hash)
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                  userinfo_keys = claims_object[:userinfo]&.keys || []
         
     | 
| 
      
 164 
     | 
    
         
            +
                  id_token_keys = claims_object[:id_token]&.keys || []
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                  (userinfo_keys + id_token_keys).uniq
         
     | 
| 
      
 167 
     | 
    
         
            +
                end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                def obtain_well_known_config_endpoints
         
     | 
| 
      
 170 
     | 
    
         
            +
                  uri = URI("#{config[:iss]}/.well-known/openid-configuration")
         
     | 
| 
      
 171 
     | 
    
         
            +
                  http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
      
 172 
     | 
    
         
            +
                  headers = { "Accept" => "application/json" }
         
     | 
| 
      
 173 
     | 
    
         
            +
                  request = Net::HTTP::Get.new(uri.request_uri, headers)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  response = http.request(request)
         
     | 
| 
      
 175 
     | 
    
         
            +
                  raise "Failed to retrieve well-known: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
         
     | 
| 
      
 176 
     | 
    
         
            +
                  well_known_config = JSON.parse(response.body)
         
     | 
| 
      
 177 
     | 
    
         
            +
                  @endpoints = {
         
     | 
| 
      
 178 
     | 
    
         
            +
                    authorization_endpoint: well_known_config["authorization_endpoint"],
         
     | 
| 
      
 179 
     | 
    
         
            +
                    token_endpoint: well_known_config["token_endpoint"],
         
     | 
| 
      
 180 
     | 
    
         
            +
                    userinfo_endpoint: well_known_config["userinfo_endpoint"],
         
     | 
| 
      
 181 
     | 
    
         
            +
                    pushed_authorization_request_endpoint: well_known_config["pushed_authorization_request_endpoint"],
         
     | 
| 
      
 182 
     | 
    
         
            +
                    jwks_uri: well_known_config["jwks_uri"]
         
     | 
| 
      
 183 
     | 
    
         
            +
                  }
         
     | 
| 
      
 184 
     | 
    
         
            +
                rescue StandardError => e
         
     | 
| 
      
 185 
     | 
    
         
            +
                  raise "Failed to fetch well-known config: #{e.message}"
         
     | 
| 
      
 186 
     | 
    
         
            +
                end
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                def create_request_object(params)
         
     | 
| 
      
 189 
     | 
    
         
            +
                  extended_params = params.merge({
         
     | 
| 
      
 190 
     | 
    
         
            +
                                                   iss: config[:client_id],
         
     | 
| 
      
 191 
     | 
    
         
            +
                                                   aud: config[:iss],
         
     | 
| 
      
 192 
     | 
    
         
            +
                                                   exp: Time.now.to_i + 60,
         
     | 
| 
      
 193 
     | 
    
         
            +
                                                   iat: Time.now.to_i,
         
     | 
| 
      
 194 
     | 
    
         
            +
                                                   nbf: Time.now.to_i
         
     | 
| 
      
 195 
     | 
    
         
            +
                                                 })
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                  sig_key = sig_key()
         
     | 
| 
      
 198 
     | 
    
         
            +
                  jwt = JSON::JWT.new(extended_params)
         
     | 
| 
      
 199 
     | 
    
         
            +
                  jwt.sign(sig_key, sig_key["alg"]).to_s
         
     | 
| 
      
 200 
     | 
    
         
            +
                end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                def push_authorization_request(request_object)
         
     | 
| 
      
 203 
     | 
    
         
            +
                  uri = URI(endpoints[:pushed_authorization_request_endpoint])
         
     | 
| 
      
 204 
     | 
    
         
            +
                  http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
      
 205 
     | 
    
         
            +
                  basic_auth_credentials = Base64.strict_encode64("#{config[:client_id]}:#{config[:client_secret]}")
         
     | 
| 
      
 206 
     | 
    
         
            +
                  headers = {
         
     | 
| 
      
 207 
     | 
    
         
            +
                    "Authorization" => "Basic #{basic_auth_credentials}",
         
     | 
| 
      
 208 
     | 
    
         
            +
                    "Content-Type" => "application/x-www-form-urlencoded",
         
     | 
| 
      
 209 
     | 
    
         
            +
                    "Accept" => "application/json"
         
     | 
| 
      
 210 
     | 
    
         
            +
                  }
         
     | 
| 
      
 211 
     | 
    
         
            +
                  par_request = Net::HTTP::Post.new(uri.request_uri, headers)
         
     | 
| 
      
 212 
     | 
    
         
            +
                  par_request.set_form_data(request_object)
         
     | 
| 
      
 213 
     | 
    
         
            +
                  par_response = http.request(par_request)
         
     | 
| 
      
 214 
     | 
    
         
            +
                  raise "Failed to push authorization request: #{par_response.body}" unless par_response.is_a?(Net::HTTPSuccess)
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                  JSON.parse(par_response.body)
         
     | 
| 
      
 217 
     | 
    
         
            +
                rescue StandardError => e
         
     | 
| 
      
 218 
     | 
    
         
            +
                  raise "An error occurred: #{e.message}"
         
     | 
| 
      
 219 
     | 
    
         
            +
                end
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
                def generate_code_challenge(code_verifier)
         
     | 
| 
      
 222 
     | 
    
         
            +
                  Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier)).gsub(/=+$/, "")
         
     | 
| 
      
 223 
     | 
    
         
            +
                end
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                def sig_key
         
     | 
| 
      
 226 
     | 
    
         
            +
                  jwk_set = JSON::JWK::Set.new(JSON.parse(config[:jwks]))
         
     | 
| 
      
 227 
     | 
    
         
            +
                  jwk_set.find { |j| j[:use] == "sig" }
         
     | 
| 
      
 228 
     | 
    
         
            +
                end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                def enc_key
         
     | 
| 
      
 231 
     | 
    
         
            +
                  jwk_set = JSON::JWK::Set.new(JSON.parse(config[:jwks]))
         
     | 
| 
      
 232 
     | 
    
         
            +
                  jwk_set.find { |j| j[:use] == "enc" }
         
     | 
| 
      
 233 
     | 
    
         
            +
                end
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
                def decode_jwt(jwt_str)
         
     | 
| 
      
 236 
     | 
    
         
            +
                  if jwt_str.split(".").length == 5
         
     | 
| 
      
 237 
     | 
    
         
            +
                    jwe = JSON::JWT.decode(jwt_str, enc_key)
         
     | 
| 
      
 238 
     | 
    
         
            +
                    jwt_str = jwe.plain_text
         
     | 
| 
      
 239 
     | 
    
         
            +
                  end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                  kid = JSON::JWT.decode(jwt_str, :skip_verification).kid
         
     | 
| 
      
 242 
     | 
    
         
            +
                  sig_key = provider_key(kid)
         
     | 
| 
      
 243 
     | 
    
         
            +
                  JSON::JWT.decode(jwt_str, sig_key)
         
     | 
| 
      
 244 
     | 
    
         
            +
                end
         
     | 
| 
      
 245 
     | 
    
         
            +
             
     | 
| 
      
 246 
     | 
    
         
            +
                def provider_key(kid)
         
     | 
| 
      
 247 
     | 
    
         
            +
                  fetch_provider_keys unless provider_keys
         
     | 
| 
      
 248 
     | 
    
         
            +
                  @provider_keys[kid] || (raise "No provider key found for kid: #{kid}")
         
     | 
| 
      
 249 
     | 
    
         
            +
                end
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
                def fetch_provider_keys
         
     | 
| 
      
 252 
     | 
    
         
            +
                  jwks_uri = endpoints[:jwks_uri]
         
     | 
| 
      
 253 
     | 
    
         
            +
                  fetched_provider_keys = JSON::JWK::Set::Fetcher.fetch(jwks_uri, kid: nil, auto_detect: false)
         
     | 
| 
      
 254 
     | 
    
         
            +
                  @provider_keys = {}
         
     | 
| 
      
 255 
     | 
    
         
            +
                  fetched_provider_keys.each do |key|
         
     | 
| 
      
 256 
     | 
    
         
            +
                    @provider_keys[key["kid"]] = key
         
     | 
| 
      
 257 
     | 
    
         
            +
                  end
         
     | 
| 
      
 258 
     | 
    
         
            +
                end
         
     | 
| 
      
 259 
     | 
    
         
            +
              end
         
     | 
| 
      
 260 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative "lib/ruby_oidc_client/version"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Gem::Specification.new do |spec|
         
     | 
| 
      
 6 
     | 
    
         
            +
              spec.name = "ruby_oidc_client"
         
     | 
| 
      
 7 
     | 
    
         
            +
              spec.version = RubyOidcClient::VERSION
         
     | 
| 
      
 8 
     | 
    
         
            +
              spec.authors = ["Giovanni Alberto"]
         
     | 
| 
      
 9 
     | 
    
         
            +
              spec.email = ["delirable@gmail.com"]
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              spec.summary = "A Ruby client for interacting with OpenID Connect providers using the IDPartner class."
         
     | 
| 
      
 12 
     | 
    
         
            +
              spec.description = "The IDPartner gem provides a Ruby client for engaging with OpenID Connect providers, simplifying the process of authorization, token acquisition, and user information retrieval. It supports various authentication methods and handles endpoint discovery via well-known configuration. The gem encapsulates the complexities of generating, signing, and verifying JWTs, making OpenID Connect integration straightforward and secure."
         
     | 
| 
      
 13 
     | 
    
         
            +
              spec.homepage = "https://github.com/idpartner-app/ruby_oidc_client"
         
     | 
| 
      
 14 
     | 
    
         
            +
              spec.license = "MIT"
         
     | 
| 
      
 15 
     | 
    
         
            +
              spec.required_ruby_version = ">= 2.6.0"
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              spec.metadata["allowed_push_host"] = "https://rubygems.org"
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              spec.metadata["homepage_uri"] = spec.homepage
         
     | 
| 
      
 20 
     | 
    
         
            +
              spec.metadata["source_code_uri"] = "https://github.com/idpartner-app/ruby_oidc_client"
         
     | 
| 
      
 21 
     | 
    
         
            +
              spec.metadata["changelog_uri"] = "https://github.com/idpartner-app/ruby_oidc_client/blob/master/CHANGELOG.md"
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              # Specify which files should be added to the gem when it is released.
         
     | 
| 
      
 24 
     | 
    
         
            +
              # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
         
     | 
| 
      
 25 
     | 
    
         
            +
              spec.files = Dir.chdir(__dir__) do
         
     | 
| 
      
 26 
     | 
    
         
            +
                `git ls-files -z`.split("\x0").reject do |f|
         
     | 
| 
      
 27 
     | 
    
         
            +
                  (File.expand_path(f) == __FILE__) ||
         
     | 
| 
      
 28 
     | 
    
         
            +
                    f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
              spec.bindir = "exe"
         
     | 
| 
      
 32 
     | 
    
         
            +
              spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
         
     | 
| 
      
 33 
     | 
    
         
            +
              spec.require_paths = ["lib"]
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              # Uncomment to register a new dependency of your gem
         
     | 
| 
      
 36 
     | 
    
         
            +
              spec.add_dependency "json-jwt", "~> 1.16.3"
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              # Optionally, within your gemspec file if you prefer to declare dev/test dependencies here
         
     | 
| 
      
 39 
     | 
    
         
            +
              spec.add_development_dependency "rake", "~> 13.0"
         
     | 
| 
      
 40 
     | 
    
         
            +
              spec.add_development_dependency "rspec", "~> 3.0"
         
     | 
| 
      
 41 
     | 
    
         
            +
              spec.add_development_dependency "rubocop", "~> 1.21"
         
     | 
| 
      
 42 
     | 
    
         
            +
              spec.add_development_dependency "vcr", "~> 6.2.0"
         
     | 
| 
      
 43 
     | 
    
         
            +
              spec.add_development_dependency "webmock", "~> 3.19.1"
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              # For more information and examples about making a new gem, check out our
         
     | 
| 
      
 46 
     | 
    
         
            +
              # guide at: https://bundler.io/guides/creating_gem.html
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class IDPartner
         
     | 
| 
      
 2 
     | 
    
         
            +
              type SUPPORTED_AUTH_METHODS: Array[String]
         
     | 
| 
      
 3 
     | 
    
         
            +
              type SIGNING_ALG: String
         
     | 
| 
      
 4 
     | 
    
         
            +
              type ENCRYPTION_ALG: String
         
     | 
| 
      
 5 
     | 
    
         
            +
              type ENCRYPTION_ENC: String
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              attr_reader config: Hash[Symbol, String], endpoints: Hash[Symbol, String]
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def initialize: (Hash[Symbol, String]) -> void
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def generate_proofs: -> Hash[Symbol, String]
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def get_authorization_url: (
         
     | 
| 
      
 14 
     | 
    
         
            +
                query: Hash[Symbol, String],
         
     | 
| 
      
 15 
     | 
    
         
            +
                proofs: Hash[Symbol, String],
         
     | 
| 
      
 16 
     | 
    
         
            +
                scope: Array[String],
         
     | 
| 
      
 17 
     | 
    
         
            +
                prompt: String,
         
     | 
| 
      
 18 
     | 
    
         
            +
                claims_object: Hash[Symbol, String]
         
     | 
| 
      
 19 
     | 
    
         
            +
              ) -> String
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              def get_public_jwks: -> Hash[String, Array[untyped]]
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def token: (query: Hash[Symbol, String], proofs: Hash[Symbol, String]) -> Hash[String, String]
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              def userinfo: (String) -> Hash[String, String]
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              private
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              def get_endpoints_from_well_known_config: -> void
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def create_request_object: (Hash[Symbol, untyped]) -> String
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              def push_authorization_request: (Hash[Symbol, untyped]) -> Hash[String, untyped]
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def generate_code_challenge: (String) -> String
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              def parse_private_key: (String) -> untyped
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              def verify_jws: (String) -> Hash[Symbol, untyped]
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,155 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: ruby_oidc_client
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.0.1
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Giovanni Alberto
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire:
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: exe
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2023-11-16 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: json-jwt
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: 1.16.3
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: 1.16.3
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '13.0'
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '13.0'
         
     | 
| 
      
 41 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 42 
     | 
    
         
            +
              name: rspec
         
     | 
| 
      
 43 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 44 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 45 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 46 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 47 
     | 
    
         
            +
                    version: '3.0'
         
     | 
| 
      
 48 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 49 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 50 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 51 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 52 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 53 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '3.0'
         
     | 
| 
      
 55 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 56 
     | 
    
         
            +
              name: rubocop
         
     | 
| 
      
 57 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 58 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 59 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 60 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 61 
     | 
    
         
            +
                    version: '1.21'
         
     | 
| 
      
 62 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 63 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 64 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 65 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 66 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 67 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 68 
     | 
    
         
            +
                    version: '1.21'
         
     | 
| 
      
 69 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 70 
     | 
    
         
            +
              name: vcr
         
     | 
| 
      
 71 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 72 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 73 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 75 
     | 
    
         
            +
                    version: 6.2.0
         
     | 
| 
      
 76 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 77 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 78 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 79 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 80 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 81 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 82 
     | 
    
         
            +
                    version: 6.2.0
         
     | 
| 
      
 83 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 84 
     | 
    
         
            +
              name: webmock
         
     | 
| 
      
 85 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 86 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 87 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 88 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 89 
     | 
    
         
            +
                    version: 3.19.1
         
     | 
| 
      
 90 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 91 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 92 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 93 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 94 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 95 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 96 
     | 
    
         
            +
                    version: 3.19.1
         
     | 
| 
      
 97 
     | 
    
         
            +
            description: The IDPartner gem provides a Ruby client for engaging with OpenID Connect
         
     | 
| 
      
 98 
     | 
    
         
            +
              providers, simplifying the process of authorization, token acquisition, and user
         
     | 
| 
      
 99 
     | 
    
         
            +
              information retrieval. It supports various authentication methods and handles endpoint
         
     | 
| 
      
 100 
     | 
    
         
            +
              discovery via well-known configuration. The gem encapsulates the complexities of
         
     | 
| 
      
 101 
     | 
    
         
            +
              generating, signing, and verifying JWTs, making OpenID Connect integration straightforward
         
     | 
| 
      
 102 
     | 
    
         
            +
              and secure.
         
     | 
| 
      
 103 
     | 
    
         
            +
            email:
         
     | 
| 
      
 104 
     | 
    
         
            +
            - delirable@gmail.com
         
     | 
| 
      
 105 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 106 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 107 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 108 
     | 
    
         
            +
            files:
         
     | 
| 
      
 109 
     | 
    
         
            +
            - ".rspec"
         
     | 
| 
      
 110 
     | 
    
         
            +
            - ".rubocop.yml"
         
     | 
| 
      
 111 
     | 
    
         
            +
            - CHANGELOG.md
         
     | 
| 
      
 112 
     | 
    
         
            +
            - LICENSE.txt
         
     | 
| 
      
 113 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 114 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 115 
     | 
    
         
            +
            - examples/README.md
         
     | 
| 
      
 116 
     | 
    
         
            +
            - examples/client_secret_basic/Gemfile
         
     | 
| 
      
 117 
     | 
    
         
            +
            - examples/client_secret_basic/Gemfile.lock
         
     | 
| 
      
 118 
     | 
    
         
            +
            - examples/client_secret_basic/README.md
         
     | 
| 
      
 119 
     | 
    
         
            +
            - examples/client_secret_basic/app.rb
         
     | 
| 
      
 120 
     | 
    
         
            +
            - examples/client_secret_basic/config.json
         
     | 
| 
      
 121 
     | 
    
         
            +
            - examples/client_secret_basic/public/stylesheets/style.css
         
     | 
| 
      
 122 
     | 
    
         
            +
            - examples/client_secret_basic/views/index.erb
         
     | 
| 
      
 123 
     | 
    
         
            +
            - lib/id_partner.rb
         
     | 
| 
      
 124 
     | 
    
         
            +
            - lib/ruby_oidc_client/version.rb
         
     | 
| 
      
 125 
     | 
    
         
            +
            - ruby_oidc_client.gemspec
         
     | 
| 
      
 126 
     | 
    
         
            +
            - sig/ruby_oidc_client.rbs
         
     | 
| 
      
 127 
     | 
    
         
            +
            homepage: https://github.com/idpartner-app/ruby_oidc_client
         
     | 
| 
      
 128 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 129 
     | 
    
         
            +
            - MIT
         
     | 
| 
      
 130 
     | 
    
         
            +
            metadata:
         
     | 
| 
      
 131 
     | 
    
         
            +
              allowed_push_host: https://rubygems.org
         
     | 
| 
      
 132 
     | 
    
         
            +
              homepage_uri: https://github.com/idpartner-app/ruby_oidc_client
         
     | 
| 
      
 133 
     | 
    
         
            +
              source_code_uri: https://github.com/idpartner-app/ruby_oidc_client
         
     | 
| 
      
 134 
     | 
    
         
            +
              changelog_uri: https://github.com/idpartner-app/ruby_oidc_client/blob/master/CHANGELOG.md
         
     | 
| 
      
 135 
     | 
    
         
            +
            post_install_message:
         
     | 
| 
      
 136 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 137 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 138 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 139 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 140 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 141 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 142 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 143 
     | 
    
         
            +
                  version: 2.6.0
         
     | 
| 
      
 144 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 145 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 146 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 147 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 148 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 149 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 150 
     | 
    
         
            +
            rubygems_version: 3.4.10
         
     | 
| 
      
 151 
     | 
    
         
            +
            signing_key:
         
     | 
| 
      
 152 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 153 
     | 
    
         
            +
            summary: A Ruby client for interacting with OpenID Connect providers using the IDPartner
         
     | 
| 
      
 154 
     | 
    
         
            +
              class.
         
     | 
| 
      
 155 
     | 
    
         
            +
            test_files: []
         
     |