omniauth-krystal 1.2.0 → 1.3.0
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
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ff1b48f554292417ddac46a620f11567455881410467ab639f74fa5eb87e2628
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 0dfc2595766ef70626614953fcd08539dbe594e457ed5fcdc8efb336f5f1fce9
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 95ed948cb99326e1b8917b06ce87d25133570d1162206eac4310975e7271b033f7b4e7a734a86a6a9949d2e06737e84c77b14fc841ea58f76a5bc703e509d1b8
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 2970ce91eaa7e31c94a877f734fe5413ad21b1f5166c7c3628b7a1b0489315088c776b50e6b3fd0bf367db7928fa1ea63d58521661567954fdfa195bedfe5106
         
     | 
| 
         @@ -0,0 +1,91 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'jwt'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'omniauth/krystal/signing_keys'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module OmniAuth
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Krystal
         
     | 
| 
      
 8 
     | 
    
         
            +
                class InitiatedLoginMiddleware
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class Error < StandardError
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  class JWTDecodeError < Error
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  class JWTExpiredError < Error
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  class AntiReplayTokenAlreadyUsedError < Error
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def initialize(app, options = {})
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @app = app
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @options = options
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    @options[:provider_name] ||= 'krystal'
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @options[:identity_url] ||= ENV.fetch('KRYSTAL_IDENTITY_URL', 'https://identity.k.io')
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @options[:anti_replay_expiry_seconds] ||= 60
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    @keys = SigningKeys.new("#{@options[:identity_url]}/.well-known/signing.json")
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  # rubocop:disable Metrics/MethodLength
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # rubocop:disable Metrics/AbcSize
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def call(env)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    unless env['PATH_INFO'] == "/auth/#{@options[:provider_name]}/callback"
         
     | 
| 
      
 36 
     | 
    
         
            +
                      # If it's not a Krystal Identity auth callback then we don't
         
     | 
| 
      
 37 
     | 
    
         
            +
                      # need to do anything here.
         
     | 
| 
      
 38 
     | 
    
         
            +
                      return @app.call(env)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    request = Rack::Request.new(env)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    state = request.params['state']
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    if state.nil? || !state.start_with?('kidil_')
         
     | 
| 
      
 45 
     | 
    
         
            +
                      # Return to the app if the state is not a Krystal Identity
         
     | 
| 
      
 46 
     | 
    
         
            +
                      # initiated login.
         
     | 
| 
      
 47 
     | 
    
         
            +
                      return @app.call(env)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    # Decode the JWT and ensure that the state is valid. JWT will check
         
     | 
| 
      
 51 
     | 
    
         
            +
                    # the expiry.
         
     | 
| 
      
 52 
     | 
    
         
            +
                    data = nil
         
     | 
| 
      
 53 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 54 
     | 
    
         
            +
                      data, = JWT.decode(state.sub(/\Akidil_/, ''), nil, true, { algorithm: 'ES256', jwks: @keys })
         
     | 
| 
      
 55 
     | 
    
         
            +
                    rescue JWT::ExpiredSignature
         
     | 
| 
      
 56 
     | 
    
         
            +
                      raise JWTExpiredError, 'State parameter has expired'
         
     | 
| 
      
 57 
     | 
    
         
            +
                    rescue JWT::DecodeError
         
     | 
| 
      
 58 
     | 
    
         
            +
                      raise JWTDecodeError,
         
     | 
| 
      
 59 
     | 
    
         
            +
                            'Invalid state parameter provided (either malformed, expired or signed with the wrong key)'
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    # Verify the replay token
         
     | 
| 
      
 63 
     | 
    
         
            +
                    verify_anti_replay_token(data['ar'])
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    # Set the expected omniauth state to the state that we have been given so it
         
     | 
| 
      
 66 
     | 
    
         
            +
                    # thinks the session is trusted as normal.
         
     | 
| 
      
 67 
     | 
    
         
            +
                    env['rack.session']['omniauth.state'] = state
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    @app.call(env)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # rubocop:enable Metrics/AbcSize
         
     | 
| 
      
 72 
     | 
    
         
            +
                  # rubocop:enable Metrics/MethodLength
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  private
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  def verify_anti_replay_token(token)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    return if @options[:redis].nil?
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    redis = @options[:redis]
         
     | 
| 
      
 80 
     | 
    
         
            +
                    key = "kidil-ar:#{token}"
         
     | 
| 
      
 81 
     | 
    
         
            +
                    if redis.get(key)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      raise AntiReplayTokenAlreadyUsedError, 'Anti replay token has already been used'
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    redis.set(key,
         
     | 
| 
      
 86 
     | 
    
         
            +
                              Time.now.to_i,
         
     | 
| 
      
 87 
     | 
    
         
            +
                              nx: true, ex: @options[:anti_replay_expiry_seconds])
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'uri'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'net/http'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module OmniAuth
         
     | 
| 
      
 8 
     | 
    
         
            +
              module Krystal
         
     | 
| 
      
 9 
     | 
    
         
            +
                class SigningKeys
         
     | 
| 
      
 10 
     | 
    
         
            +
                  attr_reader :cache, :cache_set_at
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def initialize(url)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @url = url
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @cache = nil
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def call(_options)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    invalidate_cache_if_appropriate
         
     | 
| 
      
 19 
     | 
    
         
            +
                    return @cache if @cache
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    download_jwks
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @cache_set_at = Time.now.to_i
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @cache
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  private
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  # Invalidate the cache if it's been more than 5 minutes since we last
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # cached the data.
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def invalidate_cache_if_appropriate
         
     | 
| 
      
 31 
     | 
    
         
            +
                    return if @cache_set_at && @cache_set_at >= (Time.now.to_i - 300)
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    @cache = nil
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  # rubocop:disable Metrics/AbcSize
         
     | 
| 
      
 37 
     | 
    
         
            +
                  def download_jwks
         
     | 
| 
      
 38 
     | 
    
         
            +
                    uri = URI.parse(@url)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    http.use_ssl = true if uri.scheme == 'https'
         
     | 
| 
      
 41 
     | 
    
         
            +
                    request = Net::HTTP::Get.new(uri.request_uri)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    response = http.request(request)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    raise Error, "Failed to download signing keys from #{@url}" if response.code != '200'
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    body = JSON.parse(response.body)
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    @cache = JWT::JWK::Set.new(body)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @cache.select! { |key| key[:use] == 'sig' }
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @cache
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # rubocop:enable Metrics/AbcSize
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -38,6 +38,7 @@ module OmniAuth 
     | 
|
| 
       38 
38 
     | 
    
         
             
                    }
         
     | 
| 
       39 
39 
     | 
    
         
             
                  end
         
     | 
| 
       40 
40 
     | 
    
         | 
| 
      
 41 
     | 
    
         
            +
                  # rubocop:disable Metrics/AbcSize
         
     | 
| 
       41 
42 
     | 
    
         
             
                  def initialize(app, *args, &block)
         
     | 
| 
       42 
43 
     | 
    
         
             
                    super
         
     | 
| 
       43 
44 
     | 
    
         | 
| 
         @@ -45,6 +46,7 @@ module OmniAuth 
     | 
|
| 
       45 
46 
     | 
    
         
             
                    options.client_options.authorize_url ||= "#{options.client_options.url}/oauth2/auth"
         
     | 
| 
       46 
47 
     | 
    
         
             
                    options.client_options.token_url ||= "#{options.client_options.url}/oauth2/token"
         
     | 
| 
       47 
48 
     | 
    
         
             
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # rubocop:enable Metrics/AbcSize
         
     | 
| 
       48 
50 
     | 
    
         | 
| 
       49 
51 
     | 
    
         
             
                  def scope
         
     | 
| 
       50 
52 
     | 
    
         
             
                    access_token['scope']
         
     | 
    
        data/lib/omniauth-krystal.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,15 +1,43 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: omniauth-krystal
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.3.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Adam Cooke
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2023-05- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2023-05-29 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: json
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: jwt
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       13 
41 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
42 
     | 
    
         
             
              name: omniauth
         
     | 
| 
       15 
43 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -46,6 +74,8 @@ extensions: [] 
     | 
|
| 
       46 
74 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       47 
75 
     | 
    
         
             
            files:
         
     | 
| 
       48 
76 
     | 
    
         
             
            - lib/omniauth-krystal.rb
         
     | 
| 
      
 77 
     | 
    
         
            +
            - lib/omniauth/krystal/initiated_login_middleware.rb
         
     | 
| 
      
 78 
     | 
    
         
            +
            - lib/omniauth/krystal/signing_keys.rb
         
     | 
| 
       49 
79 
     | 
    
         
             
            - lib/omniauth/krystal/version.rb
         
     | 
| 
       50 
80 
     | 
    
         
             
            - lib/omniauth/strategies/krystal.rb
         
     | 
| 
       51 
81 
     | 
    
         
             
            homepage: https://github.com/krystal/omniauth-krystal
         
     |