@hanzo/iam 0.6.1 → 0.7.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/dist/auth.d.ts +1 -1
- package/dist/auth.js +3 -3
- package/dist/auth.js.map +1 -1
- package/dist/betterauth.d.ts +67 -0
- package/dist/betterauth.d.ts.map +1 -0
- package/dist/betterauth.js +64 -0
- package/dist/betterauth.js.map +1 -0
- package/dist/browser.d.ts +87 -4
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +229 -8
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +11 -22
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +39 -96
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/nextauth.d.ts +12 -10
- package/dist/nextauth.d.ts.map +1 -1
- package/dist/nextauth.js +12 -11
- package/dist/nextauth.js.map +1 -1
- package/dist/passport.d.ts +44 -0
- package/dist/passport.d.ts.map +1 -0
- package/dist/passport.js +67 -0
- package/dist/passport.js.map +1 -0
- package/dist/pkce.d.ts +2 -2
- package/dist/pkce.js +2 -2
- package/dist/react.d.ts +8 -94
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +23 -454
- package/dist/react.js.map +1 -1
- package/dist/types.d.ts +1 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/package.json +16 -7
- package/src/auth.ts +3 -3
- package/src/betterauth.ts +91 -0
- package/src/browser.ts +255 -11
- package/src/client.ts +47 -154
- package/src/index.ts +3 -4
- package/src/nextauth.ts +15 -13
- package/src/passport.ts +97 -0
- package/src/pkce.ts +2 -2
- package/src/react.ts +28 -635
- package/src/types.ts +1 -21
package/dist/auth.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JWT validation using jose library + OIDC JWKS discovery.
|
|
3
3
|
*
|
|
4
|
-
* Validates access/ID tokens issued by Hanzo IAM
|
|
4
|
+
* Validates access/ID tokens issued by Hanzo IAM.
|
|
5
5
|
*/
|
|
6
6
|
import type { IamConfig, IamAuthResult } from "./types.js";
|
|
7
7
|
/** Clear cached JWKS key sets (useful for testing or key rotation). */
|
package/dist/auth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JWT validation using jose library + OIDC JWKS discovery.
|
|
3
3
|
*
|
|
4
|
-
* Validates access/ID tokens issued by Hanzo IAM
|
|
4
|
+
* Validates access/ID tokens issued by Hanzo IAM.
|
|
5
5
|
*/
|
|
6
6
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
@@ -90,7 +90,7 @@ export async function validateToken(token, config) {
|
|
|
90
90
|
return { ok: false, reason: "iam_token_expired" };
|
|
91
91
|
}
|
|
92
92
|
if (message.includes("audience")) {
|
|
93
|
-
// Retry without audience check - some
|
|
93
|
+
// Retry without audience check - some IAM configs don't set aud
|
|
94
94
|
try {
|
|
95
95
|
const result = await jwtVerify(token, keySet, {
|
|
96
96
|
issuer,
|
|
@@ -115,7 +115,7 @@ export async function validateToken(token, config) {
|
|
|
115
115
|
if (!sub) {
|
|
116
116
|
return { ok: false, reason: "iam_subject_missing" };
|
|
117
117
|
}
|
|
118
|
-
//
|
|
118
|
+
// IAM sub format is "org/username" - extract owner
|
|
119
119
|
const parts = sub.split("/");
|
|
120
120
|
const owner = parts.length > 1 ? parts[0] : config.orgName ?? "unknown";
|
|
121
121
|
return {
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BetterAuth SSO provider configuration for IAM.
|
|
3
|
+
*
|
|
4
|
+
* Returns a provider config object compatible with BetterAuth's
|
|
5
|
+
* `socialProviders` or generic OAuth plugin.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { betterAuth } from "better-auth";
|
|
10
|
+
* import { iamProvider } from "@hanzo/iam/betterauth";
|
|
11
|
+
*
|
|
12
|
+
* export const auth = betterAuth({
|
|
13
|
+
* socialProviders: [
|
|
14
|
+
* iamProvider({
|
|
15
|
+
* serverUrl: process.env.IAM_SERVER_URL!,
|
|
16
|
+
* clientId: process.env.IAM_CLIENT_ID!,
|
|
17
|
+
* clientSecret: process.env.IAM_CLIENT_SECRET!,
|
|
18
|
+
* }),
|
|
19
|
+
* ],
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @packageDocumentation
|
|
24
|
+
*/
|
|
25
|
+
import type { IamConfig } from "./types.js";
|
|
26
|
+
export interface IamSocialProvider {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
type: "oidc";
|
|
30
|
+
issuer: string;
|
|
31
|
+
clientId: string;
|
|
32
|
+
clientSecret?: string;
|
|
33
|
+
authorization: {
|
|
34
|
+
url: string;
|
|
35
|
+
params: {
|
|
36
|
+
scope: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
token: {
|
|
40
|
+
url: string;
|
|
41
|
+
};
|
|
42
|
+
userinfo: {
|
|
43
|
+
url: string;
|
|
44
|
+
};
|
|
45
|
+
profile: (profile: Record<string, unknown>) => {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
email: string;
|
|
49
|
+
image: string | null;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create a BetterAuth-compatible social provider for IAM.
|
|
54
|
+
*
|
|
55
|
+
* Works with BetterAuth's SSO plugin or generic OAuth integration.
|
|
56
|
+
* Uses standard OIDC endpoints.
|
|
57
|
+
*/
|
|
58
|
+
export declare function iamProvider(config: IamConfig & {
|
|
59
|
+
redirectUri?: string;
|
|
60
|
+
}): IamSocialProvider;
|
|
61
|
+
/** @deprecated Use iamProvider instead */
|
|
62
|
+
export { iamProvider as hanzoIamProvider };
|
|
63
|
+
/** @deprecated Use iamProvider instead */
|
|
64
|
+
export { iamProvider as hanzoIamSocialProvider };
|
|
65
|
+
/** @deprecated Use IamSocialProvider instead */
|
|
66
|
+
export type { IamSocialProvider as HanzoIamSocialProvider };
|
|
67
|
+
//# sourceMappingURL=betterauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"betterauth.d.ts","sourceRoot":"","sources":["../src/betterauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAC1D,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK;QAC7C,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3C,iBAAiB,CA6BnB;AAGD,0CAA0C;AAC1C,OAAO,EAAE,WAAW,IAAI,gBAAgB,EAAE,CAAC;AAC3C,0CAA0C;AAC1C,OAAO,EAAE,WAAW,IAAI,sBAAsB,EAAE,CAAC;AACjD,gDAAgD;AAChD,YAAY,EAAE,iBAAiB,IAAI,sBAAsB,EAAE,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BetterAuth SSO provider configuration for IAM.
|
|
3
|
+
*
|
|
4
|
+
* Returns a provider config object compatible with BetterAuth's
|
|
5
|
+
* `socialProviders` or generic OAuth plugin.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { betterAuth } from "better-auth";
|
|
10
|
+
* import { iamProvider } from "@hanzo/iam/betterauth";
|
|
11
|
+
*
|
|
12
|
+
* export const auth = betterAuth({
|
|
13
|
+
* socialProviders: [
|
|
14
|
+
* iamProvider({
|
|
15
|
+
* serverUrl: process.env.IAM_SERVER_URL!,
|
|
16
|
+
* clientId: process.env.IAM_CLIENT_ID!,
|
|
17
|
+
* clientSecret: process.env.IAM_CLIENT_SECRET!,
|
|
18
|
+
* }),
|
|
19
|
+
* ],
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @packageDocumentation
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Create a BetterAuth-compatible social provider for IAM.
|
|
27
|
+
*
|
|
28
|
+
* Works with BetterAuth's SSO plugin or generic OAuth integration.
|
|
29
|
+
* Uses standard OIDC endpoints.
|
|
30
|
+
*/
|
|
31
|
+
export function iamProvider(config) {
|
|
32
|
+
const baseUrl = config.serverUrl.replace(/\/+$/, "");
|
|
33
|
+
return {
|
|
34
|
+
id: "iam",
|
|
35
|
+
name: "IAM",
|
|
36
|
+
type: "oidc",
|
|
37
|
+
issuer: baseUrl,
|
|
38
|
+
clientId: config.clientId,
|
|
39
|
+
clientSecret: config.clientSecret,
|
|
40
|
+
authorization: {
|
|
41
|
+
url: `${baseUrl}/oauth/authorize`,
|
|
42
|
+
params: { scope: "openid profile email" },
|
|
43
|
+
},
|
|
44
|
+
token: { url: `${baseUrl}/oauth/token` },
|
|
45
|
+
userinfo: { url: `${baseUrl}/oauth/userinfo` },
|
|
46
|
+
profile(profile) {
|
|
47
|
+
return {
|
|
48
|
+
id: profile.sub ?? profile.id ?? "",
|
|
49
|
+
name: profile.displayName ??
|
|
50
|
+
profile.name ??
|
|
51
|
+
profile.preferred_username ??
|
|
52
|
+
"",
|
|
53
|
+
email: profile.email ?? "",
|
|
54
|
+
image: profile.avatar ?? profile.picture ?? null,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Backwards-compatible aliases
|
|
60
|
+
/** @deprecated Use iamProvider instead */
|
|
61
|
+
export { iamProvider as hanzoIamProvider };
|
|
62
|
+
/** @deprecated Use iamProvider instead */
|
|
63
|
+
export { iamProvider as hanzoIamSocialProvider };
|
|
64
|
+
//# sourceMappingURL=betterauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"betterauth.js","sourceRoot":"","sources":["../src/betterauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAsBH;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,MAA4C;IAE5C,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAErD,OAAO;QACL,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,OAAO;QACf,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,aAAa,EAAE;YACb,GAAG,EAAE,GAAG,OAAO,kBAAkB;YACjC,MAAM,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;SAC1C;QACD,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,OAAO,cAAc,EAAE;QACxC,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,OAAO,iBAAiB,EAAE;QAC9C,OAAO,CAAC,OAAgC;YACtC,OAAO;gBACL,EAAE,EAAG,OAAO,CAAC,GAAc,IAAK,OAAO,CAAC,EAAa,IAAI,EAAE;gBAC3D,IAAI,EACD,OAAO,CAAC,WAAsB;oBAC9B,OAAO,CAAC,IAAe;oBACvB,OAAO,CAAC,kBAA6B;oBACtC,EAAE;gBACJ,KAAK,EAAG,OAAO,CAAC,KAAgB,IAAI,EAAE;gBACtC,KAAK,EAAG,OAAO,CAAC,MAAiB,IAAK,OAAO,CAAC,OAAkB,IAAI,IAAI;aACzE,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+BAA+B;AAC/B,0CAA0C;AAC1C,OAAO,EAAE,WAAW,IAAI,gBAAgB,EAAE,CAAC;AAC3C,0CAA0C;AAC1C,OAAO,EAAE,WAAW,IAAI,sBAAsB,EAAE,CAAC"}
|
package/dist/browser.d.ts
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Provides PKCE-based login redirect, code exchange, token refresh,
|
|
5
5
|
* popup signin, and silent signin for single-page applications.
|
|
6
6
|
*
|
|
7
|
-
* Adapted and modernized
|
|
7
|
+
* Adapted and modernized for Hanzo IAM.
|
|
8
8
|
*/
|
|
9
9
|
import type { IamConfig, TokenResponse } from "./types.js";
|
|
10
|
-
export type
|
|
10
|
+
export type IAMConfig = IamConfig & {
|
|
11
11
|
/** OAuth2 redirect URI (e.g. "https://app.hanzo.bot/auth/callback"). */
|
|
12
12
|
redirectUri: string;
|
|
13
13
|
/** OAuth2 scopes (default: "openid profile email"). */
|
|
@@ -23,11 +23,11 @@ export type BrowserIamConfig = IamConfig & {
|
|
|
23
23
|
*/
|
|
24
24
|
proxyBaseUrl?: string;
|
|
25
25
|
};
|
|
26
|
-
export declare class
|
|
26
|
+
export declare class IAM {
|
|
27
27
|
private readonly config;
|
|
28
28
|
private readonly storage;
|
|
29
29
|
private discoveryCache;
|
|
30
|
-
constructor(config:
|
|
30
|
+
constructor(config: IAMConfig);
|
|
31
31
|
private getDiscovery;
|
|
32
32
|
/**
|
|
33
33
|
* Start the OAuth2 PKCE login flow by redirecting to the IAM authorize endpoint.
|
|
@@ -87,5 +87,88 @@ export declare class BrowserIamSdk {
|
|
|
87
87
|
}): string;
|
|
88
88
|
/** Build the user profile URL on the IAM server. */
|
|
89
89
|
getUserProfileUrl(username: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* Send a verification code to a phone or email destination.
|
|
92
|
+
*
|
|
93
|
+
* @param contact `{ phone, countryCode }` for SMS, `{ email }` for email.
|
|
94
|
+
* @param method Casdoor method: `login`, `signup`, `forget`, `mfaSetup`, etc.
|
|
95
|
+
*/
|
|
96
|
+
sendVerificationCode(contact: {
|
|
97
|
+
phone: string;
|
|
98
|
+
countryCode: string;
|
|
99
|
+
} | {
|
|
100
|
+
email: string;
|
|
101
|
+
}, method?: "login" | "signup" | "forget" | "mfaSetup"): Promise<{
|
|
102
|
+
ok: boolean;
|
|
103
|
+
error?: string;
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* Look up whether a phone number is registered. Returns `{ exists: false }`
|
|
107
|
+
* on 404 or unknown numbers; `{ exists: true }` when Casdoor confirms a user.
|
|
108
|
+
*/
|
|
109
|
+
lookupPhoneUser(phone: string, countryCode: string): Promise<{
|
|
110
|
+
exists: boolean;
|
|
111
|
+
error?: string;
|
|
112
|
+
}>;
|
|
113
|
+
/**
|
|
114
|
+
* Casdoor REST signup. Returns the new user's id on success.
|
|
115
|
+
*
|
|
116
|
+
* Phone signup flow: send phoneCode via `sendVerificationCode`, then call
|
|
117
|
+
* this with the OTP in `phoneCode`. Casdoor verifies the code internally.
|
|
118
|
+
* Email signup flow: same with `email` + `emailCode`.
|
|
119
|
+
*/
|
|
120
|
+
signup(params: {
|
|
121
|
+
method: "email" | "phone";
|
|
122
|
+
name: string;
|
|
123
|
+
username?: string;
|
|
124
|
+
email?: string;
|
|
125
|
+
phone?: string;
|
|
126
|
+
countryCode?: string;
|
|
127
|
+
password?: string;
|
|
128
|
+
emailCode?: string;
|
|
129
|
+
phoneCode?: string;
|
|
130
|
+
}): Promise<{
|
|
131
|
+
id?: string;
|
|
132
|
+
ok: boolean;
|
|
133
|
+
error?: string;
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* REST login that returns an authorization code (Casdoor `/login`).
|
|
137
|
+
*
|
|
138
|
+
* Use this when you want the caller to drive the PKCE flow without a
|
|
139
|
+
* full redirect — collect credentials in your own UI, get a code back,
|
|
140
|
+
* then call `exchangeCodeForToken` to land tokens.
|
|
141
|
+
*/
|
|
142
|
+
loginWithCredentials(params: {
|
|
143
|
+
username: string;
|
|
144
|
+
password: string;
|
|
145
|
+
type?: "code" | "token";
|
|
146
|
+
redirectUri?: string;
|
|
147
|
+
}): Promise<{
|
|
148
|
+
code?: string;
|
|
149
|
+
ok: boolean;
|
|
150
|
+
error?: string;
|
|
151
|
+
}>;
|
|
152
|
+
/**
|
|
153
|
+
* Exchange an authorization code for tokens using the stored PKCE verifier.
|
|
154
|
+
* Pairs with `loginWithCredentials` for a code → tokens round-trip.
|
|
155
|
+
*/
|
|
156
|
+
exchangeCodeForToken(code: string, redirectUri?: string): Promise<TokenResponse>;
|
|
157
|
+
/**
|
|
158
|
+
* Phone OTP login: tries the numbered username variants Casdoor accepts
|
|
159
|
+
* (`{phone}`, `{countryCode}{phone}`), exchanges the resulting code for
|
|
160
|
+
* tokens. Returns the token response, or throws on failure.
|
|
161
|
+
*/
|
|
162
|
+
loginWithPhoneOTP(params: {
|
|
163
|
+
phone: string;
|
|
164
|
+
countryCode: string;
|
|
165
|
+
code: string;
|
|
166
|
+
redirectUri?: string;
|
|
167
|
+
}): Promise<TokenResponse>;
|
|
168
|
+
/**
|
|
169
|
+
* Logout via Casdoor REST `/logout` (clears server-side session) and
|
|
170
|
+
* the local storage.
|
|
171
|
+
*/
|
|
172
|
+
logout(): Promise<void>;
|
|
90
173
|
}
|
|
91
174
|
//# sourceMappingURL=browser.d.ts.map
|
package/dist/browser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAC;AAmB1E,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
package/dist/browser.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Provides PKCE-based login redirect, code exchange, token refresh,
|
|
5
5
|
* popup signin, and silent signin for single-page applications.
|
|
6
6
|
*
|
|
7
|
-
* Adapted and modernized
|
|
7
|
+
* Adapted and modernized for Hanzo IAM.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { generatePKCEChallenge, generateState } from "./pkce.js";
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
// Storage keys
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
@@ -17,7 +17,7 @@ const KEY_ACCESS_TOKEN = `${STORAGE_PREFIX}access_token`;
|
|
|
17
17
|
const KEY_REFRESH_TOKEN = `${STORAGE_PREFIX}refresh_token`;
|
|
18
18
|
const KEY_ID_TOKEN = `${STORAGE_PREFIX}id_token`;
|
|
19
19
|
const KEY_EXPIRES_AT = `${STORAGE_PREFIX}expires_at`;
|
|
20
|
-
export class
|
|
20
|
+
export class IAM {
|
|
21
21
|
config;
|
|
22
22
|
storage;
|
|
23
23
|
discoveryCache = null;
|
|
@@ -34,7 +34,7 @@ export class BrowserIamSdk {
|
|
|
34
34
|
const baseUrl = this.config.serverUrl.replace(/\/+$/, "");
|
|
35
35
|
// Try fetching the OIDC discovery document. If it fails (e.g. due to
|
|
36
36
|
// CORS when the IAM server doesn't send Access-Control-Allow-Origin),
|
|
37
|
-
// construct a fallback from well-known
|
|
37
|
+
// construct a fallback from well-known Hanzo IAM endpoint paths.
|
|
38
38
|
try {
|
|
39
39
|
const res = await fetch(`${baseUrl}/.well-known/openid-configuration`, {
|
|
40
40
|
headers: { Accept: "application/json" },
|
|
@@ -49,7 +49,7 @@ export class BrowserIamSdk {
|
|
|
49
49
|
}
|
|
50
50
|
this.discoveryCache = {
|
|
51
51
|
issuer: baseUrl,
|
|
52
|
-
authorization_endpoint: `${baseUrl}/
|
|
52
|
+
authorization_endpoint: `${baseUrl}/oauth/authorize`,
|
|
53
53
|
token_endpoint: `${baseUrl}/oauth/token`,
|
|
54
54
|
userinfo_endpoint: `${baseUrl}/oauth/userinfo`,
|
|
55
55
|
jwks_uri: `${baseUrl}/.well-known/jwks`,
|
|
@@ -70,7 +70,7 @@ export class BrowserIamSdk {
|
|
|
70
70
|
*/
|
|
71
71
|
async signinRedirect(params) {
|
|
72
72
|
const discovery = await this.getDiscovery();
|
|
73
|
-
const { codeVerifier, codeChallenge } = await
|
|
73
|
+
const { codeVerifier, codeChallenge } = await generatePKCEChallenge();
|
|
74
74
|
const state = generateState();
|
|
75
75
|
this.storage.setItem(KEY_STATE, state);
|
|
76
76
|
this.storage.setItem(KEY_CODE_VERIFIER, codeVerifier);
|
|
@@ -202,7 +202,7 @@ export class BrowserIamSdk {
|
|
|
202
202
|
*/
|
|
203
203
|
async signinPopup(params) {
|
|
204
204
|
const discovery = await this.getDiscovery();
|
|
205
|
-
const { codeVerifier, codeChallenge } = await
|
|
205
|
+
const { codeVerifier, codeChallenge } = await generatePKCEChallenge();
|
|
206
206
|
const state = generateState();
|
|
207
207
|
this.storage.setItem(KEY_STATE, state);
|
|
208
208
|
this.storage.setItem(KEY_CODE_VERIFIER, codeVerifier);
|
|
@@ -260,7 +260,7 @@ export class BrowserIamSdk {
|
|
|
260
260
|
*/
|
|
261
261
|
async signinSilent(timeoutMs = 5000) {
|
|
262
262
|
const discovery = await this.getDiscovery();
|
|
263
|
-
const { codeVerifier, codeChallenge } = await
|
|
263
|
+
const { codeVerifier, codeChallenge } = await generatePKCEChallenge();
|
|
264
264
|
const state = generateState();
|
|
265
265
|
this.storage.setItem(KEY_STATE, state);
|
|
266
266
|
this.storage.setItem(KEY_CODE_VERIFIER, codeVerifier);
|
|
@@ -409,5 +409,226 @@ export class BrowserIamSdk {
|
|
|
409
409
|
const org = this.config.orgName ?? "built-in";
|
|
410
410
|
return `${base}/users/${org}/${username}`;
|
|
411
411
|
}
|
|
412
|
+
// -----------------------------------------------------------------------
|
|
413
|
+
// Casdoor REST surface (signup, OTP, REST login, phone lookup)
|
|
414
|
+
//
|
|
415
|
+
// The OIDC layer covers redirect/PKCE, token exchange, refresh, userinfo.
|
|
416
|
+
// These methods cover the Casdoor-native endpoints that don't have an OIDC
|
|
417
|
+
// analogue: phone/email OTP, custom signup with verification codes, and
|
|
418
|
+
// direct REST login that returns an authorization code in one round-trip
|
|
419
|
+
// (used as the bridge between OTP collection and `exchangeCode`).
|
|
420
|
+
//
|
|
421
|
+
// All paths are gateway-canonical (`/login`, `/signup`,
|
|
422
|
+
// `/send-verification-code`, `/get-phone-user`) — point `serverUrl` at the
|
|
423
|
+
// gateway prefix (e.g. `https://api.dev.satschel.com/v1/iam`) and the
|
|
424
|
+
// gateway proxies to Casdoor's `/api/*` internally.
|
|
425
|
+
// -----------------------------------------------------------------------
|
|
426
|
+
/**
|
|
427
|
+
* Send a verification code to a phone or email destination.
|
|
428
|
+
*
|
|
429
|
+
* @param contact `{ phone, countryCode }` for SMS, `{ email }` for email.
|
|
430
|
+
* @param method Casdoor method: `login`, `signup`, `forget`, `mfaSetup`, etc.
|
|
431
|
+
*/
|
|
432
|
+
async sendVerificationCode(contact, method = "login") {
|
|
433
|
+
const isPhone = "phone" in contact;
|
|
434
|
+
const dest = isPhone ? contact.phone : contact.email;
|
|
435
|
+
const params = {
|
|
436
|
+
applicationId: `admin/${this.config.appName ?? "app"}`,
|
|
437
|
+
dest,
|
|
438
|
+
type: isPhone ? "phone" : "email",
|
|
439
|
+
method,
|
|
440
|
+
captchaType: "none",
|
|
441
|
+
captchaToken: "",
|
|
442
|
+
};
|
|
443
|
+
if (isPhone)
|
|
444
|
+
params.countryCode = contact.countryCode;
|
|
445
|
+
const url = `${this.config.serverUrl.replace(/\/+$/, "")}/send-verification-code`;
|
|
446
|
+
const res = await fetch(url, {
|
|
447
|
+
method: "POST",
|
|
448
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
449
|
+
body: new URLSearchParams(params).toString(),
|
|
450
|
+
});
|
|
451
|
+
const data = await res.json().catch(() => ({}));
|
|
452
|
+
if (data.status === "ok")
|
|
453
|
+
return { ok: true };
|
|
454
|
+
const msg = data.msg || `send-verification-code failed (${res.status})`;
|
|
455
|
+
// Provider misconfig is treated as soft success in dev — Casdoor still
|
|
456
|
+
// generates the code and stores it for verification.
|
|
457
|
+
if (msg.includes("SMS provider") || msg.includes("provider"))
|
|
458
|
+
return { ok: true };
|
|
459
|
+
return { ok: false, error: msg };
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Look up whether a phone number is registered. Returns `{ exists: false }`
|
|
463
|
+
* on 404 or unknown numbers; `{ exists: true }` when Casdoor confirms a user.
|
|
464
|
+
*/
|
|
465
|
+
async lookupPhoneUser(phone, countryCode) {
|
|
466
|
+
const url = `${this.config.serverUrl.replace(/\/+$/, "")}/get-phone-user`;
|
|
467
|
+
const res = await fetch(url, {
|
|
468
|
+
method: "POST",
|
|
469
|
+
headers: { "Content-Type": "application/json" },
|
|
470
|
+
body: JSON.stringify({
|
|
471
|
+
application: this.config.appName ?? "app",
|
|
472
|
+
phone,
|
|
473
|
+
countryCode,
|
|
474
|
+
}),
|
|
475
|
+
});
|
|
476
|
+
if (res.status === 404)
|
|
477
|
+
return { exists: false };
|
|
478
|
+
if (!res.ok)
|
|
479
|
+
return { exists: false, error: `lookupPhoneUser failed (${res.status})` };
|
|
480
|
+
return { exists: true };
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Casdoor REST signup. Returns the new user's id on success.
|
|
484
|
+
*
|
|
485
|
+
* Phone signup flow: send phoneCode via `sendVerificationCode`, then call
|
|
486
|
+
* this with the OTP in `phoneCode`. Casdoor verifies the code internally.
|
|
487
|
+
* Email signup flow: same with `email` + `emailCode`.
|
|
488
|
+
*/
|
|
489
|
+
async signup(params) {
|
|
490
|
+
const username = params.username ?? params.name;
|
|
491
|
+
const password = params.password ?? `Liq${Date.now()}!`;
|
|
492
|
+
const body = {
|
|
493
|
+
application: this.config.appName ?? "app",
|
|
494
|
+
organization: this.config.orgName ?? "built-in",
|
|
495
|
+
name: params.name,
|
|
496
|
+
username,
|
|
497
|
+
password,
|
|
498
|
+
confirm: password,
|
|
499
|
+
agreement: true,
|
|
500
|
+
};
|
|
501
|
+
if (params.method === "email") {
|
|
502
|
+
body.email = params.email;
|
|
503
|
+
if (params.emailCode)
|
|
504
|
+
body.emailCode = params.emailCode;
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
body.phone = params.phone;
|
|
508
|
+
body.countryCode = params.countryCode;
|
|
509
|
+
if (params.phoneCode)
|
|
510
|
+
body.phoneCode = params.phoneCode;
|
|
511
|
+
if (params.email)
|
|
512
|
+
body.email = params.email;
|
|
513
|
+
if (params.emailCode)
|
|
514
|
+
body.emailCode = params.emailCode;
|
|
515
|
+
}
|
|
516
|
+
const url = `${this.config.serverUrl.replace(/\/+$/, "")}/signup`;
|
|
517
|
+
const res = await fetch(url, {
|
|
518
|
+
method: "POST",
|
|
519
|
+
headers: { "Content-Type": "application/json" },
|
|
520
|
+
body: JSON.stringify(body),
|
|
521
|
+
});
|
|
522
|
+
const data = await res.json().catch(() => ({}));
|
|
523
|
+
if (data.status === "ok")
|
|
524
|
+
return { ok: true, id: data.data2 || data.data };
|
|
525
|
+
return { ok: false, error: data.msg || `signup failed (${res.status})` };
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* REST login that returns an authorization code (Casdoor `/login`).
|
|
529
|
+
*
|
|
530
|
+
* Use this when you want the caller to drive the PKCE flow without a
|
|
531
|
+
* full redirect — collect credentials in your own UI, get a code back,
|
|
532
|
+
* then call `exchangeCodeForToken` to land tokens.
|
|
533
|
+
*/
|
|
534
|
+
async loginWithCredentials(params) {
|
|
535
|
+
const { codeVerifier, codeChallenge } = await generatePKCEChallenge();
|
|
536
|
+
this.storage.setItem(KEY_CODE_VERIFIER, codeVerifier);
|
|
537
|
+
const url = new URL(`${this.config.serverUrl.replace(/\/+$/, "")}/login`);
|
|
538
|
+
url.searchParams.set("code_challenge", codeChallenge);
|
|
539
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
540
|
+
const res = await fetch(url.toString(), {
|
|
541
|
+
method: "POST",
|
|
542
|
+
headers: { "Content-Type": "application/json" },
|
|
543
|
+
body: JSON.stringify({
|
|
544
|
+
application: this.config.appName ?? "app",
|
|
545
|
+
organization: this.config.orgName ?? "built-in",
|
|
546
|
+
username: params.username,
|
|
547
|
+
password: params.password,
|
|
548
|
+
type: params.type ?? "code",
|
|
549
|
+
clientId: this.config.clientId,
|
|
550
|
+
redirectUri: params.redirectUri ?? this.config.redirectUri,
|
|
551
|
+
codeChallenge,
|
|
552
|
+
codeChallengeMethod: "S256",
|
|
553
|
+
autoSignin: true,
|
|
554
|
+
}),
|
|
555
|
+
});
|
|
556
|
+
const data = await res.json().catch(() => ({}));
|
|
557
|
+
if (data.status === "ok" && data.data)
|
|
558
|
+
return { ok: true, code: data.data };
|
|
559
|
+
return { ok: false, error: data.msg || `login failed (${res.status})` };
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Exchange an authorization code for tokens using the stored PKCE verifier.
|
|
563
|
+
* Pairs with `loginWithCredentials` for a code → tokens round-trip.
|
|
564
|
+
*/
|
|
565
|
+
async exchangeCodeForToken(code, redirectUri) {
|
|
566
|
+
const codeVerifier = this.storage.getItem(KEY_CODE_VERIFIER);
|
|
567
|
+
if (!codeVerifier) {
|
|
568
|
+
throw new Error("Missing PKCE verifier — call loginWithCredentials() first");
|
|
569
|
+
}
|
|
570
|
+
this.storage.removeItem(KEY_CODE_VERIFIER);
|
|
571
|
+
const discovery = await this.getDiscovery();
|
|
572
|
+
const tokenUrl = this.config.proxyBaseUrl
|
|
573
|
+
? `${this.config.proxyBaseUrl.replace(/\/+$/, "")}/auth/token`
|
|
574
|
+
: discovery.token_endpoint;
|
|
575
|
+
const body = new URLSearchParams({
|
|
576
|
+
grant_type: "authorization_code",
|
|
577
|
+
client_id: this.config.clientId,
|
|
578
|
+
code,
|
|
579
|
+
redirect_uri: redirectUri ?? this.config.redirectUri,
|
|
580
|
+
code_verifier: codeVerifier,
|
|
581
|
+
});
|
|
582
|
+
const res = await fetch(tokenUrl, {
|
|
583
|
+
method: "POST",
|
|
584
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
585
|
+
body: body.toString(),
|
|
586
|
+
});
|
|
587
|
+
if (!res.ok) {
|
|
588
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
589
|
+
throw new Error(err.error_description || err.error || "Token exchange failed");
|
|
590
|
+
}
|
|
591
|
+
const tokens = (await res.json());
|
|
592
|
+
this.storeTokens(tokens);
|
|
593
|
+
return tokens;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Phone OTP login: tries the numbered username variants Casdoor accepts
|
|
597
|
+
* (`{phone}`, `{countryCode}{phone}`), exchanges the resulting code for
|
|
598
|
+
* tokens. Returns the token response, or throws on failure.
|
|
599
|
+
*/
|
|
600
|
+
async loginWithPhoneOTP(params) {
|
|
601
|
+
const usernames = [params.phone, `${params.countryCode}${params.phone}`];
|
|
602
|
+
let lastError = "";
|
|
603
|
+
for (const username of usernames) {
|
|
604
|
+
const result = await this.loginWithCredentials({
|
|
605
|
+
username,
|
|
606
|
+
password: params.code,
|
|
607
|
+
redirectUri: params.redirectUri,
|
|
608
|
+
});
|
|
609
|
+
if (result.ok && result.code) {
|
|
610
|
+
return this.exchangeCodeForToken(result.code, params.redirectUri);
|
|
611
|
+
}
|
|
612
|
+
lastError = result.error ?? lastError;
|
|
613
|
+
}
|
|
614
|
+
throw new Error(lastError || "Phone OTP login failed");
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Logout via Casdoor REST `/logout` (clears server-side session) and
|
|
618
|
+
* the local storage.
|
|
619
|
+
*/
|
|
620
|
+
async logout() {
|
|
621
|
+
const token = this.storage.getItem(KEY_ACCESS_TOKEN);
|
|
622
|
+
try {
|
|
623
|
+
await fetch(`${this.config.serverUrl.replace(/\/+$/, "")}/oauth/logout`, {
|
|
624
|
+
method: "POST",
|
|
625
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
catch {
|
|
629
|
+
// best-effort — local cleanup is what matters
|
|
630
|
+
}
|
|
631
|
+
this.clearTokens();
|
|
632
|
+
}
|
|
412
633
|
}
|
|
413
634
|
//# sourceMappingURL=browser.js.map
|