Hmm, I don’t know where that doc came from…looks like it got merged in 3 months ago, but this is the first time I’ve seen it! I can’t vouch for whether it works or not. If you really want to use that code I’d try contacting the author and see if he can help? brendandonahue (Brendan Donahue) · GitHub
dbAuth does support TouchID/FaceID (aka WebAuthn) out of the box, you just have to say “yes” to that option when setting up the first time. But you still need to first create an account with a username and password (subsequent logins can be done with your fingerprint).
If I was going to write something from scratch I’d probably skip the standard dbAuth flow entirely: it’s really wired up to want to log someone in with a username and password. But, as long as the proper cookie exists, dbAuth will still do whatever is needed to authenticate your request and make sure you’re who you say you are.
I’d do something similar to how the forgot password flow works: you create a unique token on the server and store it in the database in the User model. Email the user a link back to the site, including that token. The link would be an api-side function, not to the web side (which is running in the browser and can’t set the secure cookie we want). Assuming the token is valid, log that person in by setting the cookie (rather than trying to call the login handler in the dbAuth code which, again, assumes a username and password).
Here’s a quick code block that sets the cookie in the response of the function, redirecting the user back to the homepage (I haven’t tested this, but it’s the general idea!):
// api/src/functions/passwordless.js
import { encryptSession } from '@redwoodjs/auth-dbauth-api'
import { db } from 'src/lib/db'
const secureCookie = (user) => {
const expires = new Date()
expires.setFullYear(expires.getFullYear() + 1)
const cookieAttrs = [
`Expires=${expires.toUTCString()}`,
'HttpOnly=true',
'Path=/',
'SameSite=Strict',
`Secure=${process.env.NODE_ENV !== 'development'}`,
]
const data = JSON.stringify({ id: user.id })
const encrypted = encryptSession(data)
return [`session=${encrypted}`, ...cookieAttrs].join('; ')
}
// this is the function that gets called when the function is loaded
// this would be the link in the email, something like:
// https://example.com/.redwood/functions/passwordless?token=abc123
export const handler = async (event) => {
const { token } = event.queryStringParameters
const user = await db.user.findFirst({
where: { passwordToken: token }
})
if (user) {
return {
statusCode: 302,
headers: {
'Set-Cookie': secureCookie(user),
Location: '/',
},
}
} else {
return {
statusCode: 302,
headers: {
Location: '/?error=Token+not+found',
},
}
}
}
If the token isn’t valid then the error
query string variable will be available and you can show them the message on the page they’re redirected to. Otherwise, they’re logged in!