humankey 0.3.0 → 0.3.1
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 +165 -4
- package/dist/adapter-core-CFN3y3L0.d.ts +41 -0
- package/dist/adapter-core-CoBcSqdG.d.cts +41 -0
- package/dist/chunk-2KMBF2TH.cjs +27 -0
- package/dist/chunk-G2IT2ZP5.cjs +124 -0
- package/dist/chunk-G2X46ZRM.mjs +27 -0
- package/dist/chunk-RANGHXC7.mjs +75 -0
- package/dist/chunk-RQSBOE7B.cjs +76 -0
- package/dist/chunk-SZPXOH7Z.cjs +75 -0
- package/dist/chunk-U2AGXRNW.mjs +76 -0
- package/dist/chunk-Y6KQC7WK.mjs +124 -0
- package/dist/confirm-6RXLI2SS.cjs +9 -0
- package/dist/confirm-TSVTFC4N.mjs +9 -0
- package/dist/express.cjs +14 -102
- package/dist/express.d.cts +5 -38
- package/dist/express.d.ts +5 -38
- package/dist/express.mjs +17 -105
- package/dist/fastify.cjs +30 -0
- package/dist/fastify.d.cts +22 -0
- package/dist/fastify.d.ts +22 -0
- package/dist/fastify.mjs +30 -0
- package/dist/hono.cjs +35 -0
- package/dist/hono.d.cts +22 -0
- package/dist/hono.d.ts +22 -0
- package/dist/hono.mjs +35 -0
- package/dist/index.cjs +5 -144
- package/dist/index.mjs +10 -149
- package/dist/nextjs.cjs +40 -0
- package/dist/nextjs.d.cts +28 -0
- package/dist/nextjs.d.ts +28 -0
- package/dist/nextjs.mjs +40 -0
- package/dist/react.cjs +131 -0
- package/dist/react.d.cts +60 -0
- package/dist/react.d.ts +60 -0
- package/dist/react.mjs +131 -0
- package/dist/register-Y2ADAMHB.cjs +7 -0
- package/dist/register-ZGR2LHQ6.mjs +7 -0
- package/dist/tap-DJVQXGEJ.mjs +8 -0
- package/dist/tap-N5RN26H2.cjs +8 -0
- package/package.json +58 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
var _chunk2KMBF2THcjs = require('./chunk-2KMBF2TH.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
var _chunkJI6NIMGKcjs = require('./chunk-JI6NIMGK.cjs');
|
|
11
|
+
|
|
12
|
+
// src/tap.ts
|
|
13
|
+
var _browser = require('@simplewebauthn/browser');
|
|
14
|
+
async function requestTap(request) {
|
|
15
|
+
const {
|
|
16
|
+
challenge,
|
|
17
|
+
action,
|
|
18
|
+
confirmation,
|
|
19
|
+
userInput,
|
|
20
|
+
allowCredentials,
|
|
21
|
+
rpID,
|
|
22
|
+
userVerification = "required",
|
|
23
|
+
timeout = 6e4
|
|
24
|
+
} = request;
|
|
25
|
+
_chunk2KMBF2THcjs.validateConfirmation.call(void 0, confirmation, userInput);
|
|
26
|
+
const actionHashBuffer = await _chunkJI6NIMGKcjs.hashAction.call(void 0, action);
|
|
27
|
+
const confirmationInput = `${confirmation.code}:${userInput.toUpperCase().trim()}`;
|
|
28
|
+
const confirmationHashBuffer = await crypto.subtle.digest(
|
|
29
|
+
"SHA-256",
|
|
30
|
+
new TextEncoder().encode(confirmationInput)
|
|
31
|
+
);
|
|
32
|
+
const challengeBuffer = _chunkJI6NIMGKcjs.base64urlToBuffer.call(void 0, challenge);
|
|
33
|
+
const finalChallenge = await _chunkJI6NIMGKcjs.combineAndHash.call(void 0,
|
|
34
|
+
challengeBuffer,
|
|
35
|
+
actionHashBuffer,
|
|
36
|
+
confirmationHashBuffer
|
|
37
|
+
);
|
|
38
|
+
const actionHash = _chunkJI6NIMGKcjs.bufferToBase64url.call(void 0, actionHashBuffer);
|
|
39
|
+
const confirmationHash = _chunkJI6NIMGKcjs.bufferToBase64url.call(void 0, confirmationHashBuffer);
|
|
40
|
+
try {
|
|
41
|
+
const response = await _browser.startAuthentication.call(void 0, {
|
|
42
|
+
optionsJSON: {
|
|
43
|
+
challenge: _chunkJI6NIMGKcjs.bufferToBase64url.call(void 0, finalChallenge),
|
|
44
|
+
rpId: rpID,
|
|
45
|
+
allowCredentials: allowCredentials.map((cred) => ({
|
|
46
|
+
id: cred.id,
|
|
47
|
+
type: "public-key",
|
|
48
|
+
transports: cred.transports
|
|
49
|
+
})),
|
|
50
|
+
userVerification,
|
|
51
|
+
timeout
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
response,
|
|
56
|
+
action,
|
|
57
|
+
actionHash,
|
|
58
|
+
confirmationHash,
|
|
59
|
+
userInput: userInput.toUpperCase().trim()
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (error instanceof Error && error.name === "NotAllowedError") {
|
|
63
|
+
throw new (0, _chunkJI6NIMGKcjs.HumanKeyError)(
|
|
64
|
+
"User cancelled the hardware key tap",
|
|
65
|
+
"USER_CANCELLED",
|
|
66
|
+
error
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
exports.requestTap = requestTap;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HumanKeyError,
|
|
3
|
+
bufferToBase64url
|
|
4
|
+
} from "./chunk-CLQSCDXC.mjs";
|
|
5
|
+
|
|
6
|
+
// src/register.ts
|
|
7
|
+
import { startRegistration } from "@simplewebauthn/browser";
|
|
8
|
+
async function registerKey(request) {
|
|
9
|
+
const {
|
|
10
|
+
challenge,
|
|
11
|
+
rpID,
|
|
12
|
+
rpName,
|
|
13
|
+
userName,
|
|
14
|
+
userDisplayName = userName,
|
|
15
|
+
excludeCredentials = [],
|
|
16
|
+
attestation = "direct",
|
|
17
|
+
userVerification = "required",
|
|
18
|
+
timeout = 6e4
|
|
19
|
+
} = request;
|
|
20
|
+
const userIdBytes = new TextEncoder().encode(userName);
|
|
21
|
+
const userIdHash = await crypto.subtle.digest("SHA-256", userIdBytes);
|
|
22
|
+
const userId = bufferToBase64url(userIdHash);
|
|
23
|
+
try {
|
|
24
|
+
const response = await startRegistration({
|
|
25
|
+
optionsJSON: {
|
|
26
|
+
rp: { name: rpName, id: rpID },
|
|
27
|
+
user: {
|
|
28
|
+
id: userId,
|
|
29
|
+
name: userName,
|
|
30
|
+
displayName: userDisplayName
|
|
31
|
+
},
|
|
32
|
+
challenge,
|
|
33
|
+
pubKeyCredParams: [
|
|
34
|
+
{ alg: -7, type: "public-key" },
|
|
35
|
+
// ES256
|
|
36
|
+
{ alg: -257, type: "public-key" }
|
|
37
|
+
// RS256
|
|
38
|
+
],
|
|
39
|
+
timeout,
|
|
40
|
+
attestation,
|
|
41
|
+
excludeCredentials: excludeCredentials.map((cred) => ({
|
|
42
|
+
id: cred.id,
|
|
43
|
+
type: "public-key",
|
|
44
|
+
transports: cred.transports
|
|
45
|
+
})),
|
|
46
|
+
authenticatorSelection: {
|
|
47
|
+
authenticatorAttachment: "cross-platform",
|
|
48
|
+
residentKey: "preferred",
|
|
49
|
+
userVerification
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
credentialId: response.id,
|
|
55
|
+
response,
|
|
56
|
+
transports: response.response.transports
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (error instanceof Error && error.name === "NotAllowedError") {
|
|
60
|
+
throw new HumanKeyError(
|
|
61
|
+
"User cancelled hardware key registration",
|
|
62
|
+
"USER_CANCELLED",
|
|
63
|
+
error
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
throw new HumanKeyError(
|
|
67
|
+
`Registration failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
68
|
+
"REGISTRATION_FAILED",
|
|
69
|
+
error
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
registerKey
|
|
76
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createChallenge,
|
|
3
|
+
verifyRegistration,
|
|
4
|
+
verifyTapProof
|
|
5
|
+
} from "./chunk-XXJAJHNP.mjs";
|
|
6
|
+
import {
|
|
7
|
+
HumanKeyError
|
|
8
|
+
} from "./chunk-CLQSCDXC.mjs";
|
|
9
|
+
|
|
10
|
+
// src/adapter-core.ts
|
|
11
|
+
var MemoryChallengeStore = class {
|
|
12
|
+
store = /* @__PURE__ */ new Map();
|
|
13
|
+
async set(id, challenge, ttlMs) {
|
|
14
|
+
this.store.set(id, { challenge, expiresAt: Date.now() + ttlMs });
|
|
15
|
+
this.cleanup();
|
|
16
|
+
}
|
|
17
|
+
async get(id) {
|
|
18
|
+
const entry = this.store.get(id);
|
|
19
|
+
if (!entry) return null;
|
|
20
|
+
this.store.delete(id);
|
|
21
|
+
if (Date.now() > entry.expiresAt) return null;
|
|
22
|
+
return entry.challenge;
|
|
23
|
+
}
|
|
24
|
+
cleanup() {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
for (const [id, entry] of this.store) {
|
|
27
|
+
if (now > entry.expiresAt) this.store.delete(id);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
function resolveConfig(config) {
|
|
32
|
+
return {
|
|
33
|
+
rpID: config.rpID,
|
|
34
|
+
rpName: config.rpName,
|
|
35
|
+
origin: config.origin,
|
|
36
|
+
challengeTTL: config.challengeTTL ?? 6e4,
|
|
37
|
+
challengeStore: config.challengeStore ?? new MemoryChallengeStore(),
|
|
38
|
+
getCredential: config.getCredential,
|
|
39
|
+
onRegister: config.onRegister,
|
|
40
|
+
onVerify: config.onVerify,
|
|
41
|
+
requireUserVerification: config.requireUserVerification ?? true,
|
|
42
|
+
allowedAAGUIDs: config.allowedAAGUIDs
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async function handleChallenge(config) {
|
|
46
|
+
try {
|
|
47
|
+
const challenge = createChallenge();
|
|
48
|
+
const challengeId = crypto.randomUUID();
|
|
49
|
+
await config.challengeStore.set(challengeId, challenge, config.challengeTTL);
|
|
50
|
+
return { status: 200, body: { challengeId, challenge } };
|
|
51
|
+
} catch {
|
|
52
|
+
return { status: 500, body: { error: "Internal server error" } };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function handleRegister(config, body) {
|
|
56
|
+
try {
|
|
57
|
+
const challenge = await config.challengeStore.get(body.challengeId);
|
|
58
|
+
if (!challenge) {
|
|
59
|
+
return { status: 400, body: { error: "Challenge not found or expired" } };
|
|
60
|
+
}
|
|
61
|
+
const result = await verifyRegistration({
|
|
62
|
+
response: body.response,
|
|
63
|
+
expectedChallenge: challenge,
|
|
64
|
+
expectedOrigin: config.origin,
|
|
65
|
+
expectedRPID: config.rpID,
|
|
66
|
+
requireUserVerification: config.requireUserVerification,
|
|
67
|
+
allowedAAGUIDs: config.allowedAAGUIDs
|
|
68
|
+
});
|
|
69
|
+
await config.onRegister(result.credential);
|
|
70
|
+
return { status: 200, body: { ok: true, credentialId: result.credential.id } };
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return errorResult(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function handleVerify(config, body) {
|
|
76
|
+
try {
|
|
77
|
+
const challenge = await config.challengeStore.get(body.challengeId);
|
|
78
|
+
if (!challenge) {
|
|
79
|
+
return { status: 400, body: { error: "Challenge not found or expired" } };
|
|
80
|
+
}
|
|
81
|
+
const proof = body.proof;
|
|
82
|
+
const credential = await config.getCredential(proof.response?.id);
|
|
83
|
+
if (!credential) {
|
|
84
|
+
return { status: 400, body: { error: "Credential not found" } };
|
|
85
|
+
}
|
|
86
|
+
const result = await verifyTapProof({
|
|
87
|
+
proof,
|
|
88
|
+
credential,
|
|
89
|
+
expectedChallenge: challenge,
|
|
90
|
+
expectedAction: body.action,
|
|
91
|
+
expectedOrigin: config.origin,
|
|
92
|
+
expectedRPID: config.rpID,
|
|
93
|
+
requireUserVerification: config.requireUserVerification
|
|
94
|
+
});
|
|
95
|
+
credential.counter = result.newCounter;
|
|
96
|
+
if (config.onVerify) {
|
|
97
|
+
await config.onVerify(result, body.action);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
status: 200,
|
|
101
|
+
body: {
|
|
102
|
+
verified: result.verified,
|
|
103
|
+
confirmationValid: result.confirmationValid,
|
|
104
|
+
userVerified: result.userVerified
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return errorResult(error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function errorResult(error) {
|
|
112
|
+
if (error instanceof HumanKeyError) {
|
|
113
|
+
return { status: 400, body: { error: error.message, code: error.code } };
|
|
114
|
+
}
|
|
115
|
+
return { status: 500, body: { error: "Internal server error" } };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export {
|
|
119
|
+
MemoryChallengeStore,
|
|
120
|
+
resolveConfig,
|
|
121
|
+
handleChallenge,
|
|
122
|
+
handleRegister,
|
|
123
|
+
handleVerify
|
|
124
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
var _chunk2KMBF2THcjs = require('./chunk-2KMBF2TH.cjs');
|
|
5
|
+
require('./chunk-JI6NIMGK.cjs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
exports.createConfirmation = _chunk2KMBF2THcjs.createConfirmation; exports.validateConfirmation = _chunk2KMBF2THcjs.validateConfirmation;
|
package/dist/express.cjs
CHANGED
|
@@ -1,124 +1,36 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var _chunkFW3YUVJCcjs = require('./chunk-FW3YUVJC.cjs');
|
|
6
5
|
|
|
7
6
|
|
|
8
|
-
var
|
|
7
|
+
var _chunkG2IT2ZP5cjs = require('./chunk-G2IT2ZP5.cjs');
|
|
8
|
+
require('./chunk-FW3YUVJC.cjs');
|
|
9
|
+
require('./chunk-JI6NIMGK.cjs');
|
|
9
10
|
|
|
10
11
|
// src/express.ts
|
|
11
12
|
var _express = require('express');
|
|
12
|
-
var MemoryChallengeStore = (_class = class {constructor() { _class.prototype.__init.call(this); }
|
|
13
|
-
__init() {this.store = /* @__PURE__ */ new Map()}
|
|
14
|
-
async set(id, challenge, ttlMs) {
|
|
15
|
-
this.store.set(id, { challenge, expiresAt: Date.now() + ttlMs });
|
|
16
|
-
this.cleanup();
|
|
17
|
-
}
|
|
18
|
-
async get(id) {
|
|
19
|
-
const entry = this.store.get(id);
|
|
20
|
-
if (!entry) return null;
|
|
21
|
-
this.store.delete(id);
|
|
22
|
-
if (Date.now() > entry.expiresAt) return null;
|
|
23
|
-
return entry.challenge;
|
|
24
|
-
}
|
|
25
|
-
cleanup() {
|
|
26
|
-
const now = Date.now();
|
|
27
|
-
for (const [id, entry] of this.store) {
|
|
28
|
-
if (now > entry.expiresAt) this.store.delete(id);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}, _class);
|
|
32
13
|
function createHumanKeyRouter(config) {
|
|
33
|
-
const
|
|
34
|
-
rpID,
|
|
35
|
-
origin,
|
|
36
|
-
challengeTTL = 6e4,
|
|
37
|
-
challengeStore = new MemoryChallengeStore(),
|
|
38
|
-
getCredential,
|
|
39
|
-
onRegister,
|
|
40
|
-
onVerify,
|
|
41
|
-
requireUserVerification = true,
|
|
42
|
-
allowedAAGUIDs
|
|
43
|
-
} = config;
|
|
14
|
+
const resolved = _chunkG2IT2ZP5cjs.resolveConfig.call(void 0, config);
|
|
44
15
|
const router = _express.Router.call(void 0, );
|
|
45
16
|
router.post("/challenge", async (_req, res) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const challengeId = crypto.randomUUID();
|
|
49
|
-
await challengeStore.set(challengeId, challenge, challengeTTL);
|
|
50
|
-
res.json({ challengeId, challenge });
|
|
51
|
-
} catch (error) {
|
|
52
|
-
handleError(res, error);
|
|
53
|
-
}
|
|
17
|
+
const result = await _chunkG2IT2ZP5cjs.handleChallenge.call(void 0, resolved);
|
|
18
|
+
sendResult(res, result);
|
|
54
19
|
});
|
|
55
20
|
router.post("/register", async (req, res) => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const challenge = await challengeStore.get(challengeId);
|
|
59
|
-
if (!challenge) {
|
|
60
|
-
res.status(400).json({ error: "Challenge not found or expired" });
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const result = await _chunkFW3YUVJCcjs.verifyRegistration.call(void 0, {
|
|
64
|
-
response,
|
|
65
|
-
expectedChallenge: challenge,
|
|
66
|
-
expectedOrigin: origin,
|
|
67
|
-
expectedRPID: rpID,
|
|
68
|
-
requireUserVerification,
|
|
69
|
-
allowedAAGUIDs
|
|
70
|
-
});
|
|
71
|
-
await onRegister(result.credential);
|
|
72
|
-
res.json({ ok: true, credentialId: result.credential.id });
|
|
73
|
-
} catch (error) {
|
|
74
|
-
handleError(res, error);
|
|
75
|
-
}
|
|
21
|
+
const result = await _chunkG2IT2ZP5cjs.handleRegister.call(void 0, resolved, req.body);
|
|
22
|
+
sendResult(res, result);
|
|
76
23
|
});
|
|
77
24
|
router.post("/verify", async (req, res) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const challenge = await challengeStore.get(challengeId);
|
|
81
|
-
if (!challenge) {
|
|
82
|
-
res.status(400).json({ error: "Challenge not found or expired" });
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
const credential = await getCredential(_optionalChain([proof, 'access', _ => _.response, 'optionalAccess', _2 => _2.id]));
|
|
86
|
-
if (!credential) {
|
|
87
|
-
res.status(400).json({ error: "Credential not found" });
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const result = await _chunkFW3YUVJCcjs.verifyTapProof.call(void 0, {
|
|
91
|
-
proof,
|
|
92
|
-
credential,
|
|
93
|
-
expectedChallenge: challenge,
|
|
94
|
-
expectedAction: action,
|
|
95
|
-
expectedOrigin: origin,
|
|
96
|
-
expectedRPID: rpID,
|
|
97
|
-
requireUserVerification
|
|
98
|
-
});
|
|
99
|
-
credential.counter = result.newCounter;
|
|
100
|
-
if (onVerify) {
|
|
101
|
-
await onVerify(result, action);
|
|
102
|
-
}
|
|
103
|
-
res.json({
|
|
104
|
-
verified: result.verified,
|
|
105
|
-
confirmationValid: result.confirmationValid,
|
|
106
|
-
userVerified: result.userVerified
|
|
107
|
-
});
|
|
108
|
-
} catch (error) {
|
|
109
|
-
handleError(res, error);
|
|
110
|
-
}
|
|
25
|
+
const result = await _chunkG2IT2ZP5cjs.handleVerify.call(void 0, resolved, req.body);
|
|
26
|
+
sendResult(res, result);
|
|
111
27
|
});
|
|
112
28
|
return router;
|
|
113
29
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
res.status(400).json({ error: error.message, code: error.code });
|
|
117
|
-
} else {
|
|
118
|
-
res.status(500).json({ error: "Internal server error" });
|
|
119
|
-
}
|
|
30
|
+
function sendResult(res, result) {
|
|
31
|
+
res.status(result.status).json(result.body);
|
|
120
32
|
}
|
|
121
33
|
|
|
122
34
|
|
|
123
35
|
|
|
124
|
-
exports.MemoryChallengeStore = MemoryChallengeStore; exports.createHumanKeyRouter = createHumanKeyRouter;
|
|
36
|
+
exports.MemoryChallengeStore = _chunkG2IT2ZP5cjs.MemoryChallengeStore; exports.createHumanKeyRouter = createHumanKeyRouter;
|
package/dist/express.d.cts
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import {
|
|
2
|
+
import { H as HumanKeyAdapterConfig } from './adapter-core-CoBcSqdG.cjs';
|
|
3
|
+
export { C as ChallengeStore, M as MemoryChallengeStore } from './adapter-core-CoBcSqdG.cjs';
|
|
4
|
+
export { A as ActionPayload, c as TapCredential, V as VerifyResult } from './types-By44RNIt.cjs';
|
|
3
5
|
import '@simplewebauthn/server';
|
|
4
6
|
|
|
5
|
-
/** Abstraction for challenge storage with TTL and single-use semantics. */
|
|
6
|
-
interface ChallengeStore {
|
|
7
|
-
/** Store a challenge with a TTL. */
|
|
8
|
-
set(id: string, challenge: string, ttlMs: number): Promise<void>;
|
|
9
|
-
/** Retrieve and delete a challenge (single-use). Returns null if expired or not found. */
|
|
10
|
-
get(id: string): Promise<string | null>;
|
|
11
|
-
}
|
|
12
|
-
/** In-memory challenge store with TTL enforcement. Use a database or Redis in production. */
|
|
13
|
-
declare class MemoryChallengeStore implements ChallengeStore {
|
|
14
|
-
private store;
|
|
15
|
-
set(id: string, challenge: string, ttlMs: number): Promise<void>;
|
|
16
|
-
get(id: string): Promise<string | null>;
|
|
17
|
-
private cleanup;
|
|
18
|
-
}
|
|
19
7
|
/** Configuration for the humankey Express router. */
|
|
20
|
-
|
|
21
|
-
/** Relying party ID, e.g. "example.com" */
|
|
22
|
-
rpID: string;
|
|
23
|
-
/** Relying party display name */
|
|
24
|
-
rpName: string;
|
|
25
|
-
/** Expected origin(s), e.g. "https://example.com" */
|
|
26
|
-
origin: string | string[];
|
|
27
|
-
/** Challenge TTL in ms (default: 60000) */
|
|
28
|
-
challengeTTL?: number;
|
|
29
|
-
/** Custom challenge store (default: MemoryChallengeStore) */
|
|
30
|
-
challengeStore?: ChallengeStore;
|
|
31
|
-
/** Look up a stored credential by ID */
|
|
32
|
-
getCredential: (credentialId: string) => Promise<TapCredential | null>;
|
|
33
|
-
/** Called after successful registration — store the credential */
|
|
34
|
-
onRegister: (credential: TapCredential) => Promise<void>;
|
|
35
|
-
/** Called after successful tap verification */
|
|
36
|
-
onVerify?: (result: VerifyResult, action: ActionPayload) => Promise<void>;
|
|
37
|
-
/** Require user verification (default: true) */
|
|
38
|
-
requireUserVerification?: boolean;
|
|
39
|
-
/** Restrict to specific authenticator models by AAGUID */
|
|
40
|
-
allowedAAGUIDs?: string[];
|
|
41
|
-
}
|
|
8
|
+
type HumanKeyExpressConfig = HumanKeyAdapterConfig;
|
|
42
9
|
/**
|
|
43
10
|
* Create an Express Router with pre-built humankey routes.
|
|
44
11
|
*
|
|
@@ -49,4 +16,4 @@ interface HumanKeyExpressConfig {
|
|
|
49
16
|
*/
|
|
50
17
|
declare function createHumanKeyRouter(config: HumanKeyExpressConfig): Router;
|
|
51
18
|
|
|
52
|
-
export {
|
|
19
|
+
export { type HumanKeyExpressConfig, createHumanKeyRouter };
|
package/dist/express.d.ts
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import {
|
|
2
|
+
import { H as HumanKeyAdapterConfig } from './adapter-core-CFN3y3L0.js';
|
|
3
|
+
export { C as ChallengeStore, M as MemoryChallengeStore } from './adapter-core-CFN3y3L0.js';
|
|
4
|
+
export { A as ActionPayload, c as TapCredential, V as VerifyResult } from './types-By44RNIt.js';
|
|
3
5
|
import '@simplewebauthn/server';
|
|
4
6
|
|
|
5
|
-
/** Abstraction for challenge storage with TTL and single-use semantics. */
|
|
6
|
-
interface ChallengeStore {
|
|
7
|
-
/** Store a challenge with a TTL. */
|
|
8
|
-
set(id: string, challenge: string, ttlMs: number): Promise<void>;
|
|
9
|
-
/** Retrieve and delete a challenge (single-use). Returns null if expired or not found. */
|
|
10
|
-
get(id: string): Promise<string | null>;
|
|
11
|
-
}
|
|
12
|
-
/** In-memory challenge store with TTL enforcement. Use a database or Redis in production. */
|
|
13
|
-
declare class MemoryChallengeStore implements ChallengeStore {
|
|
14
|
-
private store;
|
|
15
|
-
set(id: string, challenge: string, ttlMs: number): Promise<void>;
|
|
16
|
-
get(id: string): Promise<string | null>;
|
|
17
|
-
private cleanup;
|
|
18
|
-
}
|
|
19
7
|
/** Configuration for the humankey Express router. */
|
|
20
|
-
|
|
21
|
-
/** Relying party ID, e.g. "example.com" */
|
|
22
|
-
rpID: string;
|
|
23
|
-
/** Relying party display name */
|
|
24
|
-
rpName: string;
|
|
25
|
-
/** Expected origin(s), e.g. "https://example.com" */
|
|
26
|
-
origin: string | string[];
|
|
27
|
-
/** Challenge TTL in ms (default: 60000) */
|
|
28
|
-
challengeTTL?: number;
|
|
29
|
-
/** Custom challenge store (default: MemoryChallengeStore) */
|
|
30
|
-
challengeStore?: ChallengeStore;
|
|
31
|
-
/** Look up a stored credential by ID */
|
|
32
|
-
getCredential: (credentialId: string) => Promise<TapCredential | null>;
|
|
33
|
-
/** Called after successful registration — store the credential */
|
|
34
|
-
onRegister: (credential: TapCredential) => Promise<void>;
|
|
35
|
-
/** Called after successful tap verification */
|
|
36
|
-
onVerify?: (result: VerifyResult, action: ActionPayload) => Promise<void>;
|
|
37
|
-
/** Require user verification (default: true) */
|
|
38
|
-
requireUserVerification?: boolean;
|
|
39
|
-
/** Restrict to specific authenticator models by AAGUID */
|
|
40
|
-
allowedAAGUIDs?: string[];
|
|
41
|
-
}
|
|
8
|
+
type HumanKeyExpressConfig = HumanKeyAdapterConfig;
|
|
42
9
|
/**
|
|
43
10
|
* Create an Express Router with pre-built humankey routes.
|
|
44
11
|
*
|
|
@@ -49,4 +16,4 @@ interface HumanKeyExpressConfig {
|
|
|
49
16
|
*/
|
|
50
17
|
declare function createHumanKeyRouter(config: HumanKeyExpressConfig): Router;
|
|
51
18
|
|
|
52
|
-
export {
|
|
19
|
+
export { type HumanKeyExpressConfig, createHumanKeyRouter };
|
package/dist/express.mjs
CHANGED
|
@@ -1,122 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
MemoryChallengeStore,
|
|
3
|
+
handleChallenge,
|
|
4
|
+
handleRegister,
|
|
5
|
+
handleVerify,
|
|
6
|
+
resolveConfig
|
|
7
|
+
} from "./chunk-Y6KQC7WK.mjs";
|
|
8
|
+
import "./chunk-XXJAJHNP.mjs";
|
|
9
|
+
import "./chunk-CLQSCDXC.mjs";
|
|
9
10
|
|
|
10
11
|
// src/express.ts
|
|
11
12
|
import { Router } from "express";
|
|
12
|
-
var MemoryChallengeStore = class {
|
|
13
|
-
store = /* @__PURE__ */ new Map();
|
|
14
|
-
async set(id, challenge, ttlMs) {
|
|
15
|
-
this.store.set(id, { challenge, expiresAt: Date.now() + ttlMs });
|
|
16
|
-
this.cleanup();
|
|
17
|
-
}
|
|
18
|
-
async get(id) {
|
|
19
|
-
const entry = this.store.get(id);
|
|
20
|
-
if (!entry) return null;
|
|
21
|
-
this.store.delete(id);
|
|
22
|
-
if (Date.now() > entry.expiresAt) return null;
|
|
23
|
-
return entry.challenge;
|
|
24
|
-
}
|
|
25
|
-
cleanup() {
|
|
26
|
-
const now = Date.now();
|
|
27
|
-
for (const [id, entry] of this.store) {
|
|
28
|
-
if (now > entry.expiresAt) this.store.delete(id);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
13
|
function createHumanKeyRouter(config) {
|
|
33
|
-
const
|
|
34
|
-
rpID,
|
|
35
|
-
origin,
|
|
36
|
-
challengeTTL = 6e4,
|
|
37
|
-
challengeStore = new MemoryChallengeStore(),
|
|
38
|
-
getCredential,
|
|
39
|
-
onRegister,
|
|
40
|
-
onVerify,
|
|
41
|
-
requireUserVerification = true,
|
|
42
|
-
allowedAAGUIDs
|
|
43
|
-
} = config;
|
|
14
|
+
const resolved = resolveConfig(config);
|
|
44
15
|
const router = Router();
|
|
45
16
|
router.post("/challenge", async (_req, res) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const challengeId = crypto.randomUUID();
|
|
49
|
-
await challengeStore.set(challengeId, challenge, challengeTTL);
|
|
50
|
-
res.json({ challengeId, challenge });
|
|
51
|
-
} catch (error) {
|
|
52
|
-
handleError(res, error);
|
|
53
|
-
}
|
|
17
|
+
const result = await handleChallenge(resolved);
|
|
18
|
+
sendResult(res, result);
|
|
54
19
|
});
|
|
55
20
|
router.post("/register", async (req, res) => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const challenge = await challengeStore.get(challengeId);
|
|
59
|
-
if (!challenge) {
|
|
60
|
-
res.status(400).json({ error: "Challenge not found or expired" });
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const result = await verifyRegistration({
|
|
64
|
-
response,
|
|
65
|
-
expectedChallenge: challenge,
|
|
66
|
-
expectedOrigin: origin,
|
|
67
|
-
expectedRPID: rpID,
|
|
68
|
-
requireUserVerification,
|
|
69
|
-
allowedAAGUIDs
|
|
70
|
-
});
|
|
71
|
-
await onRegister(result.credential);
|
|
72
|
-
res.json({ ok: true, credentialId: result.credential.id });
|
|
73
|
-
} catch (error) {
|
|
74
|
-
handleError(res, error);
|
|
75
|
-
}
|
|
21
|
+
const result = await handleRegister(resolved, req.body);
|
|
22
|
+
sendResult(res, result);
|
|
76
23
|
});
|
|
77
24
|
router.post("/verify", async (req, res) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const challenge = await challengeStore.get(challengeId);
|
|
81
|
-
if (!challenge) {
|
|
82
|
-
res.status(400).json({ error: "Challenge not found or expired" });
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
const credential = await getCredential(proof.response?.id);
|
|
86
|
-
if (!credential) {
|
|
87
|
-
res.status(400).json({ error: "Credential not found" });
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const result = await verifyTapProof({
|
|
91
|
-
proof,
|
|
92
|
-
credential,
|
|
93
|
-
expectedChallenge: challenge,
|
|
94
|
-
expectedAction: action,
|
|
95
|
-
expectedOrigin: origin,
|
|
96
|
-
expectedRPID: rpID,
|
|
97
|
-
requireUserVerification
|
|
98
|
-
});
|
|
99
|
-
credential.counter = result.newCounter;
|
|
100
|
-
if (onVerify) {
|
|
101
|
-
await onVerify(result, action);
|
|
102
|
-
}
|
|
103
|
-
res.json({
|
|
104
|
-
verified: result.verified,
|
|
105
|
-
confirmationValid: result.confirmationValid,
|
|
106
|
-
userVerified: result.userVerified
|
|
107
|
-
});
|
|
108
|
-
} catch (error) {
|
|
109
|
-
handleError(res, error);
|
|
110
|
-
}
|
|
25
|
+
const result = await handleVerify(resolved, req.body);
|
|
26
|
+
sendResult(res, result);
|
|
111
27
|
});
|
|
112
28
|
return router;
|
|
113
29
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
res.status(400).json({ error: error.message, code: error.code });
|
|
117
|
-
} else {
|
|
118
|
-
res.status(500).json({ error: "Internal server error" });
|
|
119
|
-
}
|
|
30
|
+
function sendResult(res, result) {
|
|
31
|
+
res.status(result.status).json(result.body);
|
|
120
32
|
}
|
|
121
33
|
export {
|
|
122
34
|
MemoryChallengeStore,
|