@companyio/auth-client 0.1.3 → 0.1.4

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 ADDED
@@ -0,0 +1,23 @@
1
+ # @companyio/auth-client
2
+
3
+ Platform-neutral client for password sign-in, signup, session restoration, logout, current-user requests, and organization lookup.
4
+
5
+ ## Setup
6
+
7
+ ```ts
8
+ import { AuthClient, createBrowserStorage } from '@companyio/auth-client';
9
+
10
+ const authClient = new AuthClient({
11
+ baseUrl: import.meta.env.VITE_API_URL ?? 'http://localhost:3000',
12
+ clientId: 'my-app',
13
+ storage: createBrowserStorage(),
14
+ });
15
+ ```
16
+
17
+ Use `createMemoryStorage()` for tests or short-lived sessions. The client expects the API under `/api/v1` and sends bearer tokens automatically.
18
+
19
+ ## Build
20
+
21
+ ```bash
22
+ pnpm --filter @companyio/auth-client build
23
+ ```
package/package.json CHANGED
@@ -1,18 +1,28 @@
1
1
  {
2
2
  "name": "@companyio/auth-client",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Platform-neutral authentication and user-management client",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
8
18
  "dependencies": {
9
- "@companyio/auth-contracts": "0.1.3"
19
+ "@companyio/auth-contracts": "0.1.4"
10
20
  },
11
21
  "devDependencies": {
12
22
  "typescript": "^5.3.3"
13
23
  },
14
24
  "scripts": {
15
25
  "build": "tsc",
16
- "test": "exit 0"
26
+ "test": "vitest run"
17
27
  }
18
28
  }
package/src/index.ts DELETED
@@ -1,184 +0,0 @@
1
- import {
2
- AuthClientConfig,
3
- AuthError,
4
- AuthSession,
5
- Organization,
6
- SignInInput,
7
- SignUpInput,
8
- SessionSchema,
9
- TokenStorage,
10
- User,
11
- UserSchema,
12
- } from '@companyio/auth-contracts';
13
-
14
- const memoryStorage = (): TokenStorage => {
15
- let token: string | null = null;
16
-
17
- return {
18
- get: async () => token,
19
- set: async (value) => {
20
- token = value;
21
- },
22
- clear: async () => {
23
- token = null;
24
- },
25
- };
26
- };
27
-
28
- export const createMemoryStorage = memoryStorage;
29
-
30
- export class AuthClient {
31
- private readonly baseUrl: string;
32
- private readonly clientId: string;
33
- private readonly storage: TokenStorage;
34
- private readonly requestFetch: typeof fetch;
35
- private sessionValue: AuthSession | null = null;
36
- private listeners = new Set<(session: AuthSession | null) => void>();
37
-
38
- constructor(config: AuthClientConfig) {
39
- const configuredBaseUrl = config.baseUrl.replace(/\/$/, '');
40
- this.baseUrl = configuredBaseUrl.endsWith('/api/v1')
41
- ? configuredBaseUrl
42
- : `${configuredBaseUrl}/api/v1`;
43
- this.clientId = config.clientId;
44
- this.storage = config.storage ?? memoryStorage();
45
- this.requestFetch = (config.fetch ?? globalThis.fetch).bind(globalThis);
46
- }
47
-
48
- get session(): AuthSession | null {
49
- return this.sessionValue;
50
- }
51
-
52
- subscribe(listener: (session: AuthSession | null) => void): () => void {
53
- this.listeners.add(listener);
54
- return () => this.listeners.delete(listener);
55
- }
56
-
57
- async restore(): Promise<AuthSession | null> {
58
- const token = await this.storage.get();
59
- if (!token) return null;
60
-
61
- try {
62
- const session = await this.request<AuthSession>('/auth/session', { method: 'GET' });
63
- this.setSession(SessionSchema.parse(session));
64
- return this.sessionValue;
65
- } catch {
66
- await this.signOut();
67
- return null;
68
- }
69
- }
70
-
71
- async signIn(provider = 'default'): Promise<void> {
72
- const response = await this.request<{ authorizationUrl: string }>('/auth/authorize', {
73
- method: 'POST',
74
- body: JSON.stringify({ clientId: this.clientId, provider }),
75
- });
76
-
77
- if (typeof window !== 'undefined') {
78
- window.location.assign(response.authorizationUrl);
79
- }
80
- }
81
-
82
- async signInWithPassword(input: SignInInput): Promise<AuthSession> {
83
- const session = SessionSchema.parse(
84
- await this.request<AuthSession>('/auth/sign-in', {
85
- method: 'POST',
86
- body: JSON.stringify(input),
87
- })
88
- );
89
- this.setSession(session);
90
- return session;
91
- }
92
-
93
- async signUp(input: SignUpInput): Promise<AuthSession> {
94
- const session = SessionSchema.parse(
95
- await this.request<AuthSession>('/auth/sign-up', {
96
- method: 'POST',
97
- body: JSON.stringify(input),
98
- })
99
- );
100
- this.setSession(session);
101
- return session;
102
- }
103
-
104
- async completeSignIn(code: string, redirectUri: string): Promise<AuthSession> {
105
- const session = SessionSchema.parse(
106
- await this.request<AuthSession>('/auth/callback', {
107
- method: 'POST',
108
- body: JSON.stringify({ clientId: this.clientId, code, redirectUri }),
109
- })
110
- );
111
- this.setSession(session);
112
- return session;
113
- }
114
-
115
- async getCurrentUser(): Promise<User> {
116
- return UserSchema.parse(await this.request<User>('/users/me'));
117
- }
118
-
119
- async getOrganizations(): Promise<Organization[]> {
120
- return this.request<Organization[]>('/organizations');
121
- }
122
-
123
- async signOut(): Promise<void> {
124
- try {
125
- await this.request('/auth/logout', { method: 'POST' });
126
- } finally {
127
- await this.storage.clear();
128
- this.setSession(null);
129
- }
130
- }
131
-
132
- private setSession(session: AuthSession | null): void {
133
- this.sessionValue = session;
134
- session ? void this.storage.set(session.accessToken) : void this.storage.clear();
135
- this.listeners.forEach((listener) => listener(session));
136
- }
137
-
138
- private async request<T>(path: string, init: RequestInit = {}): Promise<T> {
139
- const token = await this.storage.get();
140
- let response: Response;
141
-
142
- try {
143
- response = await this.requestFetch(`${this.baseUrl}${path}`, {
144
- ...init,
145
- headers: {
146
- 'Content-Type': 'application/json',
147
- 'X-Client-Id': this.clientId,
148
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
149
- ...init.headers,
150
- },
151
- });
152
- } catch {
153
- throw {
154
- code: 'NETWORK_ERROR',
155
- message: `Unable to reach the API at ${this.baseUrl}. Start the API with "pnpm --filter @companyio/api dev" and try again.`,
156
- } satisfies AuthError;
157
- }
158
-
159
- if (!response.ok) {
160
- throw await this.toAuthError(response);
161
- }
162
-
163
- if (response.status === 204) return undefined as T;
164
- return (await response.json()) as T;
165
- }
166
-
167
- private async toAuthError(response: Response): Promise<AuthError> {
168
- const body = (await response.json().catch(() => null)) as { message?: string } | null;
169
- const code: AuthError['code'] =
170
- response.status === 401
171
- ? 'UNAUTHENTICATED'
172
- : response.status === 403
173
- ? 'FORBIDDEN'
174
- : response.status < 500
175
- ? 'INVALID_REQUEST'
176
- : 'UNKNOWN';
177
-
178
- return {
179
- code,
180
- message: body?.message ?? 'Authentication request failed.',
181
- status: response.status,
182
- };
183
- }
184
- }
package/tsconfig.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src",
6
- "declaration": true,
7
- "declarationMap": true
8
- },
9
- "include": ["src"],
10
- "exclude": ["node_modules", "dist"]
11
- }