Class: HaveAPI::Authentication::Token::Provider

Inherits:
Base
  • Object
show all
Defined in:
lib/haveapi/authentication/token/provider.rb

Overview

Provider for token authentication.

This provider has to be configured using Config.

Token auth contains API resource ‘token`. User can request a token by calling action `Request`. The returned token is then used for authenticating the user. Client sends the token with each request in configured Config#http_header or Config#query_parameter.

Token can be revoked by calling action ‘Revoke` and renewed with `Renew`.

Example usage:

Token model:

class ApiToken < ActiveRecord::Base
  belongs_to :user

  validates :user_id, :token, presence: true
  validates :token, length: {is: 100}

  enum lifetime: %i(fixed renewable_manual renewable_auto permanent)

  def renew
    self.valid_to = Time.now + interval
  end
end

Authentication provider configuration:

class MyTokenAuthConfig < HaveAPI::Authentication::Token::Config
  request do
    handle do |req, res|
      user = ::User.find_by(login: input[:user], password: input[:password])

      if user.nil?
        res.error = 'invalid user or password'
        next res
      end

      token = SecureRandom.hex(50)
      valid_to =
        if req.input[:lifetime] == 'permanent'
          nil
        else
          Time.now + req.input[:interval]

      user.tokens << ::Token.new(
        token: token,
        lifetime: req.input[:lifetime],
        valid_to: valid_to,
        interval: req.input[:interval],
        label: req.request.user_agent,
      )

      res.token = token
      res.valid_to = valid_to
      res.complete = true
      res.ok
    end
  end

  renew do
    handle do |req, res|
      t = ::Token.find_by(user: req.user, token: req.token)

      if t && t.lifetime.start_with('renewable')
        t.renew
        t.save
        res.valid_to = t.valid_to
        res.ok
      else
        res.error = 'unable to renew token'
        res
      end
    end
  end

  revoke do
    handle do |req, res|
      req.user.tokens.delete(token: req.token)
      res.ok
    end
  end

  def find_user_by_token(request, token)
    t = ::Token.find_by(token: token)

    if t
      # Renew the token if needed
      if t.lifetime == 'renewable_auto'
        t.renew
        t.save
      end

      t.user # return the user
    end
  end
end

Finally put the provider in the authentication chain:

api = HaveAPI.new(...)
...
api.auth_chain << HaveAPI::Authentication::Token.with_config(MyTokenAuthConfig)

Instance Attribute Summary collapse

Attributes inherited from Base

#name

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

auth_method, inherited, #register_routes

Constructor Details

#initialize(server, v, cfg) ⇒ Provider

Returns a new instance of Provider.



131
132
133
134
# File 'lib/haveapi/authentication/token/provider.rb', line 131

def initialize(server, v, cfg)
  @config = cfg.new(server, v)
  super(server, v)
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



129
130
131
# File 'lib/haveapi/authentication/token/provider.rb', line 129

def config
  @config
end

Class Method Details

.with_config(cfg) ⇒ Object

Configure the token provider

Parameters:



121
122
123
124
125
126
127
# File 'lib/haveapi/authentication/token/provider.rb', line 121

def self.with_config(cfg)
  Module.new do
    define_singleton_method(:new) do |*args|
      Provider.new(*args, cfg)
    end
  end
end

Instance Method Details

#authenticate(request) ⇒ Object

Authenticate request

Parameters:

  • request (Sinatra::Request)


152
153
154
155
156
# File 'lib/haveapi/authentication/token/provider.rb', line 152

def authenticate(request)
  t = token(request)

  t && config.find_user_by_token(request, t)
end

#describeObject



165
166
167
168
169
170
171
# File 'lib/haveapi/authentication/token/provider.rb', line 165

def describe
  {
    http_header: config.class.http_header,
    query_parameter: config.class.query_parameter,
    description: 'The client authenticates with credentials, usually username and password, and gets a token. From this point, the credentials can be forgotten and the token is used instead. Tokens can have different lifetimes, can be renewed and revoked. The token is passed either via HTTP header or query parameter.'
  }
end

#resource_moduleObject



140
141
142
143
144
145
146
147
148
# File 'lib/haveapi/authentication/token/provider.rb', line 140

def resource_module
  return @module if @module

  provider = self

  @module = Module.new do
    const_set(:Token, provider.send(:token_resource))
  end
end

#setupObject



136
137
138
# File 'lib/haveapi/authentication/token/provider.rb', line 136

def setup
  @server.allow_header(config.class.http_header)
end

#token(request) ⇒ String

Extract token from HTTP request

Parameters:

  • request (Sinatra::Request)

Returns:

  • (String)


161
162
163
# File 'lib/haveapi/authentication/token/provider.rb', line 161

def token(request)
  request.params[config.class.query_parameter] || request.env[header_to_env]
end