@longzai-intelligence-auth/sdk 0.0.2
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-client.d.ts +38 -0
- package/dist/auth-client.js +95 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/token-interceptor.d.ts +4 -0
- package/dist/token-interceptor.js +36 -0
- package/dist/token-storage.d.ts +11 -0
- package/dist/token-storage.js +64 -0
- package/dist/types/auth-state.types.d.ts +13 -0
- package/dist/types/auth-state.types.js +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { LoginRequest, LoginResponse, RegisterRequest, RegisterResponse, RefreshTokenRequest, RefreshTokenResponse, PasswordResetRequest, PasswordResetConfirmRequest, ChangePasswordRequest, MessageResponse } from "@longzai-intelligence-auth/core";
|
|
2
|
+
import type { AuthUser } from "./types/auth-state.types";
|
|
3
|
+
export type FetchFn = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
4
|
+
export type AuthStrategyType = "jwt" | "standalone";
|
|
5
|
+
export type AuthClientOptions = {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
apiPrefix?: string;
|
|
8
|
+
strategy?: AuthStrategyType;
|
|
9
|
+
fetchFn?: FetchFn;
|
|
10
|
+
};
|
|
11
|
+
export type BaseAuthClient = {
|
|
12
|
+
login(data: LoginRequest): Promise<LoginResponse>;
|
|
13
|
+
register(data: RegisterRequest): Promise<RegisterResponse>;
|
|
14
|
+
refreshToken(data: RefreshTokenRequest): Promise<RefreshTokenResponse>;
|
|
15
|
+
logout(accessToken: string): Promise<MessageResponse>;
|
|
16
|
+
getProfile(accessToken: string): Promise<{
|
|
17
|
+
user: AuthUser;
|
|
18
|
+
}>;
|
|
19
|
+
changePassword(accessToken: string, data: ChangePasswordRequest): Promise<MessageResponse>;
|
|
20
|
+
requestPasswordReset(data: PasswordResetRequest): Promise<MessageResponse>;
|
|
21
|
+
confirmPasswordReset(data: PasswordResetConfirmRequest): Promise<MessageResponse>;
|
|
22
|
+
};
|
|
23
|
+
export type JwtAuthClient = BaseAuthClient & {
|
|
24
|
+
verifyToken(accessToken: string): Promise<{
|
|
25
|
+
valid: boolean;
|
|
26
|
+
payload?: Record<string, unknown>;
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
29
|
+
export type AuthClient<T extends AuthStrategyType = "standalone"> = T extends "jwt" ? JwtAuthClient : BaseAuthClient;
|
|
30
|
+
export declare class AuthClientError extends Error {
|
|
31
|
+
code: string;
|
|
32
|
+
statusCode: number;
|
|
33
|
+
constructor(code: string, message: string, statusCode: number);
|
|
34
|
+
}
|
|
35
|
+
export declare function createAuthClient<T extends AuthStrategyType>(options: AuthClientOptions & {
|
|
36
|
+
strategy: T;
|
|
37
|
+
}): T extends "jwt" ? JwtAuthClient : BaseAuthClient;
|
|
38
|
+
export declare function createAuthClient(options: AuthClientOptions): BaseAuthClient;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export class AuthClientError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
statusCode;
|
|
4
|
+
constructor(code, message, statusCode) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "AuthClientError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.statusCode = statusCode;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
async function httpGet(baseUrl, apiPrefix, fetchFn, path, accessToken) {
|
|
12
|
+
const headers = { "Content-Type": "application/json" };
|
|
13
|
+
if (accessToken) {
|
|
14
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
15
|
+
}
|
|
16
|
+
const response = await fetchFn(`${baseUrl}${apiPrefix}${path}`, {
|
|
17
|
+
method: "GET",
|
|
18
|
+
headers,
|
|
19
|
+
});
|
|
20
|
+
return handleResponse(response);
|
|
21
|
+
}
|
|
22
|
+
async function httpPost(baseUrl, apiPrefix, fetchFn, path, body, accessToken) {
|
|
23
|
+
const headers = { "Content-Type": "application/json" };
|
|
24
|
+
if (accessToken) {
|
|
25
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
26
|
+
}
|
|
27
|
+
const response = await fetchFn(`${baseUrl}${apiPrefix}${path}`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers,
|
|
30
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
31
|
+
});
|
|
32
|
+
return handleResponse(response);
|
|
33
|
+
}
|
|
34
|
+
async function httpPut(baseUrl, apiPrefix, fetchFn, path, body, accessToken) {
|
|
35
|
+
const response = await fetchFn(`${baseUrl}${apiPrefix}${path}`, {
|
|
36
|
+
method: "PUT",
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
Authorization: `Bearer ${accessToken}`,
|
|
40
|
+
},
|
|
41
|
+
body: JSON.stringify(body),
|
|
42
|
+
});
|
|
43
|
+
return handleResponse(response);
|
|
44
|
+
}
|
|
45
|
+
async function handleResponse(response) {
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const error = await response.json().catch(() => ({ message: "请求失败" }));
|
|
48
|
+
throw new AuthClientError(error.code ?? "UNKNOWN_ERROR", error.message ?? "请求失败", response.status);
|
|
49
|
+
}
|
|
50
|
+
return response.json();
|
|
51
|
+
}
|
|
52
|
+
function createBaseAuthClient(baseUrl, apiPrefix, fetchFn) {
|
|
53
|
+
return {
|
|
54
|
+
async login(data) {
|
|
55
|
+
return httpPost(baseUrl, apiPrefix, fetchFn, "/auth/login", data);
|
|
56
|
+
},
|
|
57
|
+
async register(data) {
|
|
58
|
+
return httpPost(baseUrl, apiPrefix, fetchFn, "/auth/register", data);
|
|
59
|
+
},
|
|
60
|
+
async refreshToken(data) {
|
|
61
|
+
return httpPost(baseUrl, apiPrefix, fetchFn, "/auth/refresh", data);
|
|
62
|
+
},
|
|
63
|
+
async logout(accessToken) {
|
|
64
|
+
return httpPost(baseUrl, apiPrefix, fetchFn, "/auth/logout", undefined, accessToken);
|
|
65
|
+
},
|
|
66
|
+
async getProfile(accessToken) {
|
|
67
|
+
return httpGet(baseUrl, apiPrefix, fetchFn, "/users/me", accessToken);
|
|
68
|
+
},
|
|
69
|
+
async changePassword(accessToken, data) {
|
|
70
|
+
return httpPut(baseUrl, apiPrefix, fetchFn, "/users/me/password", data, accessToken);
|
|
71
|
+
},
|
|
72
|
+
async requestPasswordReset(data) {
|
|
73
|
+
return httpPost(baseUrl, apiPrefix, fetchFn, "/auth/password-reset/request", data);
|
|
74
|
+
},
|
|
75
|
+
async confirmPasswordReset(data) {
|
|
76
|
+
return httpPost(baseUrl, apiPrefix, fetchFn, "/auth/password-reset/confirm", data);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function createAuthClient(options) {
|
|
81
|
+
const baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
82
|
+
const apiPrefix = options.apiPrefix ?? "/api/v1";
|
|
83
|
+
const strategy = options.strategy ?? "standalone";
|
|
84
|
+
const fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
85
|
+
const base = createBaseAuthClient(baseUrl, apiPrefix, fetchFn);
|
|
86
|
+
if (strategy === "jwt") {
|
|
87
|
+
return {
|
|
88
|
+
...base,
|
|
89
|
+
async verifyToken(accessToken) {
|
|
90
|
+
return httpPost(baseUrl, apiPrefix, fetchFn, "/auth/verify", undefined, accessToken);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return base;
|
|
95
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createAuthClient, AuthClientError } from "./auth-client";
|
|
2
|
+
export type { AuthClientOptions, AuthStrategyType, FetchFn, BaseAuthClient, JwtAuthClient, AuthClient } from "./auth-client";
|
|
3
|
+
export { createTokenStorage } from "./token-storage";
|
|
4
|
+
export type { TokenStorage } from "./token-storage";
|
|
5
|
+
export { createTokenInterceptor, createRefreshInterceptor } from "./token-interceptor";
|
|
6
|
+
export type { StorageType, AuthUser, SessionState, } from "./types/auth-state.types";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TokenStorage } from "./token-storage";
|
|
2
|
+
import type { BaseAuthClient, FetchFn } from "./auth-client";
|
|
3
|
+
export declare function createTokenInterceptor(storage: TokenStorage, fetchFn?: FetchFn): FetchFn;
|
|
4
|
+
export declare function createRefreshInterceptor(storage: TokenStorage, client: BaseAuthClient, fetchFn?: FetchFn): FetchFn;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function createTokenInterceptor(storage, fetchFn = globalThis.fetch) {
|
|
2
|
+
return async (input, init) => {
|
|
3
|
+
const token = storage.getToken();
|
|
4
|
+
const headers = new Headers(init?.headers);
|
|
5
|
+
if (token && !headers.has("Authorization")) {
|
|
6
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
7
|
+
}
|
|
8
|
+
return fetchFn(input, { ...init, headers });
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function createRefreshInterceptor(storage, client, fetchFn = globalThis.fetch) {
|
|
12
|
+
const tokenFetch = createTokenInterceptor(storage, fetchFn);
|
|
13
|
+
return async (input, init) => {
|
|
14
|
+
const response = await tokenFetch(input, init);
|
|
15
|
+
if (response.status === 401) {
|
|
16
|
+
const refreshToken = storage.getRefreshToken();
|
|
17
|
+
if (!refreshToken) {
|
|
18
|
+
storage.clear();
|
|
19
|
+
return response;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const tokens = await client.refreshToken({ refreshToken });
|
|
23
|
+
storage.setToken(tokens.accessToken);
|
|
24
|
+
storage.setRefreshToken(tokens.refreshToken);
|
|
25
|
+
const newHeaders = new Headers(init?.headers);
|
|
26
|
+
newHeaders.set("Authorization", `Bearer ${tokens.accessToken}`);
|
|
27
|
+
return fetchFn(input, { ...init, headers: newHeaders });
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
storage.clear();
|
|
31
|
+
return response;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return response;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { StorageType } from "./types/auth-state.types";
|
|
2
|
+
export type TokenStorage = {
|
|
3
|
+
getToken(): string | null;
|
|
4
|
+
setToken(token: string): void;
|
|
5
|
+
removeToken(): void;
|
|
6
|
+
getRefreshToken(): string | null;
|
|
7
|
+
setRefreshToken(token: string): void;
|
|
8
|
+
removeRefreshToken(): void;
|
|
9
|
+
clear(): void;
|
|
10
|
+
};
|
|
11
|
+
export declare function createTokenStorage(type: StorageType): TokenStorage;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export function createTokenStorage(type) {
|
|
2
|
+
switch (type) {
|
|
3
|
+
case "localStorage":
|
|
4
|
+
return new LocalStorageAdapter("localStorage");
|
|
5
|
+
case "sessionStorage":
|
|
6
|
+
return new LocalStorageAdapter("sessionStorage");
|
|
7
|
+
case "memory":
|
|
8
|
+
return new MemoryStorageAdapter();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
class LocalStorageAdapter {
|
|
12
|
+
store;
|
|
13
|
+
constructor(storeType) {
|
|
14
|
+
this.store = globalThis[storeType];
|
|
15
|
+
}
|
|
16
|
+
getToken() {
|
|
17
|
+
return this.store.getItem("lzi_access_token");
|
|
18
|
+
}
|
|
19
|
+
setToken(token) {
|
|
20
|
+
this.store.setItem("lzi_access_token", token);
|
|
21
|
+
}
|
|
22
|
+
removeToken() {
|
|
23
|
+
this.store.removeItem("lzi_access_token");
|
|
24
|
+
}
|
|
25
|
+
getRefreshToken() {
|
|
26
|
+
return this.store.getItem("lzi_refresh_token");
|
|
27
|
+
}
|
|
28
|
+
setRefreshToken(token) {
|
|
29
|
+
this.store.setItem("lzi_refresh_token", token);
|
|
30
|
+
}
|
|
31
|
+
removeRefreshToken() {
|
|
32
|
+
this.store.removeItem("lzi_refresh_token");
|
|
33
|
+
}
|
|
34
|
+
clear() {
|
|
35
|
+
this.removeToken();
|
|
36
|
+
this.removeRefreshToken();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
class MemoryStorageAdapter {
|
|
40
|
+
accessToken = null;
|
|
41
|
+
refreshToken = null;
|
|
42
|
+
getToken() {
|
|
43
|
+
return this.accessToken;
|
|
44
|
+
}
|
|
45
|
+
setToken(token) {
|
|
46
|
+
this.accessToken = token;
|
|
47
|
+
}
|
|
48
|
+
removeToken() {
|
|
49
|
+
this.accessToken = null;
|
|
50
|
+
}
|
|
51
|
+
getRefreshToken() {
|
|
52
|
+
return this.refreshToken;
|
|
53
|
+
}
|
|
54
|
+
setRefreshToken(token) {
|
|
55
|
+
this.refreshToken = token;
|
|
56
|
+
}
|
|
57
|
+
removeRefreshToken() {
|
|
58
|
+
this.refreshToken = null;
|
|
59
|
+
}
|
|
60
|
+
clear() {
|
|
61
|
+
this.removeToken();
|
|
62
|
+
this.removeRefreshToken();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { UserInfo } from "@longzai-intelligence-auth/core";
|
|
2
|
+
export type StorageType = "localStorage" | "sessionStorage" | "memory";
|
|
3
|
+
export interface AuthUser extends UserInfo {
|
|
4
|
+
email: string;
|
|
5
|
+
name: string;
|
|
6
|
+
avatar?: string;
|
|
7
|
+
}
|
|
8
|
+
export type SessionState = {
|
|
9
|
+
user: AuthUser | null;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
isAuthenticated: boolean;
|
|
12
|
+
error: string | null;
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@longzai-intelligence-auth/sdk",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"license": "UNLICENSED",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "bun build src/index.ts --outdir dist --target bun",
|
|
30
|
+
"build:declaration": "tsgo --declaration --emitDeclarationOnly --outDir dist -p tsconfig/app.json",
|
|
31
|
+
"build:prod": "NODE_ENV=production tsdown",
|
|
32
|
+
"prepublishOnly": "bun run build:prod",
|
|
33
|
+
"typecheck": "bun run typecheck:app && bun run typecheck:node && bun run typecheck:test",
|
|
34
|
+
"typecheck:app": "tsgo --noEmit -p tsconfig/app.json",
|
|
35
|
+
"typecheck:node": "tsgo --noEmit -p tsconfig/node.json",
|
|
36
|
+
"typecheck:test": "tsgo --noEmit -p tsconfig/test.json",
|
|
37
|
+
"lint": "oxlint && oxfmt --check",
|
|
38
|
+
"lint:fix": "oxlint --fix && oxfmt",
|
|
39
|
+
"test": "bun test",
|
|
40
|
+
"test:watch": "bun test --watch",
|
|
41
|
+
"test:coverage": "bun test --coverage",
|
|
42
|
+
"clean": "rm -rf dist out .cache"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@longzai-intelligence-auth/core": "0.0.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {}
|
|
48
|
+
}
|