hydra-keycloak-client 0.1.13 → 0.1.15
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/.github/workflows/release.yml +26 -0
- data/.gitignore +2 -0
- data/Dockerfile.release +11 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +13 -6
- data/lib/hydra/keycloak/client.rb +75 -69
- data/lib/hydra/keycloak/code_verifier.rb +2 -1
- data/lib/hydra/keycloak/container.rb +15 -3
- data/lib/hydra/keycloak/token.rb +9 -8
- data/lib/hydra/keycloak/{queries/http_client.rb → tokens/gateway.rb} +9 -4
- data/lib/hydra/keycloak/{queries/gateway.rb → tokens/repo.rb} +35 -16
- data/lib/hydra/keycloak/urls.rb +31 -16
- data/lib/hydra/keycloak/version.rb +1 -1
- data/release.sh +9 -0
- metadata +10 -7
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e44c13e82949716bb5f91393dab600335b8c5d201065edff00b286084f15399d
         | 
| 4 | 
            +
              data.tar.gz: 23662cc9f3ffdd48f77c1d05baeff95ea72af6681a5813cd46f22d155c532b1d
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b9fb3fe92fc2c065ff622a5dcbea04541a94c2f9bb0bac1a3d0b7f34af058f1801869a068d8720937fbd38cb349fc3b4a707a72c7a9a43f0763aa74c3fe5fd33
         | 
| 7 | 
            +
              data.tar.gz: 0e5ec4ae6acfd1f9ca880f24741077cff808cd9a3d1ffdb1bc27c6db801f6b3d5c36864729401827d7918d341d5d3033e2ed2fe37e226cdc5f1a6522687aebf5
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            name: Create release
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            on:
         | 
| 4 | 
            +
              push:
         | 
| 5 | 
            +
                tags:
         | 
| 6 | 
            +
                  - '*'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            jobs:
         | 
| 9 | 
            +
              deploy:
         | 
| 10 | 
            +
                runs-on: self-hosted
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                steps:
         | 
| 13 | 
            +
                  - uses: actions/checkout@v2
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  - name: Set env
         | 
| 16 | 
            +
                    run: echo "RELEASE_VERSION=${{ github.ref_name }}" >> $GITHUB_ENV
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  - name: Build releaser image
         | 
| 19 | 
            +
                    run: docker build -f Dockerfile.release -t hkc:release .
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  - name: Release gem
         | 
| 22 | 
            +
                    run: |
         | 
| 23 | 
            +
                      docker run --rm --name hkc_release \
         | 
| 24 | 
            +
                                 -e RUBYGEMS_TOKEN=${{ secrets.RUBYGEMS_TOKEN }} \
         | 
| 25 | 
            +
                                 -e RELEASE_VERSION=${{ env.RELEASE_VERSION }} \
         | 
| 26 | 
            +
                                 hkc:release
         | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/Dockerfile.release
    ADDED
    
    
    
        data/Gemfile
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                hydra-keycloak-client (0.1. | 
| 4 | 
            +
                hydra-keycloak-client (0.1.15)
         | 
| 5 5 | 
             
                  dry-auto_inject
         | 
| 6 6 | 
             
                  dry-container
         | 
| 7 7 | 
             
                  dry-monads
         | 
| @@ -15,6 +15,8 @@ GEM | |
| 15 15 | 
             
                ast (2.4.2)
         | 
| 16 16 | 
             
                coderay (1.1.3)
         | 
| 17 17 | 
             
                concurrent-ruby (1.1.10)
         | 
| 18 | 
            +
                connection_pool (2.3.0)
         | 
| 19 | 
            +
                dalli (3.2.3)
         | 
| 18 20 | 
             
                diff-lcs (1.4.4)
         | 
| 19 21 | 
             
                docile (1.4.0)
         | 
| 20 22 | 
             
                dry-auto_inject (0.9.0)
         | 
| @@ -22,12 +24,11 @@ GEM | |
| 22 24 | 
             
                dry-configurable (0.15.0)
         | 
| 23 25 | 
             
                  concurrent-ruby (~> 1.0)
         | 
| 24 26 | 
             
                  dry-core (~> 0.6)
         | 
| 25 | 
            -
                dry-container (0. | 
| 27 | 
            +
                dry-container (0.10.1)
         | 
| 26 28 | 
             
                  concurrent-ruby (~> 1.0)
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                dry-core (0.7.1)
         | 
| 29 | 
            +
                dry-core (0.8.1)
         | 
| 29 30 | 
             
                  concurrent-ruby (~> 1.0)
         | 
| 30 | 
            -
                dry-inflector (0. | 
| 31 | 
            +
                dry-inflector (0.3.0)
         | 
| 31 32 | 
             
                dry-initializer (3.1.1)
         | 
| 32 33 | 
             
                dry-logic (1.2.0)
         | 
| 33 34 | 
             
                  concurrent-ruby (~> 1.0)
         | 
| @@ -63,6 +64,10 @@ GEM | |
| 63 64 | 
             
                  method_source (~> 1.0)
         | 
| 64 65 | 
             
                rainbow (3.1.1)
         | 
| 65 66 | 
             
                rake (12.3.3)
         | 
| 67 | 
            +
                redis (5.0.5)
         | 
| 68 | 
            +
                  redis-client (>= 0.9.0)
         | 
| 69 | 
            +
                redis-client (0.11.2)
         | 
| 70 | 
            +
                  connection_pool
         | 
| 66 71 | 
             
                regexp_parser (2.2.1)
         | 
| 67 72 | 
             
                rexml (3.2.5)
         | 
| 68 73 | 
             
                rspec (3.10.0)
         | 
| @@ -105,13 +110,15 @@ PLATFORMS | |
| 105 110 | 
             
              ruby
         | 
| 106 111 |  | 
| 107 112 | 
             
            DEPENDENCIES
         | 
| 113 | 
            +
              dalli
         | 
| 108 114 | 
             
              hydra-keycloak-client!
         | 
| 109 115 | 
             
              pry
         | 
| 110 116 | 
             
              rake (~> 12.0)
         | 
| 117 | 
            +
              redis
         | 
| 111 118 | 
             
              rspec (~> 3.0)
         | 
| 112 119 | 
             
              rubocop (~> 1.26)
         | 
| 113 120 | 
             
              simplecov
         | 
| 114 121 | 
             
              simplecov-cobertura
         | 
| 115 122 |  | 
| 116 123 | 
             
            BUNDLED WITH
         | 
| 117 | 
            -
               2. | 
| 124 | 
            +
               2.3.5
         | 
| @@ -12,16 +12,39 @@ module Hydra | |
| 12 12 | 
             
              module Keycloak
         | 
| 13 13 | 
             
                class ConfigurationError < ::StandardError; end
         | 
| 14 14 |  | 
| 15 | 
            +
                MEMCACHED_SCHEMA = Dry::Schema.JSON do
         | 
| 16 | 
            +
                  required(:memcached_host).filled(:string)
         | 
| 17 | 
            +
                  required(:memcached_port).filled(:integer)
         | 
| 18 | 
            +
                  required(:memcached_namespace).filled(:string)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                REDIS_SCHEMA = Dry::Schema.JSON do
         | 
| 22 | 
            +
                  required(:redis_host).filled(:string)
         | 
| 23 | 
            +
                  required(:redis_port).filled(:integer)
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                CONFIG_SCHEMA = Dry::Schema.JSON do
         | 
| 27 | 
            +
                  required(:auth_server_url).filled(:string)
         | 
| 28 | 
            +
                  required(:realm).filled(:string)
         | 
| 29 | 
            +
                  required(:client_id).filled(:string)
         | 
| 30 | 
            +
                  required(:redirect_uri).filled(:string)
         | 
| 31 | 
            +
                  required(:secret).filled(:string)
         | 
| 32 | 
            +
                  required(:logout_redirect).filled(:string)
         | 
| 33 | 
            +
                  required(:store_client).value(included_in?: %w[redis memcached])
         | 
| 34 | 
            +
                  required(:store_client_options).hash(MEMCACHED_SCHEMA | REDIS_SCHEMA)
         | 
| 35 | 
            +
                  optional(:scope).array(:str?)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 15 38 | 
             
                class ClientCreator
         | 
| 16 39 | 
             
                  extend ::Hydra::Keycloak::Mixin
         | 
| 17 40 |  | 
| 18 41 | 
             
                  class << self
         | 
| 19 42 | 
             
                    def call(config:)
         | 
| 20 | 
            -
                      register_containers(validate_config(config))
         | 
| 43 | 
            +
                      register_containers(validate_config(config).to_h)
         | 
| 21 44 | 
             
                    end
         | 
| 22 45 |  | 
| 23 46 | 
             
                    def validate_config(config)
         | 
| 24 | 
            -
                      validated_config =  | 
| 47 | 
            +
                      validated_config = CONFIG_SCHEMA.call(config)
         | 
| 25 48 |  | 
| 26 49 | 
             
                      if validated_config.failure?
         | 
| 27 50 | 
             
                        raise ConfigurationError, "Wrong configuration params: #{validated_config.errors(full: true).to_h}"
         | 
| @@ -30,40 +53,8 @@ module Hydra | |
| 30 53 | 
             
                      validated_config
         | 
| 31 54 | 
             
                    end
         | 
| 32 55 |  | 
| 33 | 
            -
                    def config_schema
         | 
| 34 | 
            -
                      memcached_schema = ::Hydra::Keycloak::ClientCreator.memcached_schema
         | 
| 35 | 
            -
                      redis_schema     = ::Hydra::Keycloak::ClientCreator.redis_schema
         | 
| 36 | 
            -
                      Dry::Schema.JSON do
         | 
| 37 | 
            -
                        required(:auth_server_url).filled(:string)
         | 
| 38 | 
            -
                        required(:realm).filled(:string)
         | 
| 39 | 
            -
                        required(:client_id).filled(:string)
         | 
| 40 | 
            -
                        required(:redirect_uri).filled(:string)
         | 
| 41 | 
            -
                        required(:secret).filled(:string)
         | 
| 42 | 
            -
                        required(:logout_redirect).filled(:string)
         | 
| 43 | 
            -
                        required(:store_client).value(included_in?: %w[redis memcached])
         | 
| 44 | 
            -
                        required(:store_client_options).hash(memcached_schema | redis_schema)
         | 
| 45 | 
            -
                        optional(:scope).array(:str?)
         | 
| 46 | 
            -
                      end
         | 
| 47 | 
            -
                    end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                    def memcached_schema
         | 
| 50 | 
            -
                      Dry::Schema.JSON do
         | 
| 51 | 
            -
                        required(:memcached_host).filled(:string)
         | 
| 52 | 
            -
                        required(:memcached_port).filled(:integer)
         | 
| 53 | 
            -
                        required(:memcached_namespace).filled(:string)
         | 
| 54 | 
            -
                      end
         | 
| 55 | 
            -
                    end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                    def redis_schema
         | 
| 58 | 
            -
                      Dry::Schema.JSON do
         | 
| 59 | 
            -
                        required(:redis_host).filled(:string)
         | 
| 60 | 
            -
                        required(:redis_port).filled(:integer)
         | 
| 61 | 
            -
                      end
         | 
| 62 | 
            -
                    end
         | 
| 63 | 
            -
             | 
| 64 56 | 
             
                    def register_containers(validated_config)
         | 
| 65 57 | 
             
                      register_urls(validated_config)
         | 
| 66 | 
            -
                      register_queries
         | 
| 67 58 | 
             
                      register_store_client(validated_config)
         | 
| 68 59 | 
             
                      register_store
         | 
| 69 60 | 
             
                      register_code_verifier
         | 
| @@ -79,42 +70,43 @@ module Hydra | |
| 79 70 | 
             
                      end
         | 
| 80 71 | 
             
                    end
         | 
| 81 72 |  | 
| 82 | 
            -
                    def  | 
| 83 | 
            -
                       | 
| 84 | 
            -
             | 
| 73 | 
            +
                    def register_store_client(config)
         | 
| 74 | 
            +
                      case config.fetch(:store_client)
         | 
| 75 | 
            +
                      when 'redis'
         | 
| 76 | 
            +
                        register_redis_store(config.fetch(:store_client_options))
         | 
| 77 | 
            +
                      when 'memcached'
         | 
| 78 | 
            +
                        register_memcached_store(config.fetch(:store_client_options))
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    def register_redis_store(redis_host:, redis_port:)
         | 
| 83 | 
            +
                      require 'hydra/keycloak/store/adapters/redis'
         | 
| 85 84 |  | 
| 86 | 
            -
             | 
| 85 | 
            +
                      container.register :redis do
         | 
| 86 | 
            +
                        ::Redis.new(host: redis_host, port: redis_port)
         | 
| 87 | 
            +
                      end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                      container.register :store_client do
         | 
| 90 | 
            +
                        require 'hydra/keycloak/store/redis_client'
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                        ::Hydra::Keycloak::Store::RedisClient.new
         | 
| 87 93 | 
             
                      end
         | 
| 88 94 | 
             
                    end
         | 
| 89 95 |  | 
| 90 | 
            -
                    def  | 
| 91 | 
            -
                       | 
| 92 | 
            -
                      when 'redis'
         | 
| 93 | 
            -
                        require 'hydra/keycloak/store/adapters/redis'
         | 
| 96 | 
            +
                    def register_memcached_store(memcached_host:, memcached_port:, memcached_namespace:)
         | 
| 97 | 
            +
                      require 'hydra/keycloak/store/adapters/memcached'
         | 
| 94 98 |  | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 99 | 
            +
                      container.register :dalli do
         | 
| 100 | 
            +
                        ::Dalli::Client.new(
         | 
| 101 | 
            +
                          "#{memcached_host}:#{memcached_port}",
         | 
| 102 | 
            +
                          namespace: memcached_namespace
         | 
| 103 | 
            +
                        )
         | 
| 104 | 
            +
                      end
         | 
| 98 105 |  | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 106 | 
            +
                      container.register :store_client do
         | 
| 107 | 
            +
                        require 'hydra/keycloak/store/memcached_client'
         | 
| 101 108 |  | 
| 102 | 
            -
             | 
| 103 | 
            -
                        end
         | 
| 104 | 
            -
                      when 'memcached'
         | 
| 105 | 
            -
                        require 'hydra/keycloak/store/adapters/memcached'
         | 
| 106 | 
            -
                        container.register :dalli do
         | 
| 107 | 
            -
                          ::Dalli::Client.new(
         | 
| 108 | 
            -
                            "#{config[:store_client_options][:memcached_host]}:#{config[:store_client_options][:memcached_port]}",
         | 
| 109 | 
            -
                            namespace: config[:store_client_options][:memcached_namespace]
         | 
| 110 | 
            -
                          )
         | 
| 111 | 
            -
                        end
         | 
| 112 | 
            -
             | 
| 113 | 
            -
                        container.register :store_client do
         | 
| 114 | 
            -
                          require 'hydra/keycloak/store/memcached_client'
         | 
| 115 | 
            -
             | 
| 116 | 
            -
                          ::Hydra::Keycloak::Store::MemcachedClient.new
         | 
| 117 | 
            -
                        end
         | 
| 109 | 
            +
                        ::Hydra::Keycloak::Store::MemcachedClient.new
         | 
| 118 110 | 
             
                      end
         | 
| 119 111 | 
             
                    end
         | 
| 120 112 |  | 
| @@ -135,7 +127,7 @@ module Hydra | |
| 135 127 | 
             
                class Client
         | 
| 136 128 | 
             
                  extend ::Hydra::Keycloak::Mixin
         | 
| 137 129 | 
             
                  include ::Dry::Monads[:result, :do]
         | 
| 138 | 
            -
                  inject['urls', ' | 
| 130 | 
            +
                  inject['urls', 'tokens_repo', 'store', 'code_verifier']
         | 
| 139 131 |  | 
| 140 132 | 
             
                  def auth_url
         | 
| 141 133 | 
             
                    code_verifier.generate
         | 
| @@ -143,9 +135,23 @@ module Hydra | |
| 143 135 | 
             
                  end
         | 
| 144 136 |  | 
| 145 137 | 
             
                  def authenticate!(auth_code)
         | 
| 146 | 
            -
                     | 
| 138 | 
            +
                    tokens_repo.get_tokens(auth_code, code_verifier.value).fmap do |tokens|
         | 
| 139 | 
            +
                      access_token  = tokens[:access_token]
         | 
| 140 | 
            +
                      id_token      = tokens[:id_token]
         | 
| 141 | 
            +
                      refresh_token = tokens[:refresh_token]
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                      session_state = access_token.session_state
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                      save_token(session_state, 'access_token',  access_token)
         | 
| 146 | 
            +
                      save_token(session_state, 'id_token',      id_token)
         | 
| 147 | 
            +
                      save_token(session_state, 'refresh_token', refresh_token)
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                      session_state
         | 
| 150 | 
            +
                    end
         | 
| 151 | 
            +
                  end
         | 
| 147 152 |  | 
| 148 | 
            -
             | 
| 153 | 
            +
                  def authenticate_by_password!(username, password)
         | 
| 154 | 
            +
                    tokens_repo.get_tokens_by_password(username, password).fmap do |tokens|
         | 
| 149 155 | 
             
                      access_token  = tokens[:access_token]
         | 
| 150 156 | 
             
                      id_token      = tokens[:id_token]
         | 
| 151 157 | 
             
                      refresh_token = tokens[:refresh_token]
         | 
| @@ -180,7 +186,7 @@ module Hydra | |
| 180 186 | 
             
                      access_token = yield fetch_token(session_state, 'access_token')
         | 
| 181 187 | 
             
                    end
         | 
| 182 188 |  | 
| 183 | 
            -
                     | 
| 189 | 
            +
                    tokens_repo.introspect_token(access_token.source)
         | 
| 184 190 | 
             
                  end
         | 
| 185 191 |  | 
| 186 192 | 
             
                  def access_token_jti(session_state)
         | 
| @@ -198,7 +204,7 @@ module Hydra | |
| 198 204 | 
             
                  end
         | 
| 199 205 |  | 
| 200 206 | 
             
                  def introspect_token(token)
         | 
| 201 | 
            -
                     | 
| 207 | 
            +
                    tokens_repo.introspect_token(token)
         | 
| 202 208 | 
             
                  end
         | 
| 203 209 |  | 
| 204 210 | 
             
                  private
         | 
| @@ -229,7 +235,7 @@ module Hydra | |
| 229 235 |  | 
| 230 236 | 
             
                  def refresh_tokens(session_state)
         | 
| 231 237 | 
             
                    refresh_token = yield fetch_token(session_state, 'refresh_token')
         | 
| 232 | 
            -
                    new_tokens = yield  | 
| 238 | 
            +
                    new_tokens = yield tokens_repo.refresh_tokens(refresh_token.source)
         | 
| 233 239 |  | 
| 234 240 | 
             
                    yield save_token(session_state, 'access_token',  new_tokens[:access_token])
         | 
| 235 241 | 
             
                    yield save_token(session_state, 'id_token',      new_tokens[:id_token])
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'base64'
         | 
| 3 4 | 
             
            require 'digest'
         | 
| 4 5 | 
             
            require 'securerandom'
         | 
| 5 6 |  | 
| @@ -26,7 +27,7 @@ module Hydra | |
| 26 27 |  | 
| 27 28 | 
             
                  def _generate_pkce(code_verifier)
         | 
| 28 29 | 
             
                    # https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
         | 
| 29 | 
            -
                    Digest::SHA256. | 
| 30 | 
            +
                    Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier), padding: false)
         | 
| 30 31 | 
             
                  end
         | 
| 31 32 | 
             
                end
         | 
| 32 33 | 
             
              end
         | 
| @@ -8,10 +8,22 @@ module Hydra | |
| 8 8 | 
             
                class Container
         | 
| 9 9 | 
             
                  extend Dry::Container::Mixin
         | 
| 10 10 |  | 
| 11 | 
            -
                  register(: | 
| 12 | 
            -
                    require ' | 
| 11 | 
            +
                  register(:http) do
         | 
| 12 | 
            +
                    require 'net/http'
         | 
| 13 13 |  | 
| 14 | 
            -
                    :: | 
| 14 | 
            +
                    Net::HTTP
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  register(:tokens_gateway) do
         | 
| 18 | 
            +
                    require 'hydra/keycloak/tokens/gateway'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    ::Hydra::Keycloak::Tokens::Gateway.new
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  register(:tokens_repo) do
         | 
| 24 | 
            +
                    require 'hydra/keycloak/tokens/repo'
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    ::Hydra::Keycloak::Tokens::Repo.new
         | 
| 15 27 | 
             
                  end
         | 
| 16 28 | 
             
                end
         | 
| 17 29 |  | 
    
        data/lib/hydra/keycloak/token.rb
    CHANGED
    
    | @@ -5,21 +5,22 @@ require 'jwt' | |
| 5 5 | 
             
            module Hydra
         | 
| 6 6 | 
             
              module Keycloak
         | 
| 7 7 | 
             
                class Token
         | 
| 8 | 
            +
                  REGISTERED_CLAIMS = %i[iss sub aud exp nbf iat jti].freeze
         | 
| 9 | 
            +
                  PUBLIC_CLAIMS     = %i[auth_time scope].freeze
         | 
| 10 | 
            +
                  PRIVATE_CLAIMS    = %i[session_state resource_access].freeze
         | 
| 11 | 
            +
             | 
| 8 12 | 
             
                  attr_reader :source,
         | 
| 9 | 
            -
                              : | 
| 10 | 
            -
                               | 
| 11 | 
            -
                               | 
| 12 | 
            -
                               | 
| 13 | 
            -
                              :session_state,
         | 
| 14 | 
            -
                              :scope,
         | 
| 15 | 
            -
                              :jti
         | 
| 13 | 
            +
                              :data,
         | 
| 14 | 
            +
                              *REGISTERED_CLAIMS,
         | 
| 15 | 
            +
                              *PUBLIC_CLAIMS,
         | 
| 16 | 
            +
                              *PRIVATE_CLAIMS
         | 
| 16 17 |  | 
| 17 18 | 
             
                  def initialize(source)
         | 
| 18 19 | 
             
                    @source = source
         | 
| 19 20 | 
             
                    @data = ::JWT.decode(source, nil, false).first.transform_keys(&:to_sym)
         | 
| 20 21 | 
             
                  end
         | 
| 21 22 |  | 
| 22 | 
            -
                   | 
| 23 | 
            +
                  [*REGISTERED_CLAIMS, *PUBLIC_CLAIMS, *PRIVATE_CLAIMS].each do |field|
         | 
| 23 24 | 
             
                    define_method(field) do
         | 
| 24 25 | 
             
                      @data.fetch(field)
         | 
| 25 26 | 
             
                    end
         | 
| @@ -3,13 +3,18 @@ | |
| 3 3 | 
             
            require 'net/http'
         | 
| 4 4 | 
             
            require 'json'
         | 
| 5 5 | 
             
            require 'dry/monads'
         | 
| 6 | 
            +
            require 'hydra/keycloak/container'
         | 
| 6 7 |  | 
| 7 8 | 
             
            module Hydra
         | 
| 8 9 | 
             
              module Keycloak
         | 
| 9 | 
            -
                module  | 
| 10 | 
            -
                  class  | 
| 10 | 
            +
                module Tokens
         | 
| 11 | 
            +
                  class Gateway
         | 
| 12 | 
            +
                    extend ::Hydra::Keycloak::Mixin
         | 
| 13 | 
            +
             | 
| 11 14 | 
             
                    include ::Dry::Monads[:result]
         | 
| 12 15 |  | 
| 16 | 
            +
                    inject['http']
         | 
| 17 | 
            +
             | 
| 13 18 | 
             
                    NETWORK_ERRORS = [Timeout::Error,
         | 
| 14 19 | 
             
                                      Errno::EINVAL,
         | 
| 15 20 | 
             
                                      Errno::ECONNRESET,
         | 
| @@ -19,8 +24,8 @@ module Hydra | |
| 19 24 | 
             
                                      Net::HTTPHeaderSyntaxError,
         | 
| 20 25 | 
             
                                      Net::ProtocolError].freeze
         | 
| 21 26 |  | 
| 22 | 
            -
                    def  | 
| 23 | 
            -
                      response =  | 
| 27 | 
            +
                    def post(path, body)
         | 
| 28 | 
            +
                      response = http.post_form(URI(path), **body)
         | 
| 24 29 |  | 
| 25 30 | 
             
                      if response.code == '200'
         | 
| 26 31 | 
             
                        json = JSON.parse(response.body)
         | 
| @@ -7,15 +7,19 @@ require 'hydra/keycloak/token' | |
| 7 7 |  | 
| 8 8 | 
             
            module Hydra
         | 
| 9 9 | 
             
              module Keycloak
         | 
| 10 | 
            -
                module  | 
| 11 | 
            -
                  class  | 
| 10 | 
            +
                module Tokens
         | 
| 11 | 
            +
                  class Repo
         | 
| 12 12 | 
             
                    extend ::Hydra::Keycloak::Mixin
         | 
| 13 13 | 
             
                    include ::Dry::Monads[:result]
         | 
| 14 | 
            -
                    inject[' | 
| 14 | 
            +
                    inject['tokens_gateway', 'urls']
         | 
| 15 15 |  | 
| 16 16 | 
             
                    def get_tokens(auth_code, code_verifier)
         | 
| 17 | 
            -
                       | 
| 18 | 
            -
             | 
| 17 | 
            +
                      return Failure(status: 400, code: :auth_code_was_not_received) unless auth_code
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      result = tokens_gateway.post(
         | 
| 20 | 
            +
                        urls.token_endpoint,
         | 
| 21 | 
            +
                        urls.auth_code_token_request_body(auth_code, code_verifier)
         | 
| 22 | 
            +
                      )
         | 
| 19 23 |  | 
| 20 24 | 
             
                      result.fmap do |tokens|
         | 
| 21 25 | 
             
                        {
         | 
| @@ -26,9 +30,28 @@ module Hydra | |
| 26 30 | 
             
                      end
         | 
| 27 31 | 
             
                    end
         | 
| 28 32 |  | 
| 29 | 
            -
                    def  | 
| 30 | 
            -
                       | 
| 31 | 
            -
             | 
| 33 | 
            +
                    def get_tokens_by_password(username, password)
         | 
| 34 | 
            +
                      return Failure(status: 400, code: :username_or_password_is_empty) if username.nil? || password.nil?
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                      result = tokens_gateway.post(
         | 
| 37 | 
            +
                        urls.token_endpoint,
         | 
| 38 | 
            +
                        urls.password_token_request_body(username, password)
         | 
| 39 | 
            +
                      )
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      result.fmap do |tokens|
         | 
| 42 | 
            +
                        {
         | 
| 43 | 
            +
                          access_token: ::Hydra::Keycloak::Token.new(tokens['access_token']),
         | 
| 44 | 
            +
                          id_token: ::Hydra::Keycloak::Token.new(tokens['id_token']),
         | 
| 45 | 
            +
                          refresh_token: ::Hydra::Keycloak::Token.new(tokens['refresh_token'])
         | 
| 46 | 
            +
                        }
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    def introspect_token(token)
         | 
| 51 | 
            +
                      tokens_gateway.post(
         | 
| 52 | 
            +
                        urls.introspection_endpoint,
         | 
| 53 | 
            +
                        urls.introspection_request_body(token)
         | 
| 54 | 
            +
                      ).bind do |result|
         | 
| 32 55 | 
             
                        if result['active']
         | 
| 33 56 | 
             
                          Success(result)
         | 
| 34 57 | 
             
                        else
         | 
| @@ -38,8 +61,10 @@ module Hydra | |
| 38 61 | 
             
                    end
         | 
| 39 62 |  | 
| 40 63 | 
             
                    def refresh_tokens(refresh_token)
         | 
| 41 | 
            -
                       | 
| 42 | 
            -
             | 
| 64 | 
            +
                      tokens_gateway.post(
         | 
| 65 | 
            +
                        urls.token_endpoint,
         | 
| 66 | 
            +
                        urls.refresh_request_body(refresh_token)
         | 
| 67 | 
            +
                      ).bind do |result|
         | 
| 43 68 | 
             
                        if result['error']
         | 
| 44 69 | 
             
                          Failure(status: 400, code: :token_refreshing_error)
         | 
| 45 70 | 
             
                        else
         | 
| @@ -51,12 +76,6 @@ module Hydra | |
| 51 76 | 
             
                        end
         | 
| 52 77 | 
             
                      end
         | 
| 53 78 | 
             
                    end
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                    private
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                    def make_request(path, body)
         | 
| 58 | 
            -
                      http_client.do_post_request(path, body)
         | 
| 59 | 
            -
                    end
         | 
| 60 79 | 
             
                  end
         | 
| 61 80 | 
             
                end
         | 
| 62 81 | 
             
              end
         | 
    
        data/lib/hydra/keycloak/urls.rb
    CHANGED
    
    | @@ -10,22 +10,26 @@ module Hydra | |
| 10 10 | 
             
                  end
         | 
| 11 11 |  | 
| 12 12 | 
             
                  def auth_url(code_challenge)
         | 
| 13 | 
            -
                    " | 
| 14 | 
            -
                       | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 13 | 
            +
                    URI(URI.join(@config[:auth_server_url], "realms/#{@config[:realm]}/protocol/openid-connect/auth")).tap do |uri|
         | 
| 14 | 
            +
                      uri.query = URI.encode_www_form(
         | 
| 15 | 
            +
                        {
         | 
| 16 | 
            +
                          response_type: 'code',
         | 
| 17 | 
            +
                          client_id: @config[:client_id],
         | 
| 18 | 
            +
                          redirect_uri: @config[:redirect_uri],
         | 
| 19 | 
            +
                          nonce: @config[:secret],
         | 
| 20 | 
            +
                          scope: scope,
         | 
| 21 | 
            +
                          code_challenge: code_challenge,
         | 
| 22 | 
            +
                          code_challenge_method: 'S256'
         | 
| 23 | 
            +
                        }
         | 
| 24 | 
            +
                      )
         | 
| 25 | 
            +
                    end.to_s
         | 
| 22 26 | 
             
                  end
         | 
| 23 27 |  | 
| 24 28 | 
             
                  def token_endpoint
         | 
| 25 | 
            -
                     | 
| 29 | 
            +
                    URI.join(@config[:auth_server_url], "realms/#{@config[:realm]}/protocol/openid-connect/token")
         | 
| 26 30 | 
             
                  end
         | 
| 27 31 |  | 
| 28 | 
            -
                  def  | 
| 32 | 
            +
                  def auth_code_token_request_body(auth_code, code_verifier)
         | 
| 29 33 | 
             
                    {
         | 
| 30 34 | 
             
                      grant_type: 'authorization_code',
         | 
| 31 35 | 
             
                      code: auth_code,
         | 
| @@ -36,8 +40,19 @@ module Hydra | |
| 36 40 | 
             
                    }
         | 
| 37 41 | 
             
                  end
         | 
| 38 42 |  | 
| 43 | 
            +
                  def password_token_request_body(username, password)
         | 
| 44 | 
            +
                    {
         | 
| 45 | 
            +
                      grant_type: 'password',
         | 
| 46 | 
            +
                      username: username,
         | 
| 47 | 
            +
                      password: password,
         | 
| 48 | 
            +
                      scope: scope,
         | 
| 49 | 
            +
                      client_id: @config[:client_id],
         | 
| 50 | 
            +
                      client_secret: @config[:secret]
         | 
| 51 | 
            +
                    }
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 39 54 | 
             
                  def introspection_endpoint
         | 
| 40 | 
            -
                     | 
| 55 | 
            +
                    URI.join(@config[:auth_server_url], "realms/#{@config[:realm]}/protocol/openid-connect/token/introspect")
         | 
| 41 56 | 
             
                  end
         | 
| 42 57 |  | 
| 43 58 | 
             
                  def introspection_request_body(token)
         | 
| @@ -50,9 +65,9 @@ module Hydra | |
| 50 65 | 
             
                  end
         | 
| 51 66 |  | 
| 52 67 | 
             
                  def end_session_url(id_token)
         | 
| 53 | 
            -
                     | 
| 54 | 
            -
             | 
| 55 | 
            -
                     | 
| 68 | 
            +
                    URI.join(@config[:auth_server_url], "realms/#{@config[:realm]}/protocol/openid-connect/logout").tap do |uri|
         | 
| 69 | 
            +
                      uri.query = URI.encode_www_form(id_token_hint: id_token, post_logout_redirect_uri: @config[:logout_redirect])
         | 
| 70 | 
            +
                    end.to_s
         | 
| 56 71 | 
             
                  end
         | 
| 57 72 |  | 
| 58 73 | 
             
                  def refresh_request_body(refresh_token)
         | 
| @@ -68,7 +83,7 @@ module Hydra | |
| 68 83 | 
             
                  private
         | 
| 69 84 |  | 
| 70 85 | 
             
                  def scope
         | 
| 71 | 
            -
                     | 
| 86 | 
            +
                    [*DEFAULT_SCOPE, *@config[:scope]].join(' ')
         | 
| 72 87 | 
             
                  end
         | 
| 73 88 | 
             
                end
         | 
| 74 89 | 
             
              end
         | 
    
        data/release.sh
    ADDED
    
    | @@ -0,0 +1,9 @@ | |
| 1 | 
            +
            #!/usr/bin/env bash
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            mkdir -p $HOME/.gem
         | 
| 4 | 
            +
            touch $HOME/.gem/credentials
         | 
| 5 | 
            +
            chmod 0600 $HOME/.gem/credentials
         | 
| 6 | 
            +
            printf -- "---\n:rubygems_api_key: $RUBYGEMS_TOKEN\n" > $HOME/.gem/credentials
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            gem build hydra-keycloak-client.gemspec
         | 
| 9 | 
            +
            gem push hydra-keycloak-client-$RELEASE_VERSION.gem
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: hydra-keycloak-client
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.1. | 
| 4 | 
            +
              version: 0.1.15
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Fedor Kosolapov
         | 
| 8 | 
            -
            autorequire:
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022- | 
| 11 | 
            +
            date: 2022-12-22 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: jwt
         | 
| @@ -104,9 +104,11 @@ extensions: [] | |
| 104 104 | 
             
            extra_rdoc_files: []
         | 
| 105 105 | 
             
            files:
         | 
| 106 106 | 
             
            - ".github/workflows/main.yml"
         | 
| 107 | 
            +
            - ".github/workflows/release.yml"
         | 
| 107 108 | 
             
            - ".gitignore"
         | 
| 108 109 | 
             
            - ".rspec"
         | 
| 109 110 | 
             
            - ".rubocop.yml"
         | 
| 111 | 
            +
            - Dockerfile.release
         | 
| 110 112 | 
             
            - Dockerfile.test
         | 
| 111 113 | 
             
            - Gemfile
         | 
| 112 114 | 
             
            - Gemfile.lock
         | 
| @@ -118,16 +120,17 @@ files: | |
| 118 120 | 
             
            - lib/hydra/keycloak/client.rb
         | 
| 119 121 | 
             
            - lib/hydra/keycloak/code_verifier.rb
         | 
| 120 122 | 
             
            - lib/hydra/keycloak/container.rb
         | 
| 121 | 
            -
            - lib/hydra/keycloak/queries/gateway.rb
         | 
| 122 | 
            -
            - lib/hydra/keycloak/queries/http_client.rb
         | 
| 123 123 | 
             
            - lib/hydra/keycloak/store/adapters/memcached.rb
         | 
| 124 124 | 
             
            - lib/hydra/keycloak/store/adapters/redis.rb
         | 
| 125 125 | 
             
            - lib/hydra/keycloak/store/gateway.rb
         | 
| 126 126 | 
             
            - lib/hydra/keycloak/store/memcached_client.rb
         | 
| 127 127 | 
             
            - lib/hydra/keycloak/store/redis_client.rb
         | 
| 128 128 | 
             
            - lib/hydra/keycloak/token.rb
         | 
| 129 | 
            +
            - lib/hydra/keycloak/tokens/gateway.rb
         | 
| 130 | 
            +
            - lib/hydra/keycloak/tokens/repo.rb
         | 
| 129 131 | 
             
            - lib/hydra/keycloak/urls.rb
         | 
| 130 132 | 
             
            - lib/hydra/keycloak/version.rb
         | 
| 133 | 
            +
            - release.sh
         | 
| 131 134 | 
             
            - run_tests.sh
         | 
| 132 135 | 
             
            homepage: https://github.com/hydra-billing/hydra-keycloak-client
         | 
| 133 136 | 
             
            licenses: []
         | 
| @@ -136,7 +139,7 @@ metadata: | |
| 136 139 | 
             
              homepage_uri: https://github.com/hydra-billing/hydra-keycloak-client
         | 
| 137 140 | 
             
              source_code_uri: https://github.com/hydra-billing/hydra-keycloak-client
         | 
| 138 141 | 
             
              changelog_uri: https://github.com/hydra-billing/hydra-keycloak-client
         | 
| 139 | 
            -
            post_install_message:
         | 
| 142 | 
            +
            post_install_message: 
         | 
| 140 143 | 
             
            rdoc_options: []
         | 
| 141 144 | 
             
            require_paths:
         | 
| 142 145 | 
             
            - lib
         | 
| @@ -152,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 152 155 | 
             
                  version: '0'
         | 
| 153 156 | 
             
            requirements: []
         | 
| 154 157 | 
             
            rubygems_version: 3.1.6
         | 
| 155 | 
            -
            signing_key:
         | 
| 158 | 
            +
            signing_key: 
         | 
| 156 159 | 
             
            specification_version: 4
         | 
| 157 160 | 
             
            summary: Keycloak client for SSO
         | 
| 158 161 | 
             
            test_files: []
         |