nid-webauthn-emulator 0.1.3

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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +185 -0
  3. package/dist/spec/authenticator/authenticator-emulator.spec.d.ts +1 -0
  4. package/dist/spec/authenticator/authenticator-emulator.spec.js +18 -0
  5. package/dist/spec/authenticator/ctap-model.spec.d.ts +1 -0
  6. package/dist/spec/authenticator/ctap-model.spec.js +95 -0
  7. package/dist/spec/integration/integration.spec.d.ts +1 -0
  8. package/dist/spec/integration/integration.spec.js +32 -0
  9. package/dist/spec/integration/passkeys-api-client.d.ts +11 -0
  10. package/dist/spec/integration/passkeys-api-client.js +2 -0
  11. package/dist/spec/integration/webauthn-io.d.ts +32 -0
  12. package/dist/spec/integration/webauthn-io.js +100 -0
  13. package/dist/spec/libs/browser-injection.spec.d.ts +1 -0
  14. package/dist/spec/libs/browser-injection.spec.js +15 -0
  15. package/dist/spec/libs/cose-key.spec.d.ts +1 -0
  16. package/dist/spec/libs/cose-key.spec.js +47 -0
  17. package/dist/spec/repository/credential-repository.spec.d.ts +1 -0
  18. package/dist/spec/repository/credential-repository.spec.js +96 -0
  19. package/dist/spec/webauthn/cose-key.spec.d.ts +1 -0
  20. package/dist/spec/webauthn/cose-key.spec.js +74 -0
  21. package/dist/spec/webauthn/webauthn-emulator.spec.d.ts +1 -0
  22. package/dist/spec/webauthn/webauthn-emulator.spec.js +222 -0
  23. package/dist/spec/webauthn/webauthn-model-json.spec.d.ts +1 -0
  24. package/dist/spec/webauthn/webauthn-model-json.spec.js +103 -0
  25. package/dist/spec/webauthn/webauthn-model.spec.d.ts +1 -0
  26. package/dist/spec/webauthn/webauthn-model.spec.js +97 -0
  27. package/dist/spec/webauthn/webauthn-test-server.d.ts +15 -0
  28. package/dist/spec/webauthn/webauthn-test-server.js +87 -0
  29. package/dist/src/authenticator/authenticator-emulator.d.ts +68 -0
  30. package/dist/src/authenticator/authenticator-emulator.js +304 -0
  31. package/dist/src/authenticator/ctap-model.d.ts +107 -0
  32. package/dist/src/authenticator/ctap-model.js +202 -0
  33. package/dist/src/index.d.ts +16 -0
  34. package/dist/src/index.js +34 -0
  35. package/dist/src/libs/encode-utils.d.ts +15 -0
  36. package/dist/src/libs/encode-utils.js +92 -0
  37. package/dist/src/repository/credentials-file-repository.d.ts +8 -0
  38. package/dist/src/repository/credentials-file-repository.js +62 -0
  39. package/dist/src/repository/credentials-memory-repository.d.ts +7 -0
  40. package/dist/src/repository/credentials-memory-repository.js +20 -0
  41. package/dist/src/repository/credentials-repository.d.ts +39 -0
  42. package/dist/src/repository/credentials-repository.js +46 -0
  43. package/dist/src/test-utils/browser-injection.d.ts +3 -0
  44. package/dist/src/test-utils/browser-injection.js +52 -0
  45. package/dist/src/webauthn/cose-key.d.ts +16 -0
  46. package/dist/src/webauthn/cose-key.js +151 -0
  47. package/dist/src/webauthn/webauthn-emulator.d.ts +30 -0
  48. package/dist/src/webauthn/webauthn-emulator.js +142 -0
  49. package/dist/src/webauthn/webauthn-model-json.d.ts +101 -0
  50. package/dist/src/webauthn/webauthn-model-json.js +187 -0
  51. package/dist/src/webauthn/webauthn-model.d.ts +68 -0
  52. package/dist/src/webauthn/webauthn-model.js +186 -0
  53. package/package.json +37 -0
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const fs = __importStar(require("node:fs"));
30
+ const node_path_1 = __importDefault(require("node:path"));
31
+ const globals_1 = require("@jest/globals");
32
+ const credentials_file_repository_1 = require("../../src/repository/credentials-file-repository");
33
+ const credentials_memory_repository_1 = require("../../src/repository/credentials-memory-repository");
34
+ const credentials_repository_1 = require("../../src/repository/credentials-repository");
35
+ (0, globals_1.describe)("Credential Repository Test", () => {
36
+ const testCredentialJSON = {
37
+ publicKeyCredentialDescriptor: {
38
+ id: "9gd8oypZ_ccpqhk3RBjUp85EOK4NA1wtEkU7zr_BOd8",
39
+ type: "public-key",
40
+ transports: ["usb"],
41
+ },
42
+ publicKeyCredentialSource: {
43
+ type: "public-key",
44
+ id: "9gd8oypZ_ccpqhk3RBjUp85EOK4NA1wtEkU7zr_BOd8",
45
+ privateKey: "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgMh82zVqIuCWxqRCVlkDvT0H9-0BoYZlol0AVTa2DDAmhRANCAAQbND1i36p1FOClK5Hn7liDCqpz-_H5Hv3kGEc_uX-wxMoERGk7_fHgm4i5QTq27Dby30eqFPryoKA3cNIOvWqJ",
46
+ rpId: "example.com",
47
+ userHandle: "AQIDBA",
48
+ },
49
+ authenticatorData: "o3mm9u6vuaVeN4wRgDTidR5oL6ufLTCrE9ISVYbOGUdNAAAAAE5JRC1BVVRILTMxNDE1OTIAIPYHfKMqWf3HKaoZN0QY1KfORDiuDQNcLRJFO86_wTnfpQECAyYgASFYIBs0PWLfqnUU4KUrkefuWIMKqnP78fke_eQYRz-5f7DEIlggygREaTv98eCbiLlBOrbsNvLfR6oU-vKgoDdw0g69aok",
50
+ user: {
51
+ id: "AQIDBA",
52
+ name: "example",
53
+ displayName: "example",
54
+ },
55
+ };
56
+ (0, globals_1.test)("Serialize and Deserialize Test", async () => {
57
+ const deserialized = (0, credentials_repository_1.deserializeCredential)(JSON.stringify(testCredentialJSON));
58
+ const serialization = (0, credentials_repository_1.serializeCredential)(deserialized);
59
+ const reDeserialized = (0, credentials_repository_1.deserializeCredential)(serialization);
60
+ (0, globals_1.expect)(reDeserialized).toEqual(deserialized);
61
+ });
62
+ (0, globals_1.test)("Memory Repository Test", async () => {
63
+ const deserialized = (0, credentials_repository_1.deserializeCredential)(JSON.stringify(testCredentialJSON));
64
+ const repository = new credentials_memory_repository_1.PasskeysCredentialsMemoryRepository();
65
+ repository.saveCredential(deserialized);
66
+ const loaded = repository.loadCredentials();
67
+ (0, globals_1.expect)(loaded[0]).toEqual(deserialized);
68
+ (0, globals_1.expect)(loaded.length).toEqual(1);
69
+ repository.deleteCredential(deserialized);
70
+ const reLoaded = repository.loadCredentials();
71
+ (0, globals_1.expect)(reLoaded.length).toEqual(0);
72
+ });
73
+ (0, globals_1.test)("File Repository Test", async () => {
74
+ const FILE_IO_WAIT = 500;
75
+ const deserialized = (0, credentials_repository_1.deserializeCredential)(JSON.stringify(testCredentialJSON));
76
+ const TEST_CREDENTIALS_DIR = node_path_1.default.join(__dirname, "test-credentials");
77
+ try {
78
+ fs.rmSync(TEST_CREDENTIALS_DIR, { recursive: true, force: true });
79
+ const repository = new credentials_file_repository_1.PasskeysCredentialsFileRepository(TEST_CREDENTIALS_DIR);
80
+ repository.saveCredential(deserialized);
81
+ await new Promise((resolve) => setTimeout(resolve, FILE_IO_WAIT));
82
+ const loaded = repository.loadCredentials();
83
+ (0, globals_1.expect)(loaded[0]).toEqual(deserialized);
84
+ (0, globals_1.expect)(loaded.length).toEqual(1);
85
+ repository.deleteCredential(deserialized);
86
+ await new Promise((resolve) => setTimeout(resolve, FILE_IO_WAIT));
87
+ // create dummy file
88
+ fs.writeFileSync(node_path_1.default.join(TEST_CREDENTIALS_DIR, "dummy"), "dummy");
89
+ const reLoaded = repository.loadCredentials();
90
+ (0, globals_1.expect)(reLoaded.length).toEqual(0);
91
+ }
92
+ finally {
93
+ fs.rmSync(TEST_CREDENTIALS_DIR, { recursive: true, force: true });
94
+ }
95
+ });
96
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const crypto = __importStar(require("node:crypto"));
30
+ const globals_1 = require("@jest/globals");
31
+ const src_1 = require("../../src");
32
+ const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
33
+ (0, globals_1.describe)("COSEKey Tests", () => {
34
+ (0, globals_1.test)("RSA Key test", () => {
35
+ const keys = crypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
36
+ const coseKey1 = src_1.CoseKey.fromKeyObject(keys.publicKey);
37
+ const coseKey2 = src_1.CoseKey.fromDer(coseKey1.toDer());
38
+ const coseKey3 = src_1.CoseKey.fromBytes(coseKey1.toBytes());
39
+ const coseKey4 = src_1.CoseKey.fromJwk(coseKey1.toJwk());
40
+ (0, globals_1.expect)(coseKey1.equals(coseKey2)).toBeTruthy();
41
+ (0, globals_1.expect)(coseKey1.equals(coseKey3)).toBeTruthy();
42
+ (0, globals_1.expect)(coseKey1.equals(coseKey4)).toBeTruthy();
43
+ });
44
+ (0, globals_1.test)("P256 Key test", () => {
45
+ const keys = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
46
+ const coseKey1 = src_1.CoseKey.fromKeyObject(keys.publicKey);
47
+ const coseKey2 = src_1.CoseKey.fromDer(coseKey1.toDer());
48
+ const coseKey3 = src_1.CoseKey.fromBytes(coseKey1.toBytes());
49
+ const coseKey4 = src_1.CoseKey.fromJwk(coseKey1.toJwk());
50
+ (0, globals_1.expect)(coseKey1.equals(coseKey2)).toBeTruthy();
51
+ (0, globals_1.expect)(coseKey1.equals(coseKey3)).toBeTruthy();
52
+ (0, globals_1.expect)(coseKey1.equals(coseKey4)).toBeTruthy();
53
+ });
54
+ (0, globals_1.test)("Ed25519 Key test", () => {
55
+ const keys = crypto.generateKeyPairSync("ed25519");
56
+ const coseKey1 = src_1.CoseKey.fromKeyObject(keys.publicKey);
57
+ const coseKey2 = src_1.CoseKey.fromDer(coseKey1.toDer());
58
+ const coseKey3 = src_1.CoseKey.fromBytes(coseKey1.toBytes());
59
+ const coseKey4 = src_1.CoseKey.fromJwk(coseKey1.toJwk());
60
+ (0, globals_1.expect)(coseKey1.equals(coseKey2)).toBeTruthy();
61
+ (0, globals_1.expect)(coseKey1.equals(coseKey3)).toBeTruthy();
62
+ (0, globals_1.expect)(coseKey1.equals(coseKey4)).toBeTruthy();
63
+ });
64
+ (0, globals_1.test)("Unsupported key type test", () => {
65
+ const keys = crypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
66
+ const unsupportedJwk = {
67
+ ...src_1.CoseKey.fromKeyObject(keys.publicKey).toJwk(),
68
+ kty: "unsupported",
69
+ };
70
+ (0, globals_1.expect)(() => src_1.CoseKey.fromJwk(unsupportedJwk)).toThrow("Not supported key type");
71
+ const unsupportedBytes = encode_utils_1.default.encodeCbor({ 1: 123, 3: 456 });
72
+ (0, globals_1.expect)(() => src_1.CoseKey.fromBytes(unsupportedBytes)).toThrow("Not supported key type");
73
+ });
74
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const globals_1 = require("@jest/globals");
30
+ const src_1 = __importStar(require("../../src"));
31
+ const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
32
+ const webauthn_test_server_1 = require("./webauthn-test-server");
33
+ (0, globals_1.describe)("WebAuthnEmulator Registration Passkeys Test", () => {
34
+ const user = { username: "test", id: "test" };
35
+ (0, globals_1.test)("Create Passkeys test _ OK", async () => {
36
+ const emulator = new src_1.default();
37
+ const testServer = new webauthn_test_server_1.WebAuthnTestServer();
38
+ const options = await testServer.getRegistrationOptions(user);
39
+ const credential1 = emulator.createJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
40
+ await testServer.getRegistrationVerification(user, credential1);
41
+ const credential2 = emulator.createJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
42
+ await testServer.getRegistrationVerification(user, credential2);
43
+ // Last Credentials only
44
+ const credentialRecords = emulator.authenticator.params.credentialsRepository?.loadCredentials() ?? [];
45
+ (0, globals_1.expect)(credentialRecords.length).toBe(1);
46
+ (0, globals_1.expect)(encode_utils_1.default.encodeBase64Url(credentialRecords[0].publicKeyCredentialSource.id)).toBe(credential2.id);
47
+ });
48
+ (0, globals_1.test)("Create Passkeys with an illegal origin _ failed to response", async () => {
49
+ const illegalOrigin = `${webauthn_test_server_1.TEST_RP_ORIGIN}_illegal`;
50
+ const emulator = new src_1.default();
51
+ const testServer = new webauthn_test_server_1.WebAuthnTestServer();
52
+ const options = await testServer.getRegistrationOptions(user);
53
+ await (0, globals_1.expect)(async () => {
54
+ emulator.createJSON(illegalOrigin, options);
55
+ }).rejects.toThrow(src_1.InvalidRpIdError);
56
+ });
57
+ (0, globals_1.test)("Create Passkeys with no public key credential parameters _ failed to response", async () => {
58
+ const emulator = new src_1.default();
59
+ await (0, globals_1.expect)(async () => {
60
+ emulator.create(webauthn_test_server_1.TEST_RP_ORIGIN, {});
61
+ }).rejects.toThrow(src_1.NoPublicKeyError);
62
+ });
63
+ (0, globals_1.test)("Found in the exclude List _ CTAP Error", async () => {
64
+ const emulator = new src_1.default();
65
+ const testServer = new webauthn_test_server_1.WebAuthnTestServer();
66
+ const options = await testServer.getRegistrationOptions(user);
67
+ const credential = emulator.createJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
68
+ await testServer.getRegistrationVerification(user, credential);
69
+ const options2 = await testServer.getRegistrationOptions(user);
70
+ await (0, globals_1.expect)(async () => {
71
+ emulator.createJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options2);
72
+ }).rejects.toThrow(new src_1.AuthenticationEmulatorError(src_1.CTAP_STATUS_CODE.CTAP2_ERR_CREDENTIAL_EXCLUDED));
73
+ });
74
+ (0, globals_1.test)("Invalid algorithm _ CTAP Error", async () => {
75
+ const emulator = new src_1.default();
76
+ const testServer = new webauthn_test_server_1.WebAuthnTestServer();
77
+ const options = await testServer.getRegistrationOptions(user);
78
+ const illegalOptions = { ...options, pubKeyCredParams: [{ type: "public-key", alg: 99 }] };
79
+ await (0, globals_1.expect)(async () => {
80
+ emulator.createJSON(webauthn_test_server_1.TEST_RP_ORIGIN, illegalOptions);
81
+ }).rejects.toThrow(new src_1.AuthenticationEmulatorError(src_1.CTAP_STATUS_CODE.CTAP2_ERR_UNSUPPORTED_ALGORITHM));
82
+ });
83
+ (0, globals_1.test)("User registration operation failed _ CTAP Error", async () => {
84
+ const authenticator = new src_1.AuthenticatorEmulator({
85
+ userMakeCredentialInteraction: () => undefined,
86
+ });
87
+ const emulator = new src_1.default(authenticator);
88
+ const testServer = new webauthn_test_server_1.WebAuthnTestServer();
89
+ const options = await testServer.getRegistrationOptions(user);
90
+ await (0, globals_1.expect)(async () => {
91
+ emulator.createJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
92
+ }).rejects.toThrow(new src_1.AuthenticationEmulatorError(src_1.CTAP_STATUS_CODE.CTAP2_ERR_OPERATION_DENIED));
93
+ });
94
+ (0, globals_1.test)("Create Passkeys without RP ID _ OK", async () => {
95
+ const emulator = new src_1.default();
96
+ const testServer = new webauthn_test_server_1.WebAuthnTestServer();
97
+ const options = await testServer.getRegistrationOptions(user);
98
+ const customOptions = {
99
+ ...options,
100
+ rp: { name: "test" },
101
+ };
102
+ const credential1 = emulator.createJSON(webauthn_test_server_1.TEST_RP_ORIGIN, customOptions);
103
+ await testServer.getRegistrationVerification(user, credential1);
104
+ });
105
+ });
106
+ (0, globals_1.describe)("WebAuthnEmulator Authentication Passkeys Test", () => {
107
+ const user = { username: "test", id: "test" };
108
+ const createCredential = async (authenticator) => {
109
+ const testServer1 = new webauthn_test_server_1.WebAuthnTestServer();
110
+ const testServer2 = new webauthn_test_server_1.WebAuthnTestServer("https://test-rp2.com", "test-rp2.com");
111
+ const emulator = new src_1.default(authenticator);
112
+ const options1 = await testServer1.getRegistrationOptions(user);
113
+ const credential1 = emulator.createJSON(testServer1.origin, options1);
114
+ await testServer1.getRegistrationVerification(user, credential1);
115
+ const options2 = await testServer2.getRegistrationOptions(user);
116
+ const customOptions2 = {
117
+ ...options2,
118
+ authenticatorSelection: { residentKey: "discouraged" },
119
+ };
120
+ const credential2 = emulator.createJSON(testServer2.origin, customOptions2);
121
+ await testServer2.getRegistrationVerification(user, credential2);
122
+ return [emulator, testServer1];
123
+ };
124
+ (0, globals_1.test)("Create and Authenticate Passkeys test _ OK", async () => {
125
+ const [emulator, testServer] = await createCredential();
126
+ const options = await testServer.getAuthenticationOptions();
127
+ const credential = emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
128
+ await testServer.getAuthenticationVerification(credential);
129
+ });
130
+ (0, globals_1.test)("EdDSA Algorithm Passkeys test _ OK", async () => {
131
+ const authenticator = new src_1.AuthenticatorEmulator({ algorithmIdentifiers: ["EdDSA"] });
132
+ const [emulator, testServer] = await createCredential(authenticator);
133
+ const options = await testServer.getAuthenticationOptions();
134
+ const credential = emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
135
+ await testServer.getAuthenticationVerification(credential);
136
+ });
137
+ (0, globals_1.test)("RS256 Algorithm Passkeys test _ OK", async () => {
138
+ const authenticator = new src_1.AuthenticatorEmulator({ algorithmIdentifiers: ["RS256"] });
139
+ const [emulator, testServer] = await createCredential(authenticator);
140
+ const options = await testServer.getAuthenticationOptions();
141
+ const credential = emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
142
+ await testServer.getAuthenticationVerification(credential);
143
+ });
144
+ (0, globals_1.test)("SignCounter Increment test _ OK", async () => {
145
+ const authenticator = new src_1.AuthenticatorEmulator({ signCounterIncrement: 13 });
146
+ const [emulator, testServer] = await createCredential(authenticator);
147
+ const options1 = await testServer.getAuthenticationOptions();
148
+ const credential1 = emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options1);
149
+ const authData = (0, src_1.unpackAuthenticatorData)(encode_utils_1.default.decodeBase64Url(credential1.response.authenticatorData));
150
+ (0, globals_1.expect)(authData.signCount).toBe(13);
151
+ const options2 = await testServer.getAuthenticationOptions();
152
+ const credential2 = emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options2);
153
+ const authData2 = (0, src_1.unpackAuthenticatorData)(encode_utils_1.default.decodeBase64Url(credential2.response.authenticatorData));
154
+ (0, globals_1.expect)(authData2.signCount).toBe(26);
155
+ });
156
+ (0, globals_1.test)("Authenticate Passkeys with an illegal origin _ failed to response", async () => {
157
+ const [emulator, testServer] = await createCredential();
158
+ const illegalOrigin = `${webauthn_test_server_1.TEST_RP_ORIGIN}_illegal`;
159
+ const options = await testServer.getAuthenticationOptions();
160
+ await (0, globals_1.expect)(async () => {
161
+ emulator.getJSON(illegalOrigin, options);
162
+ }).rejects.toThrow(src_1.InvalidRpIdError);
163
+ });
164
+ (0, globals_1.test)("Request Passkeys with no public key credential parameters _ failed to response", async () => {
165
+ const emulator = new src_1.default();
166
+ await (0, globals_1.expect)(async () => {
167
+ emulator.get(webauthn_test_server_1.TEST_RP_ORIGIN, {});
168
+ }).rejects.toThrow(src_1.NoPublicKeyError);
169
+ });
170
+ (0, globals_1.test)("Not found in allow list _ CTAP Error", async () => {
171
+ const [emulator, testServer] = await createCredential();
172
+ const options = await testServer.getAuthenticationOptions();
173
+ await (0, globals_1.expect)(async () => {
174
+ emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, {
175
+ ...options,
176
+ allowCredentials: [{ id: "AAAAAA", type: "public-key" }],
177
+ });
178
+ }).rejects.toThrow(new src_1.AuthenticationEmulatorError(src_1.CTAP_STATUS_CODE.CTAP2_ERR_NO_CREDENTIALS));
179
+ });
180
+ (0, globals_1.test)("User authentication operation failed _ CTAP Error", async () => {
181
+ const authenticator = new src_1.AuthenticatorEmulator({
182
+ userGetAssertionInteraction: () => undefined,
183
+ });
184
+ const [emulator, testServer] = await createCredential(authenticator);
185
+ const options = await testServer.getAuthenticationOptions();
186
+ await (0, globals_1.expect)(async () => {
187
+ emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
188
+ }).rejects.toThrow(new src_1.AuthenticationEmulatorError(src_1.CTAP_STATUS_CODE.CTAP2_ERR_OPERATION_DENIED));
189
+ });
190
+ (0, globals_1.test)("Only one credential is allowed and RP ID is undefined _ credential is undefined and OK", async () => {
191
+ const [emulator, testServer] = await createCredential();
192
+ const options = await testServer.getAuthenticationOptions();
193
+ const credential = (emulator.authenticator.params.credentialsRepository?.loadCredentials() ?? [])[0];
194
+ emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, {
195
+ ...options,
196
+ rpId: undefined,
197
+ allowCredentials: [(0, src_1.toPublicKeyCredentialDescriptorJSON)(credential.publicKeyCredentialDescriptor)],
198
+ });
199
+ });
200
+ (0, globals_1.test)("Stateless Authenticator Test _ OK", async () => {
201
+ const authenticator = new src_1.AuthenticatorEmulator({ stateless: true });
202
+ const [emulator, testServer] = await createCredential(authenticator);
203
+ const options = await testServer.getAuthenticationOptions();
204
+ const credential = emulator.getJSON(webauthn_test_server_1.TEST_RP_ORIGIN, options);
205
+ await testServer.getAuthenticationVerification(credential);
206
+ const credentialRecords = emulator.authenticator.params.credentialsRepository?.loadCredentials() ?? [];
207
+ const authData = (0, src_1.unpackAuthenticatorData)(encode_utils_1.default.decodeBase64Url(credential.response.authenticatorData));
208
+ (0, globals_1.expect)(credentialRecords.length).toBe(0);
209
+ (0, globals_1.expect)(authData.signCount).toBe(0);
210
+ });
211
+ });
212
+ (0, globals_1.describe)("WebAuthnEmulator getAuthenticatorInfo Test", () => {
213
+ (0, globals_1.test)("Get Authenticator Information _ OK", () => {
214
+ const emulator = new src_1.default();
215
+ const info = emulator.getAuthenticatorInfo();
216
+ (0, globals_1.expect)(info).toEqual({
217
+ version: "FIDO_2_0",
218
+ aaguid: "TklELUFVVEgtMzE0MTU5Mg",
219
+ options: { rk: true, uv: true },
220
+ });
221
+ });
222
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const globals_1 = require("@jest/globals");
7
+ const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
8
+ const webauthn_emulator_1 = require("../../src/webauthn/webauthn-emulator");
9
+ const webauthn_model_json_1 = require("../../src/webauthn/webauthn-model-json");
10
+ (0, globals_1.describe)("WebAuthn JSON Model Test", () => {
11
+ const creationOption = {
12
+ rp: { name: "test-rp.org", id: "test-rp.org" },
13
+ user: { id: encode_utils_1.default.strToUint8Array("test-user"), name: "user", displayName: "user" },
14
+ excludeCredentials: [{ id: encode_utils_1.default.strToUint8Array("test-credential"), type: "public-key" }],
15
+ challenge: encode_utils_1.default.strToUint8Array("challenge"),
16
+ pubKeyCredParams: [{ alg: -7, type: "public-key" }],
17
+ };
18
+ const requestOption = {
19
+ challenge: encode_utils_1.default.strToUint8Array("challenge"),
20
+ allowCredentials: [{ id: encode_utils_1.default.strToUint8Array("test-credential"), type: "public-key" }],
21
+ rpId: "test-rp.org",
22
+ timeout: 60000,
23
+ userVerification: "required",
24
+ };
25
+ (0, globals_1.test)("Create Option JSON Serialize Deserialize test", async () => {
26
+ const json = (0, webauthn_model_json_1.toCreationOptionsJSON)(creationOption);
27
+ const model = (0, webauthn_model_json_1.parseCreationOptionsFromJSON)(json);
28
+ const reJson = (0, webauthn_model_json_1.toCreationOptionsJSON)(model);
29
+ (0, globals_1.expect)(reJson).toEqual(json);
30
+ });
31
+ (0, globals_1.test)("Create Response JSON Serialize Deserialize test", async () => {
32
+ const emulator = new webauthn_emulator_1.WebAuthnEmulator();
33
+ const response = emulator.create("https://test-rp.org", { publicKey: creationOption });
34
+ const json = (0, webauthn_model_json_1.toRegistrationResponseJSON)(response);
35
+ const model = (0, webauthn_model_json_1.parseRegistrationResponseFromJSON)(json);
36
+ const reJson = (0, webauthn_model_json_1.toRegistrationResponseJSON)(model);
37
+ (0, globals_1.expect)(reJson).toEqual(json);
38
+ });
39
+ (0, globals_1.test)("Get Option JSON Serialize Deserialize test", async () => {
40
+ const json = (0, webauthn_model_json_1.toRequestOptionsJSON)(requestOption);
41
+ const model = (0, webauthn_model_json_1.parseRequestOptionsFromJSON)(json);
42
+ const reJson = (0, webauthn_model_json_1.toRequestOptionsJSON)(model);
43
+ (0, globals_1.expect)(reJson).toEqual(json);
44
+ });
45
+ (0, globals_1.test)("Get Response JSON Serialize Deserialize test", async () => {
46
+ const emulator = new webauthn_emulator_1.WebAuthnEmulator();
47
+ emulator.create("https://test-rp.org", { publicKey: creationOption });
48
+ const response = emulator.get("https://test-rp.org", {
49
+ publicKey: { ...requestOption, allowCredentials: undefined },
50
+ });
51
+ const json = (0, webauthn_model_json_1.toAuthenticationResponseJSON)(response);
52
+ const model = (0, webauthn_model_json_1.parseAuthenticationResponseFromJSON)(json);
53
+ const reJson = (0, webauthn_model_json_1.toAuthenticationResponseJSON)(model);
54
+ (0, globals_1.expect)(reJson).toEqual(json);
55
+ });
56
+ (0, globals_1.test)("Create Response JSON optional test", async () => {
57
+ const emulator = new webauthn_emulator_1.WebAuthnEmulator();
58
+ const customOption = { publicKey: { ...creationOption, excludeCredentials: undefined } };
59
+ const json = emulator.createJSON("https://test-rp.org", (0, webauthn_model_json_1.toCreationOptionsJSON)(customOption.publicKey));
60
+ const customJson = {
61
+ ...json,
62
+ authenticatorAttachment: "platform",
63
+ response: { ...json.response, publicKey: undefined, publicKeyAlgorithm: undefined },
64
+ };
65
+ const model = (0, webauthn_model_json_1.parseRegistrationResponseFromJSON)(customJson);
66
+ (0, globals_1.expect)(model.authenticatorAttachment).toEqual(customJson.authenticatorAttachment);
67
+ (0, globals_1.expect)(model.response.getPublicKey()).toBeNull();
68
+ (0, globals_1.expect)(model.response.getPublicKeyAlgorithm()).toEqual(-1);
69
+ (0, globals_1.expect)((0, webauthn_model_json_1.toRegistrationResponseJSON)(model)).toEqual(customJson);
70
+ (0, globals_1.expect)(model.toJSON()).toEqual(customJson);
71
+ });
72
+ (0, globals_1.test)("Get Response JSON optional test", async () => {
73
+ const emulator = new webauthn_emulator_1.WebAuthnEmulator();
74
+ const customOption = { publicKey: { ...requestOption, allowCredentials: undefined } };
75
+ const json = emulator.getJSON("https://test-rp.org", (0, webauthn_model_json_1.toRequestOptionsJSON)(customOption.publicKey));
76
+ const customJson = {
77
+ ...json,
78
+ authenticatorAttachment: "platform",
79
+ response: { ...json.response, userHandle: undefined },
80
+ };
81
+ const model = (0, webauthn_model_json_1.parseAuthenticationResponseFromJSON)(customJson);
82
+ (0, globals_1.expect)(model.response.userHandle).toBeNull();
83
+ (0, globals_1.expect)((0, webauthn_model_json_1.toAuthenticationResponseJSON)(model)).toEqual(customJson);
84
+ (0, globals_1.expect)(model.toJSON()).toEqual(customJson);
85
+ });
86
+ (0, globals_1.test)("Parse Authentication Response with User Handle JSON test", async () => {
87
+ const testData = {
88
+ type: "public-key",
89
+ id: "AAAAAA",
90
+ rawId: "AAAAAA",
91
+ response: {
92
+ clientDataJSON: "BBBBBB",
93
+ authenticatorData: "CCCCCC",
94
+ signature: "DDDDDD",
95
+ userHandle: "EEEEEE",
96
+ },
97
+ clientExtensionResults: {},
98
+ };
99
+ const model = (0, webauthn_model_json_1.parseAuthenticationResponseFromJSON)(testData);
100
+ const json = model.toJSON();
101
+ (0, globals_1.expect)(json).toEqual(testData);
102
+ });
103
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const globals_1 = require("@jest/globals");
7
+ const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
8
+ const webauthn_emulator_1 = require("../../src/webauthn/webauthn-emulator");
9
+ const webauthn_model_1 = require("../../src/webauthn/webauthn-model");
10
+ (0, globals_1.describe)("WebAuthn Model Test", () => {
11
+ const webauthnEmulator = new webauthn_emulator_1.WebAuthnEmulator();
12
+ (0, globals_1.test)("Attestation Object pack and unpack", async () => {
13
+ const createResponse = webauthnEmulator.create("https://test-rp.org", {
14
+ publicKey: {
15
+ rp: { name: "test-rp.org", id: "test-rp.org" },
16
+ user: { id: encode_utils_1.default.strToUint8Array("test-user"), name: "user", displayName: "user" },
17
+ challenge: encode_utils_1.default.strToUint8Array("challenge"),
18
+ pubKeyCredParams: [{ alg: -7, type: "public-key" }],
19
+ },
20
+ });
21
+ const testData = new Uint8Array(createResponse.response.attestationObject);
22
+ const unpacked = (0, webauthn_model_1.unpackAttestationObject)(testData);
23
+ const rePacked = (0, webauthn_model_1.packAttestationObject)(unpacked);
24
+ const reUnpacked = (0, webauthn_model_1.unpackAttestationObject)(rePacked);
25
+ (0, globals_1.expect)(rePacked).toEqual(testData);
26
+ (0, globals_1.expect)(reUnpacked).toEqual(unpacked);
27
+ });
28
+ (0, globals_1.test)("Attestation Object pack and unpack optional1", async () => {
29
+ const testData = {
30
+ fmt: "none",
31
+ authData: {
32
+ flags: {
33
+ userPresent: false,
34
+ userVerified: false,
35
+ backupEligibility: false,
36
+ backupState: false,
37
+ extensionData: true,
38
+ attestedCredentialData: false,
39
+ },
40
+ rpIdHash: new Uint8Array(32),
41
+ signCount: 0,
42
+ },
43
+ attStmt: { test: "test123" },
44
+ };
45
+ const packed = (0, webauthn_model_1.packAttestationObject)(testData);
46
+ const unpacked = (0, webauthn_model_1.unpackAttestationObject)(packed);
47
+ (0, globals_1.expect)(unpacked).toEqual(testData);
48
+ });
49
+ (0, globals_1.test)("Undefined User Handle PublicKey serialize test", async () => {
50
+ const testData = {
51
+ type: "public-key",
52
+ id: new Uint8Array([116, 101, 115, 116, 45, 99]),
53
+ privateKey: new Uint8Array([116, 101, 115, 116, 45, 99]),
54
+ rpId: new webauthn_model_1.RpId("test-rp.org"),
55
+ userHandle: undefined,
56
+ };
57
+ const json = (0, webauthn_model_1.toPublickeyCredentialSourceJSON)(testData);
58
+ const model = (0, webauthn_model_1.parsePublicKeyCredentialSourceFromJSON)(json);
59
+ (0, globals_1.expect)(model).toEqual(testData);
60
+ });
61
+ globals_1.test.each([
62
+ [
63
+ { residentKey: "required", userVerification: "required" },
64
+ { rk: true, uv: true, up: true },
65
+ ],
66
+ [
67
+ { residentKey: "preferred", userVerification: "preferred" },
68
+ { rk: true, uv: true, up: true },
69
+ ],
70
+ [
71
+ { residentKey: "discouraged", userVerification: "discouraged" },
72
+ { rk: false, uv: false, up: true },
73
+ ],
74
+ [
75
+ { requireResidentKey: true, userVerification: "preferred" },
76
+ { rk: true, uv: true, up: true },
77
+ ],
78
+ [
79
+ { residentKey: "discouraged", userVerification: "preferred" },
80
+ { rk: false, uv: true, up: true },
81
+ ],
82
+ [
83
+ { requireResidentKey: false, userVerification: "preferred" },
84
+ { rk: false, uv: true, up: true },
85
+ ],
86
+ ])("toFido2CreateOptions test: $a", (criteria, expected) => {
87
+ (0, globals_1.expect)((0, webauthn_model_1.toFido2CreateOptions)(criteria)).toEqual(expected);
88
+ });
89
+ globals_1.test.each([
90
+ ["required", true],
91
+ ["preferred", true],
92
+ ["discouraged", false],
93
+ [undefined, false],
94
+ ])("UserVerificationRequirement test: $a", (criteria, expected) => {
95
+ (0, globals_1.expect)((0, webauthn_model_1.toFido2RequestOptions)(criteria).uv).toEqual(expected);
96
+ });
97
+ });
@@ -0,0 +1,15 @@
1
+ import type { AuthenticationResponseJSON, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON } from "../../src";
2
+ import type { PasskeysApiClient, PasskeysUser } from "../integration/passkeys-api-client";
3
+ export declare const TEST_RP_ORIGIN = "https://test-rp.com";
4
+ export declare const TEST_RP_ID = "test-rp.com";
5
+ export declare class WebAuthnTestServer implements PasskeysApiClient {
6
+ origin: string;
7
+ rpID: string;
8
+ private challenges;
9
+ private credentials;
10
+ constructor(origin?: string, rpID?: string);
11
+ getRegistrationOptions(user: PasskeysUser): Promise<PublicKeyCredentialCreationOptionsJSON>;
12
+ getRegistrationVerification(user: PasskeysUser, response: RegistrationResponseJSON): Promise<void>;
13
+ getAuthenticationOptions(): Promise<PublicKeyCredentialRequestOptionsJSON>;
14
+ getAuthenticationVerification(response: AuthenticationResponseJSON): Promise<void>;
15
+ }