nid-webauthn-emulator 0.2.2 → 0.2.4
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.
- package/README.md +12 -6
- package/dist/spec/webauthn/webauthn-emulator.spec.js +23 -0
- package/dist/src/webauthn/webauthn-emulator.d.ts +4 -4
- package/dist/src/webauthn/webauthn-emulator.js +8 -8
- package/dist/src/webauthn/webauthn-model.d.ts +1 -1
- package/dist/src/webauthn/webauthn-model.js +15 -7
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -125,13 +125,13 @@ import WebAuthnEmulator, {
|
|
|
125
125
|
type PublicKeyCredentialRequestOptionsJSON,
|
|
126
126
|
} from "nid-webauthn-emulator";
|
|
127
127
|
|
|
128
|
-
async function startWebAuthnEmulator(page: Page, origin: string, debug = false) {
|
|
128
|
+
async function startWebAuthnEmulator(page: Page, origin: string, debug = false, relatedOrigins: string[] = []) {
|
|
129
129
|
const emulator = new WebAuthnEmulator();
|
|
130
130
|
|
|
131
131
|
await page.exposeFunction(
|
|
132
132
|
BrowserInjection.WebAuthnEmulatorCreate,
|
|
133
133
|
async (optionsJSON: PublicKeyCredentialCreationOptionsJSON) => {
|
|
134
|
-
const response = emulator.createJSON(origin, optionsJSON);
|
|
134
|
+
const response = emulator.createJSON(origin, optionsJSON, relatedOrigins);
|
|
135
135
|
return response;
|
|
136
136
|
},
|
|
137
137
|
);
|
|
@@ -139,7 +139,7 @@ async function startWebAuthnEmulator(page: Page, origin: string, debug = false)
|
|
|
139
139
|
await page.exposeFunction(
|
|
140
140
|
BrowserInjection.WebAuthnEmulatorGet,
|
|
141
141
|
async (optionsJSON: PublicKeyCredentialRequestOptionsJSON) => {
|
|
142
|
-
const response = emulator.getJSON(origin, optionsJSON);
|
|
142
|
+
const response = emulator.getJSON(origin, optionsJSON, relatedOrigins);
|
|
143
143
|
return response;
|
|
144
144
|
},
|
|
145
145
|
);
|
|
@@ -155,21 +155,23 @@ async function startWebAuthnEmulator(page: Page, origin: string, debug = false)
|
|
|
155
155
|
BrowserInjection.WebAuthnEmulatorSignalAllAcceptedCredentials,
|
|
156
156
|
async (options: AllAcceptedCredentialsOptionsJSON) => {
|
|
157
157
|
emulator.signalAllAcceptedCredentials(options);
|
|
158
|
-
}
|
|
158
|
+
},
|
|
159
159
|
);
|
|
160
160
|
|
|
161
161
|
await page.exposeFunction(
|
|
162
162
|
BrowserInjection.WebAuthnEmulatorSignalCurrentUserDetails,
|
|
163
163
|
async (options: CurrentUserDetailsOptionsJSON) => {
|
|
164
164
|
emulator.signalCurrentUserDetails(options);
|
|
165
|
-
}
|
|
165
|
+
},
|
|
166
166
|
);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
test.describe("Passkeys Tests", { tag: ["@daily"] }, () => {
|
|
170
170
|
test("Passkeys login test", async ({ page }) => {
|
|
171
171
|
// Page内で最初に1回だけ定義する exposed functions
|
|
172
|
-
|
|
172
|
+
// 必要に応じて関連オリジンを指定できます
|
|
173
|
+
const relatedOrigins = ["https://sub.example.com", "https://alt.example.com"];
|
|
174
|
+
await startWebAuthnEmulator(page, env, true, relatedOrigins);
|
|
173
175
|
await page.goto("https://example.com/passkeys/login");
|
|
174
176
|
|
|
175
177
|
// Passkeys の WebAuthn API をフック開始
|
|
@@ -184,6 +186,10 @@ test.describe("Passkeys Tests", { tag: ["@daily"] }, () => {
|
|
|
184
186
|
- `window.webAuthnEmulatorGet`: `WebAuthnEmulator.getJSON` の Exposed Function
|
|
185
187
|
- `window.webAuthnEmulatorCreate`: `WebAuthnEmulator.createJSON` の Exposed Function
|
|
186
188
|
- `window.webAuthnEmulatorSignalUnknownCredential`: `WebAuthnEmulator.signalUnknownCredential` の Exposed Function
|
|
189
|
+
- `window.webAuthnEmulatorSignalAllAcceptedCredentials`: `WebAuthnEmulator.signalAllAcceptedCredentials` の Exposed Function
|
|
190
|
+
- `window.webAuthnEmulatorSignalCurrentUserDetails`: `WebAuthnEmulator.signalCurrentUserDetails` の Exposed Function
|
|
191
|
+
|
|
192
|
+
さらに`startWebAuthnEmulator`関数は`relatedOrigins`パラメータをサポートしています。これにより、異なるオリジンからのリクエストでも同じRP IDを使用できるようになります。例えば、マルチドメイン環境(`example.com`と`sub.example.com`など)でPasskeysを使用する場合に便利です。`relatedOrigins` の値はRP IDで指定されたドメインでホストされた `/.well-known/webauthn` の内容と同じです。
|
|
187
193
|
|
|
188
194
|
これらは Page グローバルに定義されるため、Page インスタンスにつき 1 回だけ定義する必要があります。
|
|
189
195
|
|
|
@@ -337,6 +337,29 @@ const webauthn_test_server_1 = require("./webauthn-test-server");
|
|
|
337
337
|
(0, globals_1.expect)(userCredentialsAfter.length).toBe(0);
|
|
338
338
|
});
|
|
339
339
|
});
|
|
340
|
+
(0, globals_1.describe)("WebAuthnEmulator relatedOrigins Test", () => {
|
|
341
|
+
(0, globals_1.test)("Get with relatedOrigins _ OK with origin not matching rpId", async () => {
|
|
342
|
+
const user = { username: "test-related", id: "test-related" };
|
|
343
|
+
const emulator = new src_1.default();
|
|
344
|
+
// Origin is different from the RP ID
|
|
345
|
+
const testServer = new webauthn_test_server_1.WebAuthnTestServer("https://test-rp.com", "test-rp2.com");
|
|
346
|
+
const relatedOrigins = ["https://test-rp-a.com", "https://test-rp-b.com", "https://test-rp.com"];
|
|
347
|
+
// Register a credential
|
|
348
|
+
const regOptions = await testServer.getRegistrationOptions(user);
|
|
349
|
+
const credential = emulator.createJSON(testServer.origin, regOptions, relatedOrigins);
|
|
350
|
+
await testServer.getRegistrationVerification(user, credential);
|
|
351
|
+
// Create authentication options
|
|
352
|
+
const authOptions = await testServer.getAuthenticationOptions();
|
|
353
|
+
// Standard RP ID validation only
|
|
354
|
+
await (0, globals_1.expect)(async () => {
|
|
355
|
+
emulator.getJSON(testServer.origin, authOptions);
|
|
356
|
+
}).rejects.toThrow(src_1.InvalidRpIdError);
|
|
357
|
+
// Related origins validation
|
|
358
|
+
const authResponse = emulator.getJSON(testServer.origin, authOptions, relatedOrigins);
|
|
359
|
+
(0, globals_1.expect)(authResponse).toBeDefined();
|
|
360
|
+
(0, globals_1.expect)(authResponse.id).toBe(credential.id);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
340
363
|
(0, globals_1.describe)("WebAuthnEmulator signalCurrentUserDetails Test", () => {
|
|
341
364
|
(0, globals_1.test)("Signal Current User Details _ OK", async () => {
|
|
342
365
|
// Create user
|
|
@@ -20,8 +20,8 @@ export declare class InvalidRpIdError extends WebAuthnEmulatorError {
|
|
|
20
20
|
export declare class WebAuthnEmulator {
|
|
21
21
|
authenticator: AuthenticatorEmulator;
|
|
22
22
|
constructor(authenticator?: AuthenticatorEmulator);
|
|
23
|
-
getJSON(origin: string, optionsJSON: PublicKeyCredentialRequestOptionsJSON): AuthenticationResponseJSON;
|
|
24
|
-
createJSON(origin: string, optionsJSON: PublicKeyCredentialCreationOptionsJSON): RegistrationResponseJSON;
|
|
23
|
+
getJSON(origin: string, optionsJSON: PublicKeyCredentialRequestOptionsJSON, relatedOrigins?: string[]): AuthenticationResponseJSON;
|
|
24
|
+
createJSON(origin: string, optionsJSON: PublicKeyCredentialCreationOptionsJSON, relatedOrigins?: string[]): RegistrationResponseJSON;
|
|
25
25
|
getAuthenticatorInfo(): AuthenticatorInfo;
|
|
26
26
|
signalUnknownCredential(options: UnknownCredentialOptionsJSON): void;
|
|
27
27
|
/**
|
|
@@ -39,7 +39,7 @@ export declare class WebAuthnEmulator {
|
|
|
39
39
|
*/
|
|
40
40
|
signalCurrentUserDetails(options: CurrentUserDetailsOptionsJSON): void;
|
|
41
41
|
/** @see https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get */
|
|
42
|
-
get(origin: string, options: CredentialRequestOptions): RequestPublicKeyCredential;
|
|
42
|
+
get(origin: string, options: CredentialRequestOptions, relatedOrigins?: string[]): RequestPublicKeyCredential;
|
|
43
43
|
/** @see https://developer.mozilla.org/ja/docs/Web/API/CredentialsContainer/create */
|
|
44
|
-
create(origin: string, options: CredentialCreationOptions): CreatePublicKeyCredential;
|
|
44
|
+
create(origin: string, options: CredentialCreationOptions, relatedOrigins?: string[]): CreatePublicKeyCredential;
|
|
45
45
|
}
|
|
@@ -27,14 +27,14 @@ class WebAuthnEmulator {
|
|
|
27
27
|
constructor(authenticator = new authenticator_emulator_1.AuthenticatorEmulator()) {
|
|
28
28
|
this.authenticator = authenticator;
|
|
29
29
|
}
|
|
30
|
-
getJSON(origin, optionsJSON) {
|
|
30
|
+
getJSON(origin, optionsJSON, relatedOrigins = []) {
|
|
31
31
|
const options = (0, webauthn_model_json_1.parseRequestOptionsFromJSON)(optionsJSON);
|
|
32
|
-
const response = this.get(origin, { publicKey: options });
|
|
32
|
+
const response = this.get(origin, { publicKey: options }, relatedOrigins);
|
|
33
33
|
return response.toJSON();
|
|
34
34
|
}
|
|
35
|
-
createJSON(origin, optionsJSON) {
|
|
35
|
+
createJSON(origin, optionsJSON, relatedOrigins = []) {
|
|
36
36
|
const options = (0, webauthn_model_json_1.parseCreationOptionsFromJSON)(optionsJSON);
|
|
37
|
-
const response = this.create(origin, { publicKey: options });
|
|
37
|
+
const response = this.create(origin, { publicKey: options }, relatedOrigins);
|
|
38
38
|
return response.toJSON();
|
|
39
39
|
}
|
|
40
40
|
getAuthenticatorInfo() {
|
|
@@ -129,11 +129,11 @@ class WebAuthnEmulator {
|
|
|
129
129
|
}));
|
|
130
130
|
}
|
|
131
131
|
/** @see https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get */
|
|
132
|
-
get(origin, options) {
|
|
132
|
+
get(origin, options, relatedOrigins = []) {
|
|
133
133
|
if (!options.publicKey)
|
|
134
134
|
throw new NoPublicKeyError("PublicKeyCredentialCreationOptions are required");
|
|
135
135
|
const rpId = new webauthn_model_1.RpId(options.publicKey.rpId ?? new URL(origin).hostname);
|
|
136
|
-
if (!rpId.validate(origin))
|
|
136
|
+
if (!rpId.validate(origin, relatedOrigins))
|
|
137
137
|
throw new InvalidRpIdError(`Invalid rpId: RP_ID=${rpId.value}, ORIGIN=${origin}`);
|
|
138
138
|
const clientData = {
|
|
139
139
|
type: "webauthn.get",
|
|
@@ -170,11 +170,11 @@ class WebAuthnEmulator {
|
|
|
170
170
|
return publicKeyCredential;
|
|
171
171
|
}
|
|
172
172
|
/** @see https://developer.mozilla.org/ja/docs/Web/API/CredentialsContainer/create */
|
|
173
|
-
create(origin, options) {
|
|
173
|
+
create(origin, options, relatedOrigins = []) {
|
|
174
174
|
if (!options.publicKey)
|
|
175
175
|
throw new NoPublicKeyError("PublicKeyCredentialCreationOptions are required");
|
|
176
176
|
const rpId = new webauthn_model_1.RpId(options.publicKey.rp.id ?? new URL(origin).hostname);
|
|
177
|
-
if (!rpId.validate(origin))
|
|
177
|
+
if (!rpId.validate(origin, relatedOrigins))
|
|
178
178
|
throw new InvalidRpIdError(`Invalid rpId: RP_ID=${rpId.value}, ORIGIN=${origin}`);
|
|
179
179
|
const clientData = {
|
|
180
180
|
challenge: encode_utils_1.default.encodeBase64Url(options.publicKey.challenge),
|
|
@@ -5,7 +5,7 @@ export declare class RpId {
|
|
|
5
5
|
constructor(value: string);
|
|
6
6
|
get hash(): Uint8Array;
|
|
7
7
|
/** @see https://www.w3.org/TR/webauthn-3/#sctn-validating-origin */
|
|
8
|
-
validate(origin: string): boolean;
|
|
8
|
+
validate(origin: string, relatedOrigins?: string[]): boolean;
|
|
9
9
|
}
|
|
10
10
|
/** @see https://www.w3.org/TR/webauthn-3/#public-key-credential-source */
|
|
11
11
|
export type PublicKeyCredentialSource = {
|
|
@@ -25,14 +25,22 @@ class RpId {
|
|
|
25
25
|
return new Uint8Array((0, node_crypto_1.createHash)("sha256").update(this.value).digest());
|
|
26
26
|
}
|
|
27
27
|
/** @see https://www.w3.org/TR/webauthn-3/#sctn-validating-origin */
|
|
28
|
-
validate(origin) {
|
|
28
|
+
validate(origin, relatedOrigins = []) {
|
|
29
29
|
const parsedOrigin = (0, tldts_1.parse)(origin, { validHosts: ["localhost"] });
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
const expectedOrigins = [this.value, ...relatedOrigins];
|
|
31
|
+
// Check each origin using standard validation
|
|
32
|
+
for (const expectedOrigin of expectedOrigins) {
|
|
33
|
+
const parsedExpectedOrigin = (0, tldts_1.parse)(expectedOrigin, { validHosts: ["localhost"] });
|
|
34
|
+
const isValid = Boolean(parsedOrigin.domain &&
|
|
35
|
+
parsedOrigin.subdomain !== null &&
|
|
36
|
+
parsedExpectedOrigin.subdomain !== null &&
|
|
37
|
+
parsedOrigin.domain === parsedExpectedOrigin.domain &&
|
|
38
|
+
parsedOrigin.subdomain.endsWith(parsedExpectedOrigin.subdomain));
|
|
39
|
+
if (isValid) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
exports.RpId = RpId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nid-webauthn-emulator",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"test",
|
|
6
6
|
"e2e",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@jest/globals": "^29.7.0",
|
|
20
20
|
"@simplewebauthn/server": "^13.1.1",
|
|
21
21
|
"@tsconfig/recommended": "^1.0.8",
|
|
22
|
-
"@types/node": "^22.
|
|
22
|
+
"@types/node": "^22.15.17",
|
|
23
23
|
"jest": "^29.7.0",
|
|
24
24
|
"ts-jest": "^29.3.2",
|
|
25
25
|
"ts-node": "^10.9.2",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"cbor": "^10.0.3",
|
|
30
|
-
"tldts": "^7.0.
|
|
30
|
+
"tldts": "^7.0.7"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"check": "biome check .",
|