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)


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

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)
  @token_equality_cache = {} if @token_equality_cache.size > 10000
  result
end

Instance Method Details

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



178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'app/models/devise_token_auth/concerns/user.rb', line 178

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



200
201
202
203
204
205
# File 'app/models/devise_token_auth/concerns/user.rb', line 200

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)


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

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)



165
166
167
168
169
170
171
172
173
174
175
176
# File 'app/models/devise_token_auth/concerns/user.rb', line 165

def create_new_auth_token(client_id = nil)
  now = Time.zone.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



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

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.zone.now + token_lifespan).to_i

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

  clean_old_tokens

  [client_id, token, expiry]
end

#extend_batch_buffer(token, client_id) ⇒ Object



207
208
209
210
# File 'app/models/devise_token_auth/concerns/user.rb', line 207

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


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

def send_confirmation_notification?; false; end

#token_can_be_reused?(token, client_id) ⇒ Boolean

allow batch requests to use the previous token

Returns:

  • (Boolean)


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'app/models/devise_token_auth/concerns/user.rb', line 147

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.zone.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)


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

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.zone.now &&

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

#token_lifespanObject



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

def token_lifespan
  DeviseTokenAuth.token_lifespan
end

#token_validation_responseObject



216
217
218
# File 'app/models/devise_token_auth/concerns/user.rb', line 216

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

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



192
193
194
195
196
197
198
# File 'app/models/devise_token_auth/concerns/user.rb', line 192

def update_auth_header(token, client_id = 'default')
  headers = build_auth_header(token, client_id)
  clean_old_tokens
  save!

  headers
end

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

Returns:

  • (Boolean)


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

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
  false
end