@rodit/rodit-auth-be 9.11.14 → 9.11.16
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/lib/auth/tokenservice.js +77 -44
- package/package.json +1 -1
package/lib/auth/tokenservice.js
CHANGED
|
@@ -25,9 +25,8 @@ logger.infoWithContext("TokenService using SessionManager instance", {
|
|
|
25
25
|
timestamp: new Date().toISOString()
|
|
26
26
|
});
|
|
27
27
|
const stateManager = require('../blockchain/statemanager');
|
|
28
|
-
const {
|
|
29
|
-
nearorg_rpc_tokenfromroditid,
|
|
30
|
-
nearorg_rpc_tokensfromaccountid,
|
|
28
|
+
const {
|
|
29
|
+
nearorg_rpc_tokenfromroditid,
|
|
31
30
|
nearorg_rpc_fetchpublickeybytes,
|
|
32
31
|
} = require("../blockchain/blockchainservice");
|
|
33
32
|
|
|
@@ -62,6 +61,15 @@ function isCanonicalBase64Url(value) {
|
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
|
|
64
|
+
/** Login peer RODiT token id embedded in JWT sub (`{sp};sub={peerTokenId}`). */
|
|
65
|
+
function extractLoginPeerRoditIdFromSub(sub) {
|
|
66
|
+
if (!sub || typeof sub !== "string") {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
const subParts = sub.split(";sub=");
|
|
70
|
+
return subParts.length > 1 ? subParts[1] : "";
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
function parseRoditJwtDurationSeconds(metadata) {
|
|
66
74
|
const parsed = parseInt(metadata?.jwt_duration, 10);
|
|
67
75
|
if (Number.isFinite(parsed) && parsed > 0) {
|
|
@@ -1727,20 +1735,30 @@ function resolveCredentialExpirationUnix(now, sessionExpiration, own_rodit) {
|
|
|
1727
1735
|
const startTime = Date.now();
|
|
1728
1736
|
|
|
1729
1737
|
try {
|
|
1738
|
+
const extractedSub = extractLoginPeerRoditIdFromSub(token.sub);
|
|
1739
|
+
if (!extractedSub) {
|
|
1740
|
+
const duration = Date.now() - startTime;
|
|
1741
|
+
logger.warn("Brief token validation failed - missing login peer in sub", {
|
|
1742
|
+
component: "JwtAuth",
|
|
1743
|
+
method: "brief_validate_jwt_token_be",
|
|
1744
|
+
requestId,
|
|
1745
|
+
duration,
|
|
1746
|
+
tokenJti: token.jti,
|
|
1747
|
+
tokenSub: token.sub,
|
|
1748
|
+
});
|
|
1749
|
+
logger.metric("jwt_brief_validation", duration, {
|
|
1750
|
+
result: "failure",
|
|
1751
|
+
token_jti: token.jti || "unknown",
|
|
1752
|
+
id_match: "false",
|
|
1753
|
+
});
|
|
1754
|
+
return { isValid: false, notAfter: null };
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1730
1757
|
const tokenFetchStart = Date.now();
|
|
1731
|
-
const peer_rodit =
|
|
1732
|
-
await nearorg_rpc_tokensfromaccountid(
|
|
1733
|
-
|
|
1734
|
-
token.aud
|
|
1735
|
-
);
|
|
1758
|
+
const peer_rodit = await nearorg_rpc_tokenfromroditid(extractedSub);
|
|
1736
1759
|
const tokenFetchDuration = Date.now() - tokenFetchStart;
|
|
1737
1760
|
|
|
1738
|
-
const
|
|
1739
|
-
const extractedSub = subParts.length > 1 ? subParts[1] : "";
|
|
1740
|
-
|
|
1741
|
-
const isValid =
|
|
1742
|
-
peer_rodit.token_id === extractedSub &&
|
|
1743
|
-
peer_rodit.owner_id === token.aud;
|
|
1761
|
+
const isValid = !!peer_rodit?.token_id && peer_rodit.token_id === extractedSub;
|
|
1744
1762
|
|
|
1745
1763
|
const totalDuration = Date.now() - startTime;
|
|
1746
1764
|
|
|
@@ -1753,6 +1771,7 @@ function resolveCredentialExpirationUnix(now, sessionExpiration, own_rodit) {
|
|
|
1753
1771
|
tokenFetchDuration,
|
|
1754
1772
|
tokenJti: token.jti,
|
|
1755
1773
|
peerRoditId: peer_rodit.token_id,
|
|
1774
|
+
extractedSub,
|
|
1756
1775
|
notAfter: peer_rodit.metadata.not_after,
|
|
1757
1776
|
});
|
|
1758
1777
|
|
|
@@ -1769,26 +1788,23 @@ function resolveCredentialExpirationUnix(now, sessionExpiration, own_rodit) {
|
|
|
1769
1788
|
duration: totalDuration,
|
|
1770
1789
|
tokenFetchDuration,
|
|
1771
1790
|
tokenJti: token.jti,
|
|
1772
|
-
peerRoditId: peer_rodit
|
|
1791
|
+
peerRoditId: peer_rodit?.token_id || null,
|
|
1773
1792
|
extractedSub,
|
|
1774
1793
|
tokenAud: token.aud,
|
|
1775
|
-
|
|
1776
|
-
idMatch: peer_rodit.token_id === extractedSub,
|
|
1777
|
-
ownerMatch: peer_rodit.owner_id === token.aud,
|
|
1794
|
+
idMatch: peer_rodit?.token_id === extractedSub,
|
|
1778
1795
|
});
|
|
1779
1796
|
|
|
1780
1797
|
// Add metrics for failed brief validations
|
|
1781
1798
|
logger.metric("jwt_brief_validation", totalDuration, {
|
|
1782
1799
|
result: "failure",
|
|
1783
1800
|
token_jti: token.jti || "unknown",
|
|
1784
|
-
id_match: peer_rodit
|
|
1785
|
-
owner_match: peer_rodit.owner_id === token.aud ? "true" : "false",
|
|
1801
|
+
id_match: peer_rodit?.token_id === extractedSub ? "true" : "false",
|
|
1786
1802
|
});
|
|
1787
1803
|
}
|
|
1788
1804
|
|
|
1789
1805
|
return {
|
|
1790
1806
|
isValid,
|
|
1791
|
-
notAfter: peer_rodit
|
|
1807
|
+
notAfter: peer_rodit?.metadata?.not_after ?? null,
|
|
1792
1808
|
};
|
|
1793
1809
|
} catch (error) {
|
|
1794
1810
|
const duration = Date.now() - startTime;
|
|
@@ -1831,14 +1847,32 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
1831
1847
|
const startTime = performance.now(); // More precise timing measurement
|
|
1832
1848
|
|
|
1833
1849
|
try {
|
|
1850
|
+
const extractedSub = extractLoginPeerRoditIdFromSub(token.sub);
|
|
1851
|
+
if (!extractedSub) {
|
|
1852
|
+
logger.warn("Thorough token validation failed - missing login peer in sub", {
|
|
1853
|
+
component: "JwtAuth",
|
|
1854
|
+
method: "thorough_validate_jwt_token_be",
|
|
1855
|
+
requestId,
|
|
1856
|
+
duration: performance.now() - startTime,
|
|
1857
|
+
tokenJti: token?.jti,
|
|
1858
|
+
tokenSub: token?.sub,
|
|
1859
|
+
});
|
|
1860
|
+
logger.metric &&
|
|
1861
|
+
logger.metric("jwt_thorough_validation", performance.now() - startTime, {
|
|
1862
|
+
result: "missing_login_peer_sub",
|
|
1863
|
+
token_jti: token.jti || "unknown",
|
|
1864
|
+
});
|
|
1865
|
+
return { isValid: false, notAfter: null };
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1834
1868
|
// Fetch configuration with better timing measurements
|
|
1835
1869
|
const configStart = performance.now();
|
|
1836
1870
|
const config_own_rodit = await stateManager.getConfigOwnRodit();
|
|
1837
1871
|
const configDuration = performance.now() - configStart;
|
|
1838
1872
|
|
|
1839
|
-
// Fetch peer RODiT
|
|
1873
|
+
// Fetch login peer RODiT (client identity from sub), not server passport (rodit_id claim)
|
|
1840
1874
|
const tokenFetchStart = performance.now();
|
|
1841
|
-
const peer_rodit = await nearorg_rpc_tokenfromroditid(
|
|
1875
|
+
const peer_rodit = await nearorg_rpc_tokenfromroditid(extractedSub);
|
|
1842
1876
|
const tokenFetchDuration = performance.now() - tokenFetchStart;
|
|
1843
1877
|
|
|
1844
1878
|
if (!peer_rodit) {
|
|
@@ -1846,7 +1880,7 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
1846
1880
|
component: "JwtAuth",
|
|
1847
1881
|
requestId,
|
|
1848
1882
|
duration: performance.now() - startTime,
|
|
1849
|
-
|
|
1883
|
+
loginPeerRoditId: extractedSub,
|
|
1850
1884
|
});
|
|
1851
1885
|
|
|
1852
1886
|
// Add metrics for failed token fetch
|
|
@@ -2075,11 +2109,7 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2075
2109
|
};
|
|
2076
2110
|
}
|
|
2077
2111
|
|
|
2078
|
-
|
|
2079
|
-
const subParts = token.sub.split(";sub=");
|
|
2080
|
-
const extractedSub = subParts.length > 1 ? subParts[1] : "";
|
|
2081
|
-
|
|
2082
|
-
logger.debug("Extracted subject from token", {
|
|
2112
|
+
logger.debug("Login peer identity check", {
|
|
2083
2113
|
requestId,
|
|
2084
2114
|
extractedSub,
|
|
2085
2115
|
tokenSub: token.sub,
|
|
@@ -2088,10 +2118,8 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2088
2118
|
tokenAud: token.aud,
|
|
2089
2119
|
});
|
|
2090
2120
|
|
|
2091
|
-
// Additional identity checks
|
|
2092
2121
|
const idMatch = peer_rodit.token_id === extractedSub;
|
|
2093
|
-
const
|
|
2094
|
-
const isValid = idMatch && ownerMatch;
|
|
2122
|
+
const isValid = idMatch;
|
|
2095
2123
|
|
|
2096
2124
|
const totalDuration = performance.now() - startTime;
|
|
2097
2125
|
|
|
@@ -2114,10 +2142,6 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2114
2142
|
peer_rodit_id: peer_rodit.token_id,
|
|
2115
2143
|
});
|
|
2116
2144
|
} else {
|
|
2117
|
-
const failedIdentityChecks = [];
|
|
2118
|
-
if (!idMatch) failedIdentityChecks.push("token_id_mismatch");
|
|
2119
|
-
if (!ownerMatch) failedIdentityChecks.push("owner_id_mismatch");
|
|
2120
|
-
|
|
2121
2145
|
logger.warn("Token identity verification failed", {
|
|
2122
2146
|
component: "JwtAuth",
|
|
2123
2147
|
method: "thorough_validate_jwt_token_be",
|
|
@@ -2129,8 +2153,7 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2129
2153
|
tokenAud: token.aud,
|
|
2130
2154
|
peerRoditOwnerId: peer_rodit.owner_id,
|
|
2131
2155
|
idMatch,
|
|
2132
|
-
|
|
2133
|
-
failedIdentityChecks,
|
|
2156
|
+
failedIdentityChecks: ["token_id_mismatch"],
|
|
2134
2157
|
});
|
|
2135
2158
|
|
|
2136
2159
|
// Add metrics for identity mismatch with more details
|
|
@@ -2138,19 +2161,22 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2138
2161
|
logger.metric("jwt_thorough_validation", totalDuration, {
|
|
2139
2162
|
result: "identity_mismatch",
|
|
2140
2163
|
token_jti: token.jti || "unknown",
|
|
2141
|
-
id_match:
|
|
2142
|
-
|
|
2143
|
-
failed_checks: failedIdentityChecks.join(","),
|
|
2164
|
+
id_match: "false",
|
|
2165
|
+
failed_checks: "token_id_mismatch",
|
|
2144
2166
|
peer_rodit_id: peer_rodit.token_id,
|
|
2145
2167
|
});
|
|
2146
2168
|
}
|
|
2147
2169
|
|
|
2170
|
+
const failedIdentityChecks = !isValid ? ["token_id_mismatch"] : [];
|
|
2171
|
+
|
|
2148
2172
|
return {
|
|
2149
2173
|
isValid,
|
|
2150
2174
|
notAfter: peer_rodit.metadata.not_after,
|
|
2151
2175
|
error: !isValid ? "Token identity verification failed" : undefined,
|
|
2152
2176
|
errorCode: !isValid ? "SERVER_TOKEN_IDENTITY_MISMATCH" : undefined,
|
|
2153
|
-
errorMessage: !isValid
|
|
2177
|
+
errorMessage: !isValid
|
|
2178
|
+
? `Server token identity mismatch: ${failedIdentityChecks.join(", ")}`
|
|
2179
|
+
: undefined
|
|
2154
2180
|
};
|
|
2155
2181
|
} catch (error) {
|
|
2156
2182
|
const duration = performance.now() - startTime;
|
|
@@ -2409,10 +2435,17 @@ async function thorough_validate_jwt_token_be(token, requestId = ulid()) {
|
|
|
2409
2435
|
|
|
2410
2436
|
|
|
2411
2437
|
// Export the class directly (will be instantiated in rodit.js)
|
|
2412
|
-
module.exports = {
|
|
2438
|
+
module.exports = {
|
|
2439
|
+
generate_jwt_token,
|
|
2440
|
+
base64url2jwk_public_key,
|
|
2413
2441
|
checkandrenew_jwt_token,
|
|
2414
2442
|
thorough_validate_jwt_token_be,
|
|
2415
2443
|
brief_validate_jwt_token_be,
|
|
2416
2444
|
generate_jwt_token_fromtoken,
|
|
2417
|
-
verify_jwt_token,
|
|
2445
|
+
verify_jwt_token,
|
|
2446
|
+
validate_jwt_token_be,
|
|
2447
|
+
generate_session_termination_token,
|
|
2448
|
+
parseRoditJwtDurationSeconds,
|
|
2449
|
+
resolveSessionExpirationUnix,
|
|
2450
|
+
resolveCredentialExpirationUnix,
|
|
2418
2451
|
};
|