@yeying-community/web3-bs 1.0.5 → 1.0.7
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 +113 -77
- package/dist/auth/central.d.ts +77 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/ucan.d.ts +18 -2
- package/dist/dapp.d.ts +8 -0
- package/dist/web3-bs.esm.js +633 -46
- package/dist/web3-bs.esm.js.map +1 -1
- package/dist/web3-bs.umd.js +647 -45
- package/dist/web3-bs.umd.js.map +1 -1
- package/package.json +5 -3
package/dist/web3-bs.esm.js
CHANGED
|
@@ -193,12 +193,12 @@ function onChainChanged(provider, handler) {
|
|
|
193
193
|
return () => provider.removeListener?.('chainChanged', handler);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
function normalizeBaseUrl$
|
|
196
|
+
function normalizeBaseUrl$2(baseUrl) {
|
|
197
197
|
return baseUrl.replace(/\/+$/, '');
|
|
198
198
|
}
|
|
199
|
-
function joinUrl$
|
|
199
|
+
function joinUrl$2(baseUrl, path) {
|
|
200
200
|
const trimmed = path.replace(/^\/+/, '');
|
|
201
|
-
return `${normalizeBaseUrl$
|
|
201
|
+
return `${normalizeBaseUrl$2(baseUrl)}/${trimmed}`;
|
|
202
202
|
}
|
|
203
203
|
const DEFAULT_TOKEN_KEY = 'authToken';
|
|
204
204
|
let cachedAccessToken = null;
|
|
@@ -209,10 +209,10 @@ function resolveTokenKey(options) {
|
|
|
209
209
|
function shouldStoreToken(options) {
|
|
210
210
|
return options?.storeToken !== false;
|
|
211
211
|
}
|
|
212
|
-
function resolveFetcher(options) {
|
|
212
|
+
function resolveFetcher$1(options) {
|
|
213
213
|
return options?.fetcher || fetch;
|
|
214
214
|
}
|
|
215
|
-
function resolveCredentials(options) {
|
|
215
|
+
function resolveCredentials$1(options) {
|
|
216
216
|
return options?.credentials ?? 'include';
|
|
217
217
|
}
|
|
218
218
|
function readStoredToken(options) {
|
|
@@ -352,11 +352,11 @@ async function signMessage(options) {
|
|
|
352
352
|
async function loginWithChallenge(options = {}) {
|
|
353
353
|
const provider = options.provider || (await requireProvider());
|
|
354
354
|
const address = await resolveAddress$1(provider, options.address);
|
|
355
|
-
const fetcher = resolveFetcher(options);
|
|
356
|
-
const credentials = resolveCredentials(options);
|
|
355
|
+
const fetcher = resolveFetcher$1(options);
|
|
356
|
+
const credentials = resolveCredentials$1(options);
|
|
357
357
|
const baseUrl = options.baseUrl || '/api/v1/public/auth';
|
|
358
|
-
const challengeUrl = joinUrl$
|
|
359
|
-
const verifyUrl = joinUrl$
|
|
358
|
+
const challengeUrl = joinUrl$2(baseUrl, options.challengePath || 'challenge');
|
|
359
|
+
const verifyUrl = joinUrl$2(baseUrl, options.verifyPath || 'verify');
|
|
360
360
|
const challengeBody = {
|
|
361
361
|
address,
|
|
362
362
|
};
|
|
@@ -420,10 +420,10 @@ async function refreshAccessToken(options = {}) {
|
|
|
420
420
|
return refreshInFlight;
|
|
421
421
|
}
|
|
422
422
|
const task = (async () => {
|
|
423
|
-
const fetcher = resolveFetcher(options);
|
|
424
|
-
const credentials = resolveCredentials(options);
|
|
423
|
+
const fetcher = resolveFetcher$1(options);
|
|
424
|
+
const credentials = resolveCredentials$1(options);
|
|
425
425
|
const baseUrl = options.baseUrl || '/api/v1/public/auth';
|
|
426
|
-
const refreshUrl = joinUrl$
|
|
426
|
+
const refreshUrl = joinUrl$2(baseUrl, options.refreshPath || 'refresh');
|
|
427
427
|
const refreshRes = await fetcher(refreshUrl, {
|
|
428
428
|
method: 'POST',
|
|
429
429
|
headers: {
|
|
@@ -452,10 +452,10 @@ async function refreshAccessToken(options = {}) {
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
async function logout(options = {}) {
|
|
455
|
-
const fetcher = resolveFetcher(options);
|
|
456
|
-
const credentials = resolveCredentials(options);
|
|
455
|
+
const fetcher = resolveFetcher$1(options);
|
|
456
|
+
const credentials = resolveCredentials$1(options);
|
|
457
457
|
const baseUrl = options.baseUrl || '/api/v1/public/auth';
|
|
458
|
-
const logoutUrl = joinUrl$
|
|
458
|
+
const logoutUrl = joinUrl$2(baseUrl, options.logoutPath || 'logout');
|
|
459
459
|
const logoutRes = await fetcher(logoutUrl, {
|
|
460
460
|
method: 'POST',
|
|
461
461
|
headers: {
|
|
@@ -478,8 +478,8 @@ async function logout(options = {}) {
|
|
|
478
478
|
return { response: payload };
|
|
479
479
|
}
|
|
480
480
|
async function authFetch(input, init = {}, options = {}) {
|
|
481
|
-
const fetcher = resolveFetcher(options);
|
|
482
|
-
const credentials = resolveCredentials(options);
|
|
481
|
+
const fetcher = resolveFetcher$1(options);
|
|
482
|
+
const credentials = resolveCredentials$1(options);
|
|
483
483
|
const retryOnUnauthorized = options.retryOnUnauthorized !== false;
|
|
484
484
|
const performRequest = async (tokenOverride) => {
|
|
485
485
|
const headers = new Headers(init.headers || {});
|
|
@@ -511,6 +511,8 @@ const DEFAULT_SESSION_TTL = 24 * 60 * 60 * 1000;
|
|
|
511
511
|
const DEFAULT_UCAN_TTL = 5 * 60 * 1000;
|
|
512
512
|
const DB_NAME = 'yeying-web3';
|
|
513
513
|
const DB_STORE = 'ucan-sessions';
|
|
514
|
+
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
515
|
+
const DID_KEY_ED25519_MULTICODEC = new Uint8Array([0xed, 0x01]);
|
|
514
516
|
const textEncoder = new TextEncoder();
|
|
515
517
|
function toBase64Url(data) {
|
|
516
518
|
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
@@ -520,6 +522,70 @@ function toBase64Url(data) {
|
|
|
520
522
|
}
|
|
521
523
|
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
522
524
|
}
|
|
525
|
+
function normalizeActionExpression(raw) {
|
|
526
|
+
const normalized = String(raw || '').trim().toLowerCase().replace(/\|/g, ',');
|
|
527
|
+
if (!normalized)
|
|
528
|
+
return '';
|
|
529
|
+
const parts = normalized
|
|
530
|
+
.split(',')
|
|
531
|
+
.map(part => part.trim())
|
|
532
|
+
.filter(Boolean);
|
|
533
|
+
if (!parts.length)
|
|
534
|
+
return '';
|
|
535
|
+
return Array.from(new Set(parts)).join(',');
|
|
536
|
+
}
|
|
537
|
+
function getCapabilityResource(cap) {
|
|
538
|
+
if (!cap || typeof cap !== 'object')
|
|
539
|
+
return '';
|
|
540
|
+
const withValue = typeof cap.with === 'string' ? cap.with.trim() : '';
|
|
541
|
+
if (withValue)
|
|
542
|
+
return withValue;
|
|
543
|
+
return typeof cap.resource === 'string' ? cap.resource.trim() : '';
|
|
544
|
+
}
|
|
545
|
+
function getCapabilityAction(cap) {
|
|
546
|
+
if (!cap || typeof cap !== 'object')
|
|
547
|
+
return '';
|
|
548
|
+
const canValue = typeof cap.can === 'string' ? cap.can.trim() : '';
|
|
549
|
+
if (canValue)
|
|
550
|
+
return normalizeActionExpression(canValue);
|
|
551
|
+
const actionValue = typeof cap.action === 'string' ? cap.action.trim() : '';
|
|
552
|
+
return normalizeActionExpression(actionValue);
|
|
553
|
+
}
|
|
554
|
+
function normalizeUcanCapability(cap, options = {}) {
|
|
555
|
+
const includeLegacyAliases = options.includeLegacyAliases !== false;
|
|
556
|
+
const resource = getCapabilityResource(cap);
|
|
557
|
+
const action = getCapabilityAction(cap);
|
|
558
|
+
if (!resource || !action)
|
|
559
|
+
return null;
|
|
560
|
+
const normalized = {
|
|
561
|
+
with: resource,
|
|
562
|
+
can: action,
|
|
563
|
+
};
|
|
564
|
+
if (includeLegacyAliases) {
|
|
565
|
+
normalized.resource = resource;
|
|
566
|
+
normalized.action = action;
|
|
567
|
+
}
|
|
568
|
+
if (cap && Object.prototype.hasOwnProperty.call(cap, 'nb')) {
|
|
569
|
+
normalized.nb = cap.nb;
|
|
570
|
+
}
|
|
571
|
+
return normalized;
|
|
572
|
+
}
|
|
573
|
+
function normalizeUcanCapabilities(caps, options = {}) {
|
|
574
|
+
const includeLegacyAliases = options.includeLegacyAliases !== false;
|
|
575
|
+
const seen = new Set();
|
|
576
|
+
const result = [];
|
|
577
|
+
for (const cap of caps || []) {
|
|
578
|
+
const normalized = normalizeUcanCapability(cap, { includeLegacyAliases });
|
|
579
|
+
if (!normalized)
|
|
580
|
+
continue;
|
|
581
|
+
const key = `${normalized.with}|${normalized.can}`;
|
|
582
|
+
if (seen.has(key))
|
|
583
|
+
continue;
|
|
584
|
+
seen.add(key);
|
|
585
|
+
result.push(normalized);
|
|
586
|
+
}
|
|
587
|
+
return result;
|
|
588
|
+
}
|
|
523
589
|
function encodeJson(value) {
|
|
524
590
|
return toBase64Url(textEncoder.encode(JSON.stringify(value)));
|
|
525
591
|
}
|
|
@@ -533,6 +599,123 @@ function randomNonce(bytes = 16) {
|
|
|
533
599
|
function normalizeExpiry(exp, fallbackMs) {
|
|
534
600
|
return Date.now() + fallbackMs;
|
|
535
601
|
}
|
|
602
|
+
function isSessionExpired(expiresAt, nowMs = Date.now()) {
|
|
603
|
+
return typeof expiresAt === 'number' && nowMs >= expiresAt;
|
|
604
|
+
}
|
|
605
|
+
function encodeBase58(bytes) {
|
|
606
|
+
if (bytes.length === 0)
|
|
607
|
+
return '';
|
|
608
|
+
let value = 0n;
|
|
609
|
+
for (const byte of bytes) {
|
|
610
|
+
value = (value << 8n) + BigInt(byte);
|
|
611
|
+
}
|
|
612
|
+
let encoded = '';
|
|
613
|
+
while (value > 0n) {
|
|
614
|
+
const mod = Number(value % 58n);
|
|
615
|
+
encoded = `${BASE58_ALPHABET[mod]}${encoded}`;
|
|
616
|
+
value /= 58n;
|
|
617
|
+
}
|
|
618
|
+
let leadingZeroCount = 0;
|
|
619
|
+
while (leadingZeroCount < bytes.length && bytes[leadingZeroCount] === 0) {
|
|
620
|
+
leadingZeroCount += 1;
|
|
621
|
+
}
|
|
622
|
+
if (leadingZeroCount > 0) {
|
|
623
|
+
encoded = `${'1'.repeat(leadingZeroCount)}${encoded}`;
|
|
624
|
+
}
|
|
625
|
+
return encoded || '1';
|
|
626
|
+
}
|
|
627
|
+
function ensureWebCrypto() {
|
|
628
|
+
if (typeof crypto === 'undefined' || !crypto.subtle) {
|
|
629
|
+
throw new Error('WebCrypto not available for UCAN session');
|
|
630
|
+
}
|
|
631
|
+
return crypto;
|
|
632
|
+
}
|
|
633
|
+
function parseSessionId(options) {
|
|
634
|
+
return options.id || DEFAULT_SESSION_ID;
|
|
635
|
+
}
|
|
636
|
+
function isLocalSessionRecord(record) {
|
|
637
|
+
return Boolean(record?.source === 'local' || record?.privateKeyJwk);
|
|
638
|
+
}
|
|
639
|
+
async function buildDidKey(publicKey) {
|
|
640
|
+
const webCrypto = ensureWebCrypto();
|
|
641
|
+
const raw = new Uint8Array(await webCrypto.subtle.exportKey('raw', publicKey));
|
|
642
|
+
const prefixed = new Uint8Array(DID_KEY_ED25519_MULTICODEC.length + raw.length);
|
|
643
|
+
prefixed.set(DID_KEY_ED25519_MULTICODEC, 0);
|
|
644
|
+
prefixed.set(raw, DID_KEY_ED25519_MULTICODEC.length);
|
|
645
|
+
return `did:key:z${encodeBase58(prefixed)}`;
|
|
646
|
+
}
|
|
647
|
+
async function importLocalPrivateKey(privateKeyJwk) {
|
|
648
|
+
const webCrypto = ensureWebCrypto();
|
|
649
|
+
return await webCrypto.subtle.importKey('jwk', privateKeyJwk, 'Ed25519', true, ['sign']);
|
|
650
|
+
}
|
|
651
|
+
async function loadLocalSessionFromRecord(id, record) {
|
|
652
|
+
if (!record || !isLocalSessionRecord(record) || !record.privateKeyJwk) {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
if (isSessionExpired(record.expiresAt)) {
|
|
656
|
+
await deleteSessionRecord(id);
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
try {
|
|
660
|
+
const privateKey = await importLocalPrivateKey(record.privateKeyJwk);
|
|
661
|
+
return {
|
|
662
|
+
id: record.id || id,
|
|
663
|
+
did: record.did,
|
|
664
|
+
createdAt: record.createdAt,
|
|
665
|
+
expiresAt: record.expiresAt,
|
|
666
|
+
source: 'local',
|
|
667
|
+
privateKey,
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function shouldKeepRootForSession(root, did, nowMs) {
|
|
675
|
+
if (!root)
|
|
676
|
+
return false;
|
|
677
|
+
if (root.aud && root.aud !== did)
|
|
678
|
+
return false;
|
|
679
|
+
if (isRootExpired(root, nowMs))
|
|
680
|
+
return false;
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
async function createLocalSession(options, record) {
|
|
684
|
+
const webCrypto = ensureWebCrypto();
|
|
685
|
+
const sessionId = parseSessionId(options);
|
|
686
|
+
if (!options.forceNew) {
|
|
687
|
+
const existing = await loadLocalSessionFromRecord(sessionId, record);
|
|
688
|
+
if (existing)
|
|
689
|
+
return existing;
|
|
690
|
+
}
|
|
691
|
+
const pair = (await webCrypto.subtle.generateKey('Ed25519', true, ['sign', 'verify']));
|
|
692
|
+
const [privateKeyJwk, publicKeyJwk, did] = await Promise.all([
|
|
693
|
+
webCrypto.subtle.exportKey('jwk', pair.privateKey),
|
|
694
|
+
webCrypto.subtle.exportKey('jwk', pair.publicKey),
|
|
695
|
+
buildDidKey(pair.publicKey),
|
|
696
|
+
]);
|
|
697
|
+
const createdAt = Date.now();
|
|
698
|
+
const expiresAt = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_SESSION_TTL);
|
|
699
|
+
const root = shouldKeepRootForSession(record?.root, did, createdAt) ? record?.root : undefined;
|
|
700
|
+
await writeSessionRecord({
|
|
701
|
+
id: sessionId,
|
|
702
|
+
did,
|
|
703
|
+
createdAt,
|
|
704
|
+
expiresAt,
|
|
705
|
+
source: 'local',
|
|
706
|
+
privateKeyJwk,
|
|
707
|
+
publicKeyJwk,
|
|
708
|
+
root,
|
|
709
|
+
});
|
|
710
|
+
return {
|
|
711
|
+
id: sessionId,
|
|
712
|
+
did,
|
|
713
|
+
createdAt,
|
|
714
|
+
expiresAt,
|
|
715
|
+
source: 'local',
|
|
716
|
+
privateKey: pair.privateKey,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
536
719
|
function openDb() {
|
|
537
720
|
if (typeof indexedDB === 'undefined') {
|
|
538
721
|
return Promise.reject(new Error('IndexedDB not available'));
|
|
@@ -595,17 +778,19 @@ async function deleteSessionRecord(id) {
|
|
|
595
778
|
}
|
|
596
779
|
}
|
|
597
780
|
async function getUcanSession(id = DEFAULT_SESSION_ID, provider) {
|
|
781
|
+
const record = await readSessionRecord(id);
|
|
598
782
|
const walletProvider = provider || (typeof window !== 'undefined'
|
|
599
783
|
? await getProvider({ preferYeYing: true })
|
|
600
784
|
: null);
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
785
|
+
if (walletProvider) {
|
|
786
|
+
try {
|
|
787
|
+
return await requestWalletUcanSession(walletProvider, { id });
|
|
788
|
+
}
|
|
789
|
+
catch {
|
|
790
|
+
return await loadLocalSessionFromRecord(id, record);
|
|
791
|
+
}
|
|
608
792
|
}
|
|
793
|
+
return await loadLocalSessionFromRecord(id, record);
|
|
609
794
|
}
|
|
610
795
|
async function requestWalletUcanSession(provider, options) {
|
|
611
796
|
const sessionId = options.id || DEFAULT_SESSION_ID;
|
|
@@ -630,6 +815,7 @@ async function requestWalletUcanSession(provider, options) {
|
|
|
630
815
|
did: result.did,
|
|
631
816
|
createdAt,
|
|
632
817
|
expiresAt,
|
|
818
|
+
source: 'wallet',
|
|
633
819
|
root: existing?.root,
|
|
634
820
|
};
|
|
635
821
|
if (nextRecord.root && nextRecord.root.aud && nextRecord.root.aud !== nextRecord.did) {
|
|
@@ -641,6 +827,7 @@ async function requestWalletUcanSession(provider, options) {
|
|
|
641
827
|
did: result.did,
|
|
642
828
|
createdAt,
|
|
643
829
|
expiresAt,
|
|
830
|
+
source: 'wallet',
|
|
644
831
|
signer: async (signingInput, payload) => {
|
|
645
832
|
const signatureResult = (await provider.request({
|
|
646
833
|
method: 'yeying_ucan_sign',
|
|
@@ -663,13 +850,20 @@ async function requestWalletUcanSession(provider, options) {
|
|
|
663
850
|
};
|
|
664
851
|
}
|
|
665
852
|
async function createUcanSession(options = {}) {
|
|
853
|
+
const sessionId = parseSessionId(options);
|
|
854
|
+
const record = await readSessionRecord(sessionId);
|
|
666
855
|
const provider = options.provider || (typeof window !== 'undefined'
|
|
667
856
|
? await getProvider({ preferYeYing: true })
|
|
668
857
|
: null);
|
|
669
|
-
if (
|
|
670
|
-
|
|
858
|
+
if (provider) {
|
|
859
|
+
try {
|
|
860
|
+
return await requestWalletUcanSession(provider, { ...options, id: sessionId });
|
|
861
|
+
}
|
|
862
|
+
catch {
|
|
863
|
+
// fallback to local ed25519 session
|
|
864
|
+
}
|
|
671
865
|
}
|
|
672
|
-
return await
|
|
866
|
+
return await createLocalSession({ ...options, id: sessionId }, record);
|
|
673
867
|
}
|
|
674
868
|
async function clearUcanSession(id = DEFAULT_SESSION_ID) {
|
|
675
869
|
await deleteSessionRecord(id);
|
|
@@ -680,6 +874,7 @@ async function storeUcanRoot(root, id = DEFAULT_SESSION_ID) {
|
|
|
680
874
|
const expiresAt = record?.expiresAt ?? null;
|
|
681
875
|
const did = record?.did || root.aud;
|
|
682
876
|
const nextRecord = {
|
|
877
|
+
...(record || {}),
|
|
683
878
|
id,
|
|
684
879
|
did,
|
|
685
880
|
createdAt,
|
|
@@ -693,7 +888,9 @@ async function getStoredUcanRoot(id = DEFAULT_SESSION_ID) {
|
|
|
693
888
|
return record?.root || null;
|
|
694
889
|
}
|
|
695
890
|
function capsEqual(a, b) {
|
|
696
|
-
|
|
891
|
+
const left = normalizeUcanCapabilities(a, { includeLegacyAliases: false });
|
|
892
|
+
const right = normalizeUcanCapabilities(b, { includeLegacyAliases: false });
|
|
893
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
697
894
|
}
|
|
698
895
|
function isRootExpired(root, nowMs) {
|
|
699
896
|
return Boolean(root.exp && nowMs > root.exp);
|
|
@@ -768,9 +965,13 @@ async function createRootUcan(options) {
|
|
|
768
965
|
const nonce = options.nonce || randomNonce(8);
|
|
769
966
|
const exp = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_SESSION_TTL);
|
|
770
967
|
const nbf = options.notBeforeMs;
|
|
968
|
+
const normalizedCapabilities = normalizeUcanCapabilities(options.capabilities);
|
|
969
|
+
if (!normalizedCapabilities.length) {
|
|
970
|
+
throw new Error('Missing UCAN capabilities');
|
|
971
|
+
}
|
|
771
972
|
const statementPayload = {
|
|
772
973
|
aud: session.did,
|
|
773
|
-
cap:
|
|
974
|
+
cap: normalizedCapabilities,
|
|
774
975
|
exp,
|
|
775
976
|
};
|
|
776
977
|
if (nbf)
|
|
@@ -793,7 +994,7 @@ async function createRootUcan(options) {
|
|
|
793
994
|
type: 'siwe',
|
|
794
995
|
iss: `did:pkh:eth:${address.toLowerCase()}`,
|
|
795
996
|
aud: session.did,
|
|
796
|
-
cap:
|
|
997
|
+
cap: normalizedCapabilities,
|
|
797
998
|
exp,
|
|
798
999
|
nbf,
|
|
799
1000
|
siwe: {
|
|
@@ -842,11 +1043,15 @@ async function createDelegationUcan(options) {
|
|
|
842
1043
|
}));
|
|
843
1044
|
if (!issuer)
|
|
844
1045
|
throw new Error('Missing UCAN session key');
|
|
1046
|
+
const normalizedCapabilities = normalizeUcanCapabilities(options.capabilities);
|
|
1047
|
+
if (!normalizedCapabilities.length) {
|
|
1048
|
+
throw new Error('Missing UCAN capabilities');
|
|
1049
|
+
}
|
|
845
1050
|
const exp = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_UCAN_TTL);
|
|
846
1051
|
const payload = {
|
|
847
1052
|
iss: issuer.did,
|
|
848
1053
|
aud: options.audience,
|
|
849
|
-
cap:
|
|
1054
|
+
cap: normalizedCapabilities,
|
|
850
1055
|
exp,
|
|
851
1056
|
nbf: options.notBeforeMs,
|
|
852
1057
|
prf: await resolveProofs(options, issuer),
|
|
@@ -860,11 +1065,15 @@ async function createInvocationUcan(options) {
|
|
|
860
1065
|
}));
|
|
861
1066
|
if (!issuer)
|
|
862
1067
|
throw new Error('Missing UCAN session key');
|
|
1068
|
+
const normalizedCapabilities = normalizeUcanCapabilities(options.capabilities);
|
|
1069
|
+
if (!normalizedCapabilities.length) {
|
|
1070
|
+
throw new Error('Missing UCAN capabilities');
|
|
1071
|
+
}
|
|
863
1072
|
const exp = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_UCAN_TTL);
|
|
864
1073
|
const payload = {
|
|
865
1074
|
iss: issuer.did,
|
|
866
1075
|
aud: options.audience,
|
|
867
|
-
cap:
|
|
1076
|
+
cap: normalizedCapabilities,
|
|
868
1077
|
exp,
|
|
869
1078
|
nbf: options.notBeforeMs,
|
|
870
1079
|
prf: await resolveProofs(options, issuer),
|
|
@@ -897,6 +1106,314 @@ async function authUcanFetch(input, init = {}, options = {}) {
|
|
|
897
1106
|
});
|
|
898
1107
|
}
|
|
899
1108
|
|
|
1109
|
+
const DEFAULT_BASE_URL = '/api/v1/public/auth/central';
|
|
1110
|
+
const DEFAULT_ISSUER_PATH = 'issuer';
|
|
1111
|
+
const DEFAULT_SESSION_PATH = 'session';
|
|
1112
|
+
const DEFAULT_ISSUE_PATH = 'issue';
|
|
1113
|
+
const DEFAULT_SESSION_TOKEN_KEY = 'centralUcanSessionToken';
|
|
1114
|
+
let cachedCentralSessionToken = null;
|
|
1115
|
+
function normalizeBaseUrl$1(baseUrl) {
|
|
1116
|
+
return baseUrl.replace(/\/+$/, '');
|
|
1117
|
+
}
|
|
1118
|
+
function joinUrl$1(baseUrl, path) {
|
|
1119
|
+
const trimmed = path.replace(/^\/+/, '');
|
|
1120
|
+
return `${normalizeBaseUrl$1(baseUrl)}/${trimmed}`;
|
|
1121
|
+
}
|
|
1122
|
+
function resolveBaseUrl(options) {
|
|
1123
|
+
return options?.baseUrl || DEFAULT_BASE_URL;
|
|
1124
|
+
}
|
|
1125
|
+
function resolveFetcher(options) {
|
|
1126
|
+
return options?.fetcher || fetch;
|
|
1127
|
+
}
|
|
1128
|
+
function resolveCredentials(options) {
|
|
1129
|
+
return options?.credentials ?? 'include';
|
|
1130
|
+
}
|
|
1131
|
+
function resolveSessionTokenKey(options) {
|
|
1132
|
+
return options?.sessionTokenStorageKey || DEFAULT_SESSION_TOKEN_KEY;
|
|
1133
|
+
}
|
|
1134
|
+
function resolveAccessToken(options) {
|
|
1135
|
+
if (typeof options?.accessToken === 'string') {
|
|
1136
|
+
const token = options.accessToken.trim();
|
|
1137
|
+
return token || null;
|
|
1138
|
+
}
|
|
1139
|
+
if (options?.accessToken === null) {
|
|
1140
|
+
return null;
|
|
1141
|
+
}
|
|
1142
|
+
return getAccessToken(options);
|
|
1143
|
+
}
|
|
1144
|
+
function shouldStoreSessionToken(options) {
|
|
1145
|
+
return options?.storeSessionToken !== false;
|
|
1146
|
+
}
|
|
1147
|
+
function parseObject(value) {
|
|
1148
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1149
|
+
return {};
|
|
1150
|
+
}
|
|
1151
|
+
return value;
|
|
1152
|
+
}
|
|
1153
|
+
function parseEnvelopeData(payload) {
|
|
1154
|
+
const root = parseObject(payload);
|
|
1155
|
+
if (Object.prototype.hasOwnProperty.call(root, 'data')) {
|
|
1156
|
+
return parseObject(root.data);
|
|
1157
|
+
}
|
|
1158
|
+
return root;
|
|
1159
|
+
}
|
|
1160
|
+
function parseStringField(obj, keys) {
|
|
1161
|
+
for (const key of keys) {
|
|
1162
|
+
const value = obj[key];
|
|
1163
|
+
if (typeof value === 'string') {
|
|
1164
|
+
return value;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
return undefined;
|
|
1168
|
+
}
|
|
1169
|
+
function parseNumberField(obj, keys) {
|
|
1170
|
+
for (const key of keys) {
|
|
1171
|
+
const value = obj[key];
|
|
1172
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1173
|
+
return value;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
return undefined;
|
|
1177
|
+
}
|
|
1178
|
+
function parseCapabilitiesField(obj, keys) {
|
|
1179
|
+
for (const key of keys) {
|
|
1180
|
+
const value = obj[key];
|
|
1181
|
+
if (!Array.isArray(value))
|
|
1182
|
+
continue;
|
|
1183
|
+
const caps = value
|
|
1184
|
+
.filter(item => item && typeof item === 'object')
|
|
1185
|
+
.map(item => normalizeUcanCapability(item))
|
|
1186
|
+
.filter((cap) => Boolean(cap));
|
|
1187
|
+
return caps;
|
|
1188
|
+
}
|
|
1189
|
+
return undefined;
|
|
1190
|
+
}
|
|
1191
|
+
function parseIssuerDidField(obj) {
|
|
1192
|
+
return parseStringField(obj, ['issuerDid', 'issuer', 'did']);
|
|
1193
|
+
}
|
|
1194
|
+
function readStoredSessionToken(options) {
|
|
1195
|
+
if (!shouldStoreSessionToken(options))
|
|
1196
|
+
return null;
|
|
1197
|
+
if (typeof localStorage === 'undefined')
|
|
1198
|
+
return null;
|
|
1199
|
+
const key = resolveSessionTokenKey(options);
|
|
1200
|
+
return localStorage.getItem(key);
|
|
1201
|
+
}
|
|
1202
|
+
function persistSessionToken(token, options) {
|
|
1203
|
+
cachedCentralSessionToken = token;
|
|
1204
|
+
if (!shouldStoreSessionToken(options))
|
|
1205
|
+
return;
|
|
1206
|
+
if (typeof localStorage === 'undefined')
|
|
1207
|
+
return;
|
|
1208
|
+
const key = resolveSessionTokenKey(options);
|
|
1209
|
+
if (!token) {
|
|
1210
|
+
localStorage.removeItem(key);
|
|
1211
|
+
}
|
|
1212
|
+
else {
|
|
1213
|
+
localStorage.setItem(key, token);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
async function parseJsonBody(response) {
|
|
1217
|
+
const text = await response.text();
|
|
1218
|
+
if (!text)
|
|
1219
|
+
return null;
|
|
1220
|
+
try {
|
|
1221
|
+
return JSON.parse(text);
|
|
1222
|
+
}
|
|
1223
|
+
catch {
|
|
1224
|
+
return { raw: text };
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
function getCentralSessionToken(options) {
|
|
1228
|
+
if (cachedCentralSessionToken)
|
|
1229
|
+
return cachedCentralSessionToken;
|
|
1230
|
+
const stored = readStoredSessionToken(options);
|
|
1231
|
+
if (stored) {
|
|
1232
|
+
cachedCentralSessionToken = stored;
|
|
1233
|
+
}
|
|
1234
|
+
return stored;
|
|
1235
|
+
}
|
|
1236
|
+
function setCentralSessionToken(token, options) {
|
|
1237
|
+
persistSessionToken(token, options);
|
|
1238
|
+
}
|
|
1239
|
+
function clearCentralSessionToken(options) {
|
|
1240
|
+
cachedCentralSessionToken = null;
|
|
1241
|
+
if (typeof localStorage === 'undefined')
|
|
1242
|
+
return;
|
|
1243
|
+
const key = resolveSessionTokenKey(options);
|
|
1244
|
+
localStorage.removeItem(key);
|
|
1245
|
+
}
|
|
1246
|
+
async function getCentralIssuerInfo(options = {}) {
|
|
1247
|
+
const fetcher = resolveFetcher(options);
|
|
1248
|
+
const credentials = resolveCredentials(options);
|
|
1249
|
+
const url = joinUrl$1(resolveBaseUrl(options), options.issuerPath || DEFAULT_ISSUER_PATH);
|
|
1250
|
+
const token = resolveAccessToken(options);
|
|
1251
|
+
const headers = new Headers({
|
|
1252
|
+
accept: 'application/json',
|
|
1253
|
+
});
|
|
1254
|
+
if (token) {
|
|
1255
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
1256
|
+
}
|
|
1257
|
+
const response = await fetcher(url, {
|
|
1258
|
+
method: 'GET',
|
|
1259
|
+
headers,
|
|
1260
|
+
credentials,
|
|
1261
|
+
});
|
|
1262
|
+
const payload = await parseJsonBody(response);
|
|
1263
|
+
if (!response.ok) {
|
|
1264
|
+
throw new Error(`Central issuer request failed: ${response.status} ${JSON.stringify(payload)}`);
|
|
1265
|
+
}
|
|
1266
|
+
const data = parseEnvelopeData(payload);
|
|
1267
|
+
return {
|
|
1268
|
+
enabled: typeof data.enabled === 'boolean' ? data.enabled : undefined,
|
|
1269
|
+
issuerDid: parseIssuerDidField(data),
|
|
1270
|
+
defaultAudience: parseStringField(data, ['defaultAudience']),
|
|
1271
|
+
defaultCapabilities: parseCapabilitiesField(data, ['defaultCapabilities']),
|
|
1272
|
+
response: payload,
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
async function createCentralSession(options) {
|
|
1276
|
+
const subject = String(options?.subject || '').trim();
|
|
1277
|
+
if (!subject) {
|
|
1278
|
+
throw new Error('Missing subject');
|
|
1279
|
+
}
|
|
1280
|
+
const fetcher = resolveFetcher(options);
|
|
1281
|
+
const credentials = resolveCredentials(options);
|
|
1282
|
+
const accessToken = resolveAccessToken(options);
|
|
1283
|
+
const url = joinUrl$1(resolveBaseUrl(options), options.sessionPath || DEFAULT_SESSION_PATH);
|
|
1284
|
+
const headers = new Headers({
|
|
1285
|
+
'Content-Type': 'application/json',
|
|
1286
|
+
accept: 'application/json',
|
|
1287
|
+
});
|
|
1288
|
+
if (accessToken) {
|
|
1289
|
+
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
1290
|
+
}
|
|
1291
|
+
const response = await fetcher(url, {
|
|
1292
|
+
method: 'POST',
|
|
1293
|
+
headers,
|
|
1294
|
+
credentials,
|
|
1295
|
+
body: JSON.stringify({
|
|
1296
|
+
subject,
|
|
1297
|
+
sessionTtlMs: options.sessionTtlMs,
|
|
1298
|
+
}),
|
|
1299
|
+
});
|
|
1300
|
+
const payload = await parseJsonBody(response);
|
|
1301
|
+
if (!response.ok) {
|
|
1302
|
+
throw new Error(`Central session request failed: ${response.status} ${JSON.stringify(payload)}`);
|
|
1303
|
+
}
|
|
1304
|
+
const data = parseEnvelopeData(payload);
|
|
1305
|
+
const sessionToken = parseStringField(data, ['sessionToken']);
|
|
1306
|
+
if (!sessionToken) {
|
|
1307
|
+
throw new Error('Central session response missing sessionToken');
|
|
1308
|
+
}
|
|
1309
|
+
persistSessionToken(sessionToken, options);
|
|
1310
|
+
return {
|
|
1311
|
+
subject: parseStringField(data, ['subject']) || subject,
|
|
1312
|
+
sessionToken,
|
|
1313
|
+
expiresAt: parseNumberField(data, ['expiresAt']),
|
|
1314
|
+
issuerDid: parseIssuerDidField(data),
|
|
1315
|
+
response: payload,
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
async function issueCentralUcan(options = {}) {
|
|
1319
|
+
const sessionToken = options.sessionToken || getCentralSessionToken(options);
|
|
1320
|
+
if (!sessionToken) {
|
|
1321
|
+
throw new Error('Missing central session token');
|
|
1322
|
+
}
|
|
1323
|
+
const normalizedCapabilities = options.capabilities
|
|
1324
|
+
? normalizeUcanCapabilities(options.capabilities)
|
|
1325
|
+
: undefined;
|
|
1326
|
+
const fetcher = resolveFetcher(options);
|
|
1327
|
+
const credentials = resolveCredentials(options);
|
|
1328
|
+
const url = joinUrl$1(resolveBaseUrl(options), options.issuePath || DEFAULT_ISSUE_PATH);
|
|
1329
|
+
const response = await fetcher(url, {
|
|
1330
|
+
method: 'POST',
|
|
1331
|
+
headers: {
|
|
1332
|
+
'Content-Type': 'application/json',
|
|
1333
|
+
accept: 'application/json',
|
|
1334
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
1335
|
+
},
|
|
1336
|
+
credentials,
|
|
1337
|
+
body: JSON.stringify({
|
|
1338
|
+
audience: options.audience,
|
|
1339
|
+
capabilities: normalizedCapabilities,
|
|
1340
|
+
expiresInMs: options.expiresInMs,
|
|
1341
|
+
ttlMs: options.ttlMs,
|
|
1342
|
+
}),
|
|
1343
|
+
});
|
|
1344
|
+
const payload = await parseJsonBody(response);
|
|
1345
|
+
if (!response.ok) {
|
|
1346
|
+
throw new Error(`Central UCAN issue failed: ${response.status} ${JSON.stringify(payload)}`);
|
|
1347
|
+
}
|
|
1348
|
+
const data = parseEnvelopeData(payload);
|
|
1349
|
+
const ucan = parseStringField(data, ['ucan']);
|
|
1350
|
+
if (!ucan) {
|
|
1351
|
+
throw new Error('Central UCAN response missing ucan');
|
|
1352
|
+
}
|
|
1353
|
+
return {
|
|
1354
|
+
ucan,
|
|
1355
|
+
issuerDid: parseIssuerDidField(data),
|
|
1356
|
+
subject: parseStringField(data, ['subject']),
|
|
1357
|
+
audience: parseStringField(data, ['audience']),
|
|
1358
|
+
capabilities: parseCapabilitiesField(data, ['capabilities']),
|
|
1359
|
+
exp: parseNumberField(data, ['exp', 'expiresAt']),
|
|
1360
|
+
nbf: parseNumberField(data, ['nbf', 'notBefore']),
|
|
1361
|
+
iat: parseNumberField(data, ['iat', 'issuedAt']),
|
|
1362
|
+
response: payload,
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
async function createAndIssueCentralUcan(options) {
|
|
1366
|
+
const session = await createCentralSession({
|
|
1367
|
+
...options,
|
|
1368
|
+
subject: options.subject,
|
|
1369
|
+
sessionTtlMs: options.sessionTtlMs,
|
|
1370
|
+
});
|
|
1371
|
+
const issue = await issueCentralUcan({
|
|
1372
|
+
...options,
|
|
1373
|
+
sessionToken: session.sessionToken,
|
|
1374
|
+
audience: options.audience,
|
|
1375
|
+
capabilities: options.capabilities,
|
|
1376
|
+
expiresInMs: options.expiresInMs,
|
|
1377
|
+
ttlMs: options.ttlMs,
|
|
1378
|
+
});
|
|
1379
|
+
return { session, issue };
|
|
1380
|
+
}
|
|
1381
|
+
async function authCentralUcanFetch(input, init = {}, options = {}) {
|
|
1382
|
+
const fetcher = resolveFetcher(options);
|
|
1383
|
+
const credentials = resolveCredentials(options);
|
|
1384
|
+
let token = options.ucan || null;
|
|
1385
|
+
if (!token) {
|
|
1386
|
+
let sessionToken = options.sessionToken || getCentralSessionToken(options);
|
|
1387
|
+
if (!sessionToken) {
|
|
1388
|
+
if (!options.subject) {
|
|
1389
|
+
throw new Error('Missing central session token or subject');
|
|
1390
|
+
}
|
|
1391
|
+
const session = await createCentralSession({
|
|
1392
|
+
...options,
|
|
1393
|
+
subject: options.subject,
|
|
1394
|
+
sessionTtlMs: options.sessionTtlMs,
|
|
1395
|
+
});
|
|
1396
|
+
sessionToken = session.sessionToken;
|
|
1397
|
+
}
|
|
1398
|
+
const issued = await issueCentralUcan({
|
|
1399
|
+
...options,
|
|
1400
|
+
sessionToken,
|
|
1401
|
+
audience: options.audience,
|
|
1402
|
+
capabilities: options.capabilities,
|
|
1403
|
+
expiresInMs: options.expiresInMs,
|
|
1404
|
+
ttlMs: options.ttlMs,
|
|
1405
|
+
});
|
|
1406
|
+
token = issued.ucan;
|
|
1407
|
+
}
|
|
1408
|
+
const headers = new Headers(init.headers || {});
|
|
1409
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
1410
|
+
return fetcher(input, {
|
|
1411
|
+
...init,
|
|
1412
|
+
headers,
|
|
1413
|
+
credentials,
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
|
|
900
1417
|
function normalizeBaseUrl(baseUrl) {
|
|
901
1418
|
return baseUrl.replace(/\/+$/, '');
|
|
902
1419
|
}
|
|
@@ -1114,6 +1631,13 @@ function createWebDavClient(options) {
|
|
|
1114
1631
|
const tokenCache = new Map();
|
|
1115
1632
|
const TOKEN_SKEW_MS = 5000;
|
|
1116
1633
|
const DEFAULT_APP_ACTION = 'write';
|
|
1634
|
+
const LOOPBACK_HOST_ALIASES = new Set([
|
|
1635
|
+
'localhost',
|
|
1636
|
+
'127.0.0.1',
|
|
1637
|
+
'::1',
|
|
1638
|
+
'0:0:0:0:0:0:0:1',
|
|
1639
|
+
'0.0.0.0',
|
|
1640
|
+
]);
|
|
1117
1641
|
function normalizeAppDir(path) {
|
|
1118
1642
|
const trimmed = path.trim();
|
|
1119
1643
|
if (!trimmed)
|
|
@@ -1125,6 +1649,69 @@ function normalizeAppDir(path) {
|
|
|
1125
1649
|
function sanitizeAppId(appId) {
|
|
1126
1650
|
return appId.trim().replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
1127
1651
|
}
|
|
1652
|
+
function parseHostPort(rawHost) {
|
|
1653
|
+
const host = rawHost.trim();
|
|
1654
|
+
if (!host)
|
|
1655
|
+
return { hostname: '', port: '' };
|
|
1656
|
+
const bracketMatch = host.match(/^\[([^\]]+)\](?::([0-9]+))?$/);
|
|
1657
|
+
if (bracketMatch) {
|
|
1658
|
+
return {
|
|
1659
|
+
hostname: bracketMatch[1] || '',
|
|
1660
|
+
port: bracketMatch[2] || '',
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
const firstColon = host.indexOf(':');
|
|
1664
|
+
const lastColon = host.lastIndexOf(':');
|
|
1665
|
+
if (firstColon > -1 && firstColon === lastColon) {
|
|
1666
|
+
const hostname = host.slice(0, firstColon).trim();
|
|
1667
|
+
const port = host.slice(firstColon + 1).trim();
|
|
1668
|
+
if (/^[0-9]+$/.test(port)) {
|
|
1669
|
+
return { hostname, port };
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
return { hostname: host, port: '' };
|
|
1673
|
+
}
|
|
1674
|
+
function normalizeAppHostnameForAppId(hostname) {
|
|
1675
|
+
const normalized = (hostname || '').trim().toLowerCase();
|
|
1676
|
+
if (!normalized)
|
|
1677
|
+
return '';
|
|
1678
|
+
const bare = normalized.replace(/^\[(.*)\]$/, '$1');
|
|
1679
|
+
if (LOOPBACK_HOST_ALIASES.has(normalized) || LOOPBACK_HOST_ALIASES.has(bare)) {
|
|
1680
|
+
return 'localhost';
|
|
1681
|
+
}
|
|
1682
|
+
return bare;
|
|
1683
|
+
}
|
|
1684
|
+
function buildSanitizedAppId(hostname, port) {
|
|
1685
|
+
const normalizedHostname = normalizeAppHostnameForAppId(hostname);
|
|
1686
|
+
if (!normalizedHostname)
|
|
1687
|
+
return '';
|
|
1688
|
+
const normalizedPort = port === undefined || port === null ? '' : String(port).trim();
|
|
1689
|
+
const host = normalizedPort
|
|
1690
|
+
? `${normalizedHostname}:${normalizedPort}`
|
|
1691
|
+
: normalizedHostname;
|
|
1692
|
+
return sanitizeAppId(host);
|
|
1693
|
+
}
|
|
1694
|
+
function deriveAppIdFromHost(host) {
|
|
1695
|
+
const parsed = parseHostPort(host || '');
|
|
1696
|
+
return buildSanitizedAppId(parsed.hostname, parsed.port);
|
|
1697
|
+
}
|
|
1698
|
+
function deriveAppIdFromLocation(locationLike) {
|
|
1699
|
+
const source = locationLike ||
|
|
1700
|
+
(typeof window !== 'undefined' ? window.location : undefined);
|
|
1701
|
+
if (!source)
|
|
1702
|
+
return '';
|
|
1703
|
+
const hostname = typeof source.hostname === 'string' ? source.hostname : '';
|
|
1704
|
+
const port = source.port;
|
|
1705
|
+
if (hostname) {
|
|
1706
|
+
const appId = buildSanitizedAppId(hostname, port);
|
|
1707
|
+
if (appId)
|
|
1708
|
+
return appId;
|
|
1709
|
+
}
|
|
1710
|
+
if (typeof source.host === 'string') {
|
|
1711
|
+
return deriveAppIdFromHost(source.host);
|
|
1712
|
+
}
|
|
1713
|
+
return '';
|
|
1714
|
+
}
|
|
1128
1715
|
function normalizeAction(action) {
|
|
1129
1716
|
const trimmed = (action || '').trim();
|
|
1130
1717
|
return trimmed ? trimmed : null;
|
|
@@ -1133,25 +1720,19 @@ function buildAppCapability(options) {
|
|
|
1133
1720
|
if (!options.appId)
|
|
1134
1721
|
return null;
|
|
1135
1722
|
const action = normalizeAction(options.appAction) || DEFAULT_APP_ACTION;
|
|
1723
|
+
const resource = `app:all:${sanitizeAppId(options.appId)}`;
|
|
1136
1724
|
return {
|
|
1137
|
-
|
|
1725
|
+
with: resource,
|
|
1726
|
+
can: action,
|
|
1727
|
+
resource,
|
|
1138
1728
|
action,
|
|
1139
1729
|
};
|
|
1140
1730
|
}
|
|
1141
1731
|
function hasAppCapability(caps) {
|
|
1142
|
-
return (caps || []).some(cap =>
|
|
1732
|
+
return (caps || []).some(cap => getCapabilityResource(cap).startsWith('app:'));
|
|
1143
1733
|
}
|
|
1144
1734
|
function dedupeCapabilities(caps) {
|
|
1145
|
-
|
|
1146
|
-
return (caps || []).filter(cap => {
|
|
1147
|
-
if (!cap || !cap.resource || !cap.action)
|
|
1148
|
-
return false;
|
|
1149
|
-
const key = `${cap.resource}|${cap.action}`;
|
|
1150
|
-
if (seen.has(key))
|
|
1151
|
-
return false;
|
|
1152
|
-
seen.add(key);
|
|
1153
|
-
return true;
|
|
1154
|
-
});
|
|
1735
|
+
return normalizeUcanCapabilities(caps);
|
|
1155
1736
|
}
|
|
1156
1737
|
function ensureAppCapability(caps, options) {
|
|
1157
1738
|
const appCap = buildAppCapability(options);
|
|
@@ -1171,7 +1752,13 @@ function resolveAppDir(options) {
|
|
|
1171
1752
|
return undefined;
|
|
1172
1753
|
}
|
|
1173
1754
|
function buildCapsKey(caps) {
|
|
1174
|
-
|
|
1755
|
+
const canonical = (caps || [])
|
|
1756
|
+
.map(cap => ({
|
|
1757
|
+
with: getCapabilityResource(cap),
|
|
1758
|
+
can: getCapabilityAction(cap),
|
|
1759
|
+
}))
|
|
1760
|
+
.filter(cap => Boolean(cap.with && cap.can));
|
|
1761
|
+
return JSON.stringify(canonical);
|
|
1175
1762
|
}
|
|
1176
1763
|
function buildTokenCacheKey(issuer, audience, caps) {
|
|
1177
1764
|
return `${issuer.did}|${audience}|${buildCapsKey(caps)}`;
|
|
@@ -1348,5 +1935,5 @@ async function initDappSession(options) {
|
|
|
1348
1935
|
return result;
|
|
1349
1936
|
}
|
|
1350
1937
|
|
|
1351
|
-
export { WebDavClient, authFetch, authUcanFetch, clearAccessToken, clearUcanSession, createDelegationUcan, createInvocationUcan, createRootUcan, createUcanSession, createWebDavClient, getAccessToken, getAccounts, getBalance, getChainId, getOrCreateUcanRoot, getPreferredAccount, getProvider, getStoredUcanRoot, getUcanSession, initDappSession, initWebDavStorage, isYeYingProvider, loginWithChallenge, logout, onAccountsChanged, onChainChanged, refreshAccessToken, requestAccounts, requireProvider, setAccessToken, signMessage, storeUcanRoot, watchAccounts };
|
|
1938
|
+
export { WebDavClient, authCentralUcanFetch, authFetch, authUcanFetch, clearAccessToken, clearCentralSessionToken, clearUcanSession, createAndIssueCentralUcan, createCentralSession, createDelegationUcan, createInvocationUcan, createRootUcan, createUcanSession, createWebDavClient, deriveAppIdFromHost, deriveAppIdFromLocation, getAccessToken, getAccounts, getBalance, getCapabilityAction, getCapabilityResource, getCentralIssuerInfo, getCentralSessionToken, getChainId, getOrCreateUcanRoot, getPreferredAccount, getProvider, getStoredUcanRoot, getUcanSession, initDappSession, initWebDavStorage, isYeYingProvider, issueCentralUcan, loginWithChallenge, logout, normalizeAppHostnameForAppId, normalizeUcanCapabilities, normalizeUcanCapability, onAccountsChanged, onChainChanged, refreshAccessToken, requestAccounts, requireProvider, setAccessToken, setCentralSessionToken, signMessage, storeUcanRoot, watchAccounts };
|
|
1352
1939
|
//# sourceMappingURL=web3-bs.esm.js.map
|