@stackbe/sdk 0.2.0 → 0.3.0
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 +263 -77
- package/dist/index.d.mts +150 -17
- package/dist/index.d.ts +150 -17
- package/dist/index.js +156 -21
- package/dist/index.mjs +156 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39,9 +39,62 @@ var StackBEError = class extends Error {
|
|
|
39
39
|
this.statusCode = statusCode;
|
|
40
40
|
this.code = code;
|
|
41
41
|
}
|
|
42
|
+
/** Check if this is a specific error type */
|
|
43
|
+
is(code) {
|
|
44
|
+
return this.code === code;
|
|
45
|
+
}
|
|
46
|
+
/** Check if this is an auth-related error */
|
|
47
|
+
isAuthError() {
|
|
48
|
+
return [
|
|
49
|
+
"TOKEN_EXPIRED",
|
|
50
|
+
"TOKEN_ALREADY_USED",
|
|
51
|
+
"TOKEN_INVALID",
|
|
52
|
+
"SESSION_EXPIRED",
|
|
53
|
+
"SESSION_INVALID",
|
|
54
|
+
"UNAUTHORIZED"
|
|
55
|
+
].includes(this.code);
|
|
56
|
+
}
|
|
57
|
+
/** Check if this is a "not found" error */
|
|
58
|
+
isNotFoundError() {
|
|
59
|
+
return [
|
|
60
|
+
"NOT_FOUND",
|
|
61
|
+
"CUSTOMER_NOT_FOUND",
|
|
62
|
+
"SUBSCRIPTION_NOT_FOUND",
|
|
63
|
+
"PLAN_NOT_FOUND",
|
|
64
|
+
"APP_NOT_FOUND"
|
|
65
|
+
].includes(this.code);
|
|
66
|
+
}
|
|
42
67
|
};
|
|
43
68
|
|
|
44
69
|
// src/http.ts
|
|
70
|
+
function mapErrorCode(status, message, errorType) {
|
|
71
|
+
if (errorType && errorType !== "Error") {
|
|
72
|
+
const codeFromError = errorType.toUpperCase().replace(/\s+/g, "_");
|
|
73
|
+
if (codeFromError.includes("NOT_FOUND")) return "NOT_FOUND";
|
|
74
|
+
if (codeFromError.includes("UNAUTHORIZED")) return "UNAUTHORIZED";
|
|
75
|
+
}
|
|
76
|
+
const lowerMessage = message.toLowerCase();
|
|
77
|
+
if (lowerMessage.includes("token") && lowerMessage.includes("expired")) return "TOKEN_EXPIRED";
|
|
78
|
+
if (lowerMessage.includes("already been used")) return "TOKEN_ALREADY_USED";
|
|
79
|
+
if (lowerMessage.includes("invalid") && lowerMessage.includes("token")) return "TOKEN_INVALID";
|
|
80
|
+
if (lowerMessage.includes("session") && lowerMessage.includes("expired")) return "SESSION_EXPIRED";
|
|
81
|
+
if (lowerMessage.includes("session") && lowerMessage.includes("invalid")) return "SESSION_INVALID";
|
|
82
|
+
if (lowerMessage.includes("customer") && lowerMessage.includes("not found")) return "CUSTOMER_NOT_FOUND";
|
|
83
|
+
if (lowerMessage.includes("subscription") && lowerMessage.includes("not found")) return "SUBSCRIPTION_NOT_FOUND";
|
|
84
|
+
if (lowerMessage.includes("plan") && lowerMessage.includes("not found")) return "PLAN_NOT_FOUND";
|
|
85
|
+
if (lowerMessage.includes("app") && lowerMessage.includes("not found")) return "APP_NOT_FOUND";
|
|
86
|
+
if (lowerMessage.includes("not found")) return "NOT_FOUND";
|
|
87
|
+
if (lowerMessage.includes("limit") && lowerMessage.includes("exceeded")) return "USAGE_LIMIT_EXCEEDED";
|
|
88
|
+
if (lowerMessage.includes("metric") && lowerMessage.includes("not found")) return "METRIC_NOT_FOUND";
|
|
89
|
+
if (lowerMessage.includes("feature") && lowerMessage.includes("not available")) return "FEATURE_NOT_AVAILABLE";
|
|
90
|
+
if (lowerMessage.includes("no active subscription")) return "NO_ACTIVE_SUBSCRIPTION";
|
|
91
|
+
if (lowerMessage.includes("required")) return "MISSING_REQUIRED_FIELD";
|
|
92
|
+
if (lowerMessage.includes("invalid") && lowerMessage.includes("email")) return "INVALID_EMAIL";
|
|
93
|
+
if (status === 400) return "VALIDATION_ERROR";
|
|
94
|
+
if (status === 401) return "UNAUTHORIZED";
|
|
95
|
+
if (status === 404) return "NOT_FOUND";
|
|
96
|
+
return "UNKNOWN_ERROR";
|
|
97
|
+
}
|
|
45
98
|
var HttpClient = class {
|
|
46
99
|
constructor(config) {
|
|
47
100
|
this.config = config;
|
|
@@ -79,11 +132,9 @@ var HttpClient = class {
|
|
|
79
132
|
const data = await response.json();
|
|
80
133
|
if (!response.ok) {
|
|
81
134
|
const errorData = data;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
errorData.error || "UNKNOWN_ERROR"
|
|
86
|
-
);
|
|
135
|
+
const message = errorData.message || "Unknown error";
|
|
136
|
+
const code = errorData.code || mapErrorCode(response.status, message, errorData.error || "");
|
|
137
|
+
throw new StackBEError(message, errorData.statusCode || response.status, code);
|
|
87
138
|
}
|
|
88
139
|
return data;
|
|
89
140
|
} catch (error) {
|
|
@@ -635,6 +686,16 @@ var SubscriptionsClient = class {
|
|
|
635
686
|
};
|
|
636
687
|
|
|
637
688
|
// src/auth.ts
|
|
689
|
+
function decodeJwt(token) {
|
|
690
|
+
try {
|
|
691
|
+
const parts = token.split(".");
|
|
692
|
+
if (parts.length !== 3) return null;
|
|
693
|
+
const payload = Buffer.from(parts[1], "base64").toString("utf-8");
|
|
694
|
+
return JSON.parse(payload);
|
|
695
|
+
} catch {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
638
699
|
var AuthClient = class {
|
|
639
700
|
constructor(http, appId) {
|
|
640
701
|
this.http = http;
|
|
@@ -673,32 +734,80 @@ var AuthClient = class {
|
|
|
673
734
|
* Verify a magic link token and get a session token.
|
|
674
735
|
* Call this when the user clicks the magic link.
|
|
675
736
|
*
|
|
737
|
+
* Returns the session token along with tenant and organization context.
|
|
738
|
+
*
|
|
676
739
|
* @example
|
|
677
740
|
* ```typescript
|
|
678
741
|
* // In your /verify route handler
|
|
679
742
|
* const { token } = req.query;
|
|
680
743
|
*
|
|
681
|
-
*
|
|
744
|
+
* try {
|
|
745
|
+
* const result = await stackbe.auth.verifyToken(token);
|
|
682
746
|
*
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
* res.cookie('session', result.token, { httpOnly: true });
|
|
747
|
+
* // result includes: customerId, email, sessionToken, tenantId, organizationId
|
|
748
|
+
* res.cookie('session', result.sessionToken, { httpOnly: true });
|
|
686
749
|
* res.redirect('/dashboard');
|
|
687
|
-
* }
|
|
688
|
-
*
|
|
750
|
+
* } catch (error) {
|
|
751
|
+
* if (error instanceof StackBEError) {
|
|
752
|
+
* if (error.code === 'TOKEN_EXPIRED') {
|
|
753
|
+
* res.redirect('/login?error=expired');
|
|
754
|
+
* } else if (error.code === 'TOKEN_ALREADY_USED') {
|
|
755
|
+
* res.redirect('/login?error=used');
|
|
756
|
+
* }
|
|
757
|
+
* }
|
|
689
758
|
* }
|
|
690
759
|
* ```
|
|
691
760
|
*/
|
|
692
761
|
async verifyToken(token) {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
{
|
|
762
|
+
const response = await fetch(
|
|
763
|
+
`${this.http.baseUrl}/v1/apps/${this.appId}/auth/verify?token=${encodeURIComponent(token)}`,
|
|
764
|
+
{
|
|
765
|
+
headers: {
|
|
766
|
+
"Content-Type": "application/json"
|
|
767
|
+
}
|
|
768
|
+
}
|
|
696
769
|
);
|
|
770
|
+
if (!response.ok) {
|
|
771
|
+
const errorData = await response.json().catch(() => ({}));
|
|
772
|
+
const message = errorData.message || "Token verification failed";
|
|
773
|
+
let code = "TOKEN_INVALID";
|
|
774
|
+
if (message.includes("expired")) {
|
|
775
|
+
code = "TOKEN_EXPIRED";
|
|
776
|
+
} else if (message.includes("already been used")) {
|
|
777
|
+
code = "TOKEN_ALREADY_USED";
|
|
778
|
+
}
|
|
779
|
+
throw new StackBEError(message, response.status, code);
|
|
780
|
+
}
|
|
781
|
+
const data = await response.json();
|
|
782
|
+
let tenantId;
|
|
783
|
+
let organizationId;
|
|
784
|
+
let orgRole;
|
|
785
|
+
if (data.sessionToken) {
|
|
786
|
+
const payload = decodeJwt(data.sessionToken);
|
|
787
|
+
if (payload) {
|
|
788
|
+
tenantId = payload.tenantId;
|
|
789
|
+
organizationId = payload.organizationId;
|
|
790
|
+
orgRole = payload.orgRole;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return {
|
|
794
|
+
success: data.success ?? true,
|
|
795
|
+
valid: true,
|
|
796
|
+
customerId: data.customerId,
|
|
797
|
+
email: data.email,
|
|
798
|
+
sessionToken: data.sessionToken,
|
|
799
|
+
tenantId,
|
|
800
|
+
organizationId,
|
|
801
|
+
orgRole,
|
|
802
|
+
redirectUrl: data.redirectUrl
|
|
803
|
+
};
|
|
697
804
|
}
|
|
698
805
|
/**
|
|
699
806
|
* Get the current session for an authenticated customer.
|
|
700
807
|
* Use this to validate session tokens and get customer data.
|
|
701
808
|
*
|
|
809
|
+
* Returns session info including tenant and organization context extracted from the JWT.
|
|
810
|
+
*
|
|
702
811
|
* @example
|
|
703
812
|
* ```typescript
|
|
704
813
|
* // Validate session on each request
|
|
@@ -707,16 +816,18 @@ var AuthClient = class {
|
|
|
707
816
|
* const session = await stackbe.auth.getSession(sessionToken);
|
|
708
817
|
*
|
|
709
818
|
* if (session) {
|
|
710
|
-
*
|
|
711
|
-
*
|
|
712
|
-
*
|
|
819
|
+
* console.log(session.customerId);
|
|
820
|
+
* console.log(session.tenantId); // Tenant context
|
|
821
|
+
* console.log(session.organizationId); // Org context (if applicable)
|
|
822
|
+
* console.log(session.subscription);
|
|
823
|
+
* console.log(session.entitlements);
|
|
713
824
|
* }
|
|
714
825
|
* ```
|
|
715
826
|
*/
|
|
716
827
|
async getSession(sessionToken) {
|
|
717
828
|
try {
|
|
718
829
|
const response = await fetch(
|
|
719
|
-
`${this.http.baseUrl}/v1/apps/${this.appId}/auth/session
|
|
830
|
+
`${this.http.baseUrl}/v1/apps/${this.appId}/auth/session`,
|
|
720
831
|
{
|
|
721
832
|
headers: {
|
|
722
833
|
"Authorization": `Bearer ${sessionToken}`,
|
|
@@ -728,10 +839,34 @@ var AuthClient = class {
|
|
|
728
839
|
if (response.status === 401) {
|
|
729
840
|
return null;
|
|
730
841
|
}
|
|
731
|
-
throw new
|
|
842
|
+
throw new StackBEError("Failed to get session", response.status, "SESSION_INVALID");
|
|
843
|
+
}
|
|
844
|
+
const data = await response.json();
|
|
845
|
+
let tenantId;
|
|
846
|
+
let organizationId;
|
|
847
|
+
let orgRole;
|
|
848
|
+
const payload = decodeJwt(sessionToken);
|
|
849
|
+
if (payload) {
|
|
850
|
+
tenantId = payload.tenantId;
|
|
851
|
+
organizationId = payload.organizationId;
|
|
852
|
+
orgRole = payload.orgRole;
|
|
853
|
+
}
|
|
854
|
+
return {
|
|
855
|
+
valid: data.valid ?? true,
|
|
856
|
+
customerId: data.customerId,
|
|
857
|
+
email: data.email,
|
|
858
|
+
expiresAt: data.expiresAt,
|
|
859
|
+
tenantId: tenantId ?? data.tenantId,
|
|
860
|
+
organizationId: data.organizationId ?? organizationId,
|
|
861
|
+
orgRole: data.orgRole ?? orgRole,
|
|
862
|
+
customer: data.customer,
|
|
863
|
+
subscription: data.subscription,
|
|
864
|
+
entitlements: data.entitlements
|
|
865
|
+
};
|
|
866
|
+
} catch (error) {
|
|
867
|
+
if (error instanceof StackBEError) {
|
|
868
|
+
throw error;
|
|
732
869
|
}
|
|
733
|
-
return await response.json();
|
|
734
|
-
} catch {
|
|
735
870
|
return null;
|
|
736
871
|
}
|
|
737
872
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -6,9 +6,62 @@ var StackBEError = class extends Error {
|
|
|
6
6
|
this.statusCode = statusCode;
|
|
7
7
|
this.code = code;
|
|
8
8
|
}
|
|
9
|
+
/** Check if this is a specific error type */
|
|
10
|
+
is(code) {
|
|
11
|
+
return this.code === code;
|
|
12
|
+
}
|
|
13
|
+
/** Check if this is an auth-related error */
|
|
14
|
+
isAuthError() {
|
|
15
|
+
return [
|
|
16
|
+
"TOKEN_EXPIRED",
|
|
17
|
+
"TOKEN_ALREADY_USED",
|
|
18
|
+
"TOKEN_INVALID",
|
|
19
|
+
"SESSION_EXPIRED",
|
|
20
|
+
"SESSION_INVALID",
|
|
21
|
+
"UNAUTHORIZED"
|
|
22
|
+
].includes(this.code);
|
|
23
|
+
}
|
|
24
|
+
/** Check if this is a "not found" error */
|
|
25
|
+
isNotFoundError() {
|
|
26
|
+
return [
|
|
27
|
+
"NOT_FOUND",
|
|
28
|
+
"CUSTOMER_NOT_FOUND",
|
|
29
|
+
"SUBSCRIPTION_NOT_FOUND",
|
|
30
|
+
"PLAN_NOT_FOUND",
|
|
31
|
+
"APP_NOT_FOUND"
|
|
32
|
+
].includes(this.code);
|
|
33
|
+
}
|
|
9
34
|
};
|
|
10
35
|
|
|
11
36
|
// src/http.ts
|
|
37
|
+
function mapErrorCode(status, message, errorType) {
|
|
38
|
+
if (errorType && errorType !== "Error") {
|
|
39
|
+
const codeFromError = errorType.toUpperCase().replace(/\s+/g, "_");
|
|
40
|
+
if (codeFromError.includes("NOT_FOUND")) return "NOT_FOUND";
|
|
41
|
+
if (codeFromError.includes("UNAUTHORIZED")) return "UNAUTHORIZED";
|
|
42
|
+
}
|
|
43
|
+
const lowerMessage = message.toLowerCase();
|
|
44
|
+
if (lowerMessage.includes("token") && lowerMessage.includes("expired")) return "TOKEN_EXPIRED";
|
|
45
|
+
if (lowerMessage.includes("already been used")) return "TOKEN_ALREADY_USED";
|
|
46
|
+
if (lowerMessage.includes("invalid") && lowerMessage.includes("token")) return "TOKEN_INVALID";
|
|
47
|
+
if (lowerMessage.includes("session") && lowerMessage.includes("expired")) return "SESSION_EXPIRED";
|
|
48
|
+
if (lowerMessage.includes("session") && lowerMessage.includes("invalid")) return "SESSION_INVALID";
|
|
49
|
+
if (lowerMessage.includes("customer") && lowerMessage.includes("not found")) return "CUSTOMER_NOT_FOUND";
|
|
50
|
+
if (lowerMessage.includes("subscription") && lowerMessage.includes("not found")) return "SUBSCRIPTION_NOT_FOUND";
|
|
51
|
+
if (lowerMessage.includes("plan") && lowerMessage.includes("not found")) return "PLAN_NOT_FOUND";
|
|
52
|
+
if (lowerMessage.includes("app") && lowerMessage.includes("not found")) return "APP_NOT_FOUND";
|
|
53
|
+
if (lowerMessage.includes("not found")) return "NOT_FOUND";
|
|
54
|
+
if (lowerMessage.includes("limit") && lowerMessage.includes("exceeded")) return "USAGE_LIMIT_EXCEEDED";
|
|
55
|
+
if (lowerMessage.includes("metric") && lowerMessage.includes("not found")) return "METRIC_NOT_FOUND";
|
|
56
|
+
if (lowerMessage.includes("feature") && lowerMessage.includes("not available")) return "FEATURE_NOT_AVAILABLE";
|
|
57
|
+
if (lowerMessage.includes("no active subscription")) return "NO_ACTIVE_SUBSCRIPTION";
|
|
58
|
+
if (lowerMessage.includes("required")) return "MISSING_REQUIRED_FIELD";
|
|
59
|
+
if (lowerMessage.includes("invalid") && lowerMessage.includes("email")) return "INVALID_EMAIL";
|
|
60
|
+
if (status === 400) return "VALIDATION_ERROR";
|
|
61
|
+
if (status === 401) return "UNAUTHORIZED";
|
|
62
|
+
if (status === 404) return "NOT_FOUND";
|
|
63
|
+
return "UNKNOWN_ERROR";
|
|
64
|
+
}
|
|
12
65
|
var HttpClient = class {
|
|
13
66
|
constructor(config) {
|
|
14
67
|
this.config = config;
|
|
@@ -46,11 +99,9 @@ var HttpClient = class {
|
|
|
46
99
|
const data = await response.json();
|
|
47
100
|
if (!response.ok) {
|
|
48
101
|
const errorData = data;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
errorData.error || "UNKNOWN_ERROR"
|
|
53
|
-
);
|
|
102
|
+
const message = errorData.message || "Unknown error";
|
|
103
|
+
const code = errorData.code || mapErrorCode(response.status, message, errorData.error || "");
|
|
104
|
+
throw new StackBEError(message, errorData.statusCode || response.status, code);
|
|
54
105
|
}
|
|
55
106
|
return data;
|
|
56
107
|
} catch (error) {
|
|
@@ -602,6 +653,16 @@ var SubscriptionsClient = class {
|
|
|
602
653
|
};
|
|
603
654
|
|
|
604
655
|
// src/auth.ts
|
|
656
|
+
function decodeJwt(token) {
|
|
657
|
+
try {
|
|
658
|
+
const parts = token.split(".");
|
|
659
|
+
if (parts.length !== 3) return null;
|
|
660
|
+
const payload = Buffer.from(parts[1], "base64").toString("utf-8");
|
|
661
|
+
return JSON.parse(payload);
|
|
662
|
+
} catch {
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
605
666
|
var AuthClient = class {
|
|
606
667
|
constructor(http, appId) {
|
|
607
668
|
this.http = http;
|
|
@@ -640,32 +701,80 @@ var AuthClient = class {
|
|
|
640
701
|
* Verify a magic link token and get a session token.
|
|
641
702
|
* Call this when the user clicks the magic link.
|
|
642
703
|
*
|
|
704
|
+
* Returns the session token along with tenant and organization context.
|
|
705
|
+
*
|
|
643
706
|
* @example
|
|
644
707
|
* ```typescript
|
|
645
708
|
* // In your /verify route handler
|
|
646
709
|
* const { token } = req.query;
|
|
647
710
|
*
|
|
648
|
-
*
|
|
711
|
+
* try {
|
|
712
|
+
* const result = await stackbe.auth.verifyToken(token);
|
|
649
713
|
*
|
|
650
|
-
*
|
|
651
|
-
*
|
|
652
|
-
* res.cookie('session', result.token, { httpOnly: true });
|
|
714
|
+
* // result includes: customerId, email, sessionToken, tenantId, organizationId
|
|
715
|
+
* res.cookie('session', result.sessionToken, { httpOnly: true });
|
|
653
716
|
* res.redirect('/dashboard');
|
|
654
|
-
* }
|
|
655
|
-
*
|
|
717
|
+
* } catch (error) {
|
|
718
|
+
* if (error instanceof StackBEError) {
|
|
719
|
+
* if (error.code === 'TOKEN_EXPIRED') {
|
|
720
|
+
* res.redirect('/login?error=expired');
|
|
721
|
+
* } else if (error.code === 'TOKEN_ALREADY_USED') {
|
|
722
|
+
* res.redirect('/login?error=used');
|
|
723
|
+
* }
|
|
724
|
+
* }
|
|
656
725
|
* }
|
|
657
726
|
* ```
|
|
658
727
|
*/
|
|
659
728
|
async verifyToken(token) {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
{
|
|
729
|
+
const response = await fetch(
|
|
730
|
+
`${this.http.baseUrl}/v1/apps/${this.appId}/auth/verify?token=${encodeURIComponent(token)}`,
|
|
731
|
+
{
|
|
732
|
+
headers: {
|
|
733
|
+
"Content-Type": "application/json"
|
|
734
|
+
}
|
|
735
|
+
}
|
|
663
736
|
);
|
|
737
|
+
if (!response.ok) {
|
|
738
|
+
const errorData = await response.json().catch(() => ({}));
|
|
739
|
+
const message = errorData.message || "Token verification failed";
|
|
740
|
+
let code = "TOKEN_INVALID";
|
|
741
|
+
if (message.includes("expired")) {
|
|
742
|
+
code = "TOKEN_EXPIRED";
|
|
743
|
+
} else if (message.includes("already been used")) {
|
|
744
|
+
code = "TOKEN_ALREADY_USED";
|
|
745
|
+
}
|
|
746
|
+
throw new StackBEError(message, response.status, code);
|
|
747
|
+
}
|
|
748
|
+
const data = await response.json();
|
|
749
|
+
let tenantId;
|
|
750
|
+
let organizationId;
|
|
751
|
+
let orgRole;
|
|
752
|
+
if (data.sessionToken) {
|
|
753
|
+
const payload = decodeJwt(data.sessionToken);
|
|
754
|
+
if (payload) {
|
|
755
|
+
tenantId = payload.tenantId;
|
|
756
|
+
organizationId = payload.organizationId;
|
|
757
|
+
orgRole = payload.orgRole;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return {
|
|
761
|
+
success: data.success ?? true,
|
|
762
|
+
valid: true,
|
|
763
|
+
customerId: data.customerId,
|
|
764
|
+
email: data.email,
|
|
765
|
+
sessionToken: data.sessionToken,
|
|
766
|
+
tenantId,
|
|
767
|
+
organizationId,
|
|
768
|
+
orgRole,
|
|
769
|
+
redirectUrl: data.redirectUrl
|
|
770
|
+
};
|
|
664
771
|
}
|
|
665
772
|
/**
|
|
666
773
|
* Get the current session for an authenticated customer.
|
|
667
774
|
* Use this to validate session tokens and get customer data.
|
|
668
775
|
*
|
|
776
|
+
* Returns session info including tenant and organization context extracted from the JWT.
|
|
777
|
+
*
|
|
669
778
|
* @example
|
|
670
779
|
* ```typescript
|
|
671
780
|
* // Validate session on each request
|
|
@@ -674,16 +783,18 @@ var AuthClient = class {
|
|
|
674
783
|
* const session = await stackbe.auth.getSession(sessionToken);
|
|
675
784
|
*
|
|
676
785
|
* if (session) {
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
786
|
+
* console.log(session.customerId);
|
|
787
|
+
* console.log(session.tenantId); // Tenant context
|
|
788
|
+
* console.log(session.organizationId); // Org context (if applicable)
|
|
789
|
+
* console.log(session.subscription);
|
|
790
|
+
* console.log(session.entitlements);
|
|
680
791
|
* }
|
|
681
792
|
* ```
|
|
682
793
|
*/
|
|
683
794
|
async getSession(sessionToken) {
|
|
684
795
|
try {
|
|
685
796
|
const response = await fetch(
|
|
686
|
-
`${this.http.baseUrl}/v1/apps/${this.appId}/auth/session
|
|
797
|
+
`${this.http.baseUrl}/v1/apps/${this.appId}/auth/session`,
|
|
687
798
|
{
|
|
688
799
|
headers: {
|
|
689
800
|
"Authorization": `Bearer ${sessionToken}`,
|
|
@@ -695,10 +806,34 @@ var AuthClient = class {
|
|
|
695
806
|
if (response.status === 401) {
|
|
696
807
|
return null;
|
|
697
808
|
}
|
|
698
|
-
throw new
|
|
809
|
+
throw new StackBEError("Failed to get session", response.status, "SESSION_INVALID");
|
|
810
|
+
}
|
|
811
|
+
const data = await response.json();
|
|
812
|
+
let tenantId;
|
|
813
|
+
let organizationId;
|
|
814
|
+
let orgRole;
|
|
815
|
+
const payload = decodeJwt(sessionToken);
|
|
816
|
+
if (payload) {
|
|
817
|
+
tenantId = payload.tenantId;
|
|
818
|
+
organizationId = payload.organizationId;
|
|
819
|
+
orgRole = payload.orgRole;
|
|
820
|
+
}
|
|
821
|
+
return {
|
|
822
|
+
valid: data.valid ?? true,
|
|
823
|
+
customerId: data.customerId,
|
|
824
|
+
email: data.email,
|
|
825
|
+
expiresAt: data.expiresAt,
|
|
826
|
+
tenantId: tenantId ?? data.tenantId,
|
|
827
|
+
organizationId: data.organizationId ?? organizationId,
|
|
828
|
+
orgRole: data.orgRole ?? orgRole,
|
|
829
|
+
customer: data.customer,
|
|
830
|
+
subscription: data.subscription,
|
|
831
|
+
entitlements: data.entitlements
|
|
832
|
+
};
|
|
833
|
+
} catch (error) {
|
|
834
|
+
if (error instanceof StackBEError) {
|
|
835
|
+
throw error;
|
|
699
836
|
}
|
|
700
|
-
return await response.json();
|
|
701
|
-
} catch {
|
|
702
837
|
return null;
|
|
703
838
|
}
|
|
704
839
|
}
|
package/package.json
CHANGED