Better Auth CorePass Passkey Plugin
This package is a plugin for the Better Auth ecosystem, extending the functionality of the `@better-auth/passkey` plugin with CorePass-specific identity enrichment. It facilitates user registration via passkeys while integrating signed identity and profile data, such as Core ID, email, and KYC (Know Your Customer) flags, directly from the CorePass application. The plugin performs robust Ed448 signature verification on incoming enrichment data, validates Core IDs using `blockchain-wallet-validator`, and enforces configurable requirements like age verification (`requireO18y`, `requireO21y`) and KYC. It also dynamically updates user profiles and session data, including a `corepass_profile` with configurable expiry. Currently at version 0.1.18, the library exhibits a rapid release cadence with frequent updates and bug fixes, indicating active development. A key differentiator is its strict 'passkey-only access' policy, which blocks users without a registered passkey from most authentication endpoints, making it ideal for anonymous bootstrap flows requiring eventual strong identity binding.
Common errors
-
Invalid Core ID or network not allowed.
cause The Core ID provided during the CorePass enrichment process is either syntactically invalid or its blockchain network is not included in the `allowNetwork` option within the plugin configuration.fixVerify that the Core ID being sent from the CorePass application is valid. Additionally, ensure the `allowNetwork` array in your `createCorePassPasskeyPlugin` options includes the specific CorePass blockchain network(s) from which you expect to receive data. -
POST /auth/webauthn/data 404 Not Found
cause The client-side application is attempting to send CorePass enrichment data to an incorrect or unmounted endpoint, or the `better-auth` service is not configured to handle the `/webauthn/data` route.fixConfirm that your client-side code sends enrichment data to the correct `/webauthn/data` path relative to your `better-auth` base path (e.g., `/auth/webauthn/data` if your `AuthService` router is mounted at `/auth`). Also, ensure the `createCorePassPasskeyPlugin` is correctly included in the `plugins` array of your `AuthService` instance. -
Error: Account creation failed: Email required.
cause One of the email requirement options (`requireRegistrationEmail`, `requireEmail`, or `requireAtLeastOneEmail`) is set to `true` in the plugin configuration, but no email address was provided during the initial passkey registration or through CorePass enrichment.fixAdjust your client-side flow to ensure an email address is provided either in the request body of the initial anonymous sign-in or is guaranteed to be delivered via CorePass enrichment, satisfying the configured plugin requirements. -
Error: Cannot update user: conflict.
cause An operation (e.g., updating user data during enrichment) resulted in a conflict, likely due to a uniqueness constraint violation (e.g., attempting to register an email or Core ID that already exists for another user).fixReview the plugin's configuration for any uniqueness requirements (e.g., unique email, unique Core ID). Investigate the user flow to ensure that duplicate registrations are handled gracefully, perhaps by prompting for a different credential or linking to an existing account.
Warnings
- breaking The enrichment endpoint for CorePass data was renamed from `/passkey/data` to `/webauthn/data`. Any existing client-side integrations sending data to the old path will fail.
- gotcha The plugin enforces 'strict passkey-only access' by default, meaning users without at least one registered passkey are blocked from most authentication endpoints. This can unexpectedly restrict anonymous bootstrap flows.
- gotcha By default, `finalize: 'after'` is enabled, which holds users on an 'on hold' status until CorePass enrichment data is successfully received. This means users are not immediately active post-passkey registration.
- gotcha If `userData.dataExp` (data expiry in minutes) is configured, the `corepass_profile` data will be automatically omitted from the session's `user.profile` object once it expires, requiring re-enrichment.
Install
-
npm install better-auth-corepass-passkey -
yarn add better-auth-corepass-passkey -
pnpm add better-auth-corepass-passkey
Imports
- createCorePassPasskeyPlugin
const createCorePassPasskeyPlugin = require('better-auth-corepass-passkey').default;import { createCorePassPasskeyPlugin } from 'better-auth-corepass-passkey' - CorePassPasskeyPluginOptions
import { CorePassPasskeyPluginOptions } from 'better-auth-corepass-passkey';import type { CorePassPasskeyPluginOptions } from 'better-auth-corepass-passkey' - CorePassProfile
import { CorePassProfile } from 'better-auth-corepass-passkey';import type { CorePassProfile } from 'better-auth-corepass-passkey'
Quickstart
import { AuthService } from 'better-auth';
import { createPasskeyPlugin } from '@better-auth/passkey';
import { createCorePassPasskeyPlugin } from 'better-auth-corepass-passkey';
import express from 'express';
// In a production environment, these should be loaded from secure environment variables.
const COREPASS_PUBLIC_KEY = process.env.COREPASS_PUBLIC_KEY ?? 'YOUR_COREPASS_PUBLIC_KEY_HERE';
const WEB_AUTHN_RP_ID = process.env.WEB_AUTHN_RP_ID ?? 'localhost'; // Your application's domain
const WEB_AUTHN_RP_NAME = process.env.WEB_AUTHN_RP_NAME ?? 'My Secure App'; // Your application's name
async function setupAuthService() {
const authService = new AuthService({
// ... other AuthService configuration options
plugins: [
createPasskeyPlugin({
rpId: WEB_AUTHN_RP_ID,
rpName: WEB_AUTHN_RP_NAME,
// other passkey plugin options, e.g., challenge timeout
}),
createCorePassPasskeyPlugin({
corePassPublicKey: COREPASS_PUBLIC_KEY,
// requireO18y: true, // Example: Require user to be over 18
// requireKyc: true, // Example: Require KYC verification
// allowNetwork: ['mainnet', 'testnet'], // Example: Allowed CorePass networks
})
],
// ... additional AuthService options, e.g., session management
});
const app = express();
app.use(express.json()); // Middleware to parse JSON request bodies
app.use('/auth', authService.router); // Mount Better Auth routes at /auth
// Example protected route, accessible only after successful passkey registration
// and CorePass enrichment, if configured to be required.
app.get('/api/profile', (req, res) => {
const session = authService.getSession(req); // Assuming a session is established
if (session && session.user && session.user.profile && 'coreId' in session.user.profile) {
return res.json({ message: `Welcome, CorePass user!`, profile: session.user.profile });
}
res.status(401).send('Unauthorized: CorePass profile not found or expired.');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Better Auth service running on http://localhost:${PORT}/auth`);
console.log(`Example protected endpoint: http://localhost:${PORT}/api/profile`);
});
}
setupAuthService().catch(console.error);