@hanzo/iam 0.1.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/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/auth.d.ts +16 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +130 -0
- package/dist/auth.js.map +1 -0
- package/dist/billing.d.ts +41 -0
- package/dist/billing.d.ts.map +1 -0
- package/dist/billing.js +154 -0
- package/dist/billing.js.map +1 -0
- package/dist/browser.d.ts +83 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +370 -0
- package/dist/browser.js.map +1 -0
- package/dist/client.d.ts +55 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +238 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/pkce.d.ts +13 -0
- package/dist/pkce.d.ts.map +1 -0
- package/dist/pkce.js +36 -0
- package/dist/pkce.js.map +1 -0
- package/dist/react.d.ts +123 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +422 -0
- package/dist/react.js.map +1 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +93 -0
- package/src/auth.ts +151 -0
- package/src/billing.ts +238 -0
- package/src/browser.ts +451 -0
- package/src/client.ts +316 -0
- package/src/index.ts +52 -0
- package/src/pkce.ts +43 -0
- package/src/react.ts +533 -0
- package/src/types.ts +221 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hanzo AI Inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# @hanzo/iam
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [Hanzo IAM](https://iam.hanzo.ai) — identity & access management built on [Casdoor](https://casdoor.org).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **JWT Validation** — OIDC discovery + JWKS-based signature verification via [jose](https://github.com/panva/jose)
|
|
8
|
+
- **OAuth2 PKCE** — Browser-side login flows (redirect, popup, silent signin)
|
|
9
|
+
- **API Client** — Users, organizations, OIDC discovery, token exchange
|
|
10
|
+
- **Billing** — Subscriptions, plans, pricing, payments, orders
|
|
11
|
+
- **TypeScript** — Full type safety with exported interfaces
|
|
12
|
+
- **Zero Config** — Auto-discovers endpoints via `.well-known/openid-configuration`
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @hanzo/iam
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @hanzo/iam
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Server-Side Usage
|
|
23
|
+
|
|
24
|
+
### Validate a JWT
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { validateToken } from "@hanzo/iam";
|
|
28
|
+
|
|
29
|
+
const result = await validateToken(accessToken, {
|
|
30
|
+
serverUrl: "https://iam.hanzo.ai",
|
|
31
|
+
clientId: "my-app",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (result.ok) {
|
|
35
|
+
console.log(result.userId, result.email, result.owner);
|
|
36
|
+
} else {
|
|
37
|
+
console.error(result.reason);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### API Client
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { IamClient } from "@hanzo/iam";
|
|
45
|
+
|
|
46
|
+
const client = new IamClient({
|
|
47
|
+
serverUrl: "https://iam.hanzo.ai",
|
|
48
|
+
clientId: "my-app",
|
|
49
|
+
clientSecret: process.env.IAM_CLIENT_SECRET,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// OAuth2 authorization URL
|
|
53
|
+
const authUrl = await client.getAuthorizationUrl({
|
|
54
|
+
redirectUri: "https://myapp.com/callback",
|
|
55
|
+
state: "random-state",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Exchange code for tokens
|
|
59
|
+
const tokens = await client.exchangeCode({
|
|
60
|
+
code: "auth-code",
|
|
61
|
+
redirectUri: "https://myapp.com/callback",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Get user info
|
|
65
|
+
const user = await client.getUserInfo(tokens.access_token);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Billing
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { IamBillingClient } from "@hanzo/iam";
|
|
72
|
+
|
|
73
|
+
const billing = new IamBillingClient({
|
|
74
|
+
serverUrl: "https://iam.hanzo.ai",
|
|
75
|
+
clientId: "my-app",
|
|
76
|
+
clientSecret: process.env.IAM_CLIENT_SECRET,
|
|
77
|
+
orgName: "my-org",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const { active, subscription, plan } = await billing.isSubscriptionActive("my-org");
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Browser Usage (SPA)
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { BrowserIamSdk } from "@hanzo/iam";
|
|
87
|
+
|
|
88
|
+
const iam = new BrowserIamSdk({
|
|
89
|
+
serverUrl: "https://iam.hanzo.ai",
|
|
90
|
+
clientId: "my-spa",
|
|
91
|
+
redirectUri: "https://myapp.com/auth/callback",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Start login
|
|
95
|
+
await iam.signinRedirect();
|
|
96
|
+
|
|
97
|
+
// On callback page
|
|
98
|
+
const tokens = await iam.handleCallback();
|
|
99
|
+
|
|
100
|
+
// Get a valid token (auto-refreshes if expired)
|
|
101
|
+
const token = await iam.getValidAccessToken();
|
|
102
|
+
|
|
103
|
+
// Fetch user info
|
|
104
|
+
const user = await iam.getUserInfo();
|
|
105
|
+
|
|
106
|
+
// Logout
|
|
107
|
+
iam.clearTokens();
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Sub-path Imports
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Server-side JWT validation only (lighter import)
|
|
114
|
+
import { validateToken } from "@hanzo/iam/auth";
|
|
115
|
+
|
|
116
|
+
// Browser PKCE flows only
|
|
117
|
+
import { BrowserIamSdk } from "@hanzo/iam/browser";
|
|
118
|
+
|
|
119
|
+
// Billing only
|
|
120
|
+
import { IamBillingClient } from "@hanzo/iam/billing";
|
|
121
|
+
|
|
122
|
+
// Types only
|
|
123
|
+
import type { IamConfig, IamUser } from "@hanzo/iam/types";
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Configuration
|
|
127
|
+
|
|
128
|
+
| Option | Required | Description |
|
|
129
|
+
|--------|----------|-------------|
|
|
130
|
+
| `serverUrl` | Yes | IAM server URL (e.g. `https://iam.hanzo.ai`) |
|
|
131
|
+
| `clientId` | Yes | OAuth2 client ID |
|
|
132
|
+
| `clientSecret` | No | Client secret (server-side confidential clients) |
|
|
133
|
+
| `orgName` | No | Organization name for scoped queries |
|
|
134
|
+
| `appName` | No | Application name |
|
|
135
|
+
|
|
136
|
+
## Documentation
|
|
137
|
+
|
|
138
|
+
Full docs: [docs.hanzo.ai/services/iam/sdk](https://docs.hanzo.ai/services/iam/sdk)
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT — [Hanzo AI](https://hanzo.ai)
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT validation using jose library + OIDC JWKS discovery.
|
|
3
|
+
*
|
|
4
|
+
* Validates access/ID tokens issued by Hanzo IAM (Casdoor).
|
|
5
|
+
*/
|
|
6
|
+
import type { IamConfig, IamAuthResult } from "./types.js";
|
|
7
|
+
/** Clear cached JWKS key sets (useful for testing or key rotation). */
|
|
8
|
+
export declare function clearJwksCache(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Validate a JWT access token against IAM's JWKS.
|
|
11
|
+
*
|
|
12
|
+
* Uses OIDC discovery to find the JWKS URI, then verifies the token
|
|
13
|
+
* signature, issuer, audience, and expiry using the `jose` library.
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateToken(token: string, config: IamConfig): Promise<IamAuthResult>;
|
|
16
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAC;AAiBzE,uEAAuE;AACvE,wBAAgB,cAAc,IAAI,IAAI,CAErC;AA4CD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,aAAa,CAAC,CAsExB"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT validation using jose library + OIDC JWKS discovery.
|
|
3
|
+
*
|
|
4
|
+
* Validates access/ID tokens issued by Hanzo IAM (Casdoor).
|
|
5
|
+
*/
|
|
6
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// JWKS key set cache (per issuer)
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
const jwksSets = new Map();
|
|
11
|
+
function getJwksKeySet(jwksUri) {
|
|
12
|
+
let keySet = jwksSets.get(jwksUri);
|
|
13
|
+
if (!keySet) {
|
|
14
|
+
keySet = createRemoteJWKSet(new URL(jwksUri));
|
|
15
|
+
jwksSets.set(jwksUri, keySet);
|
|
16
|
+
}
|
|
17
|
+
return keySet;
|
|
18
|
+
}
|
|
19
|
+
/** Clear cached JWKS key sets (useful for testing or key rotation). */
|
|
20
|
+
export function clearJwksCache() {
|
|
21
|
+
jwksSets.clear();
|
|
22
|
+
}
|
|
23
|
+
const discoveryCache = new Map();
|
|
24
|
+
const DISCOVERY_TTL_MS = 5 * 60 * 1000;
|
|
25
|
+
async function resolveJwksUri(serverUrl) {
|
|
26
|
+
const baseUrl = serverUrl.replace(/\/+$/, "");
|
|
27
|
+
const cached = discoveryCache.get(baseUrl);
|
|
28
|
+
if (cached && Date.now() - cached.fetchedAt < DISCOVERY_TTL_MS) {
|
|
29
|
+
return { jwksUri: cached.jwksUri, issuer: cached.issuer };
|
|
30
|
+
}
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timer = setTimeout(() => controller.abort(), 8_000);
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch(`${baseUrl}/.well-known/openid-configuration`, {
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
headers: { Accept: "application/json" },
|
|
37
|
+
});
|
|
38
|
+
if (!res.ok) {
|
|
39
|
+
throw new Error(`OIDC discovery failed: ${res.status}`);
|
|
40
|
+
}
|
|
41
|
+
const body = (await res.json());
|
|
42
|
+
const jwksUri = body.jwks_uri;
|
|
43
|
+
const issuer = body.issuer ?? baseUrl;
|
|
44
|
+
if (!jwksUri) {
|
|
45
|
+
throw new Error("OIDC discovery response missing jwks_uri");
|
|
46
|
+
}
|
|
47
|
+
discoveryCache.set(baseUrl, { jwksUri, issuer, fetchedAt: Date.now() });
|
|
48
|
+
return { jwksUri, issuer };
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Token validation
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
/**
|
|
58
|
+
* Validate a JWT access token against IAM's JWKS.
|
|
59
|
+
*
|
|
60
|
+
* Uses OIDC discovery to find the JWKS URI, then verifies the token
|
|
61
|
+
* signature, issuer, audience, and expiry using the `jose` library.
|
|
62
|
+
*/
|
|
63
|
+
export async function validateToken(token, config) {
|
|
64
|
+
if (!token || typeof token !== "string") {
|
|
65
|
+
return { ok: false, reason: "iam_token_missing" };
|
|
66
|
+
}
|
|
67
|
+
let jwksUri;
|
|
68
|
+
let issuer;
|
|
69
|
+
try {
|
|
70
|
+
const discovery = await resolveJwksUri(config.serverUrl);
|
|
71
|
+
jwksUri = discovery.jwksUri;
|
|
72
|
+
issuer = discovery.issuer;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return { ok: false, reason: "iam_discovery_failed" };
|
|
76
|
+
}
|
|
77
|
+
const keySet = getJwksKeySet(jwksUri);
|
|
78
|
+
let payload;
|
|
79
|
+
try {
|
|
80
|
+
const result = await jwtVerify(token, keySet, {
|
|
81
|
+
issuer,
|
|
82
|
+
audience: config.clientId,
|
|
83
|
+
clockTolerance: 30, // 30s clock skew
|
|
84
|
+
});
|
|
85
|
+
payload = result.payload;
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
89
|
+
if (message.includes("expired")) {
|
|
90
|
+
return { ok: false, reason: "iam_token_expired" };
|
|
91
|
+
}
|
|
92
|
+
if (message.includes("audience")) {
|
|
93
|
+
// Retry without audience check - some Casdoor configs don't set aud
|
|
94
|
+
try {
|
|
95
|
+
const result = await jwtVerify(token, keySet, {
|
|
96
|
+
issuer,
|
|
97
|
+
clockTolerance: 30,
|
|
98
|
+
});
|
|
99
|
+
payload = result.payload;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { ok: false, reason: "iam_signature_invalid" };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return { ok: false, reason: "iam_signature_invalid" };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const claims = payload;
|
|
110
|
+
if (!claims.sub) {
|
|
111
|
+
return { ok: false, reason: "iam_subject_missing" };
|
|
112
|
+
}
|
|
113
|
+
// Casdoor sub format is "org/username" - extract owner
|
|
114
|
+
const parts = claims.sub.split("/");
|
|
115
|
+
const owner = parts.length > 1 ? parts[0] : config.orgName ?? "unknown";
|
|
116
|
+
return {
|
|
117
|
+
ok: true,
|
|
118
|
+
userId: claims.sub,
|
|
119
|
+
email: typeof claims.email === "string" ? claims.email : undefined,
|
|
120
|
+
name: typeof claims.name === "string"
|
|
121
|
+
? claims.name
|
|
122
|
+
: typeof claims.preferred_username === "string"
|
|
123
|
+
? claims.preferred_username
|
|
124
|
+
: undefined,
|
|
125
|
+
avatar: typeof claims.picture === "string" ? claims.picture : undefined,
|
|
126
|
+
owner,
|
|
127
|
+
claims,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Billing client for Hanzo IAM (Casdoor) — subscriptions, plans, pricing, usage.
|
|
3
|
+
*/
|
|
4
|
+
import type { IamConfig, IamSubscription, IamPlan, IamPricing, IamPayment, IamOrder } from "./types.js";
|
|
5
|
+
export declare class IamBillingClient {
|
|
6
|
+
private readonly baseUrl;
|
|
7
|
+
private readonly clientId;
|
|
8
|
+
private readonly clientSecret;
|
|
9
|
+
private readonly orgName;
|
|
10
|
+
constructor(config: IamConfig);
|
|
11
|
+
private request;
|
|
12
|
+
/** Get all subscriptions for an owner. */
|
|
13
|
+
getSubscriptions(token?: string): Promise<IamSubscription[]>;
|
|
14
|
+
/** Get a specific subscription by ID ("owner/name" format). */
|
|
15
|
+
getSubscription(id: string, token?: string): Promise<IamSubscription | null>;
|
|
16
|
+
/** Get the subscription for a specific user. */
|
|
17
|
+
getUserSubscription(userId: string, token?: string): Promise<IamSubscription | null>;
|
|
18
|
+
/** Get all plans for an owner. */
|
|
19
|
+
getPlans(token?: string): Promise<IamPlan[]>;
|
|
20
|
+
/** Get a specific plan by ID. */
|
|
21
|
+
getPlan(id: string, token?: string): Promise<IamPlan | null>;
|
|
22
|
+
/** Get all pricing configurations for an owner. */
|
|
23
|
+
getPricings(token?: string): Promise<IamPricing[]>;
|
|
24
|
+
/** Get a specific pricing by ID. */
|
|
25
|
+
getPricing(id: string, token?: string): Promise<IamPricing | null>;
|
|
26
|
+
/** Get all payments for an owner. */
|
|
27
|
+
getPayments(token?: string): Promise<IamPayment[]>;
|
|
28
|
+
/** Get a specific payment by ID. */
|
|
29
|
+
getPayment(id: string, token?: string): Promise<IamPayment | null>;
|
|
30
|
+
/** Get all orders for an owner. */
|
|
31
|
+
getOrders(token?: string): Promise<IamOrder[]>;
|
|
32
|
+
/** Get a specific order by ID. */
|
|
33
|
+
getOrder(id: string, token?: string): Promise<IamOrder | null>;
|
|
34
|
+
/** Check if an org has an active subscription. */
|
|
35
|
+
isSubscriptionActive(orgName: string, token?: string): Promise<{
|
|
36
|
+
active: boolean;
|
|
37
|
+
subscription: IamSubscription | null;
|
|
38
|
+
plan: IamPlan | null;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=billing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../src/billing.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EACf,OAAO,EACP,UAAU,EACV,UAAU,EACV,QAAQ,EAET,MAAM,YAAY,CAAC;AAIpB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,MAAM,EAAE,SAAS;YAWf,OAAO;IA4DrB,0CAA0C;IACpC,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IASlE,+DAA+D;IACzD,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQlF,gDAAgD;IAC1C,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAS1F,kCAAkC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IASlD,iCAAiC;IAC3B,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAYlE,mDAAmD;IAC7C,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IASxD,oCAAoC;IAC9B,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAYxE,qCAAqC;IAC/B,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IASxD,oCAAoC;IAC9B,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAYxE,mCAAmC;IAC7B,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IASpD,kCAAkC;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAYpE,kDAAkD;IAC5C,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QACnE,MAAM,EAAE,OAAO,CAAC;QAChB,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;QACrC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;KACtB,CAAC;CAkBH"}
|
package/dist/billing.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Billing client for Hanzo IAM (Casdoor) — subscriptions, plans, pricing, usage.
|
|
3
|
+
*/
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
5
|
+
export class IamBillingClient {
|
|
6
|
+
baseUrl;
|
|
7
|
+
clientId;
|
|
8
|
+
clientSecret;
|
|
9
|
+
orgName;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.baseUrl = config.serverUrl.replace(/\/+$/, "");
|
|
12
|
+
this.clientId = config.clientId;
|
|
13
|
+
this.clientSecret = config.clientSecret;
|
|
14
|
+
this.orgName = config.orgName;
|
|
15
|
+
}
|
|
16
|
+
// -----------------------------------------------------------------------
|
|
17
|
+
// Internal HTTP helper
|
|
18
|
+
// -----------------------------------------------------------------------
|
|
19
|
+
async request(path, opts) {
|
|
20
|
+
const url = new URL(path, this.baseUrl);
|
|
21
|
+
if (opts?.params) {
|
|
22
|
+
for (const [k, v] of Object.entries(opts.params)) {
|
|
23
|
+
url.searchParams.set(k, v);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
28
|
+
const headers = {
|
|
29
|
+
Accept: "application/json",
|
|
30
|
+
};
|
|
31
|
+
if (opts?.token) {
|
|
32
|
+
headers.Authorization = `Bearer ${opts.token}`;
|
|
33
|
+
}
|
|
34
|
+
if (opts?.body) {
|
|
35
|
+
headers["Content-Type"] = "application/json";
|
|
36
|
+
}
|
|
37
|
+
if (this.clientSecret && !opts?.token) {
|
|
38
|
+
const credentials = `${this.clientId}:${this.clientSecret}`;
|
|
39
|
+
const basic = typeof Buffer !== "undefined"
|
|
40
|
+
? Buffer.from(credentials).toString("base64")
|
|
41
|
+
: btoa(credentials);
|
|
42
|
+
headers.Authorization = `Basic ${basic}`;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(url.toString(), {
|
|
46
|
+
method: opts?.method ?? "GET",
|
|
47
|
+
headers,
|
|
48
|
+
body: opts?.body ? JSON.stringify(opts.body) : undefined,
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
const text = await res.text().catch(() => "");
|
|
53
|
+
throw new Error(`IAM billing request failed (${res.status}): ${text}`.trim());
|
|
54
|
+
}
|
|
55
|
+
return (await res.json());
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// -----------------------------------------------------------------------
|
|
62
|
+
// Subscriptions
|
|
63
|
+
// -----------------------------------------------------------------------
|
|
64
|
+
/** Get all subscriptions for an owner. */
|
|
65
|
+
async getSubscriptions(token) {
|
|
66
|
+
const owner = this.orgName ?? "admin";
|
|
67
|
+
const resp = await this.request("/api/get-subscriptions", { params: { owner }, token });
|
|
68
|
+
return resp.data ?? [];
|
|
69
|
+
}
|
|
70
|
+
/** Get a specific subscription by ID ("owner/name" format). */
|
|
71
|
+
async getSubscription(id, token) {
|
|
72
|
+
const resp = await this.request("/api/get-subscription", { params: { id }, token });
|
|
73
|
+
return resp.data ?? null;
|
|
74
|
+
}
|
|
75
|
+
/** Get the subscription for a specific user. */
|
|
76
|
+
async getUserSubscription(userId, token) {
|
|
77
|
+
const subs = await this.getSubscriptions(token);
|
|
78
|
+
return subs.find((s) => s.user === userId) ?? null;
|
|
79
|
+
}
|
|
80
|
+
// -----------------------------------------------------------------------
|
|
81
|
+
// Plans
|
|
82
|
+
// -----------------------------------------------------------------------
|
|
83
|
+
/** Get all plans for an owner. */
|
|
84
|
+
async getPlans(token) {
|
|
85
|
+
const owner = this.orgName ?? "admin";
|
|
86
|
+
const resp = await this.request("/api/get-plans", { params: { owner }, token });
|
|
87
|
+
return resp.data ?? [];
|
|
88
|
+
}
|
|
89
|
+
/** Get a specific plan by ID. */
|
|
90
|
+
async getPlan(id, token) {
|
|
91
|
+
const resp = await this.request("/api/get-plan", { params: { id }, token });
|
|
92
|
+
return resp.data ?? null;
|
|
93
|
+
}
|
|
94
|
+
// -----------------------------------------------------------------------
|
|
95
|
+
// Pricing
|
|
96
|
+
// -----------------------------------------------------------------------
|
|
97
|
+
/** Get all pricing configurations for an owner. */
|
|
98
|
+
async getPricings(token) {
|
|
99
|
+
const owner = this.orgName ?? "admin";
|
|
100
|
+
const resp = await this.request("/api/get-pricings", { params: { owner }, token });
|
|
101
|
+
return resp.data ?? [];
|
|
102
|
+
}
|
|
103
|
+
/** Get a specific pricing by ID. */
|
|
104
|
+
async getPricing(id, token) {
|
|
105
|
+
const resp = await this.request("/api/get-pricing", { params: { id }, token });
|
|
106
|
+
return resp.data ?? null;
|
|
107
|
+
}
|
|
108
|
+
// -----------------------------------------------------------------------
|
|
109
|
+
// Payments
|
|
110
|
+
// -----------------------------------------------------------------------
|
|
111
|
+
/** Get all payments for an owner. */
|
|
112
|
+
async getPayments(token) {
|
|
113
|
+
const owner = this.orgName ?? "admin";
|
|
114
|
+
const resp = await this.request("/api/get-payments", { params: { owner }, token });
|
|
115
|
+
return resp.data ?? [];
|
|
116
|
+
}
|
|
117
|
+
/** Get a specific payment by ID. */
|
|
118
|
+
async getPayment(id, token) {
|
|
119
|
+
const resp = await this.request("/api/get-payment", { params: { id }, token });
|
|
120
|
+
return resp.data ?? null;
|
|
121
|
+
}
|
|
122
|
+
// -----------------------------------------------------------------------
|
|
123
|
+
// Orders (if supported by IAM)
|
|
124
|
+
// -----------------------------------------------------------------------
|
|
125
|
+
/** Get all orders for an owner. */
|
|
126
|
+
async getOrders(token) {
|
|
127
|
+
const owner = this.orgName ?? "admin";
|
|
128
|
+
const resp = await this.request("/api/get-orders", { params: { owner }, token });
|
|
129
|
+
return resp.data ?? [];
|
|
130
|
+
}
|
|
131
|
+
/** Get a specific order by ID. */
|
|
132
|
+
async getOrder(id, token) {
|
|
133
|
+
const resp = await this.request("/api/get-order", { params: { id }, token });
|
|
134
|
+
return resp.data ?? null;
|
|
135
|
+
}
|
|
136
|
+
// -----------------------------------------------------------------------
|
|
137
|
+
// Convenience: check subscription status for an org
|
|
138
|
+
// -----------------------------------------------------------------------
|
|
139
|
+
/** Check if an org has an active subscription. */
|
|
140
|
+
async isSubscriptionActive(orgName, token) {
|
|
141
|
+
const subs = await this.getSubscriptions(token);
|
|
142
|
+
// Find subscription matching the org
|
|
143
|
+
const sub = subs.find((s) => s.owner === orgName && (s.state === "Active" || s.state === "active")) ?? null;
|
|
144
|
+
if (!sub) {
|
|
145
|
+
return { active: false, subscription: null, plan: null };
|
|
146
|
+
}
|
|
147
|
+
let plan = null;
|
|
148
|
+
if (sub.plan) {
|
|
149
|
+
plan = await this.getPlan(sub.plan, token);
|
|
150
|
+
}
|
|
151
|
+
return { active: true, subscription: sub, plan };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=billing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../src/billing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side OAuth2 flows for Hanzo IAM.
|
|
3
|
+
*
|
|
4
|
+
* Provides PKCE-based login redirect, code exchange, token refresh,
|
|
5
|
+
* popup signin, and silent signin for single-page applications.
|
|
6
|
+
*
|
|
7
|
+
* Adapted and modernized from casdoor-js-sdk.
|
|
8
|
+
*/
|
|
9
|
+
import type { IamConfig, TokenResponse } from "./types.js";
|
|
10
|
+
export type BrowserIamConfig = IamConfig & {
|
|
11
|
+
/** OAuth2 redirect URI (e.g. "https://app.hanzo.bot/auth/callback"). */
|
|
12
|
+
redirectUri: string;
|
|
13
|
+
/** OAuth2 scopes (default: "openid profile email"). */
|
|
14
|
+
scope?: string;
|
|
15
|
+
/** Storage to use for tokens (default: sessionStorage). */
|
|
16
|
+
storage?: Storage;
|
|
17
|
+
};
|
|
18
|
+
export declare class BrowserIamSdk {
|
|
19
|
+
private readonly config;
|
|
20
|
+
private readonly storage;
|
|
21
|
+
private discoveryCache;
|
|
22
|
+
constructor(config: BrowserIamConfig);
|
|
23
|
+
private getDiscovery;
|
|
24
|
+
/**
|
|
25
|
+
* Start the OAuth2 PKCE login flow by redirecting to the IAM authorize endpoint.
|
|
26
|
+
*
|
|
27
|
+
* Generates PKCE challenge and state, stores them in session storage,
|
|
28
|
+
* then redirects the browser.
|
|
29
|
+
*/
|
|
30
|
+
signinRedirect(params?: {
|
|
31
|
+
additionalParams?: Record<string, string>;
|
|
32
|
+
}): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Handle the OAuth2 callback after redirect. Exchanges the authorization code
|
|
35
|
+
* for tokens using PKCE.
|
|
36
|
+
*
|
|
37
|
+
* Call this on your callback page (e.g. /auth/callback).
|
|
38
|
+
* Returns the token response, or throws if the state doesn't match.
|
|
39
|
+
*/
|
|
40
|
+
handleCallback(callbackUrl?: string): Promise<TokenResponse>;
|
|
41
|
+
/** Refresh the access token using the stored refresh token. */
|
|
42
|
+
refreshAccessToken(): Promise<TokenResponse>;
|
|
43
|
+
/**
|
|
44
|
+
* Open the IAM login page in a popup window. Resolves when the popup
|
|
45
|
+
* completes the OAuth flow and returns tokens.
|
|
46
|
+
*/
|
|
47
|
+
signinPopup(params?: {
|
|
48
|
+
width?: number;
|
|
49
|
+
height?: number;
|
|
50
|
+
additionalParams?: Record<string, string>;
|
|
51
|
+
}): Promise<TokenResponse>;
|
|
52
|
+
/**
|
|
53
|
+
* Attempt silent authentication via a hidden iframe.
|
|
54
|
+
* Useful for checking if the user has an active IAM session.
|
|
55
|
+
* Returns null if silent auth fails (user needs to log in interactively).
|
|
56
|
+
*/
|
|
57
|
+
signinSilent(timeoutMs?: number): Promise<TokenResponse | null>;
|
|
58
|
+
private storeTokens;
|
|
59
|
+
/** Get the stored access token (may be expired). */
|
|
60
|
+
getAccessToken(): string | null;
|
|
61
|
+
/** Get the stored refresh token. */
|
|
62
|
+
getRefreshToken(): string | null;
|
|
63
|
+
/** Get the stored ID token. */
|
|
64
|
+
getIdToken(): string | null;
|
|
65
|
+
/** Check if the stored access token is expired. */
|
|
66
|
+
isTokenExpired(): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Get a valid access token — refreshes automatically if expired.
|
|
69
|
+
* Returns null if no token and no refresh token available.
|
|
70
|
+
*/
|
|
71
|
+
getValidAccessToken(): Promise<string | null>;
|
|
72
|
+
/** Clear all stored tokens (logout). */
|
|
73
|
+
clearTokens(): void;
|
|
74
|
+
/** Fetch user info from the OIDC userinfo endpoint using the stored access token. */
|
|
75
|
+
getUserInfo(): Promise<Record<string, unknown>>;
|
|
76
|
+
/** Build the signup URL for the IAM server. */
|
|
77
|
+
getSignupUrl(params?: {
|
|
78
|
+
enablePassword?: boolean;
|
|
79
|
+
}): string;
|
|
80
|
+
/** Build the user profile URL on the IAM server. */
|
|
81
|
+
getUserProfileUrl(username: string): string;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +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,gBAAgB,GAAG,SAAS,GAAG;IACzC,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,cAAc,CAA8B;gBAExC,MAAM,EAAE,gBAAgB;YAStB,YAAY;IAkB1B;;;;;OAKG;IACG,cAAc,CAAC,MAAM,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B3F;;;;;;OAMG;IACG,cAAc,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA0DlE,+DAA+D;IACzD,kBAAkB,IAAI,OAAO,CAAC,aAAa,CAAC;IAiClD;;;OAGG;IACG,WAAW,CAAC,MAAM,CAAC,EAAE;QACzB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC3C,GAAG,OAAO,CAAC,aAAa,CAAC;IAiE1B;;;;OAIG;IACG,YAAY,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IA4DnE,OAAO,CAAC,WAAW;IAcnB,oDAAoD;IACpD,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B,oCAAoC;IACpC,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,+BAA+B;IAC/B,UAAU,IAAI,MAAM,GAAG,IAAI;IAI3B,mDAAmD;IACnD,cAAc,IAAI,OAAO;IAMzB;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgBnD,wCAAwC;IACxC,WAAW,IAAI,IAAI;IAanB,qFAAqF;IAC/E,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAmBrD,+CAA+C;IAC/C,YAAY,CAAC,MAAM,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM;IAW3D,oDAAoD;IACpD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;CAK5C"}
|