grape-jwt-authentication 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +43 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +114 -0
- data/LICENSE +21 -0
- data/README.md +391 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/doc/assets/logo.png +0 -0
- data/doc/assets/project.png +0 -0
- data/doc/assets/project.xcf +0 -0
- data/grape-jwt-authentication.gemspec +39 -0
- data/lib/grape/jwt/authentication.rb +64 -0
- data/lib/grape/jwt/authentication/configuration.rb +130 -0
- data/lib/grape/jwt/authentication/jwt.rb +112 -0
- data/lib/grape/jwt/authentication/jwt_handler.rb +118 -0
- data/lib/grape/jwt/authentication/rsa_public_key.rb +126 -0
- data/lib/grape/jwt/authentication/version.rb +9 -0
- metadata +261 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "grape/jwt/authentication"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/doc/assets/logo.png
ADDED
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'grape/jwt/authentication/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'grape-jwt-authentication'
|
9
|
+
spec.version = Grape::Jwt::Authentication::VERSION
|
10
|
+
spec.authors = ['Hermann Mayer']
|
11
|
+
spec.email = ['hermann.mayer92@gmail.com']
|
12
|
+
|
13
|
+
spec.summary = 'A reusable Grape JWT authentication concern'
|
14
|
+
spec.description = 'A reusable Grape JWT authentication concern'
|
15
|
+
spec.homepage = 'https://github.com/hausgold/grape-jwt-authentication'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = 'exe'
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
|
+
spec.add_development_dependency 'simplecov', '~> 0.15'
|
28
|
+
spec.add_development_dependency 'vcr', '~> 3.0'
|
29
|
+
spec.add_development_dependency 'webmock', '~> 3.1'
|
30
|
+
spec.add_development_dependency 'timecop', '~> 0.9.1'
|
31
|
+
spec.add_development_dependency 'rack', '~> 2.0'
|
32
|
+
spec.add_development_dependency 'rack-test', '~> 0.8.2'
|
33
|
+
|
34
|
+
spec.add_runtime_dependency 'activesupport', '>= 3.2.0'
|
35
|
+
spec.add_runtime_dependency 'grape', '~> 1.0'
|
36
|
+
spec.add_runtime_dependency 'httparty'
|
37
|
+
spec.add_runtime_dependency 'jwt', '~> 2.1'
|
38
|
+
spec.add_runtime_dependency 'recursive-open-struct', '~> 1.0'
|
39
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/concern'
|
5
|
+
require 'active_support/configurable'
|
6
|
+
require 'active_support/cache'
|
7
|
+
require 'active_support/core_ext/hash'
|
8
|
+
require 'active_support/time'
|
9
|
+
require 'active_support/time_with_zone'
|
10
|
+
|
11
|
+
require 'jwt'
|
12
|
+
|
13
|
+
require 'grape'
|
14
|
+
require 'grape/jwt/authentication/version'
|
15
|
+
require 'grape/jwt/authentication/configuration'
|
16
|
+
require 'grape/jwt/authentication/jwt_handler'
|
17
|
+
require 'grape/jwt/authentication/jwt'
|
18
|
+
require 'grape/jwt/authentication/rsa_public_key'
|
19
|
+
|
20
|
+
module Grape
|
21
|
+
module Jwt
|
22
|
+
# The Grape JWT authentication concern.
|
23
|
+
module Authentication
|
24
|
+
extend ActiveSupport::Concern
|
25
|
+
include Grape::DSL::API
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_writer :configuration
|
29
|
+
end
|
30
|
+
|
31
|
+
# Retrieve the current configuration object.
|
32
|
+
#
|
33
|
+
# @return [Configuration]
|
34
|
+
def self.configuration
|
35
|
+
@configuration ||= Configuration.new
|
36
|
+
end
|
37
|
+
|
38
|
+
# Configure the concern by providing a block which takes
|
39
|
+
# care of this task. Example:
|
40
|
+
#
|
41
|
+
# Grape::Jwt::Authentication.configure do |conf|
|
42
|
+
# # conf.xyz = [..]
|
43
|
+
# end
|
44
|
+
def self.configure
|
45
|
+
yield(configuration)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Reset the current configuration with the default one.
|
49
|
+
def self.reset_configuration!
|
50
|
+
self.configuration = Configuration.new
|
51
|
+
end
|
52
|
+
|
53
|
+
included do
|
54
|
+
# Configure a new Grape authentication strategy which will be
|
55
|
+
# backed by the JwtHandler middleware. We do not want
|
56
|
+
# gem-internal claim verification or database lookup
|
57
|
+
# functionality. Let the user handle this the way he want.
|
58
|
+
Grape::Middleware::Auth::Strategies.add(:jwt,
|
59
|
+
JwtHandler,
|
60
|
+
->(opts) { [opts] })
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Jwt
|
5
|
+
module Authentication
|
6
|
+
# The configuration for the Grape JWT authentication concern.
|
7
|
+
class Configuration
|
8
|
+
include ActiveSupport::Configurable
|
9
|
+
|
10
|
+
# The authenticator function which must be defined by the user to
|
11
|
+
# verify the given JSON Web Token. Here comes all your logic to lookup
|
12
|
+
# the related user on your database, the token claim verification
|
13
|
+
# and/or the token cryptographic signing. The function must return true
|
14
|
+
# or false to indicate the validity of the token.
|
15
|
+
#
|
16
|
+
# Grape::Jwt::Authentication.configure do |conf|
|
17
|
+
# conf.authenticator = proc do |token|
|
18
|
+
# # Verify the token the way you like.
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
config_accessor(:authenticator) { proc { false } }
|
22
|
+
|
23
|
+
# Whenever the given value on the +Authorization+ header is not a valid
|
24
|
+
# Bearer authentication scheme or the token itself is not a valid JSON
|
25
|
+
# Web Token, this user defined function will be called. You can add
|
26
|
+
# custom handling of this situations, like responding different HTTP
|
27
|
+
# status code, or bodies. By default the Rack stack will be interrupted
|
28
|
+
# and a response with the 400 Bad Request status code will be send to
|
29
|
+
# the client. The raw token (value of the +Authorization+ header) and
|
30
|
+
# the Rack app will be injected to your function for maximum
|
31
|
+
# flexibility.
|
32
|
+
#
|
33
|
+
# Grape::Jwt::Authentication.configure do |conf|
|
34
|
+
# conf.malformed_auth_handler = proc do |raw_token, app|
|
35
|
+
# # Do your own error handling.
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
config_accessor(:malformed_auth_handler) do
|
39
|
+
proc do |_raw_token, _app|
|
40
|
+
[400, {}, ['Authorization header is malformed.']]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# When the client sends a corrected formatted JSON Web Token with the
|
45
|
+
# Bearer authentication scheme within the +Authorization+ header and
|
46
|
+
# your authenticator fails for some reason (token claims, wrong
|
47
|
+
# audience, bad subject, expired token, wrong cryptographic signing
|
48
|
+
# etc), this function is called to handle the bad authentication. By
|
49
|
+
# default the Rack stack will be interrupted and a response with the
|
50
|
+
# 401 Unauthorized status code will be send to the client. You can
|
51
|
+
# customize this the way you like and send different error codes, or
|
52
|
+
# handle the error completely different. The parsed JSON Web Token and
|
53
|
+
# the Rack app will be injected to your function to allow any
|
54
|
+
# customized error handling.
|
55
|
+
#
|
56
|
+
# Grape::Jwt::Authentication.configure do |conf|
|
57
|
+
# conf.failed_auth_handler = proc do |token, app|
|
58
|
+
# # Do your own error handling.
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
config_accessor(:failed_auth_handler) do
|
62
|
+
proc do |_token, _app|
|
63
|
+
[401, {}, ['Access denied.']]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Whenever you want to use the {RsaPublicKey} class you configure the
|
68
|
+
# default URL on the singleton instance, or use the gem configure
|
69
|
+
# method and set it up accordingly. We allow the fetch of the public
|
70
|
+
# key from a remote server (HTTP/HTTPS) or from a local file which is
|
71
|
+
# accessible by the ruby process. Specify the URL or the local path
|
72
|
+
# here.
|
73
|
+
config_accessor(:rsa_public_key_url) { nil }
|
74
|
+
|
75
|
+
# You can preconfigure the {RsaPublickey} class to enable/disable
|
76
|
+
# caching. For a remote public key location it is handy to cache the
|
77
|
+
# result for some time to keep the traffic low to this resource server.
|
78
|
+
# For a local file you can skip this.
|
79
|
+
config_accessor(:rsa_public_key_caching) { false }
|
80
|
+
|
81
|
+
# When you make use of the caching of the {RsaPublicKey} class you can
|
82
|
+
# fine tune the expiration time of this cache. The RSA public key from
|
83
|
+
# your identity provider should not change this frequent, so a cache
|
84
|
+
# for at least one hour is fine. You should not set it lower than one
|
85
|
+
# minute. Keep this setting in mind when you change keys. Your
|
86
|
+
# infrastructure could be inoperable for this configured time.
|
87
|
+
config_accessor(:rsa_public_key_expiration) { 1.hour }
|
88
|
+
|
89
|
+
# The JSON Web Token isser which should be used for verification.
|
90
|
+
config_accessor(:jwt_issuer) { nil }
|
91
|
+
|
92
|
+
# The resource server (namely the one which configures this right now)
|
93
|
+
# which MUST be present on the JSON Web Token audience claim.
|
94
|
+
config_accessor(:jwt_beholder) { nil }
|
95
|
+
|
96
|
+
# You can configure a different JSON Web Token verification option hash
|
97
|
+
# if your algorithm differs or you want some extra/different options.
|
98
|
+
# Just watch out that you have to pass a proc to this configuration
|
99
|
+
# property. On the {Grape::Jwt::Authentication::Jwt} class it has to be
|
100
|
+
# a simple hash. The default is here the RS256 algorithm with enabled
|
101
|
+
# expiration check, and issuer+audience check when the
|
102
|
+
# {jwt_issuer}/{jwt_beholder} are configured accordingly.
|
103
|
+
config_accessor(:jwt_options) do
|
104
|
+
proc do
|
105
|
+
conf = Grape::Jwt::Authentication.configuration
|
106
|
+
{ algorithm: 'RS256',
|
107
|
+
exp_leeway: 30.seconds.to_i,
|
108
|
+
iss: conf.jwt_issuer,
|
109
|
+
verify_iss: !conf.jwt_issuer.nil?,
|
110
|
+
aud: conf.jwt_beholder,
|
111
|
+
verify_aud: !conf.jwt_beholder.nil?,
|
112
|
+
# @TODO: https://github.com/jwt/ruby-jwt/issues/247
|
113
|
+
verify_iat: false }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# You can configure your own verification key on the Jwt wrapper class.
|
118
|
+
# This way you can pass your HMAC secret or your ECDSA public key to
|
119
|
+
# the JSON Web Token validation method. Here you need to pass a proc,
|
120
|
+
# on the {Grape::Jwt::Authentication::Jwt} class it has to be a scalar
|
121
|
+
# value. By default we use the
|
122
|
+
# {Grape::Jwt::Authentication::RsaPublicKey} class to retrieve the RSA
|
123
|
+
# public key.
|
124
|
+
config_accessor(:jwt_verification_key) do
|
125
|
+
proc { RsaPublicKey.instance.fetch }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'recursive-open-struct'
|
4
|
+
|
5
|
+
module Grape
|
6
|
+
module Jwt
|
7
|
+
module Authentication
|
8
|
+
# A easy to use model for verification of JSON Web Tokens. This is just a
|
9
|
+
# wrapper class for the excellent ruby-jwt gem. It's completely up to you
|
10
|
+
# to use it. But be aware, its a bit optinionated by default.
|
11
|
+
class Jwt
|
12
|
+
# All the following JWT verification issues lead to a failed validation.
|
13
|
+
RESCUE_JWT_EXCEPTIONS = [
|
14
|
+
::JWT::DecodeError,
|
15
|
+
::JWT::VerificationError,
|
16
|
+
::JWT::ExpiredSignature,
|
17
|
+
::JWT::IncorrectAlgorithm,
|
18
|
+
::JWT::ImmatureSignature,
|
19
|
+
::JWT::InvalidIssuerError,
|
20
|
+
::JWT::InvalidIatError,
|
21
|
+
::JWT::InvalidAudError,
|
22
|
+
::JWT::InvalidSubError,
|
23
|
+
::JWT::InvalidJtiError,
|
24
|
+
::JWT::InvalidPayload
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
# :reek:Attribute because its fine to be extern-modifiable at these
|
28
|
+
# instances
|
29
|
+
attr_reader :payload, :token
|
30
|
+
attr_writer :verification_key, :jwt_options
|
31
|
+
attr_accessor :issuer, :beholder
|
32
|
+
|
33
|
+
# Setup a new JWT instance. You have to pass the raw JSON Web Token to
|
34
|
+
# the initializer. Example:
|
35
|
+
#
|
36
|
+
# Jwt.new('j.w.t')
|
37
|
+
# # => <Jwt>
|
38
|
+
#
|
39
|
+
# @return [Jwt]
|
40
|
+
def initialize(token)
|
41
|
+
parsed_payload = JWT.decode(token, nil, false).first.symbolize_keys
|
42
|
+
@token = token
|
43
|
+
@payload = RecursiveOpenStruct.new(parsed_payload)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks if the payload says this is a refresh token.
|
47
|
+
#
|
48
|
+
# @return [Boolean] Whenever this is a access token
|
49
|
+
def access_token?
|
50
|
+
payload.typ == 'access'
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks if the payload says this is a refresh token.
|
54
|
+
#
|
55
|
+
# @return [Boolean] Whenever this is a refresh token
|
56
|
+
def refresh_token?
|
57
|
+
payload.typ == 'refresh'
|
58
|
+
end
|
59
|
+
|
60
|
+
# Retrives the expiration date from the payload when set.
|
61
|
+
#
|
62
|
+
# @return [nil|ActiveSupport::TimeWithZone] The expiration date
|
63
|
+
def expires_at
|
64
|
+
exp = payload.exp
|
65
|
+
return nil unless exp
|
66
|
+
Time.zone.at(exp)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Deliver the public key for verification by default. This uses the
|
70
|
+
# {RsaPublicKey} class, but you can configure the verification key the
|
71
|
+
# way you like. (Especially for different algorithms, like HMAC or
|
72
|
+
# ECDSA) Just make use of the same named setter.
|
73
|
+
#
|
74
|
+
# @return [OpenSSL::PKey::RSA|Mixed] The verification key
|
75
|
+
def verification_key
|
76
|
+
unless @verification_key
|
77
|
+
conf = Grape::Jwt::Authentication.configuration
|
78
|
+
return conf.jwt_verification_key.call
|
79
|
+
end
|
80
|
+
@verification_key
|
81
|
+
end
|
82
|
+
|
83
|
+
# This getter passes back the default JWT verification option hash
|
84
|
+
# which is optinionated. You can change this the way you like by
|
85
|
+
# configuring your options with the help of the same named setter.
|
86
|
+
#
|
87
|
+
# @return [Hash] The JWT verification options hash
|
88
|
+
def jwt_options
|
89
|
+
unless @jwt_options
|
90
|
+
conf = Grape::Jwt::Authentication.configuration
|
91
|
+
return conf.jwt_options.call
|
92
|
+
end
|
93
|
+
@jwt_options
|
94
|
+
end
|
95
|
+
|
96
|
+
# Verify the current token by our hard and strict rules. Whenever the
|
97
|
+
# token was not parsed from a string, we encode the current state to a
|
98
|
+
# JWT string representation and check this.
|
99
|
+
#
|
100
|
+
# @return [Boolean] Whenever the token is valid or not
|
101
|
+
#
|
102
|
+
# :reek:NilCheck because we have to check the token
|
103
|
+
# origin and react on it
|
104
|
+
def valid?
|
105
|
+
JWT.decode(token, verification_key, true, jwt_options) && true
|
106
|
+
rescue *RESCUE_JWT_EXCEPTIONS
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Jwt
|
5
|
+
module Authentication
|
6
|
+
# Take care of the token validation and verification on this
|
7
|
+
# Rack middleware. It is a self contained implementation of a
|
8
|
+
# valid Rack handler which checks for a common JWT token
|
9
|
+
# Authorization header and calls a user given verification block
|
10
|
+
# which performs the database lookup or whatever is necessary
|
11
|
+
# for the verification.
|
12
|
+
class JwtHandler
|
13
|
+
# A internal exception handling for failed authentications.
|
14
|
+
class AuthenticationError < StandardError; end
|
15
|
+
# A internal exception handling for malformed headers.
|
16
|
+
class MalformedHeaderError < StandardError; end
|
17
|
+
|
18
|
+
# A common JWT validation regex which meets the RFC specs.
|
19
|
+
JWT_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/
|
20
|
+
|
21
|
+
# Initialize a new Rack middleware for Bearer token
|
22
|
+
# processing.
|
23
|
+
#
|
24
|
+
# @param app [Proc] The regular Rack application
|
25
|
+
# @param options [Hash] A global-overwritting configuration hash
|
26
|
+
def initialize(app, options = {})
|
27
|
+
@app = app
|
28
|
+
@options = options
|
29
|
+
end
|
30
|
+
|
31
|
+
# A shared configuration lookup helper which selects the requested
|
32
|
+
# entry from the local or global configuration object. The local
|
33
|
+
# configuration takes presedence over the global one.
|
34
|
+
#
|
35
|
+
# @param key [Symbol] The local config key
|
36
|
+
# @param global_key [Symbol] The global config key
|
37
|
+
# @return [Mixed] The configuration value
|
38
|
+
def config(key, global_key)
|
39
|
+
block = @options[key]
|
40
|
+
unless block
|
41
|
+
global_conf = Grape::Jwt::Authentication.configuration
|
42
|
+
return global_conf.send(global_key)
|
43
|
+
end
|
44
|
+
block
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get the local or global defined authenticator for the JWT handler.
|
48
|
+
#
|
49
|
+
# @return [Proc] The authenticator block
|
50
|
+
def authenticator
|
51
|
+
config(:proc, :authenticator)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get the local or global defined malformed authentication handler for
|
55
|
+
# the JWT handler.
|
56
|
+
#
|
57
|
+
# @return [Proc] The malformed authorization handler block
|
58
|
+
def malformed_handler
|
59
|
+
config(:malformed, :malformed_auth_handler)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get the local or global defined failed authentication handler for the
|
63
|
+
# JWT handler.
|
64
|
+
#
|
65
|
+
# @return [Proc] The failed authentication handler block
|
66
|
+
def failed_handler
|
67
|
+
config(:failed, :failed_auth_handler)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Validate the Bearer authentication scheme on the given
|
71
|
+
# authorization header and validate the JWT token when it was
|
72
|
+
# found.
|
73
|
+
#
|
74
|
+
# @param header [String] The authorization header value
|
75
|
+
# @return [String] The parsed and well-formatted JWT
|
76
|
+
def parse_token(header)
|
77
|
+
token = header.to_s.scan(/^Bearer (.*)/).flatten.first
|
78
|
+
raise MalformedHeaderError unless JWT_REGEX =~ token
|
79
|
+
token
|
80
|
+
end
|
81
|
+
|
82
|
+
# Perform the authentication logic on the Rack compatible
|
83
|
+
# interface.
|
84
|
+
#
|
85
|
+
# :reek:TooManyStatements because reek counts exception
|
86
|
+
# handling as statements
|
87
|
+
def call(env)
|
88
|
+
# Unfortunately Grape's middleware stack orders the error
|
89
|
+
# handling higher than the formatter. So when a error is
|
90
|
+
# raised, the Rack env was not yet analysed and the content
|
91
|
+
# type not negotiated. This would result in allways-JSON
|
92
|
+
# responses on authentication errors. We want to be smarter
|
93
|
+
# here and respond in the requested format on authentication
|
94
|
+
# errors, that why we invoke the formatter middleware here.
|
95
|
+
Grape::Middleware::Formatter.new(->(_) {}).call(env)
|
96
|
+
|
97
|
+
# Parse the JWT token and give it to the user defined block
|
98
|
+
# for futher verification. The user given block MUST return
|
99
|
+
# a positive result to allow the request to be further
|
100
|
+
# processed, or a negative result to stop processing.
|
101
|
+
token = parse_token(env['HTTP_AUTHORIZATION'])
|
102
|
+
raise AuthenticationError unless authenticator.call(token)
|
103
|
+
|
104
|
+
# Looks like we are on a good path and the given token was
|
105
|
+
# valid on all checks. So we continue the regular
|
106
|
+
# application logic now.
|
107
|
+
@app.call(env)
|
108
|
+
rescue MalformedHeaderError
|
109
|
+
# Call the user defined malformed authentication handler.
|
110
|
+
malformed_handler.call(env['HTTP_AUTHORIZATION'], @app)
|
111
|
+
rescue AuthenticationError
|
112
|
+
# Call the user defined failed authentication handler.
|
113
|
+
failed_handler.call(token, @app)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|