Class: TokenManager
- Inherits:
-
Object
- Object
- TokenManager
- Defined in:
- lib/token_manager.rb,
lib/token_manager/version.rb
Defined Under Namespace
Classes: FaradayMiddleware, RetrievePublicKeyError
Constant Summary collapse
- ALGO =
'RS256'
- VERSION =
'0.1.1'
Class Method Summary collapse
Instance Method Summary collapse
- #decode(jwt, options = {}) ⇒ Object
- #encode(payload) ⇒ Object
- #generate_private_key(expire_current_token: true) ⇒ Object
-
#initialize(options) ⇒ TokenManager
constructor
A new instance of TokenManager.
- #key_id ⇒ Object
- #public_key(kid = key_id) ⇒ Object
Constructor Details
#initialize(options) ⇒ TokenManager
Returns a new instance of TokenManager.
19 20 21 22 23 24 25 26 |
# File 'lib/token_manager.rb', line 19 def initialize() = .deep_stringify_keys @service_name = ['service_name'] || raise(ArgumentError, '`service_name` is required') @trusted_issuers = ['trusted_issuers'] || {} @token_ttl = ['token_ttl'] @public_key_ttl = ['public_key_ttl'] || 1.month @old_key_ttl = ['old_key_ttl'] || 1.week end |
Class Method Details
.token_from(headers) ⇒ Object
14 15 16 17 |
# File 'lib/token_manager.rb', line 14 def self.token_from(headers) headers['Authorization']&.match(/Token token="(\S+)"/)&.[](1) || headers['Authorization']&.match(/Bearer (\S+)/)&.[](1) end |
Instance Method Details
#decode(jwt, options = {}) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/token_manager.rb', line 40 def decode(jwt, = {}) = .merge( algorithm: ALGO, required_claims: ['exp', 'iss', 'aud'], verify_iss: true, iss: @trusted_issuers.keys, verify_aud: true, aud: @service_name ) ::JWT.decode(jwt, nil, true, ) do |header, payload| OpenSSL::PKey::RSA.new(issuer_public_key(iss: payload['iss'], kid: header['kid'])) end end |
#encode(payload) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/token_manager.rb', line 28 def encode(payload) payload = payload.stringify_keys raise(ArgumentError, '`aud` is required') unless payload.key?('aud') raise(ArgumentError, '`exp` claim or `token_ttl` config option is required') if !payload.key?('exp') && !@token_ttl payload.reverse_merge!(exp: @token_ttl.seconds.from_now.to_i) if @token_ttl payload = payload.merge(iss: @service_name) ::JWT.encode(payload, private_key, ALGO, { kid: key_id }) end |
#generate_private_key(expire_current_token: true) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/token_manager.rb', line 59 def generate_private_key(expire_current_token: true) rsa_private = OpenSSL::PKey::RSA.generate(2048) rsa_public = rsa_private.public_key next_key_id = SecureRandom.uuid with_redis do |redis| redis.multi do |multi| # set new token multi.set(cache_key(:private_key, next_key_id), rsa_private.to_pem) multi.set(cache_key(:public_key, next_key_id), rsa_public.to_pem) multi.set(cache_key(:key_id), next_key_id) if expire_current_token # expire current token multi.expire(cache_key(:private_key, key_id), @old_key_ttl) multi.expire(cache_key(:public_key, key_id), @old_key_ttl) end end end # drop memoization @key_id = next_key_id @private_key = rsa_private rsa_private end |
#key_id ⇒ Object
83 84 85 86 87 88 |
# File 'lib/token_manager.rb', line 83 def key_id @key_id ||= with_redis do |c| c.get(cache_key(:key_id)) || generate_private_key(expire_current_token: false) && c.get(cache_key(:key_id)) end end |
#public_key(kid = key_id) ⇒ Object
54 55 56 57 |
# File 'lib/token_manager.rb', line 54 def public_key(kid = key_id) @public_key ||= {} @public_key[kid.to_s] ||= with_redis { |redis| redis.get(cache_key(:public_key, kid)) } end |