@msdongcha/auth-kit 0.0.2 → 0.0.3
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/CHANGELOG.md +8 -0
- package/README.md +30 -3
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +126 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.0.3 - 2026-10-23
|
|
6
|
+
|
|
7
|
+
- `logout()` 在清理本地 TokenStore 前调用 IAM `/api/v1/auth/logout` 撤销 refresh token,并返回明确的 `LOGOUT_FAILED` 错误。
|
|
8
|
+
- 新增 `getMemberships()`,以统一类型返回当前用户的租户成员列表。
|
|
9
|
+
- 新增 `switchTenant()`,在服务端返回完整 token 后原子切换会话,并保护并发切换。
|
|
10
|
+
|
|
3
11
|
## 0.0.1
|
|
4
12
|
|
|
5
13
|
- 初始发布:无 UI Auth-kit 核心、配置校验、跳转决策、显式 SessionAdapter 与 refresh single-flight。
|
package/README.md
CHANGED
|
@@ -105,12 +105,39 @@ const result = await auth.getContext()
|
|
|
105
105
|
if (result.ok) console.log(result.value.selectedTenant)
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
### `getMemberships()`
|
|
109
|
+
|
|
110
|
+
使用 `TokenStore` 中的 access token 调用 `GET /api/v1/auth/memberships`,返回 camelCase 的租户成员列表。SDK 会保留 `membershipStatus`;列表中的成员关系不代表服务端一定允许切换。
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
const result = await auth.getMemberships()
|
|
114
|
+
if (result.ok) {
|
|
115
|
+
for (const membership of result.value) {
|
|
116
|
+
console.log(membership.tenantId, membership.isDefault)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `switchTenant(membershipId)`
|
|
122
|
+
|
|
123
|
+
使用当前 access token 调用 `POST /api/v1/auth/switch-tenant`。服务端返回完整的新 access token 和 refresh token 后,SDK 才会原子替换 `TokenStore`;请求失败或响应不完整时保留原会话。
|
|
124
|
+
|
|
125
|
+
同一客户端实例中,对相同 membership 的并发切换会合并为一次请求;切换未完成时请求其他 membership 会返回 `TENANT_SWITCH_IN_PROGRESS`。
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const result = await auth.switchTenant('membership-id')
|
|
129
|
+
if (result.ok) {
|
|
130
|
+
console.log(result.value.identity?.roles)
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
108
134
|
### `logout()`
|
|
109
135
|
|
|
110
|
-
|
|
136
|
+
如果 `TokenStore` 中存在 refresh token,则调用 `POST /api/v1/auth/logout` 撤销它,请求体为 `{ refresh_token }`。无论服务端请求成功或失败,SDK 都会清空本地 `TokenStore`;请求失败时返回 `LOGOUT_FAILED`。没有 refresh token 时只执行本地清理。
|
|
111
137
|
|
|
112
138
|
```ts
|
|
113
|
-
await auth.logout()
|
|
139
|
+
const result = await auth.logout()
|
|
140
|
+
if (!result.ok) console.error(result.error)
|
|
114
141
|
```
|
|
115
142
|
|
|
116
143
|
## 其他公开工具
|
|
@@ -147,6 +174,6 @@ await coordinator.refresh(() => refreshBusinessSession())
|
|
|
147
174
|
|
|
148
175
|
## 公开类型与结果
|
|
149
176
|
|
|
150
|
-
`AuthKitConfig`、`AuthClient`、`TokenStore`、`TokenSet`、`AuthIdentity`、`AuthContext`、`AuthKitResult`、`LoginResult`、`AuthorizeOptions`、`TenantDecision` 和相关 legacy 类型均从包根导出。由 `create()` 返回的客户端方法返回 `{ ok: true, value }` 或 `{ ok: false, error, retryable? }`;调用方应先判断 `ok` 再读取 `value`。
|
|
177
|
+
`AuthKitConfig`、`AuthClient`、`TokenStore`、`TokenSet`、`AuthIdentity`、`AuthContext`、`TenantMembership`、`AuthKitResult`、`LoginResult`、`AuthorizeOptions`、`TenantDecision` 和相关 legacy 类型均从包根导出。由 `create()` 返回的客户端方法返回 `{ ok: true, value }` 或 `{ ok: false, error, retryable? }`;调用方应先判断 `ok` 再读取 `value`。
|
|
151
178
|
|
|
152
179
|
SDK 可以解包 IAM 的 `{ success, message, data }` 响应信封,也兼容裸 JSON 响应。它不实现 IAM 后端授权、回跳白名单或 JWKS token 校验。
|
package/dist/index.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ export interface LegacyAuthKitConfig {
|
|
|
68
68
|
allowedLoginOrigins?: readonly string[];
|
|
69
69
|
isLoginHost?: boolean;
|
|
70
70
|
}
|
|
71
|
-
export type AuthErrorCode = 'AUTH_DOMAIN_MISSING' | 'AUTH_DOMAIN_INVALID' | 'CALLBACK_URL_INVALID' | 'RETURN_TO_UNAVAILABLE' | 'AUTH_ENTRY_CALLBACK_LOOP' | 'LOGIN_REQUIRED' | 'INVALID_CREDENTIALS' | 'AUTH_CONTEXT_MISSING' | 'PRINCIPAL_CONTEXT_INVALID' | 'TENANT_CONTEXT_INVALID' | 'TENANT_ACCESS_DENIED' | 'APP_ACCESS_DENIED' | 'CALLBACK_REJECTED' | 'TOKEN_EXPIRED' | 'REFRESH_FAILED' | 'LOGOUT_FAILED' | 'AUTH_PROTOCOL_UNAVAILABLE';
|
|
71
|
+
export type AuthErrorCode = 'AUTH_DOMAIN_MISSING' | 'AUTH_DOMAIN_INVALID' | 'CALLBACK_URL_INVALID' | 'RETURN_TO_UNAVAILABLE' | 'AUTH_ENTRY_CALLBACK_LOOP' | 'LOGIN_REQUIRED' | 'INVALID_CREDENTIALS' | 'AUTH_CONTEXT_MISSING' | 'PRINCIPAL_CONTEXT_INVALID' | 'TENANT_CONTEXT_INVALID' | 'TENANT_ACCESS_DENIED' | 'TENANT_SWITCH_IN_PROGRESS' | 'APP_ACCESS_DENIED' | 'CALLBACK_REJECTED' | 'TOKEN_EXPIRED' | 'REFRESH_FAILED' | 'LOGOUT_FAILED' | 'AUTH_PROTOCOL_UNAVAILABLE';
|
|
72
72
|
export type AuthKitError = AuthErrorCode | 'LOGIN_ENTRY_URL_MISSING' | 'INVALID_LOGIN_ENTRY_URL' | 'LOGIN_ENTRY_ORIGIN_DENIED' | 'INVALID_CALLBACK' | 'CROSS_APP_CONTEXT' | 'IDENTITY_CONTEXT_MISSING';
|
|
73
73
|
export type AuthKitResult<T> = {
|
|
74
74
|
ok: true;
|
|
@@ -91,6 +91,13 @@ export interface AuthIdentity {
|
|
|
91
91
|
permissions: string[];
|
|
92
92
|
attrs?: Record<string, unknown>;
|
|
93
93
|
}
|
|
94
|
+
export interface TenantMembership {
|
|
95
|
+
membershipId: string;
|
|
96
|
+
tenantId: string;
|
|
97
|
+
tenantName?: string;
|
|
98
|
+
membershipStatus: string;
|
|
99
|
+
isDefault: boolean;
|
|
100
|
+
}
|
|
94
101
|
export interface AuthorizeOptions {
|
|
95
102
|
returnTo?: string;
|
|
96
103
|
app?: string;
|
|
@@ -109,6 +116,8 @@ export interface AuthClient {
|
|
|
109
116
|
refreshToken: () => Promise<AuthKitResult<LoginResult>>;
|
|
110
117
|
getIdentity: () => Promise<AuthKitResult<AuthIdentity>>;
|
|
111
118
|
getContext: () => Promise<AuthKitResult<AuthContext>>;
|
|
119
|
+
getMemberships: () => Promise<AuthKitResult<TenantMembership[]>>;
|
|
120
|
+
switchTenant: (membershipId: string) => Promise<AuthKitResult<LoginResult>>;
|
|
112
121
|
logout: () => Promise<AuthKitResult<void>>;
|
|
113
122
|
}
|
|
114
123
|
/** 清理不应保留在 history 或 referrer 的认证参数。 */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -121,6 +121,37 @@ function normalizeIdentity(value) {
|
|
|
121
121
|
...(attrs ? { attrs } : {}),
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
|
+
function normalizeBoolean(value) {
|
|
125
|
+
if (value === true || value === 1 || value === '1' || value === 'true')
|
|
126
|
+
return true;
|
|
127
|
+
if (value === false || value === 0 || value === '0' || value === 'false')
|
|
128
|
+
return false;
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
function normalizeMemberships(value) {
|
|
132
|
+
if (!isRecord(value) || !Array.isArray(value.memberships))
|
|
133
|
+
return null;
|
|
134
|
+
const memberships = [];
|
|
135
|
+
for (const item of value.memberships) {
|
|
136
|
+
if (!isRecord(item))
|
|
137
|
+
return null;
|
|
138
|
+
const membershipId = stringValue(item.membership_id)?.trim();
|
|
139
|
+
const tenantId = stringValue(item.tenant_id)?.trim();
|
|
140
|
+
const membershipStatus = stringValue(item.membership_status)?.trim();
|
|
141
|
+
const tenantName = stringValue(item.tenant_name)?.trim();
|
|
142
|
+
const isDefault = item.is_default === undefined ? false : normalizeBoolean(item.is_default);
|
|
143
|
+
if (!membershipId || !tenantId || !membershipStatus || isDefault === undefined)
|
|
144
|
+
return null;
|
|
145
|
+
memberships.push({
|
|
146
|
+
membershipId,
|
|
147
|
+
tenantId,
|
|
148
|
+
...(tenantName ? { tenantName } : {}),
|
|
149
|
+
membershipStatus,
|
|
150
|
+
isDefault,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return memberships;
|
|
154
|
+
}
|
|
124
155
|
/** 只消费后端 selected tenant;旧接口仅允许 is_default 兼容,不猜 joined_at 或身份。 */
|
|
125
156
|
export function resolveTenantDecision(input) {
|
|
126
157
|
const context = input.context;
|
|
@@ -167,6 +198,8 @@ export function create(config) {
|
|
|
167
198
|
const tokenStore = config.tokenStore ?? createMemoryStore();
|
|
168
199
|
const authDomain = config.authDomain.replace(/\/$/, '');
|
|
169
200
|
let refreshPending = null;
|
|
201
|
+
let membershipsPending = null;
|
|
202
|
+
let switchPending = null;
|
|
170
203
|
function validate() {
|
|
171
204
|
if (!config.authDomain)
|
|
172
205
|
return fail('AUTH_DOMAIN_MISSING');
|
|
@@ -260,23 +293,45 @@ export function create(config) {
|
|
|
260
293
|
refreshPending = runRefresh().finally(() => { refreshPending = null; });
|
|
261
294
|
return refreshPending;
|
|
262
295
|
}
|
|
263
|
-
async function
|
|
296
|
+
async function getAuthorizedJson(path, init = {}, forbiddenError = 'APP_ACCESS_DENIED', preserveLegacyErrors = false) {
|
|
264
297
|
const current = tokenStore.get();
|
|
265
298
|
if (!current?.accessToken)
|
|
266
299
|
return fail('LOGIN_REQUIRED');
|
|
300
|
+
let response;
|
|
301
|
+
try {
|
|
302
|
+
response = await fetchImpl(`${authDomain}${path}`, {
|
|
303
|
+
...init,
|
|
304
|
+
headers: {
|
|
305
|
+
Authorization: `Bearer ${current.accessToken}`,
|
|
306
|
+
...(init.body === undefined ? {} : { 'Content-Type': 'application/json' }),
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
return fail('AUTH_PROTOCOL_UNAVAILABLE', true);
|
|
312
|
+
}
|
|
313
|
+
if (!response.ok) {
|
|
314
|
+
if (response.status === 401)
|
|
315
|
+
return fail('TOKEN_EXPIRED');
|
|
316
|
+
if (response.status === 403)
|
|
317
|
+
return fail(forbiddenError);
|
|
318
|
+
if (preserveLegacyErrors)
|
|
319
|
+
return responseError(response.status);
|
|
320
|
+
return fail('AUTH_PROTOCOL_UNAVAILABLE', response.status === 429 || response.status >= 500);
|
|
321
|
+
}
|
|
267
322
|
try {
|
|
268
|
-
const response = await fetchImpl(`${authDomain}/api/v1/auth/me`, { headers: { Authorization: `Bearer ${current.accessToken}` } });
|
|
269
|
-
if (!response.ok)
|
|
270
|
-
return response.status === 401 ? fail('TOKEN_EXPIRED') : responseError(response.status);
|
|
271
323
|
const responseBody = unwrapResponse(await response.json());
|
|
272
324
|
if (!responseBody.ok)
|
|
273
325
|
return fail('AUTH_PROTOCOL_UNAVAILABLE');
|
|
274
326
|
return ok(responseBody.value);
|
|
275
327
|
}
|
|
276
328
|
catch {
|
|
277
|
-
return fail('AUTH_PROTOCOL_UNAVAILABLE',
|
|
329
|
+
return fail('AUTH_PROTOCOL_UNAVAILABLE', preserveLegacyErrors);
|
|
278
330
|
}
|
|
279
331
|
}
|
|
332
|
+
function getRemoteIdentity() {
|
|
333
|
+
return getAuthorizedJson('/api/v1/auth/me', {}, 'APP_ACCESS_DENIED', true);
|
|
334
|
+
}
|
|
280
335
|
async function getContext() {
|
|
281
336
|
const remoteIdentity = await getRemoteIdentity();
|
|
282
337
|
if (!remoteIdentity.ok)
|
|
@@ -295,11 +350,74 @@ export function create(config) {
|
|
|
295
350
|
const identity = normalizeIdentity(remoteIdentity.value);
|
|
296
351
|
return identity ? ok(identity) : fail('AUTH_PROTOCOL_UNAVAILABLE');
|
|
297
352
|
}
|
|
353
|
+
async function runGetMemberships() {
|
|
354
|
+
const result = await getAuthorizedJson('/api/v1/auth/memberships', {}, 'TENANT_ACCESS_DENIED');
|
|
355
|
+
if (!result.ok)
|
|
356
|
+
return result;
|
|
357
|
+
const memberships = normalizeMemberships(result.value);
|
|
358
|
+
return memberships ? ok(memberships) : fail('AUTH_PROTOCOL_UNAVAILABLE');
|
|
359
|
+
}
|
|
360
|
+
function getMemberships() {
|
|
361
|
+
if (!membershipsPending)
|
|
362
|
+
membershipsPending = runGetMemberships().finally(() => { membershipsPending = null; });
|
|
363
|
+
return membershipsPending;
|
|
364
|
+
}
|
|
365
|
+
async function runSwitchTenant(membershipId) {
|
|
366
|
+
const result = await getAuthorizedJson('/api/v1/auth/switch-tenant', {
|
|
367
|
+
method: 'POST',
|
|
368
|
+
body: JSON.stringify({ membership_id: membershipId }),
|
|
369
|
+
}, 'TENANT_ACCESS_DENIED');
|
|
370
|
+
if (!result.ok)
|
|
371
|
+
return result;
|
|
372
|
+
const tokens = toTokenSet(result.value);
|
|
373
|
+
if (!tokens?.refreshToken)
|
|
374
|
+
return fail('AUTH_PROTOCOL_UNAVAILABLE');
|
|
375
|
+
const context = isRecord(result.value) ? normalizeContext(result.value.context ?? result.value.auth_context) ?? undefined : undefined;
|
|
376
|
+
const identity = normalizeIdentity(result.value) ?? undefined;
|
|
377
|
+
tokenStore.set(tokens);
|
|
378
|
+
return ok({ ...tokens, context, identity });
|
|
379
|
+
}
|
|
380
|
+
function switchTenant(rawMembershipId) {
|
|
381
|
+
const membershipId = rawMembershipId.trim();
|
|
382
|
+
if (!membershipId)
|
|
383
|
+
return Promise.resolve(fail('TENANT_CONTEXT_INVALID'));
|
|
384
|
+
if (switchPending) {
|
|
385
|
+
return switchPending.membershipId === membershipId
|
|
386
|
+
? switchPending.promise
|
|
387
|
+
: Promise.resolve(fail('TENANT_SWITCH_IN_PROGRESS'));
|
|
388
|
+
}
|
|
389
|
+
const promise = runSwitchTenant(membershipId).finally(() => {
|
|
390
|
+
if (switchPending?.promise === promise)
|
|
391
|
+
switchPending = null;
|
|
392
|
+
});
|
|
393
|
+
switchPending = { membershipId, promise };
|
|
394
|
+
return promise;
|
|
395
|
+
}
|
|
298
396
|
async function logout() {
|
|
299
|
-
|
|
300
|
-
|
|
397
|
+
try {
|
|
398
|
+
const current = tokenStore.get();
|
|
399
|
+
if (!current?.refreshToken)
|
|
400
|
+
return ok(undefined);
|
|
401
|
+
const valid = validate();
|
|
402
|
+
if (!valid.ok)
|
|
403
|
+
return valid;
|
|
404
|
+
const response = await fetchImpl(`${authDomain}/api/v1/auth/logout`, {
|
|
405
|
+
method: 'POST',
|
|
406
|
+
headers: { 'Content-Type': 'application/json' },
|
|
407
|
+
body: JSON.stringify({ refresh_token: current.refreshToken }),
|
|
408
|
+
});
|
|
409
|
+
if (!response.ok)
|
|
410
|
+
return fail('LOGOUT_FAILED', response.status >= 408);
|
|
411
|
+
return ok(undefined);
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
return fail('LOGOUT_FAILED', true);
|
|
415
|
+
}
|
|
416
|
+
finally {
|
|
417
|
+
tokenStore.clear();
|
|
418
|
+
}
|
|
301
419
|
}
|
|
302
|
-
return { validate, buildAuthorizeUrl, login, refreshToken, getIdentity, getContext, logout };
|
|
420
|
+
return { validate, buildAuthorizeUrl, login, refreshToken, getIdentity, getContext, getMemberships, switchTenant, logout };
|
|
303
421
|
}
|
|
304
422
|
/** 创建旧版兼容实例;新业务接入请使用 create。 */
|
|
305
423
|
export function createAuthKit(config) {
|
package/package.json
CHANGED