In your frontend, you will use the PlainKey Browser SDK. It handles user registration, passkey registration, and authentication.
Install PlainKey Browser SDK
In the terminal, install the PlainKey browser and types packages.
npm install @plainkey/browser
npm install @plainkey/types
Initialise
import { PlainKey } from "@plainkey/browser"
const plainKey = new PlainKey("YOUR_PROJECT_ID")
You can find your project ID on the PlainKey admin dashboard.
Create user with passkey
While your database is the source of truth for your users, you must also register them as users in PlainKey. A PlainKey user can have multiple passkeys.
Registration of a brand new user can be done directly in your frontend - at the same time as the registration of their first passkey.
const { data, error } = await plainKey.createUserWithPasskey("user@example.com")
This will make the browser prompt the user to create a passkey.
On success, the user is created, their passkey is registered, and the user is authenticated. The response contains an authentication token to be verified in your backend.
Follow the "On authentication success" section for what to do next.
Authenticate user
A user must have been registered in PlainKey with a passkey before they can be authenticated.
const { data, error } = await plainKey.authenticate()
By not passing any parameters to .authenticate(), the user's browser will be open to attempt authentication with any passkey stored on their device on the current domain. This is recommended for sign-in. It makes for great user experience, since the user doesn't have to enter their username or email address.
You can also pass the PlainKey User ID or userName to .authenticate(), which will restrict the authentication to that user's passkeys.
// By PlainKey User ID
const { data, error } = await plainKey.authenticate({ userId: "user-id-here" })
// Or by user name
const { data, error } = await plainKey.authenticate({ userName: "user@example.com" })
Use this when you need to authenticate a specific user and already know their identity. For example, if they are already signed in on an admin panel.
On authentication success
On authentication success, the result includes a data object, which contains an authenticationToken object.
It must be passed to your backend so it can verify the token and learn which user it belongs to.
const { data, error } = await plainKey.authenticate() // or .createUserWithPasskey() or .addPasskey()
if (error) {
// Method will not throw an error. Handle error as you see fit.
console.error(error)
return
}
if (data) {
const authenticationToken: string = data.authenticationToken.token
// Call your backend API to verify the authentication token. Do not store the token.
// On signup you'd typically pass in a userName or some identity information to the endpoint here too.
await fetch("/api/auth/exchange", {
method: "POST",
body: JSON.stringify({ token: authenticationToken }),
headers: {
"Content-Type": "application/json"
}
})
}
In the backend, you verify the authentication token using the PlainKey Server SDK or API.
This will return the user's PlainKey User ID, which you use to find or create the user in your database. The user ID is a UUID (for example: 123e4567-e89b-12d3-a456-426614174000).
You must store this user ID with your user record. This is your primary reference to the PlainKey user.
After verification and storing the user ID, you continue doing what suits your application. For sign-in, this usually involves creating your own session for the user and returning the session data to the frontend.
Go to Server Setup --> to see how to verify the authentication token in your backend.
Add passkey to existing user
A PlainKey user can have multiple passkeys.
To add a new passkey, the user must first be authenticated. Follow the "Authenticate user" section to do this.
Once authenticated, call .addPasskey() using the returned authentication token.
// 1. Authenticate the user
const { data: authData, error: authError } = await plainKey.authenticate({
userName: "user@example.com"
})
if (authError) {
// Method will not throw, but return an error object.
// Handle error as you see fit.
console.error(authError)
return
}
// 2. Create a new passkey using the returned authentication token
if (authData) {
const token = authData.authenticationToken.token
const { success, error } = await plainKey.addPasskey(token)
if (error) {
// Handle error as you see fit.
console.error(error)
return
}
}
This will make the browser prompt the user to create a passkey.
Optional: Provide a userName
.addPasskey() accepts an optional userName parameter. The userName is a label stored on the passkey.
- If a userName is not provided, the user's stored userName is used.
- If a userName is provided, it is used for the new passkey. It does not update the user's stored userName.
// Provide a userName to override the user's stored userName.
const { success, error } = await plainKey.addPasskey(
authData.authenticationToken.token,
"user@example.com"
)