Class: RackJwtVerifier::Middleware
- Inherits:
-
Object
- Object
- RackJwtVerifier::Middleware
- Defined in:
- lib/rack_jwt_verifier/middleware.rb
Overview
The primary middleware class responsible for intercepting requests, extracting the JWT, verifying it, and injecting the user's details into the Rack environment.
Constant Summary collapse
- RACK_ENV_PAYLOAD_KEY =
The default key in the Rack environment used to store the verified JWT payload. This can be accessed by downstream applications (e.g., Rails controllers) to retrieve the authenticated user's details.
"rack_jwt_verifier.payload"- RETRY_AFTER_SECONDS =
Seconds a client is told to wait before retrying after a 503.
5- NULL_LOGGER =
Used when neither a :logger option nor env is available.
Logger.new(IO::NULL)
- ERROR_RESPONSES =
Status and default plain-text body for each way a request can be refused. The reason symbol doubles as the
errorcode in JSON bodies. { missing_token: [401, "Unauthorized: Bearer token required."], invalid_token: [401, "Unauthorized: Invalid or expired JWT."], insufficient_scope: [403, "Forbidden: the token does not grant the required scope."], key_unavailable: [503, "Service Unavailable: could not fetch the token verification key."], replay_cache_unavailable: [503, "Service Unavailable: could not check the token for replay."] }.freeze
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#initialize(app, options = {}) ⇒ Middleware
constructor
A new instance of Middleware.
Constructor Details
#initialize(app, options = {}) ⇒ Middleware
Returns a new instance of Middleware.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/rack_jwt_verifier/middleware.rb', line 54 def initialize(app, = {}) @app = app @logger = [:logger] @env_key = .fetch(:env_key, RACK_ENV_PAYLOAD_KEY) @skip = validate_skip_rules(Array([:skip])) @json_errors = .fetch(:json_errors, false) @on_error = [:on_error] @require_scopes = validate_required_scopes([:require_scopes]) @require_token = resolve_require_token() # The Verifier instance is initialized with options (like public_key_url) # and is responsible for all crypto and key management. @verifier = Verifier.new() enforce_claim_policy() end |
Instance Method Details
#call(env) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/rack_jwt_verifier/middleware.rb', line 71 def call(env) return @app.call(env) if skip?(env) token = extract_token(env) # With no token the request is passed down the stack and the application # decides how to treat the unauthenticated state — unless require_token # is set, in which case it is rejected here. unless token return error_response(env, :missing_token, nil) if @require_token return @app.call(env) end # Only the verification step is guarded: a JWT::DecodeError raised by the # downstream application must propagate, not be turned into a 401 here. begin # Use the Verifier to handle the complex crypto and validation logic payload = @verifier.verify(token) rescue JWT::DecodeError => e # Invalid signature, expired, bad claim: the client's problem. logger(env).warn { "rack_jwt_verifier: token rejected: #{e.message}" } return error_response(env, :invalid_token, e) rescue KeyFetchError => e # We could not obtain the key to check the token: our problem, not the # client's, so answer 503 rather than 401. logger(env).error { "rack_jwt_verifier: #{e.message}" } return error_response(env, :key_unavailable, e) rescue ReplayCacheError => e logger(env).error { "rack_jwt_verifier: #{e.message}" } return error_response(env, :replay_cache_unavailable, e) end missing = Scopes.missing(payload, @require_scopes) unless missing.empty? error = InsufficientScopeError.new(required: @require_scopes, missing: missing) logger(env).warn { "rack_jwt_verifier: #{error.message}" } return error_response(env, :insufficient_scope, error) end # On successful verification, store the payload in the Rack environment env[@env_key] = payload @app.call(env) end |