@mrgnw/anahtar 0.0.24 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -97,7 +97,7 @@ export function createHandlers(config) {
97
97
  "passkey/login-start": {
98
98
  method: "GET",
99
99
  handler: async (event) => {
100
- const options = await generateAuthenticationChallenge(config.db, event.url);
100
+ const options = await generateAuthenticationChallenge(config.db, event.url, config);
101
101
  return json(options);
102
102
  },
103
103
  },
@@ -108,7 +108,7 @@ export function createHandlers(config) {
108
108
  const email = body?.email;
109
109
  if (!email || typeof email !== "string")
110
110
  return json({ allowCredentials: [] });
111
- const options = await generateAuthenticationChallengeForUser(config.db, email, event.url);
111
+ const options = await generateAuthenticationChallengeForUser(config.db, email, event.url, config);
112
112
  return json(options);
113
113
  },
114
114
  },
@@ -119,7 +119,7 @@ export function createHandlers(config) {
119
119
  const body = await event.request.json().catch(() => null);
120
120
  if (!body)
121
121
  return json({ error: m.errorInvalidInput }, { status: 400 });
122
- const result = await verifyAuthenticationResponse(config.db, body, event.url);
122
+ const result = await verifyAuthenticationResponse(config.db, body, event.url, config);
123
123
  if (!result)
124
124
  return json({ error: m.errorAuthFailed }, { status: 401 });
125
125
  const session = await createSession(config.db, result.user.id, config);
@@ -150,7 +150,7 @@ export function createHandlers(config) {
150
150
  return json({ error: m.errorInvalidInput }, { status: 400 });
151
151
  const { name, ...response } = body;
152
152
  const passkeyName = typeof name === "string" && name.trim() ? name.trim() : null;
153
- const result = await verifyRegistrationResponse(config.db, user.id, response, event.url, passkeyName);
153
+ const result = await verifyRegistrationResponse(config.db, user.id, response, event.url, passkeyName, config);
154
154
  if (!result.ok) {
155
155
  console.error("register-finish failed:", result.reason);
156
156
  return json({ error: m.errorPasskeyRegFailed, reason: result.reason }, { status: 400 });
package/dist/passkey.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import type { AuthenticationResponseJSON, RegistrationResponseJSON } from "@simplewebauthn/server";
2
2
  import type { AuthDB, ResolvedConfig } from "./types.js";
3
- export declare function getWebAuthnConfig(requestUrl: URL): {
3
+ export declare function getWebAuthnConfig(requestUrl: URL, config?: {
4
+ rpId?: string;
5
+ origin?: string;
6
+ }): {
4
7
  rpID: string;
5
8
  origin: string;
6
9
  };
@@ -8,15 +11,15 @@ export declare function generateRegistrationChallenge(db: AuthDB, user: {
8
11
  id: string;
9
12
  email: string;
10
13
  }, requestUrl: URL, config: ResolvedConfig): Promise<import("@simplewebauthn/server").PublicKeyCredentialCreationOptionsJSON>;
11
- export declare function verifyRegistrationResponse(db: AuthDB, userId: string, response: RegistrationResponseJSON, requestUrl: URL, name?: string | null): Promise<{
14
+ export declare function verifyRegistrationResponse(db: AuthDB, userId: string, response: RegistrationResponseJSON, requestUrl: URL, name?: string | null, config?: ResolvedConfig): Promise<{
12
15
  ok: true;
13
16
  } | {
14
17
  ok: false;
15
18
  reason: string;
16
19
  }>;
17
- export declare function generateAuthenticationChallengeForUser(db: AuthDB, email: string, requestUrl: URL): Promise<import("@simplewebauthn/server").PublicKeyCredentialRequestOptionsJSON>;
18
- export declare function generateAuthenticationChallenge(db: AuthDB, requestUrl: URL): Promise<import("@simplewebauthn/server").PublicKeyCredentialRequestOptionsJSON>;
19
- export declare function verifyAuthenticationResponse(db: AuthDB, response: AuthenticationResponseJSON, requestUrl: URL): Promise<{
20
+ export declare function generateAuthenticationChallengeForUser(db: AuthDB, email: string, requestUrl: URL, config?: ResolvedConfig): Promise<import("@simplewebauthn/server").PublicKeyCredentialRequestOptionsJSON>;
21
+ export declare function generateAuthenticationChallenge(db: AuthDB, requestUrl: URL, config?: ResolvedConfig): Promise<import("@simplewebauthn/server").PublicKeyCredentialRequestOptionsJSON>;
22
+ export declare function verifyAuthenticationResponse(db: AuthDB, response: AuthenticationResponseJSON, requestUrl: URL, config?: ResolvedConfig): Promise<{
20
23
  user: {
21
24
  id: string;
22
25
  email: string;
package/dist/passkey.js CHANGED
@@ -1,7 +1,13 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthenticationResponse as verifyAuthResponse, verifyRegistrationResponse as verifyRegResponse, } from "@simplewebauthn/server";
3
3
  const CHALLENGE_EXPIRY_MS = 5 * 60 * 1000;
4
- export function getWebAuthnConfig(requestUrl) {
4
+ export function getWebAuthnConfig(requestUrl, config) {
5
+ if (config?.rpId || config?.origin) {
6
+ return {
7
+ rpID: config.rpId ?? requestUrl.hostname,
8
+ origin: config.origin ?? requestUrl.origin,
9
+ };
10
+ }
5
11
  const envOrigin = process.env.ORIGIN;
6
12
  if (envOrigin) {
7
13
  const url = new URL(envOrigin);
@@ -10,7 +16,7 @@ export function getWebAuthnConfig(requestUrl) {
10
16
  return { rpID: requestUrl.hostname, origin: requestUrl.origin };
11
17
  }
12
18
  export async function generateRegistrationChallenge(db, user, requestUrl, config) {
13
- const { rpID } = getWebAuthnConfig(requestUrl);
19
+ const { rpID } = getWebAuthnConfig(requestUrl, config);
14
20
  const existingPasskeys = await db.getUserPasskeys(user.id);
15
21
  const excludeCredentials = existingPasskeys.map((pk) => ({
16
22
  id: pk.credentialId,
@@ -33,8 +39,8 @@ export async function generateRegistrationChallenge(db, user, requestUrl, config
33
39
  await db.storeChallenge(options.challenge, user.id, Date.now() + CHALLENGE_EXPIRY_MS);
34
40
  return options;
35
41
  }
36
- export async function verifyRegistrationResponse(db, userId, response, requestUrl, name = null) {
37
- const { rpID, origin } = getWebAuthnConfig(requestUrl);
42
+ export async function verifyRegistrationResponse(db, userId, response, requestUrl, name = null, config) {
43
+ const { rpID, origin } = getWebAuthnConfig(requestUrl, config);
38
44
  let challenge;
39
45
  try {
40
46
  const clientData = JSON.parse(Buffer.from(response.response.clientDataJSON, "base64url").toString());
@@ -81,14 +87,14 @@ export async function verifyRegistrationResponse(db, userId, response, requestUr
81
87
  return { ok: false, reason: `verification threw: ${e}` };
82
88
  }
83
89
  }
84
- export async function generateAuthenticationChallengeForUser(db, email, requestUrl) {
85
- const { rpID } = getWebAuthnConfig(requestUrl);
90
+ export async function generateAuthenticationChallengeForUser(db, email, requestUrl, config) {
91
+ const { rpID } = getWebAuthnConfig(requestUrl, config);
86
92
  const user = await db.getUserByEmail(email);
87
93
  if (!user)
88
- return generateAuthenticationChallenge(db, requestUrl);
94
+ return generateAuthenticationChallenge(db, requestUrl, config);
89
95
  const passkeys = await db.getUserPasskeys(user.id);
90
96
  if (passkeys.length === 0)
91
- return generateAuthenticationChallenge(db, requestUrl);
97
+ return generateAuthenticationChallenge(db, requestUrl, config);
92
98
  const allowCredentials = passkeys.map((pk) => ({
93
99
  id: pk.credentialId,
94
100
  transports: pk.transports
@@ -103,8 +109,8 @@ export async function generateAuthenticationChallengeForUser(db, email, requestU
103
109
  await db.storeChallenge(options.challenge, user.id, Date.now() + CHALLENGE_EXPIRY_MS);
104
110
  return options;
105
111
  }
106
- export async function generateAuthenticationChallenge(db, requestUrl) {
107
- const { rpID } = getWebAuthnConfig(requestUrl);
112
+ export async function generateAuthenticationChallenge(db, requestUrl, config) {
113
+ const { rpID } = getWebAuthnConfig(requestUrl, config);
108
114
  const options = await generateAuthenticationOptions({
109
115
  rpID,
110
116
  allowCredentials: [],
@@ -113,8 +119,8 @@ export async function generateAuthenticationChallenge(db, requestUrl) {
113
119
  await db.storeChallenge(options.challenge, "anonymous", Date.now() + CHALLENGE_EXPIRY_MS);
114
120
  return options;
115
121
  }
116
- export async function verifyAuthenticationResponse(db, response, requestUrl) {
117
- const { rpID, origin } = getWebAuthnConfig(requestUrl);
122
+ export async function verifyAuthenticationResponse(db, response, requestUrl, config) {
123
+ const { rpID, origin } = getWebAuthnConfig(requestUrl, config);
118
124
  const passkey = await db.getPasskeyByCredentialId(response.id);
119
125
  if (!passkey)
120
126
  return null;
package/dist/types.d.ts CHANGED
@@ -86,11 +86,15 @@ export interface AuthConfig {
86
86
  otpLength?: number;
87
87
  otpMaxAttempts?: number;
88
88
  rpName?: string;
89
+ rpId?: string;
90
+ origin?: string;
89
91
  locale?: string;
90
92
  messages?: Partial<import('./i18n/types.js').AuthMessages>;
91
93
  onSendOTP: (email: string, code: string) => Promise<void>;
92
94
  }
93
- export interface ResolvedConfig extends Required<Omit<AuthConfig, 'onSendOTP' | 'locale' | 'messages'>> {
95
+ export interface ResolvedConfig extends Required<Omit<AuthConfig, 'onSendOTP' | 'locale' | 'messages' | 'rpId' | 'origin'>> {
96
+ rpId?: string;
97
+ origin?: string;
94
98
  locale?: string;
95
99
  messages?: Partial<import('./i18n/types.js').AuthMessages>;
96
100
  onSendOTP: (email: string, code: string) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrgnw/anahtar",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "description": "Opinionated, reusable auth for SvelteKit. Email+OTP + passkeys.",
5
5
  "license": "MIT",
6
6
  "type": "module",