class AccessToken

Constants

AUTHORIZATION_HEADER
BEARER_HEADER
BEARER_REGEX
EXPIRES_HEADER
ID_KEY
SIGNATURE_KEY
TIME_KEY
VERSION

Attributes

encryptor[R]

Set the token encryptor strategy. By default it uses the Parsel::JSON encryptor.

request[R]

Set the HTTP request object. It must implement the `ip` and `user_agent` methods.

response[R]

Set the HTTP response object. It must implement the `headers` method.

secret[R]

Set the encryption secret.

store[R]

Set the token store strategy. By default it uses in-memory store.

ttl[R]

Set the token TTL. Defaults to 86400 (24 hours).

Public Class Methods

new(request:, response:, secret:, ttl: 3600, store: NullStore.new, encryptor: Parsel::JSON) click to toggle source
# File lib/access_token.rb, line 40
def initialize(request:, response:, secret:, ttl: 3600, store: NullStore.new, encryptor: Parsel::JSON)
  @request = request
  @response = response
  @store = store
  @secret = secret
  @ttl = ttl
  @encryptor = encryptor
end

Public Instance Methods

bearer() click to toggle source
# File lib/access_token.rb, line 77
def bearer
  request.env[AUTHORIZATION_HEADER].to_s[BEARER_REGEX, 1]
end
fresh?(timestamp) click to toggle source
# File lib/access_token.rb, line 81
def fresh?(timestamp)
  timestamp > Time.now.to_i - ttl
end
request_signature() click to toggle source
# File lib/access_token.rb, line 49
def request_signature
  @request_signature ||= Digest::SHA1.hexdigest("#{request.ip}#{request.user_agent}")
end
resolve(token = bearer) click to toggle source
# File lib/access_token.rb, line 64
def resolve(token = bearer)
  return unless store.key?(token)

  data = encryptor.decrypt(secret, token)
  store.del(token)

  return unless data
  return unless fresh?(data[TIME_KEY])
  return unless secure_compare?(request_signature, data[SIGNATURE_KEY])

  data[ID_KEY]
end
secure_compare?(a, b) click to toggle source
# File lib/access_token.rb, line 85
def secure_compare?(a, b)
   return false if a.blank? || b.blank? || a.bytesize != b.bytesize
   l = a.unpack "C#{a.bytesize}"

   res = 0
   b.each_byte { |byte| res |= byte ^ l.shift }
   res == 0
end
update(record) click to toggle source
# File lib/access_token.rb, line 53
def update(record)
  now = Time.now
  timestamp = now.to_i
  data = {TIME_KEY => timestamp, SIGNATURE_KEY => request_signature, ID_KEY => record.id}
  token = encryptor.encrypt(secret, data)
  store.set(token, timestamp, ttl)
  response[BEARER_HEADER] = token
  response[EXPIRES_HEADER] = (Time.now + ttl).httpdate
  token
end