41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# File 'lib/better_auth/passkey/routes/authentication.rb', line 41
def verify_passkey_authentication_endpoint(config)
Endpoint.new(path: "/passkey/verify-authentication", method: "POST") do |ctx|
body = Utils.normalize_hash(ctx.body)
Utils.require_key!(body, :response)
origin = Utils.origin(config, ctx)
raise APIError.new("BAD_REQUEST", message: "origin missing") if origin.to_s.empty?
verification_token = Challenges.challenge_token(ctx, config)
raise APIError.new("BAD_REQUEST", message: ErrorCodes::PASSKEY_ERROR_CODES.fetch("CHALLENGE_NOT_FOUND")) unless verification_token
challenge = Challenges.find_challenge(ctx, verification_token)
raise APIError.new("BAD_REQUEST", message: ErrorCodes::PASSKEY_ERROR_CODES.fetch("CHALLENGE_NOT_FOUND")) unless challenge
response = Credentials.webauthn_response(body[:response])
credential_id = response.fetch("id")
passkey = ctx.context.adapter.find_one(model: "passkey", where: [{field: "credentialID", value: credential_id}])
raise APIError.new("UNAUTHORIZED", message: ErrorCodes::PASSKEY_ERROR_CODES.fetch("PASSKEY_NOT_FOUND")) unless passkey
relying_party = Utils.relying_party(config, ctx, origin: origin)
credential = WebAuthn::Credential.from_get(response, relying_party: relying_party)
credential.verify(
challenge.fetch("expectedChallenge"),
public_key: Base64.strict_decode64(passkey.fetch("publicKey")),
sign_count: passkey.fetch("counter").to_i,
user_verification: false
)
Utils.call_callback(config.dig(:authentication, :after_verification), {
ctx: ctx,
verification: credential,
client_data: response
})
ctx.context.adapter.update(
model: "passkey",
where: [{field: "id", value: passkey.fetch("id")}],
update: {counter: credential.sign_count}
)
session = ctx.context.internal_adapter.create_session(passkey.fetch("userId"))
raise APIError.new("INTERNAL_SERVER_ERROR", message: ErrorCodes::PASSKEY_ERROR_CODES.fetch("UNABLE_TO_CREATE_SESSION")) unless session
user = ctx.context.internal_adapter.find_user_by_id(passkey.fetch("userId"))
raise APIError.new("INTERNAL_SERVER_ERROR", message: "User not found") unless user
Cookies.set_session_cookie(ctx, {session: session, user: user})
ctx.context.internal_adapter.delete_verification_by_identifier(verification_token)
ctx.json({session: session, user: user})
rescue WebAuthn::Error, ArgumentError => error
ctx.context.logger&.error("Failed to verify authentication", error)
raise APIError.new("BAD_REQUEST", message: ErrorCodes::PASSKEY_ERROR_CODES.fetch("AUTHENTICATION_FAILED"))
end
end
|