Module: DeviseTokenAuth::Concerns::User

Extended by:
ActiveSupport::Concern
Defined in:
app/models/devise_token_auth/concerns/user.rb

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.tokens_match?(token_hash, token) ⇒ Boolean

Returns:

  • (Boolean)


6
7
8
9
10
11
12
13
14
15
# File 'app/models/devise_token_auth/concerns/user.rb', line 6

def self.tokens_match?(token_hash, token)
  @token_equality_cache ||= {}

  key = "#{token_hash}/#{token}"
  result = @token_equality_cache[key] ||= (::BCrypt::Password.new(token_hash) == token)
  if @token_equality_cache.size > 10000
    @token_equality_cache = {}
  end
  result
end

Instance Method Details

#build_auth_header(token, client_id = 'default') ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'app/models/devise_token_auth/concerns/user.rb', line 184

def build_auth_header(token, client_id='default')
  client_id ||= 'default'

  # client may use expiry to prevent validation request if expired
  # must be cast as string or headers will break
  expiry = self.tokens[client_id]['expiry'] || self.tokens[client_id][:expiry]

  max_clients = DeviseTokenAuth.max_number_of_devices
  while self.tokens.keys.length > 0 and max_clients < self.tokens.keys.length
    oldest_token = self.tokens.min_by { |cid, v| v[:expiry] || v["expiry"] }
    self.tokens.delete(oldest_token.first)
  end

  self.save!

  return {
    DeviseTokenAuth.headers_names[:"access-token"] => token,
    DeviseTokenAuth.headers_names[:"token-type"]   => "Bearer",
    DeviseTokenAuth.headers_names[:"client"]       => client_id,
    DeviseTokenAuth.headers_names[:"expiry"]       => expiry.to_s,
    DeviseTokenAuth.headers_names[:"uid"]          => self.uid
  }
end

#build_auth_url(base_url, args) ⇒ Object



209
210
211
212
213
214
# File 'app/models/devise_token_auth/concerns/user.rb', line 209

def build_auth_url(base_url, args)
  args[:uid]    = self.uid
  args[:expiry] = self.tokens[args[:client_id]]['expiry']

  DeviseTokenAuth::Url.generate(base_url, args)
end

#confirmed?Boolean

Returns:

  • (Boolean)


223
224
225
# File 'app/models/devise_token_auth/concerns/user.rb', line 223

def confirmed?
  self.devise_modules.exclude?(:confirmable) || super
end

#create_new_auth_token(client_id = nil) ⇒ Object

update user’s auth token (should happen on each request)



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'app/models/devise_token_auth/concerns/user.rb', line 162

def create_new_auth_token(client_id=nil)
  client_id  ||= SecureRandom.urlsafe_base64(nil, false)
  last_token ||= nil
  token        = SecureRandom.urlsafe_base64(nil, false)
  token_hash   = ::BCrypt::Password.create(token)
  expiry       = (Time.now + DeviseTokenAuth.token_lifespan).to_i

  if self.tokens[client_id] and self.tokens[client_id]['token']
    last_token = self.tokens[client_id]['token']
  end

  self.tokens[client_id] = {
    token:      token_hash,
    expiry:     expiry,
    last_token: last_token,
    updated_at: Time.now
  }

  return build_auth_header(token, client_id)
end

#extend_batch_buffer(token, client_id) ⇒ Object



217
218
219
220
221
# File 'app/models/devise_token_auth/concerns/user.rb', line 217

def extend_batch_buffer(token, client_id)
  self.tokens[client_id]['updated_at'] = Time.now

  return build_auth_header(token, client_id)
end

#send_confirmation_notification?Boolean

this must be done from the controller so that additional params can be passed on from the client

Returns:

  • (Boolean)


118
119
120
# File 'app/models/devise_token_auth/concerns/user.rb', line 118

def send_confirmation_notification?
  false
end

#token_can_be_reused?(token, client_id) ⇒ Boolean

allow batch requests to use the previous token

Returns:

  • (Boolean)


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'app/models/devise_token_auth/concerns/user.rb', line 142

def token_can_be_reused?(token, client_id)
  # ghetto HashWithIndifferentAccess
  updated_at = self.tokens[client_id]['updated_at'] || self.tokens[client_id][:updated_at]
  last_token = self.tokens[client_id]['last_token'] || self.tokens[client_id][:last_token]


  return true if (
    # ensure that the last token and its creation time exist
    updated_at and last_token and

    # ensure that previous token falls within the batch buffer throttle time of the last request
    Time.parse(updated_at) > Time.now - DeviseTokenAuth.batch_request_buffer_throttle and

    # ensure that the token is valid
    ::BCrypt::Password.new(last_token) == token
  )
end

#token_is_current?(token, client_id) ⇒ Boolean

Returns:

  • (Boolean)


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/models/devise_token_auth/concerns/user.rb', line 123

def token_is_current?(token, client_id)
  # ghetto HashWithIndifferentAccess
  expiry     = self.tokens[client_id]['expiry'] || self.tokens[client_id][:expiry]
  token_hash = self.tokens[client_id]['token'] || self.tokens[client_id][:token]

  return true if (
    # ensure that expiry and token are set
    expiry and token and

    # ensure that the token has not yet expired
    DateTime.strptime(expiry.to_s, '%s') > Time.now and

    # ensure that the token is valid
    DeviseTokenAuth::Concerns::User.tokens_match?(token_hash, token)
  )
end

#token_validation_responseObject



227
228
229
230
231
# File 'app/models/devise_token_auth/concerns/user.rb', line 227

def token_validation_response
  self.as_json(except: [
    :tokens, :created_at, :updated_at
  ])
end

#valid_token?(token, client_id = 'default') ⇒ Boolean

Returns:

  • (Boolean)


103
104
105
106
107
108
109
110
111
112
113
# File 'app/models/devise_token_auth/concerns/user.rb', line 103

def valid_token?(token, client_id='default')
  client_id ||= 'default'

  return false unless self.tokens[client_id]

  return true if token_is_current?(token, client_id)
  return true if token_can_be_reused?(token, client_id)

  # return false if none of the above conditions are met
  return false
end