@tangle-network/sandbox 0.1.1 → 0.2.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 +490 -2
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +271 -1
- package/dist/client-Uve6A5C6.js +2280 -0
- package/dist/collaboration/index.d.ts +1 -1
- package/dist/collaboration/index.js +2 -1
- package/dist/collaboration-CRyb5e8F.js +201 -0
- package/dist/core.d.ts +3 -3
- package/dist/core.js +4 -1
- package/dist/errors-BI75IXOM.d.ts +1177 -0
- package/dist/errors-CljiGR__.js +262 -0
- package/dist/{index-BuS8nl3b.d.ts → index-CCsA3S0D.d.ts} +6 -1
- package/dist/{index-t7xkzv0U.d.ts → index-DhNGZ0h4.d.ts} +3 -3
- package/dist/{index-gA-oRjOi.d.ts → index-Dpj1oB5i.d.ts} +35 -4
- package/dist/index.d.ts +109 -62
- package/dist/index.js +825 -1
- package/dist/openai/index.d.ts +642 -0
- package/dist/openai/index.js +1721 -0
- package/dist/platform-integrations.d.ts +2 -0
- package/dist/platform-integrations.js +2 -0
- package/dist/{sandbox-BvZ0-Iv7.d.ts → sandbox-aBpWqler.d.ts} +1528 -41
- package/dist/sandbox-ksXTNlo-.js +3394 -0
- package/dist/session-gateway/index.js +667 -1
- package/dist/tangle/index.d.ts +1 -1
- package/dist/tangle/index.js +2 -1
- package/dist/tangle-DQ05paN7.js +826 -0
- package/package.json +93 -34
- package/LICENSE +0 -11
- package/dist/client-Bi0PeV_x.js +0 -1
- package/dist/collaboration-CVvhPU8M.js +0 -1
- package/dist/errors-AIT8qikt.d.ts +0 -491
- package/dist/errors-CdMTv7uG.js +0 -1
- package/dist/sandbox-D1JnQIJx.js +0 -1
- package/dist/tangle-CSb9rjAh.js +0 -1
package/dist/auth/index.js
CHANGED
|
@@ -1 +1,271 @@
|
|
|
1
|
-
const a0_0x478b15=a0_0x1626;function a0_0x1626(_0x38e680,_0x3dc3c8){_0x38e680=_0x38e680-0x95;const _0x97ebeb=a0_0x97eb();let _0x16263a=_0x97ebeb[_0x38e680];if(a0_0x1626['\x73\x76\x65\x71\x6e\x53']===undefined){var _0x1a8486=function(_0x542499){const _0x15c73a='\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2b\x2f\x3d';let _0x4a2e1a='',_0x5da365='';for(let _0x144f52=0x0,_0x3005a0,_0x43fe12,_0x2e9a43=0x0;_0x43fe12=_0x542499['\x63\x68\x61\x72\x41\x74'](_0x2e9a43++);~_0x43fe12&&(_0x3005a0=_0x144f52%0x4?_0x3005a0*0x40+_0x43fe12:_0x43fe12,_0x144f52++%0x4)?_0x4a2e1a+=String['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](0xff&_0x3005a0>>(-0x2*_0x144f52&0x6)):0x0){_0x43fe12=_0x15c73a['\x69\x6e\x64\x65\x78\x4f\x66'](_0x43fe12);}for(let _0x18e102=0x0,_0xd53bc0=_0x4a2e1a['\x6c\x65\x6e\x67\x74\x68'];_0x18e102<_0xd53bc0;_0x18e102++){_0x5da365+='\x25'+('\x30\x30'+_0x4a2e1a['\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74'](_0x18e102)['\x74\x6f\x53\x74\x72\x69\x6e\x67'](0x10))['\x73\x6c\x69\x63\x65'](-0x2);}return decodeURIComponent(_0x5da365);};a0_0x1626['\x5a\x46\x41\x53\x54\x70']=_0x1a8486,a0_0x1626['\x43\x56\x44\x4a\x57\x5a']={},a0_0x1626['\x73\x76\x65\x71\x6e\x53']=!![];}const _0x1abf32=_0x97ebeb[0x0],_0x27373a=_0x38e680+_0x1abf32,_0x53ed09=a0_0x1626['\x43\x56\x44\x4a\x57\x5a'][_0x27373a];return!_0x53ed09?(_0x16263a=a0_0x1626['\x5a\x46\x41\x53\x54\x70'](_0x16263a),a0_0x1626['\x43\x56\x44\x4a\x57\x5a'][_0x27373a]=_0x16263a):_0x16263a=_0x53ed09,_0x16263a;}(function(_0x5b63d9,_0x27a8a8){const _0x9e60e3=a0_0x1626,_0x447335=_0x5b63d9();while(!![]){try{const _0x3313dc=parseInt(_0x9e60e3(0x96))/0x1*(parseInt(_0x9e60e3(0xb5))/0x2)+parseInt(_0x9e60e3(0xb2))/0x3+-parseInt(_0x9e60e3(0x9a))/0x4*(parseInt(_0x9e60e3(0xa4))/0x5)+parseInt(_0x9e60e3(0xa8))/0x6*(parseInt(_0x9e60e3(0x9f))/0x7)+-parseInt(_0x9e60e3(0x9c))/0x8+-parseInt(_0x9e60e3(0x9d))/0x9+-parseInt(_0x9e60e3(0xab))/0xa;if(_0x3313dc===_0x27a8a8)break;else _0x447335['push'](_0x447335['shift']());}catch(_0x753fb9){_0x447335['push'](_0x447335['shift']());}}}(a0_0x97eb,0x64af0));import{createHmac}from'\x6e\x6f\x64\x65\x3a\x63\x72\x79\x70\x74\x6f';function base64UrlEncode(_0x47e930){const _0x22cd42=a0_0x1626,_0x14e111={'\x75\x46\x51\x6d\x4c':function(_0x177680,_0x53e136){return _0x177680===_0x53e136;},'\x61\x44\x44\x59\x52':_0x22cd42(0x99),'\x50\x51\x72\x61\x67':_0x22cd42(0xbf)};return(_0x14e111[_0x22cd42(0xa5)](typeof _0x47e930,_0x14e111[_0x22cd42(0x97)])?Buffer[_0x22cd42(0xb3)](_0x47e930):_0x47e930)[_0x22cd42(0x9b)](_0x14e111[_0x22cd42(0xb4)])[_0x22cd42(0xac)](/\+/g,'\x2d')[_0x22cd42(0xac)](/\//g,'\x5f')['\x72\x65\x70\x6c\x61\x63\x65'](/=+$/,'');}function a0_0x97eb(){const _0x297d75=['\x7a\x32\x76\x30\x76\x68\x72\x53\x74\x77\x4c\x55\x44\x78\x72\x4c\x43\x57','\x43\x67\x66\x59\x43\x32\x75','\x6d\x5a\x47\x30\x6f\x74\x43\x31\x6d\x65\x44\x74\x7a\x65\x76\x70\x74\x61','\x43\x4d\x76\x57\x42\x67\x66\x4a\x7a\x71','\x43\x68\x6a\x56\x7a\x68\x76\x4a\x44\x65\x4c\x4b','\x43\x32\x48\x48\x6d\x4a\x75\x32','\x43\x32\x66\x55\x7a\x67\x6a\x56\x45\x65\x4c\x4b','\x43\x68\x6a\x56','\x7a\x4d\x58\x56\x42\x33\x69','\x6e\x5a\x61\x34\x6f\x74\x61\x35\x76\x76\x4c\x69\x44\x76\x6a\x6e','\x7a\x4e\x6a\x56\x42\x71','\x75\x66\x66\x59\x79\x77\x43','\x6d\x74\x61\x59\x6e\x5a\x6d\x30\x6e\x4e\x6e\x55\x74\x75\x76\x73\x72\x57','\x43\x4d\x76\x57\x7a\x77\x66\x30','\x41\x78\x6e\x5a\x44\x77\x76\x64\x42\x32\x58\x53\x79\x77\x6a\x56\x43\x4d\x66\x30\x41\x77\x39\x55','\x43\x32\x4c\x4e\x42\x4d\x4c\x55\x7a\x31\x6e\x4c\x79\x33\x6a\x4c\x44\x61','\x73\x66\x6d\x59\x6e\x74\x79','\x79\x77\x6e\x4a\x7a\x78\x6e\x5a','\x44\x65\x72\x77\x76\x4b\x6d','\x7a\x67\x4c\x4e\x7a\x78\x6e\x30','\x7a\x77\x35\x30\x7a\x78\x6a\x57\x43\x4d\x4c\x5a\x7a\x71','\x7a\x4e\x6a\x4c\x7a\x71','\x79\x4d\x66\x5a\x7a\x74\x79\x30','\x44\x78\x62\x4b\x79\x78\x72\x4c','\x43\x33\x62\x53\x41\x78\x71','\x41\x78\x6e\x5a\x44\x77\x75','\x42\x67\x76\x55\x7a\x33\x72\x4f','\x43\x32\x76\x5a\x43\x32\x4c\x56\x42\x4b\x4c\x4b','\x43\x4d\x76\x48\x7a\x61','\x44\x67\x4c\x4c\x43\x47','\x43\x68\x6a\x56\x41\x4d\x76\x4a\x44\x65\x4c\x4b','\x6d\x77\x72\x49\x72\x33\x62\x33\x44\x47','\x79\x75\x72\x65\x77\x76\x69','\x42\x4d\x39\x33','\x43\x33\x72\x59\x41\x77\x35\x4e','\x6e\x65\x48\x34\x45\x4d\x35\x76\x41\x47','\x44\x67\x39\x74\x44\x68\x6a\x50\x42\x4d\x43','\x6d\x4a\x4b\x32\x6d\x64\x6d\x30\x6e\x65\x4c\x74\x41\x65\x50\x4c\x42\x57','\x6d\x5a\x43\x35\x6e\x4a\x6d\x34\x41\x76\x4c\x33\x41\x67\x31\x59','\x44\x4e\x50\x4f\x41\x77\x71','\x6e\x33\x44\x62\x79\x75\x31\x67\x72\x57','\x44\x68\x72\x53\x74\x77\x4c\x55\x44\x78\x72\x4c\x43\x57','\x7a\x67\x39\x4a\x44\x77\x31\x4c\x42\x4e\x72\x6a\x7a\x61','\x43\x33\x72\x59\x41\x77\x35\x4e\x41\x77\x7a\x35','\x73\x4c\x44\x75','\x6d\x5a\x61\x35\x6e\x74\x79\x31\x43\x4d\x54\x51\x7a\x33\x7a\x4b','\x44\x75\x7a\x72\x42\x75\x57','\x7a\x76\x62\x58\x43\x30\x65','\x44\x78\x6e\x4c\x43\x4b\x4c\x4b','\x6d\x5a\x65\x59\x6f\x74\x69\x59\x6d\x4d\x58\x65\x72\x68\x76\x4a\x79\x47'];a0_0x97eb=function(){return _0x297d75;};return a0_0x97eb();}function createSignature(_0x1aae9a,_0x5648a5){const _0x15c75e=a0_0x1626;return base64UrlEncode(createHmac(_0x15c75e(0xae),_0x5648a5)[_0x15c75e(0xc0)](_0x1aae9a)[_0x15c75e(0xbc)]());}const JWT_HEADER=base64UrlEncode(JSON[a0_0x478b15(0xa2)]({'\x61\x6c\x67':a0_0x478b15(0xb9),'\x74\x79\x70':a0_0x478b15(0xa3)}));function issueToken(_0x58754c,_0xfcd7f6,_0x172477){const _0x8947ee=a0_0x478b15,_0x2531bb=Math[_0x8947ee(0xb1)](Date[_0x8947ee(0x98)]()/0x3e8),_0x3d9f22={..._0xfcd7f6,'\x69\x61\x74':_0x2531bb,'\x65\x78\x70':_0x2531bb+_0x172477*0x3c},_0x36d7fe=JWT_HEADER+'\x2e'+base64UrlEncode(JSON[_0x8947ee(0xa2)](_0x3d9f22));return _0x36d7fe+'\x2e'+createSignature(_0x36d7fe,_0x58754c);}function issueReadToken(_0x402def,_0x49612c,_0x64e711){const _0x2035c7=a0_0x478b15;return issueToken(_0x402def,{..._0x49612c,'\x74\x79\x70':_0x2035c7(0xc5)},_0x64e711);}function issueSessionScopedToken(_0x1fa1db,_0x544aa4,_0x254c34){return issueReadToken(_0x1fa1db,_0x544aa4,_0x254c34);}function issueProjectScopedToken(_0x1af54f,_0x1a256d,_0x17b043){const _0x5b291b={'\x54\x7a\x62\x79\x4f':function(_0x195c33,_0x9c47d1,_0x87160a,_0x5efaf6){return _0x195c33(_0x9c47d1,_0x87160a,_0x5efaf6);}};return _0x5b291b['\x54\x7a\x62\x79\x4f'](issueReadToken,_0x1af54f,_0x1a256d,_0x17b043);}function issueBatchScopedToken(_0x511af9,_0x6b8b26,_0x3d6e94){return issueReadToken(_0x511af9,_0x6b8b26,_0x3d6e94);}function issueCollaborationToken(_0x326c1f,_0x2ed59e,_0x512280){const _0x1e1f73=a0_0x478b15,_0x114a73={'\x56\x69\x6e\x47\x6b':function(_0x241b85,_0x16d5b9,_0x18fc79,_0x4e1ece){return _0x241b85(_0x16d5b9,_0x18fc79,_0x4e1ece);}};return _0x114a73['\x56\x69\x6e\x47\x6b'](issueToken,_0x326c1f,{'\x73\x75\x62':_0x2ed59e[_0x1e1f73(0xa7)],'\x73\x69\x64':_0x2ed59e[_0x1e1f73(0xc4)],'\x70\x69\x64':_0x2ed59e[_0x1e1f73(0xad)],'\x63\x69\x64':_0x2ed59e[_0x1e1f73(0xaf)],'\x74\x79\x70':'\x63\x6f\x6c\x6c\x61\x62\x6f\x72\x61\x74\x69\x6f\x6e','\x70\x72\x6f\x6a\x65\x63\x74\x49\x64':_0x2ed59e[_0x1e1f73(0x95)],'\x64\x6f\x63\x75\x6d\x65\x6e\x74\x49\x64':_0x2ed59e[_0x1e1f73(0xa1)],'\x61\x63\x63\x65\x73\x73':_0x2ed59e['\x61\x63\x63\x65\x73\x73']},_0x512280);}function decodeToken(_0x376f39){const _0x46e1aa=a0_0x478b15,_0x56844a={'\x76\x7a\x68\x69\x64':function(_0x5da9d6,_0x24da89){return _0x5da9d6!==_0x24da89;},'\x74\x44\x56\x56\x43':function(_0x5c2134,_0xb5d426){return _0x5c2134+_0xb5d426;}};try{const _0x469e93=_0x376f39[_0x46e1aa(0xc1)]('\x2e');if(_0x56844a[_0x46e1aa(0x9e)](_0x469e93[_0x46e1aa(0xc3)],0x3))return null;const _0x1c90a3=_0x56844a[_0x46e1aa(0xbb)](_0x469e93[0x1],'\x3d'[_0x46e1aa(0xb6)]((0x4-_0x469e93[0x1][_0x46e1aa(0xc3)]%0x4)%0x4)),_0x198615=Buffer[_0x46e1aa(0xb3)](_0x1c90a3[_0x46e1aa(0xac)](/-/g,'\x2b')[_0x46e1aa(0xac)](/_/g,'\x2f'),'\x62\x61\x73\x65\x36\x34')[_0x46e1aa(0x9b)]();return JSON[_0x46e1aa(0xaa)](_0x198615);}catch{return null;}}function getTokenTTL(_0x35e9c5){const _0x30f975=a0_0x478b15,_0x19d6c8=Math['\x66\x6c\x6f\x6f\x72'](Date[_0x30f975(0x98)]()/0x3e8);return _0x35e9c5['\x65\x78\x70']-_0x19d6c8;}function isTokenExpiringSoon(_0x26c3c7,_0x491ace=0x3c){return getTokenTTL(_0x26c3c7)<=_0x491ace;}var ProductTokenIssuer=class{[a0_0x478b15(0xad)];[a0_0x478b15(0xb8)];['\x74\x74\x6c\x4d\x69\x6e\x75\x74\x65\x73'];constructor(_0x7c871b){const _0x13d627=a0_0x478b15;this[_0x13d627(0xad)]=_0x7c871b['\x70\x72\x6f\x64\x75\x63\x74\x49\x64'],this['\x73\x69\x67\x6e\x69\x6e\x67\x53\x65\x63\x72\x65\x74']=_0x7c871b[_0x13d627(0xb8)],this[_0x13d627(0xa0)]={'\x66\x72\x65\x65':_0x7c871b[_0x13d627(0xa0)]?.[_0x13d627(0xbe)]??0xf,'\x70\x72\x6f':_0x7c871b['\x74\x74\x6c\x4d\x69\x6e\x75\x74\x65\x73']?.[_0x13d627(0xb0)]??0xf0,'\x65\x6e\x74\x65\x72\x70\x72\x69\x73\x65':_0x7c871b['\x74\x74\x6c\x4d\x69\x6e\x75\x74\x65\x73']?.[_0x13d627(0xbd)]??0x1e0};}[a0_0x478b15(0xc2)](_0x3e448b){const _0x3dc4ac=a0_0x478b15,_0x4d3032={'\x65\x50\x71\x73\x41':_0x3dc4ac(0xbe),'\x75\x70\x4c\x45\x6f':function(_0x1b266c,_0x377427){return _0x1b266c+_0x377427;}},_0x369c3f=_0x3e448b[_0x3dc4ac(0xc6)]??_0x4d3032[_0x3dc4ac(0xa6)],_0x433ad1=this['\x74\x74\x6c\x4d\x69\x6e\x75\x74\x65\x73'][_0x369c3f]??this['\x74\x74\x6c\x4d\x69\x6e\x75\x74\x65\x73'][_0x3dc4ac(0xbe)];return{'\x74\x6f\x6b\x65\x6e':issueReadToken(this[_0x3dc4ac(0xb8)],{'\x73\x75\x62':_0x3e448b[_0x3dc4ac(0xa7)],'\x73\x69\x64':_0x3e448b[_0x3dc4ac(0xc4)],'\x70\x69\x64':this['\x70\x72\x6f\x64\x75\x63\x74\x49\x64'],'\x63\x69\x64':_0x3e448b['\x73\x61\x6e\x64\x62\x6f\x78\x49\x64']},_0x433ad1),'\x65\x78\x70\x69\x72\x65\x73\x41\x74':_0x4d3032['\x75\x70\x4c\x45\x6f'](Math[_0x3dc4ac(0xb1)](Date[_0x3dc4ac(0x98)]()/0x3e8),_0x433ad1*0x3c)};}[a0_0x478b15(0xb7)](_0x1aef0c){const _0x3393a4=a0_0x478b15,_0x4d70ac={'\x7a\x6d\x55\x73\x4d':_0x3393a4(0xbe),'\x71\x59\x6b\x4d\x4e':function(_0x1ffca3,_0x1214e2){return _0x1ffca3*_0x1214e2;}},_0x200e23=_0x1aef0c[_0x3393a4(0xc6)]??_0x4d70ac['\x7a\x6d\x55\x73\x4d'],_0x436919=this[_0x3393a4(0xa0)][_0x200e23]??this['\x74\x74\x6c\x4d\x69\x6e\x75\x74\x65\x73'][_0x3393a4(0xbe)];return{'\x74\x6f\x6b\x65\x6e':issueCollaborationToken(this[_0x3393a4(0xb8)],{'\x75\x73\x65\x72\x49\x64':_0x1aef0c[_0x3393a4(0xa7)],'\x73\x65\x73\x73\x69\x6f\x6e\x49\x64':_0x1aef0c[_0x3393a4(0xc4)],'\x70\x72\x6f\x64\x75\x63\x74\x49\x64':this['\x70\x72\x6f\x64\x75\x63\x74\x49\x64'],'\x70\x72\x6f\x6a\x65\x63\x74\x49\x64':_0x1aef0c[_0x3393a4(0x95)],'\x64\x6f\x63\x75\x6d\x65\x6e\x74\x49\x64':_0x1aef0c[_0x3393a4(0xa1)],'\x61\x63\x63\x65\x73\x73':_0x1aef0c[_0x3393a4(0xba)],'\x73\x61\x6e\x64\x62\x6f\x78\x49\x64':_0x1aef0c[_0x3393a4(0xaf)]},_0x436919),'\x65\x78\x70\x69\x72\x65\x73\x41\x74':Math[_0x3393a4(0xb1)](Date[_0x3393a4(0x98)]()/0x3e8)+_0x4d70ac['\x71\x59\x6b\x4d\x4e'](_0x436919,0x3c)};}[a0_0x478b15(0xa9)](_0x4389a3=a0_0x478b15(0xbe)){const _0x88544=a0_0x478b15;return this[_0x88544(0xa0)][_0x4389a3]??this[_0x88544(0xa0)][_0x88544(0xbe)];}};export{ProductTokenIssuer,decodeToken,getTokenTTL,isTokenExpiringSoon,issueBatchScopedToken,issueCollaborationToken,issueProjectScopedToken,issueReadToken,issueSessionScopedToken};
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
//#region src/auth/tokens.ts
|
|
3
|
+
/**
|
|
4
|
+
* JWT Token Utilities
|
|
5
|
+
*
|
|
6
|
+
* Token generation and verification using HMAC-SHA256. Server-only
|
|
7
|
+
* (uses Node.js `crypto`).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Base64URL encode (RFC 7515).
|
|
11
|
+
*/
|
|
12
|
+
function base64UrlEncode(data) {
|
|
13
|
+
return (typeof data === "string" ? Buffer.from(data) : data).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Base64URL decode (RFC 7515) to raw bytes. Returns `null` if the
|
|
17
|
+
* input contains characters outside the base64url alphabet.
|
|
18
|
+
*/
|
|
19
|
+
function decodeBase64UrlToBuffer(input) {
|
|
20
|
+
if (!/^[A-Za-z0-9_-]*$/.test(input)) return null;
|
|
21
|
+
const padded = input + "=".repeat((4 - input.length % 4) % 4);
|
|
22
|
+
return Buffer.from(padded.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create HMAC-SHA256 signature.
|
|
26
|
+
*/
|
|
27
|
+
function createSignature(data, secret) {
|
|
28
|
+
return base64UrlEncode(createHmac("sha256", secret).update(data).digest());
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* JWT header (always the same for our use case).
|
|
32
|
+
*/
|
|
33
|
+
const JWT_HEADER = base64UrlEncode(JSON.stringify({
|
|
34
|
+
alg: "HS256",
|
|
35
|
+
typ: "JWT"
|
|
36
|
+
}));
|
|
37
|
+
function issueToken(signingSecret, payload, ttlMinutes) {
|
|
38
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
39
|
+
const fullPayload = {
|
|
40
|
+
...payload,
|
|
41
|
+
iat: now,
|
|
42
|
+
exp: now + ttlMinutes * 60
|
|
43
|
+
};
|
|
44
|
+
const data = `${JWT_HEADER}.${base64UrlEncode(JSON.stringify(fullPayload))}`;
|
|
45
|
+
return `${data}.${createSignature(data, signingSecret)}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Issue a read token (JWT) for WebSocket authentication.
|
|
49
|
+
*
|
|
50
|
+
* @param signingSecret - The product's signing secret
|
|
51
|
+
* @param payload - Token payload (without iat/exp/typ, those are added)
|
|
52
|
+
* @param ttlMinutes - Token TTL in minutes
|
|
53
|
+
*/
|
|
54
|
+
function issueReadToken(signingSecret, payload, ttlMinutes) {
|
|
55
|
+
return issueToken(signingSecret, {
|
|
56
|
+
...payload,
|
|
57
|
+
typ: "read"
|
|
58
|
+
}, ttlMinutes);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Issue a session-scoped token (JWT) for WebSocket authentication.
|
|
62
|
+
* Grants access to a single session's events.
|
|
63
|
+
*/
|
|
64
|
+
function issueSessionScopedToken(signingSecret, payload, ttlMinutes) {
|
|
65
|
+
return issueReadToken(signingSecret, payload, ttlMinutes);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Issue a project-scoped token (JWT) for WebSocket authentication.
|
|
69
|
+
* Grants access to all sessions within a single project.
|
|
70
|
+
*/
|
|
71
|
+
function issueProjectScopedToken(signingSecret, payload, ttlMinutes) {
|
|
72
|
+
return issueReadToken(signingSecret, payload, ttlMinutes);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Issue a batch-scoped token (JWT) for WebSocket authentication.
|
|
76
|
+
* Grants access to multiple projects (organization-level access).
|
|
77
|
+
*/
|
|
78
|
+
function issueBatchScopedToken(signingSecret, payload, ttlMinutes) {
|
|
79
|
+
return issueReadToken(signingSecret, payload, ttlMinutes);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Issue a collaboration-scoped token (JWT) for collaborative document access.
|
|
83
|
+
* Grants read or write access to a single document in one project.
|
|
84
|
+
*/
|
|
85
|
+
function issueCollaborationToken(signingSecret, payload, ttlMinutes) {
|
|
86
|
+
return issueToken(signingSecret, {
|
|
87
|
+
sub: payload.userId,
|
|
88
|
+
sid: payload.sessionId,
|
|
89
|
+
pid: payload.productId,
|
|
90
|
+
cid: payload.sandboxId,
|
|
91
|
+
typ: "collaboration",
|
|
92
|
+
projectId: payload.projectId,
|
|
93
|
+
documentId: payload.documentId,
|
|
94
|
+
access: payload.access
|
|
95
|
+
}, ttlMinutes);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Decode a JWT **without verifying its signature**.
|
|
99
|
+
*
|
|
100
|
+
* The deliberately scary name is the API contract: an HMAC-signed token
|
|
101
|
+
* whose signature has not been verified is a self-asserted blob of JSON,
|
|
102
|
+
* not an authenticated claim. Treating its fields as authoritative
|
|
103
|
+
* (e.g. `if (unsafeDecodeToken(t).sub === userId) grantAccess()`) is a
|
|
104
|
+
* straightforward authorization bypass — an attacker can mint any
|
|
105
|
+
* payload they like.
|
|
106
|
+
*
|
|
107
|
+
* Use this only when the signature has already been validated upstream
|
|
108
|
+
* (e.g. by an API gateway that strips the token after verification),
|
|
109
|
+
* for client-side `exp` peeking to decide whether to refresh, or for
|
|
110
|
+
* routing/logging keyed off non-security-sensitive claims.
|
|
111
|
+
*
|
|
112
|
+
* For any access-control decision, use {@link verifyToken} instead.
|
|
113
|
+
*
|
|
114
|
+
* Returns `null` if the token is malformed.
|
|
115
|
+
*/
|
|
116
|
+
function unsafeDecodeToken(token) {
|
|
117
|
+
try {
|
|
118
|
+
const parts = token.split(".");
|
|
119
|
+
if (parts.length !== 3) return null;
|
|
120
|
+
const padded = parts[1] + "=".repeat((4 - parts[1].length % 4) % 4);
|
|
121
|
+
const decoded = Buffer.from(padded.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString();
|
|
122
|
+
return JSON.parse(decoded);
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Verify a JWT's HMAC-SHA256 signature against `signingSecret` and
|
|
129
|
+
* check that it has not expired. Returns the decoded payload on
|
|
130
|
+
* success, or `null` on any failure (malformed token, bad signature,
|
|
131
|
+
* expired, or unexpected algorithm).
|
|
132
|
+
*
|
|
133
|
+
* Signature comparison is constant-time. Callers must use this — not
|
|
134
|
+
* {@link unsafeDecodeToken} — for any authorization decision.
|
|
135
|
+
*
|
|
136
|
+
* @param token - The JWT to verify
|
|
137
|
+
* @param signingSecret - The same secret used to issue the token
|
|
138
|
+
* @param options.clockSkewSeconds - Tolerance for `exp` checks; default `0`
|
|
139
|
+
*/
|
|
140
|
+
function verifyToken(token, signingSecret, options = {}) {
|
|
141
|
+
try {
|
|
142
|
+
const parts = token.split(".");
|
|
143
|
+
if (parts.length !== 3) return null;
|
|
144
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
145
|
+
if (!headerB64 || !payloadB64 || !signatureB64) return null;
|
|
146
|
+
let header;
|
|
147
|
+
try {
|
|
148
|
+
const headerPadded = headerB64 + "=".repeat((4 - headerB64.length % 4) % 4);
|
|
149
|
+
const headerJson = Buffer.from(headerPadded.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString();
|
|
150
|
+
header = JSON.parse(headerJson);
|
|
151
|
+
} catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
if (header.alg !== "HS256") return null;
|
|
155
|
+
const expectedSig = createSignature(`${headerB64}.${payloadB64}`, signingSecret);
|
|
156
|
+
const providedRaw = decodeBase64UrlToBuffer(signatureB64);
|
|
157
|
+
const expectedRaw = decodeBase64UrlToBuffer(expectedSig);
|
|
158
|
+
if (!providedRaw || !expectedRaw) return null;
|
|
159
|
+
if (providedRaw.length !== expectedRaw.length) return null;
|
|
160
|
+
if (!timingSafeEqual(providedRaw, expectedRaw)) return null;
|
|
161
|
+
const payload = unsafeDecodeToken(token);
|
|
162
|
+
if (!payload) return null;
|
|
163
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
164
|
+
const skew = Math.max(0, options.clockSkewSeconds ?? 0);
|
|
165
|
+
if (typeof payload.exp !== "number" || payload.exp + skew < now) return null;
|
|
166
|
+
return payload;
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get time until token expires (in seconds).
|
|
173
|
+
* Returns negative if expired.
|
|
174
|
+
*/
|
|
175
|
+
function getTokenTTL(payload) {
|
|
176
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
177
|
+
return payload.exp - now;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Check if token is expiring soon (within buffer seconds).
|
|
181
|
+
*/
|
|
182
|
+
function isTokenExpiringSoon(payload, bufferSeconds = 60) {
|
|
183
|
+
return getTokenTTL(payload) <= bufferSeconds;
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/auth/index.ts
|
|
187
|
+
/**
|
|
188
|
+
* Authentication Utilities
|
|
189
|
+
*
|
|
190
|
+
* Token issuance for application backends. Server-only (uses Node.js crypto).
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```typescript
|
|
194
|
+
* import { ProductTokenIssuer } from "@tangle-network/sandbox/auth";
|
|
195
|
+
*
|
|
196
|
+
* const issuer = new ProductTokenIssuer({
|
|
197
|
+
* productId: "my-product",
|
|
198
|
+
* signingSecret: process.env.SANDBOX_SIGNING_SECRET!,
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* const { token, expiresAt } = issuer.issue({
|
|
202
|
+
* userId: "user_123",
|
|
203
|
+
* sessionId: "sess_abc",
|
|
204
|
+
* tier: "pro",
|
|
205
|
+
* });
|
|
206
|
+
* ```
|
|
207
|
+
*
|
|
208
|
+
* @packageDocumentation
|
|
209
|
+
*/
|
|
210
|
+
/**
|
|
211
|
+
* Token issuer for application backend services.
|
|
212
|
+
*
|
|
213
|
+
* Use this in your backend to issue read tokens for WebSocket connections.
|
|
214
|
+
*/
|
|
215
|
+
var ProductTokenIssuer = class {
|
|
216
|
+
productId;
|
|
217
|
+
signingSecret;
|
|
218
|
+
ttlMinutes;
|
|
219
|
+
constructor(config) {
|
|
220
|
+
this.productId = config.productId;
|
|
221
|
+
this.signingSecret = config.signingSecret;
|
|
222
|
+
this.ttlMinutes = {
|
|
223
|
+
free: config.ttlMinutes?.free ?? 15,
|
|
224
|
+
pro: config.ttlMinutes?.pro ?? 240,
|
|
225
|
+
enterprise: config.ttlMinutes?.enterprise ?? 480
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Issue a read token for a user session.
|
|
230
|
+
*/
|
|
231
|
+
issue(params) {
|
|
232
|
+
const tier = params.tier ?? "free";
|
|
233
|
+
const ttl = this.ttlMinutes[tier] ?? this.ttlMinutes.free;
|
|
234
|
+
return {
|
|
235
|
+
token: issueReadToken(this.signingSecret, {
|
|
236
|
+
sub: params.userId,
|
|
237
|
+
sid: params.sessionId,
|
|
238
|
+
pid: this.productId,
|
|
239
|
+
cid: params.sandboxId
|
|
240
|
+
}, ttl),
|
|
241
|
+
expiresAt: Math.floor(Date.now() / 1e3) + ttl * 60
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Issue a collaboration token for a single document.
|
|
246
|
+
*/
|
|
247
|
+
issueCollaboration(params) {
|
|
248
|
+
const tier = params.tier ?? "free";
|
|
249
|
+
const ttl = this.ttlMinutes[tier] ?? this.ttlMinutes.free;
|
|
250
|
+
return {
|
|
251
|
+
token: issueCollaborationToken(this.signingSecret, {
|
|
252
|
+
userId: params.userId,
|
|
253
|
+
sessionId: params.sessionId,
|
|
254
|
+
productId: this.productId,
|
|
255
|
+
projectId: params.projectId,
|
|
256
|
+
documentId: params.documentId,
|
|
257
|
+
access: params.access,
|
|
258
|
+
sandboxId: params.sandboxId
|
|
259
|
+
}, ttl),
|
|
260
|
+
expiresAt: Math.floor(Date.now() / 1e3) + ttl * 60
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get the TTL in minutes for a tier.
|
|
265
|
+
*/
|
|
266
|
+
getTtlMinutes(tier = "free") {
|
|
267
|
+
return this.ttlMinutes[tier] ?? this.ttlMinutes.free;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
//#endregion
|
|
271
|
+
export { ProductTokenIssuer, getTokenTTL, isTokenExpiringSoon, issueBatchScopedToken, issueCollaborationToken, issueProjectScopedToken, issueReadToken, issueSessionScopedToken, unsafeDecodeToken, verifyToken };
|