Class: BetterAuth::Adapters::InternalAdapter

Inherits:
Object
  • Object
show all
Defined in:
lib/better_auth/adapters/internal_adapter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(adapter, options) ⇒ InternalAdapter

Returns a new instance of InternalAdapter.



13
14
15
16
17
18
19
# File 'lib/better_auth/adapters/internal_adapter.rb', line 13

def initialize(adapter, options)
  @adapter = adapter
  @options = options
  @hooks = DatabaseHooks.new(adapter, options)
  @verification_locks_guard = Mutex.new
  @verification_locks = {}
end

Instance Attribute Details

#adapterObject (readonly)

Returns the value of attribute adapter.



11
12
13
# File 'lib/better_auth/adapters/internal_adapter.rb', line 11

def adapter
  @adapter
end

#hooksObject (readonly)

Returns the value of attribute hooks.



11
12
13
# File 'lib/better_auth/adapters/internal_adapter.rb', line 11

def hooks
  @hooks
end

#optionsObject (readonly)

Returns the value of attribute options.



11
12
13
# File 'lib/better_auth/adapters/internal_adapter.rb', line 11

def options
  @options
end

Instance Method Details

#consume_verification_value(identifier) ⇒ Object



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/better_auth/adapters/internal_adapter.rb', line 307

def consume_verification_value(identifier)
  identifiers = verification_identifiers(identifier)
  consumed = if secondary_storage && !verification_store_in_database?
    consume_secondary_verification(identifiers)
  else
    consume_database_verification(identifiers)
  end

  invalidate_secondary_verification(identifiers, consumed) if consumed && secondary_storage && verification_store_in_database?
  return nil unless consumed

  expires_at = normalize_time(consumed["expiresAt"] || consumed[:expiresAt])
  (expires_at > Time.now) ? normalize_verification_dates(consumed) : nil
rescue ArgumentError, TypeError
  nil
end

#count_total_users(where: nil) ⇒ Object



60
61
62
# File 'lib/better_auth/adapters/internal_adapter.rb', line 60

def count_total_users(where: nil)
  adapter.count(model: "user", where: where || [])
end

#create_account(account) ⇒ Object



35
36
37
# File 'lib/better_auth/adapters/internal_adapter.rb', line 35

def ()
  hooks.create(timestamps.merge(stringify_keys()), "account")
end

#create_oauth_user(user, account, context: nil) ⇒ Object



21
22
23
24
25
26
27
# File 'lib/better_auth/adapters/internal_adapter.rb', line 21

def create_oauth_user(user, , context: nil)
  adapter.transaction do
    created_user = create_user(user, context: context)
     = (stringify_keys().merge("userId" => created_user["id"]))
    {user: created_user, account: }
  end
end

#create_session(user_id, dont_remember_me = false, override = nil, override_all = false, context = nil) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/better_auth/adapters/internal_adapter.rb', line 73

def create_session(user_id, dont_remember_me = false, override = nil, override_all = false, context = nil)
  override = stringify_keys(override || {})
  token = override.delete("token") || SecureRandom.hex(16)
  base = {
    "ipAddress" => "",
    "userAgent" => "",
    "expiresAt" => Time.now + (dont_remember_me ? 86_400 : options.session[:expires_in].to_i),
    "userId" => user_id,
    "token" => token
  }.merge(timestamps)
  base["id"] = generated_id if secondary_storage
  data = override_all ? base.merge(override) : override.merge(base)

  custom = secondary_storage && lambda do |session_data|
    actual_session = apply_schema_create("session", session_data)
    store_session(actual_session)
    adapter.create(model: "session", data: actual_session, force_allow_id: true) if options.session[:store_session_in_database]
    actual_session
  end
  hooks.create(data, "session", custom: custom, context: context)
end

#create_user(user = nil, context: nil, **keywords) ⇒ Object



29
30
31
32
33
# File 'lib/better_auth/adapters/internal_adapter.rb', line 29

def create_user(user = nil, context: nil, **keywords)
  data = timestamps.merge(stringify_keys((user || {}).merge(keywords)))
  data["email"] = data["email"].to_s.downcase if data["email"]
  hooks.create(data, "user", context: context)
end

#create_verification_value(data) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/better_auth/adapters/internal_adapter.rb', line 263

def create_verification_value(data)
  payload = timestamps.merge(stringify_keys(data))
  stored_identifier = processed_verification_identifier(payload.fetch("identifier"))
  payload["identifier"] = stored_identifier

  custom = secondary_storage && lambda do |verification_data|
    actual = apply_schema_create("verification", verification_data)
    actual["id"] ||= generated_id
    store_verification(actual)
    adapter.create(model: "verification", data: actual, force_allow_id: true) if verification_store_in_database?
    actual
  end

  hooks.create(payload, "verification", custom: custom)
end

#delete_account(account_id) ⇒ Object



173
174
175
# File 'lib/better_auth/adapters/internal_adapter.rb', line 173

def ()
  hooks.delete([{field: "id", value: }], "account")
end

#delete_accounts(user_id) ⇒ Object



169
170
171
# File 'lib/better_auth/adapters/internal_adapter.rb', line 169

def delete_accounts(user_id)
  hooks.delete_many([{field: "userId", value: user_id}], "account")
end

#delete_session(token) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/better_auth/adapters/internal_adapter.rb', line 134

def delete_session(token)
  if secondary_storage
    data = parse_storage(secondary_storage.get(token))
    if data && data["session"]
      user_id = data["session"]["userId"]
      entries = active_session_entries(user_id).reject { |entry| entry["token"] == token }
      write_active_sessions(user_id, entries)
    end
    secondary_storage.delete(token)
    return if !options.session[:store_session_in_database] || options.session[:preserve_session_in_database]
  end

  hooks.delete([{field: "token", value: token}], "session")
end

#delete_sessions(tokens) ⇒ Object



159
160
161
162
163
164
165
166
167
# File 'lib/better_auth/adapters/internal_adapter.rb', line 159

def delete_sessions(tokens)
  session_tokens = Array(tokens)
  if secondary_storage
    session_tokens.each { |token| secondary_storage.delete(token) }
    return if !options.session[:store_session_in_database] || options.session[:preserve_session_in_database]
  end

  hooks.delete_many([{field: "token", value: session_tokens, operator: "in"}], "session")
end

#delete_user(user_id) ⇒ Object



64
65
66
67
68
69
70
71
# File 'lib/better_auth/adapters/internal_adapter.rb', line 64

def delete_user(user_id)
  deleted = hooks.delete([{field: "id", value: user_id}], "user")
  return false if deleted == false

  hooks.delete_many([{field: "userId", value: user_id}], "account")
  delete_user_sessions(user_id)
  deleted
end

#delete_user_sessions(user_id) ⇒ Object



149
150
151
152
153
154
155
156
157
# File 'lib/better_auth/adapters/internal_adapter.rb', line 149

def delete_user_sessions(user_id)
  if secondary_storage
    active_session_entries(user_id).each { |entry| secondary_storage.delete(entry["token"]) }
    secondary_storage.delete(active_key(user_id))
    return if !options.session[:store_session_in_database] || options.session[:preserve_session_in_database]
  end

  hooks.delete_many([{field: "userId", value: user_id}], "session")
end

#delete_verification_by_identifier(identifier) ⇒ Object



372
373
374
375
376
377
378
379
380
381
382
# File 'lib/better_auth/adapters/internal_adapter.rb', line 372

def delete_verification_by_identifier(identifier)
  stored_identifier = processed_verification_identifier(identifier)
  if secondary_storage
    cached = read_verification(stored_identifier)
    secondary_storage.delete(verification_key(stored_identifier))
    secondary_storage.delete(verification_id_key(cached["id"])) if cached && cached["id"]
    return nil unless verification_store_in_database?
  end

  hooks.delete([{field: "identifier", value: stored_identifier}], "verification")
end

#delete_verification_value(id) ⇒ Object



357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/better_auth/adapters/internal_adapter.rb', line 357

def delete_verification_value(id)
  if secondary_storage
    stored_identifier = secondary_storage.get(verification_id_key(id))
    if stored_identifier
      secondary_storage.delete(verification_key(stored_identifier))
      secondary_storage.delete(verification_id_key(id))
      return nil unless verification_store_in_database?
    elsif !verification_store_in_database?
      return nil
    end
  end

  hooks.delete([{field: "id", value: id}], "verification")
end

#find_account(account_id) ⇒ Object



247
248
249
# File 'lib/better_auth/adapters/internal_adapter.rb', line 247

def ()
  adapter.find_one(model: "account", where: [{field: "accountId", value: }])
end

#find_account_by_provider_id(account_id, provider_id) ⇒ Object



251
252
253
# File 'lib/better_auth/adapters/internal_adapter.rb', line 251

def (, provider_id)
  adapter.find_one(model: "account", where: [{field: "accountId", value: }, {field: "providerId", value: provider_id}])
end

#find_account_by_user_id(user_id) ⇒ Object



255
256
257
# File 'lib/better_auth/adapters/internal_adapter.rb', line 255

def (user_id)
  find_accounts(user_id)
end

#find_accounts(user_id) ⇒ Object



243
244
245
# File 'lib/better_auth/adapters/internal_adapter.rb', line 243

def find_accounts(user_id)
  adapter.find_many(model: "account", where: [{field: "userId", value: user_id}])
end

#find_oauth_user(email, account_id, provider_id) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/better_auth/adapters/internal_adapter.rb', line 197

def find_oauth_user(email, , provider_id)
   = (, provider_id)
  if 
    user = ["user"] || adapter.find_one(model: "user", where: [{field: "email", value: email.to_s.downcase}])
    return nil unless user

    linked = .dup
    linked.delete("user")
    return {user: user, linked_account: linked, accounts: [linked]}
  end

  found_user = adapter.find_one(model: "user", where: [{field: "email", value: email.to_s.downcase}])
  return nil unless found_user

  {user: found_user, linked_account: nil, accounts: find_accounts(found_user["id"])}
end

#find_session(token) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/better_auth/adapters/internal_adapter.rb', line 95

def find_session(token)
  if secondary_storage
    data = parse_storage(secondary_storage.get(token))
    unless data
      return nil unless options.session[:store_session_in_database] && !options.session[:preserve_session_in_database]
    end

    if data
      return {
        session: normalize_session_dates(data["session"]),
        user: normalize_user_dates(data["user"])
      }
    end
  end

  found = find_session_with_user(token)
  return nil unless found && found["user"]

  user = found.delete("user")
  {session: found, user: user}
end

#find_sessions(tokens) ⇒ Object



117
118
119
# File 'lib/better_auth/adapters/internal_adapter.rb', line 117

def find_sessions(tokens)
  tokens.filter_map { |token| find_session(token) }
end

#find_user_by_email(email, include_accounts: false) ⇒ Object



214
215
216
217
218
219
# File 'lib/better_auth/adapters/internal_adapter.rb', line 214

def find_user_by_email(email, include_accounts: false)
  user = adapter.find_one(model: "user", where: [{field: "email", value: email.to_s.downcase}])
  return nil unless user

  {user: user, accounts: include_accounts ? find_accounts(user["id"]) : []}
end

#find_user_by_id(user_id) ⇒ Object



221
222
223
224
225
# File 'lib/better_auth/adapters/internal_adapter.rb', line 221

def find_user_by_id(user_id)
  return nil if user_id.to_s.empty?

  adapter.find_one(model: "user", where: [{field: "id", value: user_id}])
end

#find_verification_value(identifier) ⇒ Object



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/better_auth/adapters/internal_adapter.rb', line 279

def find_verification_value(identifier)
  stored_identifier = processed_verification_identifier(identifier)
  storage_option = verification_storage_option(identifier)
  if secondary_storage
    cached = read_verification(stored_identifier)
    cached ||= read_verification(identifier) if storage_option && storage_option.to_s != "plain"
    return cached if cached
    return nil unless verification_store_in_database?
  end

  values = adapter.find_many(
    model: "verification",
    where: [{field: "identifier", value: stored_identifier}],
    sort_by: {field: "createdAt", direction: "desc"},
    limit: 1
  )
  if values.empty? && storage_option && storage_option.to_s != "plain"
    values = adapter.find_many(
      model: "verification",
      where: [{field: "identifier", value: identifier}],
      sort_by: {field: "createdAt", direction: "desc"},
      limit: 1
    )
  end
  hooks.delete_many([{field: "expiresAt", value: Time.now, operator: "lt"}], "verification") unless options.verification[:disable_cleanup]
  values.first
end


39
40
41
# File 'lib/better_auth/adapters/internal_adapter.rb', line 39

def ()
  ()
end

#list_sessions(user_id) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/better_auth/adapters/internal_adapter.rb', line 43

def list_sessions(user_id)
  if secondary_storage
    active_session_entries(user_id).filter_map do |entry|
      data = parse_storage(secondary_storage.get(entry.fetch("token")))
      next unless data && data["session"]

      normalize_session_dates(data["session"])
    end
  else
    adapter.find_many(model: "session", where: [{field: "userId", value: user_id}])
  end
end

#list_users(limit: nil, offset: nil, sort_by: nil, where: nil) ⇒ Object



56
57
58
# File 'lib/better_auth/adapters/internal_adapter.rb', line 56

def list_users(limit: nil, offset: nil, sort_by: nil, where: nil)
  adapter.find_many(model: "user", where: where || [], limit: limit, offset: offset, sort_by: sort_by)
end

#reserve_verification_value(data) ⇒ Object



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/better_auth/adapters/internal_adapter.rb', line 324

def reserve_verification_value(data)
  payload = stringify_keys(data)
  identifier = payload.fetch("identifier")
  stored_identifier = processed_verification_identifier(identifier)
  reservation_id = Crypto.sha256("reserve:#{identifier}", encoding: :base64url)
  verification = timestamps.merge(
    "id" => reservation_id,
    "identifier" => stored_identifier,
    "value" => payload.fetch("value"),
    "expiresAt" => normalize_time(payload.fetch("expiresAt"))
  )

  if secondary_storage && !verification_store_in_database?
    return reserve_secondary_verification(verification)
  end

  adapter.consume_one(
    model: "verification",
    where: [
      {field: "id", value: reservation_id},
      {field: "expiresAt", value: Time.now, operator: "lt"}
    ]
  )
  created = adapter.create_if_absent(
    model: "verification",
    data: verification,
    conflict_field: "id",
    force_allow_id: true
  )
  store_reserved_verification(verification) if created && secondary_storage
  created
end

#revoke_unproven_account_access(user_id) ⇒ Object

Removes access established before control of an email address was proven. The user is intentionally left unverified until the caller completes the proof transition and may safely mint a replacement session.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/better_auth/adapters/internal_adapter.rb', line 180

def (user_id)
  adapter.transaction do
    user = find_user_by_id(user_id)
    next unless user && !user["emailVerified"]

    find_accounts(user_id).each do ||
      next unless ["providerId"] == "credential"

      deleted = (.fetch("id"))
      raise Error, "Credential account revocation was vetoed" if deleted == false
    end

    deleted_sessions = delete_user_sessions(user_id)
    raise Error, "Session revocation was vetoed" if deleted_sessions == false
  end
end

#update_account(id, data) ⇒ Object



259
260
261
# File 'lib/better_auth/adapters/internal_adapter.rb', line 259

def (id, data)
  hooks.update(stringify_keys(data), [{field: "id", value: id}], "account")
end

#update_password(user_id, password) ⇒ Object



239
240
241
# File 'lib/better_auth/adapters/internal_adapter.rb', line 239

def update_password(user_id, password)
  hooks.update_many({password: password}, [{field: "userId", value: user_id}, {field: "providerId", value: "credential"}], "account")
end

#update_session(token, session) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/better_auth/adapters/internal_adapter.rb', line 121

def update_session(token, session)
  data = stringify_keys(session)
  if secondary_storage
    return hooks.update(data, [{field: "token", value: token}], "session", custom: lambda { |actual_data|
      stored = update_stored_session(token, actual_data)
      db = adapter.update(model: "session", where: [{field: "token", value: token}], update: actual_data) if options.session[:store_session_in_database]
      db || stored
    })
  end

  hooks.update(data, [{field: "token", value: token}], "session")
end

#update_user(user_id, data) ⇒ Object



227
228
229
230
231
# File 'lib/better_auth/adapters/internal_adapter.rb', line 227

def update_user(user_id, data)
  user = hooks.update(stringify_keys(data), [{field: "id", value: user_id}], "user")
  refresh_user_sessions(user) if user
  user
end

#update_user_by_email(email, data) ⇒ Object



233
234
235
236
237
# File 'lib/better_auth/adapters/internal_adapter.rb', line 233

def update_user_by_email(email, data)
  user = hooks.update(stringify_keys(data), [{field: "email", value: email.to_s.downcase}], "user")
  refresh_user_sessions(user) if user
  user
end

#update_verification_value(id, data) ⇒ Object



384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/better_auth/adapters/internal_adapter.rb', line 384

def update_verification_value(id, data)
  update = stringify_keys(data)
  if secondary_storage
    stored_identifier = secondary_storage.get(verification_id_key(id))
    if stored_identifier
      cached = read_verification(stored_identifier)
      if cached
        updated = cached.merge(update)
        store_verification(updated)
        return updated unless verification_store_in_database?
      end
    elsif !verification_store_in_database?
      return nil
    end
  end

  hooks.update(update, [{field: "id", value: id}], "verification")
end