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



183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'app/models/devise_token_auth/concerns/user.rb', line 183

def build_auth_header(token, client_id='default')
  # client may use expiry to prevent validation request if expired
  # must be cast as string or headers will break
  expiry = tokens[client_id]['expiry'] || tokens[client_id][:expiry]

  {
    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"]          => uid
  }
end

#build_auth_url(base_url, args) ⇒ Object



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

def build_auth_url(base_url, args)
  args[:uid]    = uid
  args[:expiry] = 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?
  devise_modules.exclude?(:confirmable) || super
end

#create_new_auth_token(client_id = nil) ⇒ Object

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



170
171
172
173
174
175
176
177
178
179
180
181
# File 'app/models/devise_token_auth/concerns/user.rb', line 170

def create_new_auth_token(client_id=nil)
  now = Time.now

  client_id, token = create_token(
    client_id: client_id,
    expiry: (now + token_lifespan).to_i,
    last_token: tokens.fetch(client_id, {})['token'],
    updated_at: now
  )

  update_auth_header(token, client_id)
end

#create_token(client_id: nil, token: nil, expiry: nil, **token_extras) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
# File 'app/models/devise_token_auth/concerns/user.rb', line 91

def create_token(client_id: nil, token: nil, expiry: nil, **token_extras)
  client_id ||= SecureRandom.urlsafe_base64(nil, false)
  token     ||= SecureRandom.urlsafe_base64(nil, false)
  expiry    ||= (Time.now + token_lifespan).to_i

  self.tokens[client_id] = {
    token: BCrypt::Password.create(token),
    expiry: expiry
  }.merge!(token_extras)

  [client_id, token, expiry]
end

#extend_batch_buffer(token, client_id) ⇒ Object



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

def extend_batch_buffer(token, client_id)
  self.tokens[client_id]['updated_at'] = Time.now
  update_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)


129
# File 'app/models/devise_token_auth/concerns/user.rb', line 129

def send_confirmation_notification?; false; end

#token_can_be_reused?(token, client_id) ⇒ Boolean

allow batch requests to use the previous token

Returns:

  • (Boolean)


151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'app/models/devise_token_auth/concerns/user.rb', line 151

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

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

    # 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 &&

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

#token_is_current?(token, client_id) ⇒ Boolean

Returns:

  • (Boolean)


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'app/models/devise_token_auth/concerns/user.rb', line 132

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

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

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

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

#token_lifespanObject



231
232
233
# File 'app/models/devise_token_auth/concerns/user.rb', line 231

def token_lifespan
  DeviseTokenAuth.token_lifespan
end

#token_validation_responseObject



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

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

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



197
198
199
200
201
202
203
204
205
206
207
# File 'app/models/devise_token_auth/concerns/user.rb', line 197

def update_auth_header(token, client_id='default')
  headers = build_auth_header(token, client_id)
  while tokens.length > 0 && DeviseTokenAuth.max_number_of_devices < tokens.length
    oldest_client_id, _tk = tokens.min_by { |_cid, v| v[:expiry] || v["expiry"] }
    tokens.delete(oldest_client_id)
  end

  save!

  headers
end

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

Returns:

  • (Boolean)


117
118
119
120
121
122
123
124
# File 'app/models/devise_token_auth/concerns/user.rb', line 117

def valid_token?(token, client_id='default')
  return false unless 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