mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-25 10:13:54 -05:00
refactor: split the passkey plugin into a separate package (#5769)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { passkeyClient } from "@better-auth/passkey/client";
|
||||
import { stripeClient } from "@better-auth/stripe/client";
|
||||
import {
|
||||
adminClient,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
oidcClient,
|
||||
oneTapClient,
|
||||
organizationClient,
|
||||
passkeyClient,
|
||||
twoFactorClient,
|
||||
} from "better-auth/client/plugins";
|
||||
import { createAuthClient } from "better-auth/react";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { passkey } from "@better-auth/passkey";
|
||||
import { sso } from "@better-auth/sso";
|
||||
import { stripe } from "@better-auth/stripe";
|
||||
import { LibsqlDialect } from "@libsql/kysely-libsql";
|
||||
@@ -16,7 +17,6 @@ import {
|
||||
organization,
|
||||
twoFactor,
|
||||
} from "better-auth/plugins";
|
||||
import { passkey } from "better-auth/plugins/passkey";
|
||||
import { MysqlDialect } from "kysely";
|
||||
import { createPool } from "mysql2/promise";
|
||||
import { Stripe } from "stripe";
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"dependencies": {
|
||||
"@better-auth/sso": "workspace:*",
|
||||
"@better-auth/stripe": "workspace:*",
|
||||
"@better-auth/passkey": "workspace:*",
|
||||
"@hookform/resolvers": "^5.2.1",
|
||||
"@libsql/client": "^0.8.1",
|
||||
"@libsql/kysely-libsql": "^0.4.1",
|
||||
|
||||
@@ -161,15 +161,18 @@ const tabs: { name: "auth.ts" | "client.ts"; code: string }[] = [
|
||||
enabled: true,
|
||||
},
|
||||
plugins: [
|
||||
organization(),
|
||||
twoFactor(),
|
||||
organization(),
|
||||
twoFactor(),
|
||||
]
|
||||
})`,
|
||||
},
|
||||
{
|
||||
name: "client.ts",
|
||||
code: `const client = createAuthClient({
|
||||
plugins: [passkeyClient()]
|
||||
plugins: [
|
||||
organizationClient(),
|
||||
twoFactorClient(),
|
||||
]
|
||||
});`,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -10,39 +10,20 @@ The passkey plugin implementation is powered by [SimpleWebAuthn](https://simplew
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
<Step>
|
||||
### Install the plugin
|
||||
|
||||
```package-install
|
||||
npm install @better-auth/passkey
|
||||
```
|
||||
</Step>
|
||||
<Step>
|
||||
### Add the plugin to your auth config
|
||||
To add the passkey plugin to your auth config, you need to import the plugin and pass it to the `plugins` option of the auth instance.
|
||||
|
||||
**Options**
|
||||
|
||||
`rpID`: A unique identifier for your website based on your auth server origin. 'localhost' is okay for local dev. RP ID can be formed by discarding zero or more labels from the left of its effective domain
|
||||
until it hits an effective TLD. So `www.example.com` can use the RP IDs `www.example.com` or `example.com`. But not `com`, because that's an eTLD.
|
||||
|
||||
`rpName`: Human-readable title for your website
|
||||
|
||||
`origin`: The origin URL at which your better-auth server is hosted. `http://localhost` and `http://localhost:PORT` are also valid. Do **NOT** include any trailing /
|
||||
|
||||
`authenticatorSelection`: Allows customization of WebAuthn authenticator selection criteria. Leave unspecified for default settings.
|
||||
- `authenticatorAttachment`: Specifies the type of authenticator
|
||||
- `platform`: Authenticator is attached to the platform (e.g., fingerprint reader)
|
||||
- `cross-platform`: Authenticator is not attached to the platform (e.g., security key)
|
||||
- Default: `not set` (both platform and cross-platform allowed, with platform preferred)
|
||||
- `residentKey`: Determines credential storage behavior.
|
||||
- `required`: User MUST store credentials on the authenticator (highest security)
|
||||
- `preferred`: Encourages credential storage but not mandatory
|
||||
- `discouraged`: No credential storage required (fastest experience)
|
||||
- Default: `preferred`
|
||||
- `userVerification`: Controls biometric/PIN verification during authentication:
|
||||
- `required`: User MUST verify identity (highest security)
|
||||
- `preferred`: Verification encouraged but not mandatory
|
||||
- `discouraged`: No verification required (fastest experience)
|
||||
- Default: `preferred`
|
||||
|
||||
|
||||
```ts title="auth.ts"
|
||||
import { betterAuth } from "better-auth"
|
||||
import { passkey } from "better-auth/plugins/passkey" // [!code highlight]
|
||||
import { passkey } from "@better-auth/passkey" // [!code highlight]
|
||||
|
||||
export const auth = betterAuth({
|
||||
plugins: [ // [!code highlight]
|
||||
@@ -75,7 +56,7 @@ The passkey plugin implementation is powered by [SimpleWebAuthn](https://simplew
|
||||
|
||||
```ts title="auth-client.ts"
|
||||
import { createAuthClient } from "better-auth/client"
|
||||
import { passkeyClient } from "better-auth/client/plugins"
|
||||
import { passkeyClient } from "@better-auth/passkey/client"
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
plugins: [ // [!code highlight]
|
||||
@@ -340,6 +321,23 @@ until it hits an effective TLD. So `www.example.com` can use the RP IDs `www.exa
|
||||
|
||||
**origin**: The origin URL at which your better-auth server is hosted. `http://localhost` and `http://localhost:PORT` are also valid. Do NOT include any trailing /.
|
||||
|
||||
**authenticatorSelection**: Allows customization of WebAuthn authenticator selection criteria. When unspecified, both platform and cross-platform authenticators are allowed with `preferred` settings for `residentKey` and `userVerification`.
|
||||
**authenticatorSelection**: Allows customization of WebAuthn authenticator selection criteria. Leave unspecified for default settings.
|
||||
|
||||
**aaguid**: (optional) Authenticator Attestation GUID. This is a unique identifier for the passkey provider (device or authenticator type) and can be used to identify the type of passkey device used during registration or authentication.
|
||||
- `authenticatorAttachment`: Specifies the type of authenticator
|
||||
- `platform`: Authenticator is attached to the platform (e.g., fingerprint reader)
|
||||
- `cross-platform`: Authenticator is not attached to the platform (e.g., security key)
|
||||
- Default: `not set` (both platform and cross-platform allowed, with platform preferred)
|
||||
- `residentKey`: Determines credential storage behavior.
|
||||
- `required`: User MUST store credentials on the authenticator (highest security)
|
||||
- `preferred`: Encourages credential storage but not mandatory
|
||||
- `discouraged`: No credential storage required (fastest experience)
|
||||
- Default: `preferred`
|
||||
- `userVerification`: Controls biometric/PIN verification during authentication:
|
||||
- `required`: User MUST verify identity (highest security)
|
||||
- `preferred`: Verification encouraged but not mandatory
|
||||
- `discouraged`: No verification required (fastest experience)
|
||||
- Default: `preferred`
|
||||
|
||||
**advanced**: Advanced options
|
||||
|
||||
- `webAuthnChallengeCookie`: Cookie name for storing WebAuthn challenge ID during authentication flow (Default: `better-auth-passkey`)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"dependencies": {
|
||||
"@better-auth/sso": "workspace:*",
|
||||
"@better-auth/stripe": "workspace:*",
|
||||
"@better-auth/passkey": "workspace:*",
|
||||
"better-auth": "workspace:*",
|
||||
"stripe": "^18.5.0"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { passkey } from "@better-auth/passkey";
|
||||
import { sso } from "@better-auth/sso";
|
||||
import { stripe } from "@better-auth/stripe";
|
||||
import { betterAuth } from "better-auth";
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
organization,
|
||||
twoFactor,
|
||||
} from "better-auth/plugins";
|
||||
import { passkey } from "better-auth/plugins/passkey";
|
||||
import { Stripe } from "stripe";
|
||||
|
||||
export const auth = betterAuth({
|
||||
|
||||
@@ -257,11 +257,6 @@
|
||||
"types": "./dist/plugins/one-time-token/index.d.ts",
|
||||
"default": "./dist/plugins/one-time-token/index.js"
|
||||
},
|
||||
"./plugins/passkey": {
|
||||
"better-auth-dev-source": "./src/plugins/passkey/index.ts",
|
||||
"types": "./dist/plugins/passkey/index.d.ts",
|
||||
"default": "./dist/plugins/passkey/index.js"
|
||||
},
|
||||
"./plugins/phone-number": {
|
||||
"better-auth-dev-source": "./src/plugins/phone-number/index.ts",
|
||||
"types": "./dist/plugins/phone-number/index.d.ts",
|
||||
@@ -416,9 +411,6 @@
|
||||
"plugins/organization/access": [
|
||||
"./dist/plugins/organization/access/index.d.ts"
|
||||
],
|
||||
"plugins/passkey": [
|
||||
"./dist/plugins/passkey/index.d.ts"
|
||||
],
|
||||
"plugins/phone-number": [
|
||||
"./dist/plugins/phone-number/index.d.ts"
|
||||
],
|
||||
@@ -443,8 +435,6 @@
|
||||
"@better-fetch/fetch": "catalog:",
|
||||
"@noble/ciphers": "^2.0.0",
|
||||
"@noble/hashes": "^2.0.0",
|
||||
"@simplewebauthn/browser": "^13.1.2",
|
||||
"@simplewebauthn/server": "^13.1.2",
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"better-call": "catalog:",
|
||||
"defu": "^6.1.4",
|
||||
|
||||
@@ -8,7 +8,7 @@ import { describe, expect, expectTypeOf, it, vi } from "vitest";
|
||||
import type { Ref } from "vue";
|
||||
import { twoFactorClient } from "../plugins";
|
||||
import type { Session, SessionQueryParams } from "../types";
|
||||
import { organizationClient, passkeyClient } from "./plugins";
|
||||
import { emailOTPClient, organizationClient } from "./plugins";
|
||||
import { createAuthClient as createReactClient } from "./react";
|
||||
import { createAuthClient as createSolidClient } from "./solid";
|
||||
import { createAuthClient as createSvelteClient } from "./svelte";
|
||||
@@ -278,7 +278,7 @@ describe("type", () => {
|
||||
|
||||
it("should infer session react", () => {
|
||||
const client = createReactClient({
|
||||
plugins: [organizationClient(), twoFactorClient(), passkeyClient()],
|
||||
plugins: [organizationClient(), twoFactorClient(), emailOTPClient()],
|
||||
});
|
||||
const $infer = client.$Infer.Session;
|
||||
expectTypeOf<typeof $infer.user>().toEqualTypeOf<{
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export type * from "@simplewebauthn/server";
|
||||
export * from "../../plugins/additional-fields/client";
|
||||
export * from "../../plugins/admin/client";
|
||||
export * from "../../plugins/anonymous/client";
|
||||
@@ -15,7 +14,6 @@ export * from "../../plugins/oidc-provider/client";
|
||||
export * from "../../plugins/one-tap/client";
|
||||
export * from "../../plugins/one-time-token/client";
|
||||
export * from "../../plugins/organization/client";
|
||||
export * from "../../plugins/passkey/client";
|
||||
export * from "../../plugins/phone-number/client";
|
||||
export * from "../../plugins/siwe/client";
|
||||
export * from "../../plugins/two-factor/client";
|
||||
|
||||
@@ -51,7 +51,6 @@ export default defineConfig({
|
||||
"./src/plugins/oauth-proxy/index.ts",
|
||||
"./src/plugins/organization/index.ts",
|
||||
"./src/plugins/organization/access/index.ts",
|
||||
"./src/plugins/passkey/index.ts",
|
||||
"./src/plugins/phone-number/index.ts",
|
||||
"./src/plugins/two-factor/index.ts",
|
||||
"./src/plugins/username/index.ts",
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"exports": "./dist/index.js",
|
||||
"bin": "./dist/index.js",
|
||||
"devDependencies": {
|
||||
"@better-auth/passkey": "workspace:*",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/prompts": "^2.4.9",
|
||||
|
||||
@@ -97,8 +97,8 @@ export const supportedPlugins = [
|
||||
id: "passkey",
|
||||
name: "passkey",
|
||||
clientName: "passkeyClient",
|
||||
path: `better-auth/plugins/passkey`,
|
||||
clientPath: "better-auth/client/plugins",
|
||||
path: `@better-auth/passkey`,
|
||||
clientPath: "@better-auth/passkey/client",
|
||||
},
|
||||
{
|
||||
id: "generic-oauth",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { passkey } from "@better-auth/passkey";
|
||||
import type { BetterAuthOptions } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { twoFactor, username } from "better-auth/plugins";
|
||||
import { passkey } from "better-auth/plugins/passkey";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { generateDrizzleSchema } from "../src/generators/drizzle";
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Better Auth Passkey Plugin
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Using npm
|
||||
npm install better-auth @better-auth/passkey
|
||||
|
||||
# Using yarn
|
||||
yarn add better-auth @better-auth/passkey
|
||||
|
||||
# Using pnpm
|
||||
pnpm add better-auth @better-auth/passkey
|
||||
|
||||
# Using bun
|
||||
bun add better-auth @better-auth/passkey
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Server
|
||||
|
||||
```typescript
|
||||
import { betterAuth } from "better-auth";
|
||||
import { passkey } from "@better-auth/passkey";
|
||||
|
||||
export const auth = betterAuth({
|
||||
plugins: [
|
||||
passkey({
|
||||
rpID: "example.com",
|
||||
rpName: "My App",
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
```typescript
|
||||
import { createAuthClient } from "better-auth/client";
|
||||
import { passkeyClient } from "@better-auth/passkey/client";
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
plugins: [passkeyClient()],
|
||||
});
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
For more information, visit the [Better Auth Passkey documentation](https://better-auth.com/docs/plugins/passkey).
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "@better-auth/passkey",
|
||||
"version": "1.4.0-beta.15",
|
||||
"type": "module",
|
||||
"description": "Passkey plugin for Better Auth",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest",
|
||||
"build": "tsdown",
|
||||
"dev": "tsdown --watch",
|
||||
"typecheck": "tsc --project tsconfig.json"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"better-auth-dev-source": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./client": {
|
||||
"better-auth-dev-source": "./src/client.ts",
|
||||
"types": "./dist/client.d.ts",
|
||||
"default": "./dist/client.js"
|
||||
}
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"./dist/index.d.ts"
|
||||
],
|
||||
"client": [
|
||||
"./dist/client.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@better-auth/core": "workspace:*",
|
||||
"better-auth": "workspace:*",
|
||||
"tsdown": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@simplewebauthn/browser": "^13.1.2",
|
||||
"@simplewebauthn/server": "^13.1.2",
|
||||
"zod": "^4.1.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"nanostores": "^1.0.1",
|
||||
"@better-auth/utils": "0.3.0",
|
||||
"better-call": "catalog:",
|
||||
"@better-fetch/fetch": "catalog:",
|
||||
"@better-auth/core": "workspace:*",
|
||||
"better-auth": "workspace:*"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/better-auth/better-auth",
|
||||
"directory": "packages/passkey"
|
||||
},
|
||||
"homepage": "https://www.better-auth.com/docs/plugins/passkey",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"passkey",
|
||||
"typescript",
|
||||
"better-auth"
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
@@ -9,10 +9,11 @@ import {
|
||||
startRegistration,
|
||||
WebAuthnError,
|
||||
} from "@simplewebauthn/browser";
|
||||
import { useAuthQuery } from "better-auth/client";
|
||||
import type { Session, User } from "better-auth/types";
|
||||
import { atom } from "nanostores";
|
||||
import { useAuthQuery } from "../../client";
|
||||
import type { Session, User } from "../../types";
|
||||
import type { Passkey, passkey as passkeyPl } from ".";
|
||||
import type { passkey } from ".";
|
||||
import type { Passkey } from "./types";
|
||||
|
||||
export const getPasskeyActions = (
|
||||
$fetch: BetterFetch,
|
||||
@@ -216,7 +217,7 @@ export const passkeyClient = () => {
|
||||
const $listPasskeys = atom<any>();
|
||||
return {
|
||||
id: "passkey",
|
||||
$InferServerPlugin: {} as ReturnType<typeof passkeyPl>,
|
||||
$InferServerPlugin: {} as ReturnType<typeof passkey>,
|
||||
getActions: ($fetch, $store) =>
|
||||
getPasskeyActions($fetch, {
|
||||
$listPasskeys,
|
||||
@@ -0,0 +1,12 @@
|
||||
import { defineErrorCodes } from "@better-auth/core/utils";
|
||||
|
||||
export const PASSKEY_ERROR_CODES = defineErrorCodes({
|
||||
CHALLENGE_NOT_FOUND: "Challenge not found",
|
||||
YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY:
|
||||
"You are not allowed to register this passkey",
|
||||
FAILED_TO_VERIFY_REGISTRATION: "Failed to verify registration",
|
||||
PASSKEY_NOT_FOUND: "Passkey not found",
|
||||
AUTHENTICATION_FAILED: "Authentication failed",
|
||||
UNABLE_TO_CREATE_SESSION: "Unable to create session",
|
||||
FAILED_TO_UPDATE_PASSKEY: "Failed to update passkey",
|
||||
});
|
||||
+29
-155
@@ -1,12 +1,9 @@
|
||||
import type { BetterAuthPlugin } from "@better-auth/core";
|
||||
import { createAuthEndpoint } from "@better-auth/core/api";
|
||||
import type { BetterAuthPluginDBSchema } from "@better-auth/core/db";
|
||||
import { defineErrorCodes } from "@better-auth/core/utils";
|
||||
import { base64 } from "@better-auth/utils/base64";
|
||||
import type {
|
||||
AuthenticationResponseJSON,
|
||||
AuthenticatorTransportFuture,
|
||||
CredentialDeviceType,
|
||||
PublicKeyCredentialCreationOptionsJSON,
|
||||
} from "@simplewebauthn/server";
|
||||
import {
|
||||
@@ -15,97 +12,21 @@ import {
|
||||
verifyAuthenticationResponse,
|
||||
verifyRegistrationResponse,
|
||||
} from "@simplewebauthn/server";
|
||||
import { generateId } from "better-auth";
|
||||
import {
|
||||
freshSessionMiddleware,
|
||||
getSessionFromCtx,
|
||||
sessionMiddleware,
|
||||
} from "better-auth/api";
|
||||
import { setSessionCookie } from "better-auth/cookies";
|
||||
import { generateRandomString } from "better-auth/crypto";
|
||||
import { mergeSchema } from "better-auth/db";
|
||||
import { APIError } from "better-call";
|
||||
import * as z from "zod";
|
||||
import { sessionMiddleware } from "../../api";
|
||||
import { freshSessionMiddleware, getSessionFromCtx } from "../../api/routes";
|
||||
import { setSessionCookie } from "../../cookies";
|
||||
import { generateRandomString } from "../../crypto/random";
|
||||
import { mergeSchema } from "../../db/schema";
|
||||
import type { InferOptionSchema } from "../../types/plugins";
|
||||
import { generateId } from "../../utils";
|
||||
|
||||
interface WebAuthnChallengeValue {
|
||||
expectedChallenge: string;
|
||||
userData: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
const ERROR_CODES = defineErrorCodes({
|
||||
CHALLENGE_NOT_FOUND: "Challenge not found",
|
||||
YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY:
|
||||
"You are not allowed to register this passkey",
|
||||
FAILED_TO_VERIFY_REGISTRATION: "Failed to verify registration",
|
||||
PASSKEY_NOT_FOUND: "Passkey not found",
|
||||
AUTHENTICATION_FAILED: "Authentication failed",
|
||||
UNABLE_TO_CREATE_SESSION: "Unable to create session",
|
||||
FAILED_TO_UPDATE_PASSKEY: "Failed to update passkey",
|
||||
});
|
||||
|
||||
function getRpID(options: PasskeyOptions, baseURL?: string | undefined) {
|
||||
return (
|
||||
options.rpID || (baseURL ? new URL(baseURL).hostname : "localhost") // default rpID
|
||||
);
|
||||
}
|
||||
|
||||
export interface PasskeyOptions {
|
||||
/**
|
||||
* A unique identifier for your website. 'localhost' is okay for
|
||||
* local dev
|
||||
*
|
||||
* @default "localhost"
|
||||
*/
|
||||
rpID?: string | undefined;
|
||||
/**
|
||||
* Human-readable title for your website
|
||||
*
|
||||
* @default "Better Auth"
|
||||
*/
|
||||
rpName?: string | undefined;
|
||||
/**
|
||||
* The URL at which registrations and authentications should occur.
|
||||
* `http://localhost` and `http://localhost:PORT` are also valid.
|
||||
* Do NOT include any trailing /
|
||||
*
|
||||
* if this isn't provided. The client itself will
|
||||
* pass this value.
|
||||
*/
|
||||
origin?: (string | string[] | null) | undefined;
|
||||
|
||||
/**
|
||||
* Allow customization of the authenticatorSelection options
|
||||
* during passkey registration.
|
||||
*/
|
||||
authenticatorSelection?: AuthenticatorSelectionCriteria | undefined;
|
||||
|
||||
/**
|
||||
* Advanced options
|
||||
*/
|
||||
advanced?:
|
||||
| {
|
||||
webAuthnChallengeCookie?: string;
|
||||
}
|
||||
| undefined;
|
||||
/**
|
||||
* Schema for the passkey model
|
||||
*/
|
||||
schema?: InferOptionSchema<typeof schema> | undefined;
|
||||
}
|
||||
|
||||
export type Passkey = {
|
||||
id: string;
|
||||
name?: string | undefined;
|
||||
publicKey: string;
|
||||
userId: string;
|
||||
credentialID: string;
|
||||
counter: number;
|
||||
deviceType: CredentialDeviceType;
|
||||
backedUp: boolean;
|
||||
transports?: string | undefined;
|
||||
createdAt: Date;
|
||||
aaguid?: string | undefined;
|
||||
};
|
||||
import { PASSKEY_ERROR_CODES } from "./error-codes";
|
||||
import { schema } from "./schema";
|
||||
import type { Passkey, PasskeyOptions, WebAuthnChallengeValue } from "./types";
|
||||
import { getRpID } from "./utils";
|
||||
|
||||
export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
const opts = {
|
||||
@@ -531,7 +452,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
);
|
||||
if (!challengeId) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: ERROR_CODES.CHALLENGE_NOT_FOUND,
|
||||
message: PASSKEY_ERROR_CODES.CHALLENGE_NOT_FOUND,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -550,7 +471,8 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
|
||||
if (userData.id !== ctx.context.session.user.id) {
|
||||
throw new APIError("UNAUTHORIZED", {
|
||||
message: ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY,
|
||||
message:
|
||||
PASSKEY_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -604,7 +526,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw new APIError("INTERNAL_SERVER_ERROR", {
|
||||
message: ERROR_CODES.FAILED_TO_VERIFY_REGISTRATION,
|
||||
message: PASSKEY_ERROR_CODES.FAILED_TO_VERIFY_REGISTRATION,
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -664,7 +586,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
);
|
||||
if (!challengeId) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: ERROR_CODES.CHALLENGE_NOT_FOUND,
|
||||
message: PASSKEY_ERROR_CODES.CHALLENGE_NOT_FOUND,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -674,7 +596,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
);
|
||||
if (!data) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: ERROR_CODES.CHALLENGE_NOT_FOUND,
|
||||
message: PASSKEY_ERROR_CODES.CHALLENGE_NOT_FOUND,
|
||||
});
|
||||
}
|
||||
const { expectedChallenge } = JSON.parse(
|
||||
@@ -691,7 +613,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
});
|
||||
if (!passkey) {
|
||||
throw new APIError("UNAUTHORIZED", {
|
||||
message: ERROR_CODES.PASSKEY_NOT_FOUND,
|
||||
message: PASSKEY_ERROR_CODES.PASSKEY_NOT_FOUND,
|
||||
});
|
||||
}
|
||||
try {
|
||||
@@ -713,7 +635,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
const { verified } = verification;
|
||||
if (!verified)
|
||||
throw new APIError("UNAUTHORIZED", {
|
||||
message: ERROR_CODES.AUTHENTICATION_FAILED,
|
||||
message: PASSKEY_ERROR_CODES.AUTHENTICATION_FAILED,
|
||||
});
|
||||
|
||||
await ctx.context.adapter.update<Passkey>({
|
||||
@@ -733,7 +655,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
);
|
||||
if (!s) {
|
||||
throw new APIError("INTERNAL_SERVER_ERROR", {
|
||||
message: ERROR_CODES.UNABLE_TO_CREATE_SESSION,
|
||||
message: PASSKEY_ERROR_CODES.UNABLE_TO_CREATE_SESSION,
|
||||
});
|
||||
}
|
||||
const user = await ctx.context.internalAdapter.findUserById(
|
||||
@@ -759,7 +681,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
} catch (e) {
|
||||
ctx.context.logger.error("Failed to verify authentication", e);
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: ERROR_CODES.AUTHENTICATION_FAILED,
|
||||
message: PASSKEY_ERROR_CODES.AUTHENTICATION_FAILED,
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -956,13 +878,14 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
|
||||
if (!passkey) {
|
||||
throw new APIError("NOT_FOUND", {
|
||||
message: ERROR_CODES.PASSKEY_NOT_FOUND,
|
||||
message: PASSKEY_ERROR_CODES.PASSKEY_NOT_FOUND,
|
||||
});
|
||||
}
|
||||
|
||||
if (passkey.userId !== ctx.context.session.user.id) {
|
||||
throw new APIError("UNAUTHORIZED", {
|
||||
message: ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY,
|
||||
message:
|
||||
PASSKEY_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -981,7 +904,7 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
|
||||
if (!updatedPasskey) {
|
||||
throw new APIError("INTERNAL_SERVER_ERROR", {
|
||||
message: ERROR_CODES.FAILED_TO_UPDATE_PASSKEY,
|
||||
message: PASSKEY_ERROR_CODES.FAILED_TO_UPDATE_PASSKEY,
|
||||
});
|
||||
}
|
||||
return ctx.json(
|
||||
@@ -996,57 +919,8 @@ export const passkey = (options?: PasskeyOptions | undefined) => {
|
||||
),
|
||||
},
|
||||
schema: mergeSchema(schema, options?.schema),
|
||||
$ERROR_CODES: ERROR_CODES,
|
||||
$ERROR_CODES: PASSKEY_ERROR_CODES,
|
||||
} satisfies BetterAuthPlugin;
|
||||
};
|
||||
|
||||
const schema = {
|
||||
passkey: {
|
||||
fields: {
|
||||
name: {
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
publicKey: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
userId: {
|
||||
type: "string",
|
||||
references: {
|
||||
model: "user",
|
||||
field: "id",
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
credentialID: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
counter: {
|
||||
type: "number",
|
||||
required: true,
|
||||
},
|
||||
deviceType: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
backedUp: {
|
||||
type: "boolean",
|
||||
required: true,
|
||||
},
|
||||
transports: {
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
createdAt: {
|
||||
type: "date",
|
||||
required: false,
|
||||
},
|
||||
aaguid: {
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies BetterAuthPluginDBSchema;
|
||||
export type { Passkey, PasskeyOptions };
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { createAuthClient } from "better-auth/client";
|
||||
import { getTestInstanceMemory as getTestInstance } from "better-auth/test";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createAuthClient } from "../../client";
|
||||
import { getTestInstance } from "../../test-utils/test-instance";
|
||||
import { type Passkey, passkey } from ".";
|
||||
import { passkeyClient } from "./client";
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { BetterAuthPluginDBSchema } from "@better-auth/core/db";
|
||||
|
||||
export const schema = {
|
||||
passkey: {
|
||||
fields: {
|
||||
name: {
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
publicKey: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
userId: {
|
||||
type: "string",
|
||||
references: {
|
||||
model: "user",
|
||||
field: "id",
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
credentialID: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
counter: {
|
||||
type: "number",
|
||||
required: true,
|
||||
},
|
||||
deviceType: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
backedUp: {
|
||||
type: "boolean",
|
||||
required: true,
|
||||
},
|
||||
transports: {
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
createdAt: {
|
||||
type: "date",
|
||||
required: false,
|
||||
},
|
||||
aaguid: {
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies BetterAuthPluginDBSchema;
|
||||
@@ -0,0 +1,74 @@
|
||||
import type { CredentialDeviceType } from "@simplewebauthn/server";
|
||||
import type { InferOptionSchema } from "better-auth/types";
|
||||
import type { schema } from "./schema";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface WebAuthnChallengeValue {
|
||||
expectedChallenge: string;
|
||||
userData: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PasskeyOptions {
|
||||
/**
|
||||
* A unique identifier for your website. 'localhost' is okay for
|
||||
* local dev
|
||||
*
|
||||
* @default "localhost"
|
||||
*/
|
||||
rpID?: string | undefined;
|
||||
/**
|
||||
* Human-readable title for your website
|
||||
*
|
||||
* @default "Better Auth"
|
||||
*/
|
||||
rpName?: string | undefined;
|
||||
/**
|
||||
* The URL at which registrations and authentications should occur.
|
||||
* `http://localhost` and `http://localhost:PORT` are also valid.
|
||||
* Do NOT include any trailing /
|
||||
*
|
||||
* if this isn't provided. The client itself will
|
||||
* pass this value.
|
||||
*/
|
||||
origin?: (string | string[] | null) | undefined;
|
||||
/**
|
||||
* Allow customization of the authenticatorSelection options
|
||||
* during passkey registration.
|
||||
*/
|
||||
authenticatorSelection?: AuthenticatorSelectionCriteria | undefined;
|
||||
/**
|
||||
* Advanced options
|
||||
*/
|
||||
advanced?:
|
||||
| {
|
||||
/**
|
||||
* Cookie name for storing WebAuthn challenge ID during authentication flow
|
||||
*
|
||||
* @default "better-auth-passkey"
|
||||
*/
|
||||
webAuthnChallengeCookie?: string;
|
||||
}
|
||||
| undefined;
|
||||
/**
|
||||
* Schema for the passkey model
|
||||
*/
|
||||
schema?: InferOptionSchema<typeof schema> | undefined;
|
||||
}
|
||||
|
||||
export type Passkey = {
|
||||
id: string;
|
||||
name?: string | undefined;
|
||||
publicKey: string;
|
||||
userId: string;
|
||||
credentialID: string;
|
||||
counter: number;
|
||||
deviceType: CredentialDeviceType;
|
||||
backedUp: boolean;
|
||||
transports?: string | undefined;
|
||||
createdAt: Date;
|
||||
aaguid?: string | undefined;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { PasskeyOptions } from "./types";
|
||||
|
||||
export function getRpID(options: PasskeyOptions, baseURL?: string | undefined) {
|
||||
return (
|
||||
options.rpID || (baseURL ? new URL(baseURL).hostname : "localhost") // default rpID
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["esnext", "dom", "dom.iterable"]
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../better-auth/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from "tsdown";
|
||||
|
||||
export default defineConfig({
|
||||
dts: { build: true, incremental: true },
|
||||
format: ["esm"],
|
||||
entry: ["./src/index.ts", "./src/client.ts"],
|
||||
external: [
|
||||
"nanostores",
|
||||
"@better-auth/utils",
|
||||
"better-call",
|
||||
"@better-fetch/fetch",
|
||||
"@better-auth/core",
|
||||
"better-auth",
|
||||
],
|
||||
treeshake: true,
|
||||
});
|
||||
Generated
+67
-33
@@ -206,6 +206,9 @@ importers:
|
||||
|
||||
demo/nextjs:
|
||||
dependencies:
|
||||
'@better-auth/passkey':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/passkey
|
||||
'@better-auth/sso':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/sso
|
||||
@@ -352,7 +355,7 @@ importers:
|
||||
version: 12.23.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
geist:
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2(next@16.0.1(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
|
||||
version: 1.4.2(next@16.0.1(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
|
||||
input-otp:
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
@@ -639,7 +642,7 @@ importers:
|
||||
version: 15.8.3(@oramacloud/client@2.1.4)(@tanstack/react-router@1.131.27(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(algoliasearch@5.36.0)(lucide-react@0.542.0(react@19.2.0))(next@16.0.1(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(tailwindcss@4.1.13)
|
||||
geist:
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2(next@16.0.1(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
|
||||
version: 1.4.2(next@16.0.1(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))
|
||||
gray-matter:
|
||||
specifier: ^4.0.3
|
||||
version: 4.0.3
|
||||
@@ -873,6 +876,9 @@ importers:
|
||||
|
||||
e2e/smoke/test/fixtures/tsconfig-declaration:
|
||||
dependencies:
|
||||
'@better-auth/passkey':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../../packages/passkey
|
||||
'@better-auth/sso':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../../packages/sso
|
||||
@@ -946,12 +952,6 @@ importers:
|
||||
'@noble/hashes':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
'@simplewebauthn/browser':
|
||||
specifier: ^13.1.2
|
||||
version: 13.1.2
|
||||
'@simplewebauthn/server':
|
||||
specifier: ^13.1.2
|
||||
version: 13.1.2
|
||||
'@standard-schema/spec':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
@@ -1161,6 +1161,9 @@ importers:
|
||||
specifier: ^4.1.5
|
||||
version: 4.1.5
|
||||
devDependencies:
|
||||
'@better-auth/passkey':
|
||||
specifier: workspace:*
|
||||
version: link:../passkey
|
||||
'@types/better-sqlite3':
|
||||
specifier: ^7.6.13
|
||||
version: 7.6.13
|
||||
@@ -1263,6 +1266,40 @@ importers:
|
||||
specifier: 'catalog:'
|
||||
version: 0.15.11(typescript@5.9.3)
|
||||
|
||||
packages/passkey:
|
||||
dependencies:
|
||||
'@better-auth/utils':
|
||||
specifier: 0.3.0
|
||||
version: 0.3.0
|
||||
'@better-fetch/fetch':
|
||||
specifier: 'catalog:'
|
||||
version: 1.1.18
|
||||
'@simplewebauthn/browser':
|
||||
specifier: ^13.1.2
|
||||
version: 13.1.2
|
||||
'@simplewebauthn/server':
|
||||
specifier: ^13.1.2
|
||||
version: 13.1.2
|
||||
better-call:
|
||||
specifier: 'catalog:'
|
||||
version: 1.0.24
|
||||
nanostores:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
zod:
|
||||
specifier: ^4.1.5
|
||||
version: 4.1.5
|
||||
devDependencies:
|
||||
'@better-auth/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
better-auth:
|
||||
specifier: workspace:*
|
||||
version: link:../better-auth
|
||||
tsdown:
|
||||
specifier: 'catalog:'
|
||||
version: 0.15.11(typescript@5.9.3)
|
||||
|
||||
packages/sso:
|
||||
dependencies:
|
||||
'@better-fetch/fetch':
|
||||
@@ -13695,7 +13732,7 @@ snapshots:
|
||||
'@babel/helper-member-expression-to-functions@7.27.1':
|
||||
dependencies:
|
||||
'@babel/traverse': 7.28.4
|
||||
'@babel/types': 7.28.4
|
||||
'@babel/types': 7.28.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -13728,7 +13765,7 @@ snapshots:
|
||||
|
||||
'@babel/helper-optimise-call-expression@7.27.1':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.4
|
||||
'@babel/types': 7.28.5
|
||||
|
||||
'@babel/helper-plugin-utils@7.27.1': {}
|
||||
|
||||
@@ -15367,7 +15404,7 @@ snapshots:
|
||||
postcss: 8.4.49
|
||||
resolve-from: 5.0.0
|
||||
optionalDependencies:
|
||||
expo: 54.0.21(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(graphql@16.11.0)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)
|
||||
expo: 54.0.21(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(graphql@16.11.0)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@react-native/metro-config@0.81.0(@babel/core@7.28.4))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
@@ -15452,7 +15489,7 @@ snapshots:
|
||||
'@expo/json-file': 10.0.7
|
||||
'@react-native/normalize-colors': 0.81.5
|
||||
debug: 4.4.3
|
||||
expo: 54.0.21(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(graphql@16.11.0)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)
|
||||
expo: 54.0.21(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(graphql@16.11.0)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@react-native/metro-config@0.81.0(@babel/core@7.28.4))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)
|
||||
resolve-from: 5.0.0
|
||||
semver: 7.7.3
|
||||
xml2js: 0.6.0
|
||||
@@ -17676,9 +17713,7 @@ snapshots:
|
||||
metro-runtime: 0.83.3
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
optional: true
|
||||
|
||||
'@react-native/normalize-colors@0.74.89': {}
|
||||
@@ -17960,7 +17995,7 @@ snapshots:
|
||||
estree-walker: 2.0.2
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
is-reference: 1.2.1
|
||||
magic-string: 0.30.19
|
||||
magic-string: 0.30.21
|
||||
picomatch: 4.0.3
|
||||
optionalDependencies:
|
||||
rollup: 4.52.5
|
||||
@@ -17969,7 +18004,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.2.0(rollup@4.52.5)
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.19
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
rollup: 4.52.5
|
||||
|
||||
@@ -17992,7 +18027,7 @@ snapshots:
|
||||
'@rollup/plugin-replace@6.0.2(rollup@4.52.5)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.2.0(rollup@4.52.5)
|
||||
magic-string: 0.30.19
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
rollup: 4.52.5
|
||||
|
||||
@@ -18630,7 +18665,7 @@ snapshots:
|
||||
'@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.28.4
|
||||
'@babel/types': 7.28.4
|
||||
'@babel/types': 7.28.5
|
||||
'@tanstack/directive-functions-plugin': 1.131.2(vite@7.1.11(@types/node@24.9.2)(jiti@2.6.1)(less@4.4.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
babel-dead-code-elimination: 1.0.10
|
||||
tiny-invariant: 1.3.3
|
||||
@@ -18658,7 +18693,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.26.2
|
||||
'@babel/core': 7.28.4
|
||||
'@babel/types': 7.28.4
|
||||
'@babel/types': 7.28.5
|
||||
'@tanstack/router-core': 1.131.27
|
||||
'@tanstack/router-generator': 1.131.27
|
||||
'@tanstack/router-plugin': 1.131.27(@tanstack/react-router@1.131.27(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite-plugin-solid@2.11.8(solid-js@1.9.9)(vite@7.1.11(@types/node@24.9.2)(jiti@2.6.1)(less@4.4.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.1.11(@types/node@24.9.2)(jiti@2.6.1)(less@4.4.1)(lightningcss@1.30.1)(sass@1.90.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
|
||||
@@ -19222,7 +19257,7 @@ snapshots:
|
||||
|
||||
'@vue/compiler-core@3.5.19':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.4
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/shared': 3.5.19
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
@@ -19669,7 +19704,7 @@ snapshots:
|
||||
resolve-from: 5.0.0
|
||||
optionalDependencies:
|
||||
'@babel/runtime': 7.28.4
|
||||
expo: 54.0.21(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(graphql@16.11.0)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)
|
||||
expo: 54.0.21(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(graphql@16.11.0)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@react-native/metro-config@0.81.0(@babel/core@7.28.4))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- supports-color
|
||||
@@ -19716,7 +19751,7 @@ snapshots:
|
||||
'@better-auth/utils': 0.3.0
|
||||
'@better-fetch/fetch': 1.1.18
|
||||
rou3: 0.5.1
|
||||
set-cookie-parser: 2.7.1
|
||||
set-cookie-parser: 2.7.2
|
||||
uncrypto: 0.1.3
|
||||
|
||||
better-opn@3.0.2:
|
||||
@@ -21159,7 +21194,7 @@ snapshots:
|
||||
|
||||
expo-keep-awake@15.0.7(expo@54.0.21)(react@19.2.0):
|
||||
dependencies:
|
||||
expo: 54.0.21(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(graphql@16.11.0)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)
|
||||
expo: 54.0.21(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(graphql@16.11.0)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@react-native/metro-config@0.81.0(@babel/core@7.28.4))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)
|
||||
react: 19.2.0
|
||||
|
||||
expo-linking@7.1.7(expo@54.0.21)(react-native@0.80.2(@babel/core@7.28.4)(@react-native-community/cli@20.0.1(typescript@5.9.3))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0):
|
||||
@@ -21811,7 +21846,7 @@ snapshots:
|
||||
|
||||
function-bind@1.1.2: {}
|
||||
|
||||
geist@1.4.2(next@16.0.1(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)):
|
||||
geist@1.4.2(next@16.0.1(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)):
|
||||
dependencies:
|
||||
next: 16.0.1(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)
|
||||
|
||||
@@ -22947,8 +22982,8 @@ snapshots:
|
||||
|
||||
magicast@0.2.11:
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.4
|
||||
'@babel/types': 7.28.4
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/types': 7.28.5
|
||||
recast: 0.23.11
|
||||
|
||||
magicast@0.3.5:
|
||||
@@ -23567,11 +23602,11 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.27.1
|
||||
'@babel/core': 7.28.4
|
||||
'@babel/generator': 7.28.3
|
||||
'@babel/parser': 7.28.4
|
||||
'@babel/generator': 7.28.5
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.28.4
|
||||
'@babel/types': 7.28.4
|
||||
'@babel/types': 7.28.5
|
||||
accepts: 1.3.8
|
||||
chalk: 4.1.2
|
||||
ci-info: 2.0.0
|
||||
@@ -26145,8 +26180,7 @@ snapshots:
|
||||
|
||||
set-cookie-parser@2.7.1: {}
|
||||
|
||||
set-cookie-parser@2.7.2:
|
||||
optional: true
|
||||
set-cookie-parser@2.7.2: {}
|
||||
|
||||
setimmediate@1.0.5: {}
|
||||
|
||||
@@ -26982,7 +27016,7 @@ snapshots:
|
||||
escape-string-regexp: 5.0.0
|
||||
estree-walker: 3.0.3
|
||||
local-pkg: 1.1.2
|
||||
magic-string: 0.30.19
|
||||
magic-string: 0.30.21
|
||||
mlly: 1.7.4
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.3
|
||||
@@ -27105,7 +27139,7 @@ snapshots:
|
||||
unwasm@0.3.11:
|
||||
dependencies:
|
||||
knitwork: 1.2.0
|
||||
magic-string: 0.30.19
|
||||
magic-string: 0.30.21
|
||||
mlly: 1.7.4
|
||||
pathe: 2.0.3
|
||||
pkg-types: 2.3.0
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
{
|
||||
"path": "./packages/expo/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "./packages/passkey/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "./packages/sso/tsconfig.json"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user