@barcodesdk/session 0.1.0
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/LICENSE +9 -0
- package/dist/crypto.d.ts +8 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +242 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt.d.ts +14 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validate.d.ts +10 -0
- package/dist/validate.d.ts.map +1 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Copyright (c) 2026 Vijay Reddy
|
|
2
|
+
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software is proprietary beta software. Unauthorized copying,
|
|
6
|
+
modification, distribution, or use is prohibited except under a written
|
|
7
|
+
license agreement with the copyright holder.
|
|
8
|
+
|
|
9
|
+
Third-party notices for bundled dependencies are in NOTICES.md.
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function encodeBase64Url(data: Uint8Array | string): string;
|
|
2
|
+
export declare function decodeBase64Url(value: string): Uint8Array;
|
|
3
|
+
export declare function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;
|
|
4
|
+
export declare function signHmacSha256(secret: string, data: string): Promise<Uint8Array>;
|
|
5
|
+
export declare function verifyHmacSha256(secret: string, data: string, signature: Uint8Array): Promise<boolean>;
|
|
6
|
+
export declare function decodeJson<T extends Record<string, unknown>>(segment: string): T;
|
|
7
|
+
export declare function encodeJson(value: unknown): string;
|
|
8
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAGA,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAOjE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CASzD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CASvE;AAYD,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,CAAC,CAIrB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,OAAO,EAAE,MAAM,GACd,CAAC,CAGH;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEjD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { ClientSessionValidationOptions, MintSessionOptions, ParsedSessionToken, SessionClaims, SessionValidationCode, SessionValidationResult, } from "./types.js";
|
|
2
|
+
export { hasFeatureGrant, mintSessionToken, normalizeClaims, originAllowed, parseSessionToken, verifySessionSignature, } from "./jwt.js";
|
|
3
|
+
export { createSessionNonce, validateClientSession, validateServerSession, } from "./validate.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,8BAA8B,EAC9B,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
const encoder = new TextEncoder();
|
|
2
|
+
const decoder = new TextDecoder();
|
|
3
|
+
function encodeBase64Url(data) {
|
|
4
|
+
const bytes = typeof data === "string" ? encoder.encode(data) : data;
|
|
5
|
+
let binary = "";
|
|
6
|
+
for (const byte of bytes) {
|
|
7
|
+
binary += String.fromCharCode(byte);
|
|
8
|
+
}
|
|
9
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
10
|
+
}
|
|
11
|
+
function decodeBase64Url(value) {
|
|
12
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
13
|
+
const padding = normalized.length % 4 === 0 ? "" : "=".repeat(4 - normalized.length % 4);
|
|
14
|
+
const binary = atob(normalized + padding);
|
|
15
|
+
const bytes = new Uint8Array(binary.length);
|
|
16
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
17
|
+
bytes[index] = binary.charCodeAt(index);
|
|
18
|
+
}
|
|
19
|
+
return bytes;
|
|
20
|
+
}
|
|
21
|
+
async function importHmacKey(secret) {
|
|
22
|
+
return crypto.subtle.importKey(
|
|
23
|
+
"raw",
|
|
24
|
+
encoder.encode(secret),
|
|
25
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
26
|
+
false,
|
|
27
|
+
["sign", "verify"]
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
async function signHmacSha256(secret, data) {
|
|
31
|
+
const key = await importHmacKey(secret);
|
|
32
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
|
|
33
|
+
return new Uint8Array(signature);
|
|
34
|
+
}
|
|
35
|
+
async function verifyHmacSha256(secret, data, signature) {
|
|
36
|
+
const key = await importHmacKey(secret);
|
|
37
|
+
return crypto.subtle.verify(
|
|
38
|
+
"HMAC",
|
|
39
|
+
key,
|
|
40
|
+
new Uint8Array(signature),
|
|
41
|
+
encoder.encode(data)
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
function decodeJson(segment) {
|
|
45
|
+
const bytes = decodeBase64Url(segment);
|
|
46
|
+
return JSON.parse(decoder.decode(bytes));
|
|
47
|
+
}
|
|
48
|
+
function encodeJson(value) {
|
|
49
|
+
return encodeBase64Url(JSON.stringify(value));
|
|
50
|
+
}
|
|
51
|
+
const REQUIRED_CLAIMS = [
|
|
52
|
+
"tenant_id",
|
|
53
|
+
"allowed_origins",
|
|
54
|
+
"sdk_major_version",
|
|
55
|
+
"feature_grants",
|
|
56
|
+
"exp",
|
|
57
|
+
"nonce"
|
|
58
|
+
];
|
|
59
|
+
function parseSessionToken(token) {
|
|
60
|
+
const parts = token.trim().split(".");
|
|
61
|
+
if (parts.length !== 3) {
|
|
62
|
+
throw new Error("Session token must contain header, payload, and signature.");
|
|
63
|
+
}
|
|
64
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
65
|
+
if (!encodedHeader || !encodedPayload || !encodedSignature) {
|
|
66
|
+
throw new Error("Session token segments are incomplete.");
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
header: decodeJson(encodedHeader),
|
|
70
|
+
payload: decodeJson(encodedPayload),
|
|
71
|
+
signature: encodedSignature,
|
|
72
|
+
signingInput: `${encodedHeader}.${encodedPayload}`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function normalizeClaims(payload) {
|
|
76
|
+
for (const key of REQUIRED_CLAIMS) {
|
|
77
|
+
if (payload[key] === void 0 || payload[key] === null) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (typeof payload.tenant_id !== "string" || payload.tenant_id.trim() === "") {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(payload.allowed_origins) || payload.allowed_origins.some(
|
|
85
|
+
(origin) => typeof origin !== "string" || origin.trim() === ""
|
|
86
|
+
)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (typeof payload.sdk_major_version !== "number" || !Number.isInteger(payload.sdk_major_version) || payload.sdk_major_version < 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(payload.feature_grants) || payload.feature_grants.some(
|
|
93
|
+
(feature) => typeof feature !== "string" || feature.trim() === ""
|
|
94
|
+
)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp)) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
if (typeof payload.nonce !== "string" || payload.nonce.trim() === "") {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const claims = {
|
|
104
|
+
tenant_id: payload.tenant_id.trim(),
|
|
105
|
+
allowed_origins: payload.allowed_origins.map(
|
|
106
|
+
(origin) => typeof origin === "string" ? origin.trim() : ""
|
|
107
|
+
),
|
|
108
|
+
sdk_major_version: payload.sdk_major_version,
|
|
109
|
+
feature_grants: payload.feature_grants.map(
|
|
110
|
+
(feature) => typeof feature === "string" ? feature.trim() : ""
|
|
111
|
+
),
|
|
112
|
+
exp: payload.exp,
|
|
113
|
+
nonce: payload.nonce.trim()
|
|
114
|
+
};
|
|
115
|
+
if (typeof payload.iat === "number" && Number.isFinite(payload.iat)) {
|
|
116
|
+
claims.iat = payload.iat;
|
|
117
|
+
}
|
|
118
|
+
return claims;
|
|
119
|
+
}
|
|
120
|
+
async function verifySessionSignature(token, secret) {
|
|
121
|
+
const parsed = parseSessionToken(token);
|
|
122
|
+
if (parsed.header.alg !== "HS256") {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const actual = decodeBase64Url(parsed.signature);
|
|
126
|
+
return verifyHmacSha256(secret, parsed.signingInput, actual);
|
|
127
|
+
}
|
|
128
|
+
async function mintSessionToken(claims, secret, options = {}) {
|
|
129
|
+
const nowSeconds = options.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
130
|
+
const ttlSeconds = options.ttlSeconds ?? 3600;
|
|
131
|
+
const payload = {
|
|
132
|
+
tenant_id: claims.tenant_id,
|
|
133
|
+
allowed_origins: claims.allowed_origins,
|
|
134
|
+
sdk_major_version: claims.sdk_major_version,
|
|
135
|
+
feature_grants: claims.feature_grants,
|
|
136
|
+
nonce: claims.nonce,
|
|
137
|
+
iat: claims.iat ?? nowSeconds,
|
|
138
|
+
exp: claims.exp ?? nowSeconds + ttlSeconds
|
|
139
|
+
};
|
|
140
|
+
const header = { alg: "HS256", typ: "JWT" };
|
|
141
|
+
const encodedHeader = encodeJson(header);
|
|
142
|
+
const encodedPayload = encodeJson(payload);
|
|
143
|
+
const signingInput = `${encodedHeader}.${encodedPayload}`;
|
|
144
|
+
const signature = await signHmacSha256(secret, signingInput);
|
|
145
|
+
const encodedSignature = encodeBase64Url(signature);
|
|
146
|
+
return `${signingInput}.${encodedSignature}`;
|
|
147
|
+
}
|
|
148
|
+
function originAllowed(origin, allowedOrigins) {
|
|
149
|
+
const normalized = origin.trim().toLowerCase();
|
|
150
|
+
return allowedOrigins.some((allowed) => allowed.trim().toLowerCase() === normalized);
|
|
151
|
+
}
|
|
152
|
+
function hasFeatureGrant(claims, feature) {
|
|
153
|
+
return claims.feature_grants.includes(feature);
|
|
154
|
+
}
|
|
155
|
+
function validateClientSession(token, options = {}) {
|
|
156
|
+
let parsed;
|
|
157
|
+
try {
|
|
158
|
+
parsed = parseSessionToken(token);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return {
|
|
161
|
+
valid: false,
|
|
162
|
+
code: "MALFORMED",
|
|
163
|
+
message: error instanceof Error ? error.message : "Session token is malformed."
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const claims = normalizeClaims(parsed.payload);
|
|
167
|
+
if (!claims) {
|
|
168
|
+
return {
|
|
169
|
+
valid: false,
|
|
170
|
+
code: "MISSING_CLAIMS",
|
|
171
|
+
message: "Session token is missing required claims."
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const nowSeconds = options.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
175
|
+
const skew = options.clockSkewSeconds ?? 30;
|
|
176
|
+
if (claims.exp + skew < nowSeconds) {
|
|
177
|
+
return {
|
|
178
|
+
valid: false,
|
|
179
|
+
code: "EXPIRED",
|
|
180
|
+
message: "Session token has expired.",
|
|
181
|
+
claims
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (options.origin) {
|
|
185
|
+
if (!originAllowed(options.origin, claims.allowed_origins)) {
|
|
186
|
+
return {
|
|
187
|
+
valid: false,
|
|
188
|
+
code: "ORIGIN_MISMATCH",
|
|
189
|
+
message: "Session token is not valid for this origin.",
|
|
190
|
+
claims
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (options.sdkMajorVersion !== void 0 && claims.sdk_major_version !== options.sdkMajorVersion) {
|
|
195
|
+
return {
|
|
196
|
+
valid: false,
|
|
197
|
+
code: "SDK_MAJOR_MISMATCH",
|
|
198
|
+
message: "Session token is not valid for this SDK major version.",
|
|
199
|
+
claims
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
valid: true,
|
|
204
|
+
code: "VALID",
|
|
205
|
+
message: "Session token structure is valid.",
|
|
206
|
+
claims
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async function validateServerSession(token, secret, options = {}) {
|
|
210
|
+
const clientResult = validateClientSession(token, options);
|
|
211
|
+
if (!clientResult.valid) {
|
|
212
|
+
return clientResult;
|
|
213
|
+
}
|
|
214
|
+
const signatureValid = await verifySessionSignature(token, secret);
|
|
215
|
+
if (!signatureValid) {
|
|
216
|
+
return {
|
|
217
|
+
valid: false,
|
|
218
|
+
code: "SIGNATURE_INVALID",
|
|
219
|
+
message: "Session token signature is invalid.",
|
|
220
|
+
claims: clientResult.claims
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return clientResult;
|
|
224
|
+
}
|
|
225
|
+
function createSessionNonce() {
|
|
226
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
227
|
+
return crypto.randomUUID();
|
|
228
|
+
}
|
|
229
|
+
return `nonce-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
230
|
+
}
|
|
231
|
+
export {
|
|
232
|
+
createSessionNonce,
|
|
233
|
+
hasFeatureGrant,
|
|
234
|
+
mintSessionToken,
|
|
235
|
+
normalizeClaims,
|
|
236
|
+
originAllowed,
|
|
237
|
+
parseSessionToken,
|
|
238
|
+
validateClientSession,
|
|
239
|
+
validateServerSession,
|
|
240
|
+
verifySessionSignature
|
|
241
|
+
};
|
|
242
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/crypto.ts","../src/jwt.ts","../src/validate.ts"],"sourcesContent":["const encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\nexport function encodeBase64Url(data: Uint8Array | string): string {\n const bytes = typeof data === \"string\" ? encoder.encode(data) : data;\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/g, \"\");\n}\n\nexport function decodeBase64Url(value: string): Uint8Array {\n const normalized = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padding = normalized.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (normalized.length % 4));\n const binary = atob(normalized + padding);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n}\n\nexport function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) {\n return false;\n }\n let diff = 0;\n for (let index = 0; index < a.length; index += 1) {\n diff |= a[index]! ^ b[index]!;\n }\n return diff === 0;\n}\n\nasync function importHmacKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\", \"verify\"],\n );\n}\n\nexport async function signHmacSha256(\n secret: string,\n data: string,\n): Promise<Uint8Array> {\n const key = await importHmacKey(secret);\n const signature = await crypto.subtle.sign(\"HMAC\", key, encoder.encode(data));\n return new Uint8Array(signature);\n}\n\nexport async function verifyHmacSha256(\n secret: string,\n data: string,\n signature: Uint8Array,\n): Promise<boolean> {\n const key = await importHmacKey(secret);\n return crypto.subtle.verify(\n \"HMAC\",\n key,\n new Uint8Array(signature),\n encoder.encode(data),\n );\n}\n\nexport function decodeJson<T extends Record<string, unknown>>(\n segment: string,\n): T {\n const bytes = decodeBase64Url(segment);\n return JSON.parse(decoder.decode(bytes)) as T;\n}\n\nexport function encodeJson(value: unknown): string {\n return encodeBase64Url(JSON.stringify(value));\n}\n","import {\n decodeBase64Url,\n decodeJson,\n encodeBase64Url,\n encodeJson,\n signHmacSha256,\n verifyHmacSha256,\n} from \"./crypto.js\";\nimport type { ParsedSessionToken, SessionClaims } from \"./types.js\";\n\nconst REQUIRED_CLAIMS: Array<keyof SessionClaims> = [\n \"tenant_id\",\n \"allowed_origins\",\n \"sdk_major_version\",\n \"feature_grants\",\n \"exp\",\n \"nonce\",\n];\n\nexport function parseSessionToken(token: string): ParsedSessionToken {\n const parts = token.trim().split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"Session token must contain header, payload, and signature.\");\n }\n\n const [encodedHeader, encodedPayload, encodedSignature] = parts;\n if (!encodedHeader || !encodedPayload || !encodedSignature) {\n throw new Error(\"Session token segments are incomplete.\");\n }\n\n return {\n header: decodeJson<Record<string, unknown>>(encodedHeader),\n payload: decodeJson<Record<string, unknown>>(encodedPayload),\n signature: encodedSignature,\n signingInput: `${encodedHeader}.${encodedPayload}`,\n };\n}\n\nexport function normalizeClaims(payload: Record<string, unknown>): SessionClaims | null {\n for (const key of REQUIRED_CLAIMS) {\n if (payload[key] === undefined || payload[key] === null) {\n return null;\n }\n }\n\n if (typeof payload.tenant_id !== \"string\" || payload.tenant_id.trim() === \"\") {\n return null;\n }\n\n if (\n !Array.isArray(payload.allowed_origins) ||\n payload.allowed_origins.some(\n (origin) => typeof origin !== \"string\" || origin.trim() === \"\",\n )\n ) {\n return null;\n }\n\n if (\n typeof payload.sdk_major_version !== \"number\" ||\n !Number.isInteger(payload.sdk_major_version) ||\n payload.sdk_major_version < 0\n ) {\n return null;\n }\n\n if (\n !Array.isArray(payload.feature_grants) ||\n payload.feature_grants.some(\n (feature) => typeof feature !== \"string\" || feature.trim() === \"\",\n )\n ) {\n return null;\n }\n\n if (typeof payload.exp !== \"number\" || !Number.isFinite(payload.exp)) {\n return null;\n }\n\n if (typeof payload.nonce !== \"string\" || payload.nonce.trim() === \"\") {\n return null;\n }\n\n const claims: SessionClaims = {\n tenant_id: payload.tenant_id.trim(),\n allowed_origins: payload.allowed_origins.map((origin) =>\n typeof origin === \"string\" ? origin.trim() : \"\",\n ),\n sdk_major_version: payload.sdk_major_version,\n feature_grants: payload.feature_grants.map((feature) =>\n typeof feature === \"string\" ? feature.trim() : \"\",\n ),\n exp: payload.exp,\n nonce: payload.nonce.trim(),\n };\n\n if (typeof payload.iat === \"number\" && Number.isFinite(payload.iat)) {\n claims.iat = payload.iat;\n }\n\n return claims;\n}\n\nexport async function verifySessionSignature(\n token: string,\n secret: string,\n): Promise<boolean> {\n const parsed = parseSessionToken(token);\n if (parsed.header.alg !== \"HS256\") {\n return false;\n }\n const actual = decodeBase64Url(parsed.signature);\n return verifyHmacSha256(secret, parsed.signingInput, actual);\n}\n\nexport async function mintSessionToken(\n claims: Omit<SessionClaims, \"exp\" | \"iat\"> & { exp?: number; iat?: number },\n secret: string,\n options: { ttlSeconds?: number; nowSeconds?: number } = {},\n): Promise<string> {\n const nowSeconds = options.nowSeconds ?? Math.floor(Date.now() / 1000);\n const ttlSeconds = options.ttlSeconds ?? 3600;\n const payload: SessionClaims = {\n tenant_id: claims.tenant_id,\n allowed_origins: claims.allowed_origins,\n sdk_major_version: claims.sdk_major_version,\n feature_grants: claims.feature_grants,\n nonce: claims.nonce,\n iat: claims.iat ?? nowSeconds,\n exp: claims.exp ?? nowSeconds + ttlSeconds,\n };\n\n const header = { alg: \"HS256\", typ: \"JWT\" };\n const encodedHeader = encodeJson(header);\n const encodedPayload = encodeJson(payload);\n const signingInput = `${encodedHeader}.${encodedPayload}`;\n const signature = await signHmacSha256(secret, signingInput);\n const encodedSignature = encodeBase64Url(signature);\n return `${signingInput}.${encodedSignature}`;\n}\n\nexport function originAllowed(origin: string, allowedOrigins: string[]): boolean {\n const normalized = origin.trim().toLowerCase();\n return allowedOrigins.some((allowed) => allowed.trim().toLowerCase() === normalized);\n}\n\nexport function hasFeatureGrant(\n claims: SessionClaims,\n feature: string,\n): boolean {\n return claims.feature_grants.includes(feature);\n}\n","import {\n normalizeClaims,\n originAllowed,\n parseSessionToken,\n verifySessionSignature,\n} from \"./jwt.js\";\nimport type {\n ClientSessionValidationOptions,\n SessionValidationResult,\n} from \"./types.js\";\n\n/**\n * Client-side session validation for UX gating only (NFR-005).\n * Signature verification requires the server secret and is not performed here.\n */\nexport function validateClientSession(\n token: string,\n options: ClientSessionValidationOptions = {},\n): SessionValidationResult {\n let parsed;\n try {\n parsed = parseSessionToken(token);\n } catch (error) {\n return {\n valid: false,\n code: \"MALFORMED\",\n message:\n error instanceof Error ? error.message : \"Session token is malformed.\",\n };\n }\n\n const claims = normalizeClaims(parsed.payload);\n if (!claims) {\n return {\n valid: false,\n code: \"MISSING_CLAIMS\",\n message: \"Session token is missing required claims.\",\n };\n }\n\n const nowSeconds =\n options.nowSeconds ?? Math.floor(Date.now() / 1000);\n const skew = options.clockSkewSeconds ?? 30;\n if (claims.exp + skew < nowSeconds) {\n return {\n valid: false,\n code: \"EXPIRED\",\n message: \"Session token has expired.\",\n claims,\n };\n }\n\n if (options.origin) {\n if (!originAllowed(options.origin, claims.allowed_origins)) {\n return {\n valid: false,\n code: \"ORIGIN_MISMATCH\",\n message: \"Session token is not valid for this origin.\",\n claims,\n };\n }\n }\n\n if (\n options.sdkMajorVersion !== undefined &&\n claims.sdk_major_version !== options.sdkMajorVersion\n ) {\n return {\n valid: false,\n code: \"SDK_MAJOR_MISMATCH\",\n message: \"Session token is not valid for this SDK major version.\",\n claims,\n };\n }\n\n return {\n valid: true,\n code: \"VALID\",\n message: \"Session token structure is valid.\",\n claims,\n };\n}\n\n/** Server-side validation including HMAC signature. */\nexport async function validateServerSession(\n token: string,\n secret: string,\n options: ClientSessionValidationOptions = {},\n): Promise<SessionValidationResult> {\n const clientResult = validateClientSession(token, options);\n if (!clientResult.valid) {\n return clientResult;\n }\n\n const signatureValid = await verifySessionSignature(token, secret);\n if (!signatureValid) {\n return {\n valid: false,\n code: \"SIGNATURE_INVALID\",\n message: \"Session token signature is invalid.\",\n claims: clientResult.claims,\n };\n }\n\n return clientResult;\n}\n\nexport function createSessionNonce(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID();\n }\n return `nonce-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n"],"names":[],"mappings":"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"}
|
package/dist/jwt.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ParsedSessionToken, SessionClaims } from "./types.js";
|
|
2
|
+
export declare function parseSessionToken(token: string): ParsedSessionToken;
|
|
3
|
+
export declare function normalizeClaims(payload: Record<string, unknown>): SessionClaims | null;
|
|
4
|
+
export declare function verifySessionSignature(token: string, secret: string): Promise<boolean>;
|
|
5
|
+
export declare function mintSessionToken(claims: Omit<SessionClaims, "exp" | "iat"> & {
|
|
6
|
+
exp?: number;
|
|
7
|
+
iat?: number;
|
|
8
|
+
}, secret: string, options?: {
|
|
9
|
+
ttlSeconds?: number;
|
|
10
|
+
nowSeconds?: number;
|
|
11
|
+
}): Promise<string>;
|
|
12
|
+
export declare function originAllowed(origin: string, allowedOrigins: string[]): boolean;
|
|
13
|
+
export declare function hasFeatureGrant(claims: SessionClaims, feature: string): boolean;
|
|
14
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWpE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,CAiBnE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,GAAG,IAAI,CA+DtF;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3E,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAG/E;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,GACd,OAAO,CAET"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Claims embedded in a signed session JWT (FR-024). */
|
|
2
|
+
export interface SessionClaims {
|
|
3
|
+
tenant_id: string;
|
|
4
|
+
allowed_origins: string[];
|
|
5
|
+
sdk_major_version: number;
|
|
6
|
+
feature_grants: string[];
|
|
7
|
+
/** Unix seconds. */
|
|
8
|
+
exp: number;
|
|
9
|
+
/** Replay resistance — unique per mint. */
|
|
10
|
+
nonce: string;
|
|
11
|
+
/** Unix seconds. */
|
|
12
|
+
iat?: number;
|
|
13
|
+
}
|
|
14
|
+
export type SessionValidationCode = "VALID" | "MALFORMED" | "MISSING_CLAIMS" | "EXPIRED" | "ORIGIN_MISMATCH" | "SDK_MAJOR_MISMATCH" | "SIGNATURE_INVALID";
|
|
15
|
+
export interface SessionValidationResult {
|
|
16
|
+
valid: boolean;
|
|
17
|
+
code: SessionValidationCode;
|
|
18
|
+
message: string;
|
|
19
|
+
claims?: SessionClaims;
|
|
20
|
+
}
|
|
21
|
+
export interface ClientSessionValidationOptions {
|
|
22
|
+
/** Current page origin, e.g. https://app.example.com */
|
|
23
|
+
origin?: string | null;
|
|
24
|
+
/** SDK major version compiled into the host app. */
|
|
25
|
+
sdkMajorVersion?: number;
|
|
26
|
+
/** Clock skew tolerance in seconds (default 30). */
|
|
27
|
+
clockSkewSeconds?: number;
|
|
28
|
+
/** Unix seconds override for tests. */
|
|
29
|
+
nowSeconds?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface MintSessionOptions {
|
|
32
|
+
/** TTL from now in seconds (default 3600). */
|
|
33
|
+
ttlSeconds?: number;
|
|
34
|
+
/** Unix seconds override for tests. */
|
|
35
|
+
nowSeconds?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface ParsedSessionToken {
|
|
38
|
+
header: Record<string, unknown>;
|
|
39
|
+
payload: Record<string, unknown>;
|
|
40
|
+
signature: string;
|
|
41
|
+
signingInput: string;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,qBAAqB,GAC7B,OAAO,GACP,WAAW,GACX,gBAAgB,GAChB,SAAS,GACT,iBAAiB,GACjB,oBAAoB,GACpB,mBAAmB,CAAC;AAExB,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,qBAAqB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,8BAA8B;IAC7C,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ClientSessionValidationOptions, SessionValidationResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Client-side session validation for UX gating only (NFR-005).
|
|
4
|
+
* Signature verification requires the server secret and is not performed here.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateClientSession(token: string, options?: ClientSessionValidationOptions): SessionValidationResult;
|
|
7
|
+
/** Server-side validation including HMAC signature. */
|
|
8
|
+
export declare function validateServerSession(token: string, secret: string, options?: ClientSessionValidationOptions): Promise<SessionValidationResult>;
|
|
9
|
+
export declare function createSessionNonce(): string;
|
|
10
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,8BAA8B,EAC9B,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,8BAAmC,GAC3C,uBAAuB,CA+DzB;AAED,uDAAuD;AACvD,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,8BAAmC,GAC3C,OAAO,CAAC,uBAAuB,CAAC,CAiBlC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAK3C"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@barcodesdk/session",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Signed session tokens for Barcode WebSDK commercial distribution",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"author": "Vijay Reddy",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/vjiayreddy/barcodesdk.git",
|
|
10
|
+
"directory": "packages/session"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"tag": "beta"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"module": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5",
|
|
32
|
+
"vite": "^6",
|
|
33
|
+
"vitest": "^3"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.build.json",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest"
|
|
40
|
+
}
|
|
41
|
+
}
|