hummingbird-me/hummingbird

View on GitHub
app/models/token.rb

Summary

Maintainability
A
35 mins
Test Coverage
class Token
  TTL = 3.months
  PREFIX = 'REVOKED:'

  def self.secret
    Hummingbird::Application.config.jwt_secret
  end

  def initialize(string_or_user, opts={})
    if string_or_user.is_a? String
      @payload = JWT.decode(string_or_user, Token.secret)[0]
    else
      string_or_user = string_or_user.id if string_or_user.is_a? User
      @payload = {
        'jti' => SecureRandom.uuid,
        'scope' => [],
        'sub' => string_or_user,
        'iss' => Time.now.to_i,
        'exp' => TTL.from_now.to_i
      }.merge(opts.stringify_keys)
    end
  rescue JWT::ExpiredSignature
    @expired = true
  rescue JWT::DecodeError
    @invalid = true
  end

  def has_scope?(scope)
    @payload['scope'].include?(scope) || @payload['scope'].include?("all") if valid?
  end

  def expires_at
    Time.at(@payload['exp'])
  end

  def expires_in
    expires_at - Time.now
  end

  def expired?
    # Be extra careful about expiry, sometimes JWT shits itself
    @expired || expires_at < Time.now
  end

  def revoked?
    $redis.with { |conn| conn.exists(PREFIX + id) } unless id.nil?
  end

  def revoke!
    Token.revoke! id, expires_at
  end

  def self.revoke!(id, expiry)
    $redis.with do |conn|
      conn.set(PREFIX + id, Time.now)
      conn.expireat(PREFIX + id, expiry.to_i)
    end
  end

  def invalid?
    @invalid || expired? || revoked?
  end

  def valid?
    !invalid?
  end

  def user
    User.find_by(id: @payload['sub']) if valid?
  end

  def id
    @payload['jti'] unless @payload.nil?
  end

  def encode
    JWT.encode(@payload, Token.secret)
  end
  alias_method :to_s, :encode
end