@tidecloak/verify 0.9.11 → 0.9.12
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/dist/cjs/TideJWT.js +53 -0
- package/dist/esm/TideJWT.js +53 -0
- package/dist/types/TideJWT.d.ts +10 -0
- package/dist/types/TideJWT.d.ts.map +1 -0
- package/package.json +5 -4
- package/src/TideJWT.js +0 -63
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jwtVerify, createLocalJWKSet, createRemoteJWKSet } from "jose";
|
|
2
|
+
/**
|
|
3
|
+
* Verify a TideCloak-issued JWT on the server side using your imported config object.
|
|
4
|
+
*
|
|
5
|
+
* @param {object} config - Imported TideCloak configuration (parsed JSON).
|
|
6
|
+
* @param {string} token - access token to verify.
|
|
7
|
+
* @param {string[]} [allowedRoles] - Array of Keycloak realm or client roles; user must have at least one.
|
|
8
|
+
* @returns {Promise<object|null>} - The token payload if valid and role-check passes, otherwise null.
|
|
9
|
+
*/
|
|
10
|
+
export async function verifyTideCloakToken(config, token, allowedRoles = []) {
|
|
11
|
+
var _a, _b, _c;
|
|
12
|
+
try {
|
|
13
|
+
// Ensure token is provided
|
|
14
|
+
if (!token) {
|
|
15
|
+
throw new Error("No token provided");
|
|
16
|
+
}
|
|
17
|
+
// Ensure config is provided
|
|
18
|
+
if (!config || Object.keys(config).length === 0) {
|
|
19
|
+
throw new Error("Could not load TideCloak configuration");
|
|
20
|
+
}
|
|
21
|
+
// Construct issuer URL (ensure slash before 'realms')
|
|
22
|
+
const baseUrl = config["auth-server-url"];
|
|
23
|
+
const sep = baseUrl.endsWith("/") ? "" : "/";
|
|
24
|
+
const issuer = `${baseUrl}${sep}realms/${config.realm}`;
|
|
25
|
+
// Determine JWK set (use local JWKs if provided, otherwise fetch remotely)
|
|
26
|
+
const jwkSet = config.jwk
|
|
27
|
+
? createLocalJWKSet(config.jwk)
|
|
28
|
+
: createRemoteJWKSet(new URL(`${issuer}/protocol/openid-connect/certs`));
|
|
29
|
+
// Verify token signature and issuer
|
|
30
|
+
const { payload } = await jwtVerify(token, jwkSet, { issuer });
|
|
31
|
+
// Verify authorized party (client)
|
|
32
|
+
const client = config["resource"];
|
|
33
|
+
if (payload.azp !== client) {
|
|
34
|
+
throw new Error(`AZP mismatch: expected '${config.resource}', got '${payload.azp}'`);
|
|
35
|
+
}
|
|
36
|
+
// Gather all user roles from realm and client roles for the specified resource from the config.
|
|
37
|
+
const realmRoles = ((_a = payload.realm_access) === null || _a === void 0 ? void 0 : _a.roles) || [];
|
|
38
|
+
const clientRoles = ((_c = (_b = payload.resource_access) === null || _b === void 0 ? void 0 : _b[client]) === null || _c === void 0 ? void 0 : _c.roles) || [];
|
|
39
|
+
const allRoles = new Set([...realmRoles, ...clientRoles]);
|
|
40
|
+
// If allowedRoles specified, ensure at least one match
|
|
41
|
+
if (allowedRoles.length > 0) {
|
|
42
|
+
const hasAllowed = allowedRoles.some(role => allRoles.has(role));
|
|
43
|
+
if (!hasAllowed) {
|
|
44
|
+
throw new Error(`Role match failed: user roles [${[...allRoles].join(", ")}] do not include any of [${allowedRoles.join(", ")}]`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return payload;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error("[TideJWT] Token verification failed:", err);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jwtVerify, createLocalJWKSet, createRemoteJWKSet } from "jose";
|
|
2
|
+
/**
|
|
3
|
+
* Verify a TideCloak-issued JWT on the server side using your imported config object.
|
|
4
|
+
*
|
|
5
|
+
* @param {object} config - Imported TideCloak configuration (parsed JSON).
|
|
6
|
+
* @param {string} token - access token to verify.
|
|
7
|
+
* @param {string[]} [allowedRoles] - Array of Keycloak realm or client roles; user must have at least one.
|
|
8
|
+
* @returns {Promise<object|null>} - The token payload if valid and role-check passes, otherwise null.
|
|
9
|
+
*/
|
|
10
|
+
export async function verifyTideCloakToken(config, token, allowedRoles = []) {
|
|
11
|
+
var _a, _b, _c;
|
|
12
|
+
try {
|
|
13
|
+
// Ensure token is provided
|
|
14
|
+
if (!token) {
|
|
15
|
+
throw new Error("No token provided");
|
|
16
|
+
}
|
|
17
|
+
// Ensure config is provided
|
|
18
|
+
if (!config || Object.keys(config).length === 0) {
|
|
19
|
+
throw new Error("Could not load TideCloak configuration");
|
|
20
|
+
}
|
|
21
|
+
// Construct issuer URL (ensure slash before 'realms')
|
|
22
|
+
const baseUrl = config["auth-server-url"];
|
|
23
|
+
const sep = baseUrl.endsWith("/") ? "" : "/";
|
|
24
|
+
const issuer = `${baseUrl}${sep}realms/${config.realm}`;
|
|
25
|
+
// Determine JWK set (use local JWKs if provided, otherwise fetch remotely)
|
|
26
|
+
const jwkSet = config.jwk
|
|
27
|
+
? createLocalJWKSet(config.jwk)
|
|
28
|
+
: createRemoteJWKSet(new URL(`${issuer}/protocol/openid-connect/certs`));
|
|
29
|
+
// Verify token signature and issuer
|
|
30
|
+
const { payload } = await jwtVerify(token, jwkSet, { issuer });
|
|
31
|
+
// Verify authorized party (client)
|
|
32
|
+
const client = config["resource"];
|
|
33
|
+
if (payload.azp !== client) {
|
|
34
|
+
throw new Error(`AZP mismatch: expected '${config.resource}', got '${payload.azp}'`);
|
|
35
|
+
}
|
|
36
|
+
// Gather all user roles from realm and client roles for the specified resource from the config.
|
|
37
|
+
const realmRoles = ((_a = payload.realm_access) === null || _a === void 0 ? void 0 : _a.roles) || [];
|
|
38
|
+
const clientRoles = ((_c = (_b = payload.resource_access) === null || _b === void 0 ? void 0 : _b[client]) === null || _c === void 0 ? void 0 : _c.roles) || [];
|
|
39
|
+
const allRoles = new Set([...realmRoles, ...clientRoles]);
|
|
40
|
+
// If allowedRoles specified, ensure at least one match
|
|
41
|
+
if (allowedRoles.length > 0) {
|
|
42
|
+
const hasAllowed = allowedRoles.some(role => allRoles.has(role));
|
|
43
|
+
if (!hasAllowed) {
|
|
44
|
+
throw new Error(`Role match failed: user roles [${[...allRoles].join(", ")}] do not include any of [${allowedRoles.join(", ")}]`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return payload;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error("[TideJWT] Token verification failed:", err);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify a TideCloak-issued JWT on the server side using your imported config object.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} config - Imported TideCloak configuration (parsed JSON).
|
|
5
|
+
* @param {string} token - access token to verify.
|
|
6
|
+
* @param {string[]} [allowedRoles] - Array of Keycloak realm or client roles; user must have at least one.
|
|
7
|
+
* @returns {Promise<object|null>} - The token payload if valid and role-check passes, otherwise null.
|
|
8
|
+
*/
|
|
9
|
+
export function verifyTideCloakToken(config: object, token: string, allowedRoles?: string[]): Promise<object | null>;
|
|
10
|
+
//# sourceMappingURL=TideJWT.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TideJWT.d.ts","sourceRoot":"","sources":["../../src/TideJWT.js"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,6CALW,MAAM,SACN,MAAM,iBACN,MAAM,EAAE,GACN,OAAO,CAAC,MAAM,GAAC,IAAI,CAAC,CAsDhC"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tidecloak/verify",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.12",
|
|
4
4
|
"description": "A lightweight utility for server-side verification of TideCloak-issued JSON Web Tokens (JWTs).",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"import": "./dist/esm/TideJWT.js",
|
|
8
|
+
"types": "./dist/types/TideJWT.d.ts",
|
|
9
|
+
"require": "./dist/cjs/TideJWT.js"
|
|
9
10
|
}
|
|
10
11
|
},
|
|
11
12
|
"files": [
|
|
12
|
-
"
|
|
13
|
+
"dist"
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|
|
15
16
|
"build:cjs": "tsc -p tsconfig.cjs.json",
|
package/src/TideJWT.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { jwtVerify, createLocalJWKSet, createRemoteJWKSet } from "jose";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Verify a TideCloak-issued JWT on the server side using your imported config object.
|
|
5
|
-
*
|
|
6
|
-
* @param {object} config - Imported TideCloak configuration (parsed JSON).
|
|
7
|
-
* @param {string} token - access token to verify.
|
|
8
|
-
* @param {string[]} [allowedRoles] - Array of Keycloak realm or client roles; user must have at least one.
|
|
9
|
-
* @returns {Promise<object|null>} - The token payload if valid and role-check passes, otherwise null.
|
|
10
|
-
*/
|
|
11
|
-
export async function verifyTideCloakToken(config, token, allowedRoles = []) {
|
|
12
|
-
try {
|
|
13
|
-
|
|
14
|
-
// Ensure token is provided
|
|
15
|
-
if (!token) {
|
|
16
|
-
throw new Error("No token provided");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Ensure config is provided
|
|
20
|
-
if (!config || Object.keys(config).length === 0) {
|
|
21
|
-
throw new Error("Could not load TideCloak configuration");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Construct issuer URL (ensure slash before 'realms')
|
|
25
|
-
const baseUrl = config["auth-server-url"];
|
|
26
|
-
const sep = baseUrl.endsWith("/") ? "" : "/";
|
|
27
|
-
const issuer = `${baseUrl}${sep}realms/${config.realm}`;
|
|
28
|
-
|
|
29
|
-
// Determine JWK set (use local JWKs if provided, otherwise fetch remotely)
|
|
30
|
-
const jwkSet = config.jwk
|
|
31
|
-
? createLocalJWKSet(config.jwk)
|
|
32
|
-
: createRemoteJWKSet(new URL(`${issuer}/protocol/openid-connect/certs`));
|
|
33
|
-
|
|
34
|
-
// Verify token signature and issuer
|
|
35
|
-
const { payload } = await jwtVerify(token, jwkSet, { issuer });
|
|
36
|
-
|
|
37
|
-
// Verify authorized party (client)
|
|
38
|
-
const client = config["resource"];
|
|
39
|
-
if (payload.azp !== client) {
|
|
40
|
-
throw new Error(`AZP mismatch: expected '${config.resource}', got '${payload.azp}'`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Gather all user roles from realm and client roles for the specified resource from the config.
|
|
44
|
-
const realmRoles = payload.realm_access?.roles || [];
|
|
45
|
-
const clientRoles = payload.resource_access?.[client]?.roles || [];
|
|
46
|
-
const allRoles = new Set([...realmRoles, ...clientRoles]);
|
|
47
|
-
|
|
48
|
-
// If allowedRoles specified, ensure at least one match
|
|
49
|
-
if (allowedRoles.length > 0) {
|
|
50
|
-
const hasAllowed = allowedRoles.some(role => allRoles.has(role));
|
|
51
|
-
if (!hasAllowed) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
`Role match failed: user roles [${[...allRoles].join(", ")}] do not include any of [${allowedRoles.join(", ")}]`
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return payload;
|
|
59
|
-
} catch (err) {
|
|
60
|
-
console.error("[TideJWT] Token verification failed:", err);
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|