Class: Rack::JsonWebTokenAuth
- Inherits:
-
Object
- Object
- Rack::JsonWebTokenAuth
show all
- Includes:
- Contracts::Builtin, Contracts::Core
- Defined in:
- lib/rack/json_web_token_auth.rb,
lib/rack/json_web_token_auth/version.rb,
lib/rack/json_web_token_auth/resource.rb,
lib/rack/json_web_token_auth/contracts.rb,
lib/rack/json_web_token_auth/resources.rb,
lib/rack/json_web_token_auth/exceptions.rb
Overview
Rack Middleware for JSON Web Token Authentication
Defined Under Namespace
Classes: HttpMethodError, HttpMethods, Key, RackRequestHttpAuth, RackResponse, Resource, ResourceHttpMethods, Resources, TokenError
Constant Summary
collapse
- ENV_KEY =
'jwt.claims'.freeze
'PATH_INFO'.freeze
- VERSION =
'0.2.0'.freeze
- BEARER_TOKEN_REGEX =
The last segment gets dropped for ‘none’ algorithm since there is no signature so both of these patterns are valid. All character chunks are base64url format and periods.
Bearer abc123.abc123.abc123
Bearer abc123.abc123.
%r{
^Bearer\s{1}( # starts with Bearer and a single space
[a-zA-Z0-9\-\_]+\. # 1 or more chars followed by a single period
[a-zA-Z0-9\-\_]+\. # 1 or more chars followed by a single period
[a-zA-Z0-9\-\_]* # 0 or more chars, no trailing chars
)$
}x
Instance Method Summary
collapse
Constructor Details
Returns a new instance of JsonWebTokenAuth.
21
22
23
24
25
|
# File 'lib/rack/json_web_token_auth.rb', line 21
def initialize(app, &block)
@app = app
instance_eval(&block)
end
|
Instance Method Details
#all_resources ⇒ Object
86
87
88
|
# File 'lib/rack/json_web_token_auth.rb', line 86
def all_resources
@all_resources ||= []
end
|
#call(env) ⇒ Object
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
# File 'lib/rack/json_web_token_auth.rb', line 46
def call(env)
resource = resource_for_path(env[PATH_INFO_HEADER_KEY])
if resource.nil?
raise TokenError, 'No resource for path defined. Deny by default.'
end
if resource.public_resource?
@app.call(env)
else
if resource.invalid_http_method?(env['REQUEST_METHOD'])
raise HttpMethodError, 'HTTP request method denied'
end
unless Contract.valid?(env, RackRequestHttpAuth)
raise TokenError, 'malformed Authorization header or token'
end
token = BEARER_TOKEN_REGEX.match(env['HTTP_AUTHORIZATION'])[1]
jwt_opts = resource.opts[:jwt]
jwt = ::JwtClaims.verify(token, jwt_opts)
handle_token(env, jwt)
@app.call(env)
end
rescue TokenError => e
return_401(e.message)
rescue StandardError
return_401
end
|
#handle_token(env, jwt) ⇒ Object
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
# File 'lib/rack/json_web_token_auth.rb', line 112
def handle_token(env, jwt)
if Contract.valid?(jwt, HashOf[ok: HashOf[Symbol => Any]])
env[ENV_KEY] = Hashie.stringify_keys(jwt[:ok])
elsif Contract.valid?(jwt, HashOf[error: ArrayOf[Symbol]])
raise TokenError, "invalid JWT claims : #{jwt[:error].sort.join(', ')}"
elsif Contract.valid?(jwt, HashOf[error: 'invalid JWT'])
raise TokenError, 'invalid JWT'
elsif Contract.valid?(jwt, HashOf[error: 'invalid input'])
raise TokenError, 'invalid JWT input'
else
raise TokenError, 'unhandled JWT error'
end
end
|
#resource_for_path(path_info) ⇒ Object
91
92
93
94
95
96
97
|
# File 'lib/rack/json_web_token_auth.rb', line 91
def resource_for_path(path_info)
all_resources.each do |r|
found = r.resource_for_path(path_info)
return found unless found.nil?
end
nil
end
|
#return_401(msg = nil) ⇒ Object
100
101
102
103
104
105
106
|
# File 'lib/rack/json_web_token_auth.rb', line 100
def return_401(msg = nil)
body = msg.nil? ? 'Unauthorized' : "Unauthorized : #{msg}"
= { 'WWW-Authenticate' => 'Bearer error="invalid_token"',
'Content-Type' => 'text/plain',
'Content-Length' => body.bytesize.to_s }
[401, , [body]]
end
|
#secured(&block) ⇒ Object
28
29
30
31
32
33
34
|
# File 'lib/rack/json_web_token_auth.rb', line 28
def secured(&block)
resources = Resources.new(public_resource: false)
resources.instance_eval(&block)
all_resources << resources
end
|
#unsecured(&block) ⇒ Object
37
38
39
40
41
42
43
|
# File 'lib/rack/json_web_token_auth.rb', line 37
def unsecured(&block)
resources = Resources.new(public_resource: true)
resources.instance_eval(&block)
all_resources << resources
end
|