@makolabs/ripple 1.6.7 → 1.6.9
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.
|
@@ -88,43 +88,58 @@ async function makeAuthRequest(endpoint, options = {}) {
|
|
|
88
88
|
...options.headers
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
|
-
const
|
|
92
|
-
|
|
91
|
+
const text = await response.text();
|
|
92
|
+
let data;
|
|
93
|
+
try {
|
|
94
|
+
data = JSON.parse(text);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Not JSON, treat as plain text error (e.g., "404 page not found")
|
|
98
|
+
data = { error: text, message: text };
|
|
99
|
+
}
|
|
93
100
|
return {
|
|
94
101
|
ok: response.ok,
|
|
95
102
|
status: response.status,
|
|
96
|
-
data:
|
|
103
|
+
data: data
|
|
97
104
|
};
|
|
98
105
|
}
|
|
99
106
|
async function verifyApiKeyToken(apiKey) {
|
|
100
107
|
try {
|
|
101
|
-
const result = await makeAuthRequest('/auth/
|
|
108
|
+
const result = await makeAuthRequest('/auth/token', {
|
|
102
109
|
method: 'POST',
|
|
103
110
|
headers: {
|
|
104
111
|
'X-API-Key': apiKey
|
|
105
112
|
}
|
|
106
113
|
});
|
|
107
114
|
if (result.ok && result.data?.data?.access_token) {
|
|
108
|
-
|
|
115
|
+
const token = result.data.data.access_token;
|
|
109
116
|
const verifyResult = await makeAuthRequest('/auth/verify', {
|
|
110
|
-
method: '
|
|
117
|
+
method: 'GET',
|
|
111
118
|
headers: {
|
|
112
|
-
|
|
119
|
+
Authorization: `Bearer ${token}`
|
|
113
120
|
}
|
|
114
121
|
});
|
|
115
122
|
if (verifyResult.ok && verifyResult.data?.data) {
|
|
123
|
+
// The API returns "scope" (singular) as a space-separated string, not "scopes" array
|
|
124
|
+
const scopeString = verifyResult.data.data.scope;
|
|
125
|
+
const scopes = scopeString ? scopeString.split(' ').filter(Boolean) : [];
|
|
116
126
|
return {
|
|
117
127
|
valid: true,
|
|
118
|
-
scopes:
|
|
128
|
+
scopes: scopes
|
|
119
129
|
};
|
|
120
130
|
}
|
|
121
131
|
}
|
|
132
|
+
const errorMsg = result.data?.message ||
|
|
133
|
+
result.data?.error ||
|
|
134
|
+
`API key verification failed with status ${result.status}`;
|
|
135
|
+
console.warn('[verifyApiKeyToken] Verification failed:', errorMsg);
|
|
122
136
|
return {
|
|
123
137
|
valid: false,
|
|
124
|
-
error:
|
|
138
|
+
error: errorMsg
|
|
125
139
|
};
|
|
126
140
|
}
|
|
127
141
|
catch (error) {
|
|
142
|
+
console.error('[verifyApiKeyToken] Exception during verification:', error);
|
|
128
143
|
return {
|
|
129
144
|
valid: false,
|
|
130
145
|
error: error instanceof Error ? error.message : 'Unknown error during verification'
|
|
@@ -444,12 +459,12 @@ export const updateUserPermissions = command('unchecked', async (options) => {
|
|
|
444
459
|
if (apiKeyString) {
|
|
445
460
|
try {
|
|
446
461
|
const verification = await verifyApiKeyToken(apiKeyString);
|
|
462
|
+
console.log('[updateUserPermissions] Key verification:', verification);
|
|
447
463
|
if (verification.valid) {
|
|
448
|
-
console.log('[updateUserPermissions] Token verification successful. Scopes:', verification.scopes);
|
|
449
464
|
// Check if the scopes match what we expect
|
|
450
|
-
const scopesMatch = filteredPermissions.every(perm => verification.scopes?.includes(perm));
|
|
465
|
+
const scopesMatch = filteredPermissions.every((perm) => verification.scopes?.includes(perm));
|
|
451
466
|
if (!scopesMatch) {
|
|
452
|
-
console.warn('[updateUserPermissions]
|
|
467
|
+
console.warn('[updateUserPermissions] Scopes mismatch. Expected:', filteredPermissions, 'Got:', verification.scopes);
|
|
453
468
|
}
|
|
454
469
|
}
|
|
455
470
|
else {
|
|
@@ -490,12 +505,14 @@ export const generateApiKey = command('unchecked', async (options) => {
|
|
|
490
505
|
const userKeys = (allKeysData?.data?.data || []).filter((key) => key.status === 'active');
|
|
491
506
|
let newApiKey;
|
|
492
507
|
let wasRotated = false;
|
|
508
|
+
let oldApiKey;
|
|
509
|
+
let currentUser = null;
|
|
493
510
|
if (userKeys.length > 0 && options.revokeOld) {
|
|
494
511
|
// Use rotate endpoint (per Mako Auth API spec)
|
|
495
512
|
const keyId = userKeys[0].id;
|
|
496
|
-
// Get the old API key
|
|
497
|
-
|
|
498
|
-
|
|
513
|
+
// Get the old API key from Clerk's private_metadata
|
|
514
|
+
currentUser = await makeClerkRequest(`/users/${options.userId}`);
|
|
515
|
+
oldApiKey = currentUser?.private_metadata?.mako_api_key;
|
|
499
516
|
const rotateResult = await makeAdminRequest(`/admin/keys/${keyId}/rotate`, {
|
|
500
517
|
method: 'POST',
|
|
501
518
|
body: JSON.stringify({
|
|
@@ -512,11 +529,9 @@ export const generateApiKey = command('unchecked', async (options) => {
|
|
|
512
529
|
if (oldApiKey) {
|
|
513
530
|
try {
|
|
514
531
|
const oldKeyVerification = await verifyApiKeyToken(oldApiKey);
|
|
532
|
+
console.log('[generateApiKey] Old key verification:', oldKeyVerification.valid ? 'Still valid ⚠️' : 'Revoked ✓');
|
|
515
533
|
if (oldKeyVerification.valid) {
|
|
516
|
-
console.warn('[generateApiKey] Old API key
|
|
517
|
-
}
|
|
518
|
-
else {
|
|
519
|
-
console.log('[generateApiKey] Old API key successfully revoked');
|
|
534
|
+
console.warn('[generateApiKey] Old API key still valid after rotation');
|
|
520
535
|
}
|
|
521
536
|
}
|
|
522
537
|
catch (verifyError) {
|
|
@@ -526,16 +541,16 @@ export const generateApiKey = command('unchecked', async (options) => {
|
|
|
526
541
|
// Verify new key works with correct scopes
|
|
527
542
|
try {
|
|
528
543
|
const newKeyVerification = await verifyApiKeyToken(newApiKey);
|
|
544
|
+
console.log('[generateApiKey] New key verification:', newKeyVerification);
|
|
529
545
|
if (newKeyVerification.valid) {
|
|
530
|
-
console.log('[generateApiKey] New API key verification successful. Scopes:', newKeyVerification.scopes);
|
|
531
546
|
// Check if the scopes match what we expect
|
|
532
|
-
const scopesMatch = filteredPermissions.every(perm => newKeyVerification.scopes?.includes(perm));
|
|
547
|
+
const scopesMatch = filteredPermissions.every((perm) => newKeyVerification.scopes?.includes(perm));
|
|
533
548
|
if (!scopesMatch) {
|
|
534
|
-
console.warn('[generateApiKey]
|
|
549
|
+
console.warn('[generateApiKey] Scopes mismatch. Expected:', filteredPermissions, 'Got:', newKeyVerification.scopes);
|
|
535
550
|
}
|
|
536
551
|
}
|
|
537
552
|
else {
|
|
538
|
-
console.warn('[generateApiKey] New
|
|
553
|
+
console.warn('[generateApiKey] New key verification failed:', newKeyVerification.error);
|
|
539
554
|
}
|
|
540
555
|
}
|
|
541
556
|
catch (verifyError) {
|
|
@@ -555,16 +570,21 @@ export const generateApiKey = command('unchecked', async (options) => {
|
|
|
555
570
|
}
|
|
556
571
|
// Update Clerk profile with new key
|
|
557
572
|
try {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
573
|
+
// Reuse currentUser if already fetched (during rotation), otherwise fetch it
|
|
574
|
+
if (!currentUser) {
|
|
575
|
+
currentUser = await makeClerkRequest(`/users/${options.userId}`);
|
|
576
|
+
}
|
|
577
|
+
if (currentUser) {
|
|
578
|
+
await makeClerkRequest(`/users/${options.userId}`, {
|
|
579
|
+
method: 'PATCH',
|
|
580
|
+
body: JSON.stringify({
|
|
581
|
+
private_metadata: {
|
|
582
|
+
...(currentUser.private_metadata || {}),
|
|
583
|
+
mako_api_key: newApiKey
|
|
584
|
+
}
|
|
585
|
+
})
|
|
586
|
+
});
|
|
587
|
+
}
|
|
568
588
|
}
|
|
569
589
|
catch (clerkError) {
|
|
570
590
|
console.error('[generateApiKey] Failed to update Clerk profile:', clerkError);
|
|
@@ -573,9 +593,7 @@ export const generateApiKey = command('unchecked', async (options) => {
|
|
|
573
593
|
const result = {
|
|
574
594
|
success: true,
|
|
575
595
|
apiKey: newApiKey,
|
|
576
|
-
message: wasRotated
|
|
577
|
-
? 'API key rotated successfully'
|
|
578
|
-
: 'API key generated successfully'
|
|
596
|
+
message: wasRotated ? 'API key rotated successfully' : 'API key generated successfully'
|
|
579
597
|
};
|
|
580
598
|
return JSON.parse(JSON.stringify(result));
|
|
581
599
|
}
|
|
@@ -589,24 +607,39 @@ export const verifyToken = command('unchecked', async (options) => {
|
|
|
589
607
|
const result = await verifyApiKeyToken(options.apiKey);
|
|
590
608
|
// Also return the issued token for debugging
|
|
591
609
|
if (result.valid) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
610
|
+
try {
|
|
611
|
+
const tokenResult = await makeAuthRequest('/auth/token', {
|
|
612
|
+
method: 'POST',
|
|
613
|
+
headers: {
|
|
614
|
+
'X-API-Key': options.apiKey
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
const finalResult = {
|
|
618
|
+
valid: result.valid,
|
|
619
|
+
scopes: result.scopes,
|
|
620
|
+
token: tokenResult.data?.data?.access_token
|
|
621
|
+
};
|
|
622
|
+
// Ensure result is serializable
|
|
623
|
+
return JSON.parse(JSON.stringify(finalResult));
|
|
624
|
+
}
|
|
625
|
+
catch (tokenError) {
|
|
626
|
+
console.warn('[verifyToken] Could not fetch token:', tokenError);
|
|
627
|
+
// Return result without token
|
|
628
|
+
return JSON.parse(JSON.stringify({
|
|
629
|
+
valid: result.valid,
|
|
630
|
+
scopes: result.scopes
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
602
633
|
}
|
|
603
|
-
|
|
634
|
+
// Ensure result is serializable
|
|
635
|
+
return JSON.parse(JSON.stringify(result));
|
|
604
636
|
}
|
|
605
637
|
catch (error) {
|
|
606
638
|
console.error('[verifyToken] Error:', error);
|
|
607
|
-
|
|
639
|
+
const errorResult = {
|
|
608
640
|
valid: false,
|
|
609
641
|
error: error instanceof Error ? error.message : 'Unknown error during verification'
|
|
610
642
|
};
|
|
643
|
+
return JSON.parse(JSON.stringify(errorResult));
|
|
611
644
|
}
|
|
612
645
|
});
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
let showApiKey = $state(false);
|
|
32
32
|
let regeneratingApiKey = $state(false);
|
|
33
33
|
let verifyingToken = $state(false);
|
|
34
|
-
let tokenVerification = $state<{ valid?: boolean; scopes?: string[]; error?: string } | null>(
|
|
34
|
+
let tokenVerification = $state<{ valid?: boolean; scopes?: string[]; error?: string } | null>(
|
|
35
|
+
null
|
|
36
|
+
);
|
|
35
37
|
let initialRole = $state<string>('');
|
|
36
38
|
|
|
37
39
|
// Form data
|
|
@@ -187,7 +189,7 @@
|
|
|
187
189
|
role: roleValue,
|
|
188
190
|
permissions: role ? [...role.permissions] : []
|
|
189
191
|
};
|
|
190
|
-
|
|
192
|
+
|
|
191
193
|
// Clear token verification when permissions change
|
|
192
194
|
tokenVerification = null;
|
|
193
195
|
}
|
|
@@ -233,7 +235,7 @@
|
|
|
233
235
|
|
|
234
236
|
try {
|
|
235
237
|
verifyingToken = true;
|
|
236
|
-
formErrors.apiKey
|
|
238
|
+
delete formErrors.apiKey;
|
|
237
239
|
const result = await adapter.verifyToken({ apiKey });
|
|
238
240
|
tokenVerification = result;
|
|
239
241
|
|
|
@@ -449,8 +451,18 @@
|
|
|
449
451
|
{#if tokenVerification.valid}
|
|
450
452
|
<div class="bg-success-50 border-success-200 mt-2 rounded-lg border p-3">
|
|
451
453
|
<div class="flex items-start gap-2">
|
|
452
|
-
<svg
|
|
453
|
-
|
|
454
|
+
<svg
|
|
455
|
+
class="text-success-600 mt-0.5 h-4 w-4 shrink-0"
|
|
456
|
+
fill="none"
|
|
457
|
+
stroke="currentColor"
|
|
458
|
+
viewBox="0 0 24 24"
|
|
459
|
+
>
|
|
460
|
+
<path
|
|
461
|
+
stroke-linecap="round"
|
|
462
|
+
stroke-linejoin="round"
|
|
463
|
+
stroke-width="2"
|
|
464
|
+
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
465
|
+
></path>
|
|
454
466
|
</svg>
|
|
455
467
|
<div class="min-w-0 flex-1">
|
|
456
468
|
<p class="text-success-800 text-xs font-medium">Token verified successfully</p>
|
|
@@ -465,8 +477,18 @@
|
|
|
465
477
|
{:else}
|
|
466
478
|
<div class="bg-danger-50 border-danger-200 mt-2 rounded-lg border p-3">
|
|
467
479
|
<div class="flex items-start gap-2">
|
|
468
|
-
<svg
|
|
469
|
-
|
|
480
|
+
<svg
|
|
481
|
+
class="text-danger-600 mt-0.5 h-4 w-4 shrink-0"
|
|
482
|
+
fill="none"
|
|
483
|
+
stroke="currentColor"
|
|
484
|
+
viewBox="0 0 24 24"
|
|
485
|
+
>
|
|
486
|
+
<path
|
|
487
|
+
stroke-linecap="round"
|
|
488
|
+
stroke-linejoin="round"
|
|
489
|
+
stroke-width="2"
|
|
490
|
+
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
491
|
+
></path>
|
|
470
492
|
</svg>
|
|
471
493
|
<div class="min-w-0 flex-1">
|
|
472
494
|
<p class="text-danger-800 text-xs font-medium">Token verification failed</p>
|