@rebasepro/auth 0.0.1-canary.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/src/api.ts ADDED
@@ -0,0 +1,328 @@
1
+ import { AuthResponse, RefreshResponse, Session, UserInfo } from "./types";
2
+
3
+ /**
4
+ * Default API URL - can be overridden in hook props
5
+ */
6
+ let baseApiUrl = "";
7
+
8
+ /**
9
+ * Configure the API base URL
10
+ */
11
+ export function setApiUrl(url: string): void {
12
+ baseApiUrl = url;
13
+ }
14
+
15
+ /**
16
+ * Get the current API URL
17
+ */
18
+ export function getApiUrl(): string {
19
+ return baseApiUrl;
20
+ }
21
+
22
+ class AuthApiError extends Error {
23
+ code: string;
24
+
25
+ constructor(message: string, code: string) {
26
+ super(message);
27
+ this.code = code;
28
+ this.name = "AuthApiError";
29
+ }
30
+ }
31
+
32
+ async function handleResponse<T>(response: Response): Promise<T> {
33
+ let data: Record<string, unknown>;
34
+ try {
35
+ data = await response.json();
36
+ } catch (parseError) {
37
+ // Response wasn't JSON - could be network error or server issue
38
+ throw new AuthApiError(
39
+ `Server returned non-JSON response (status: ${response.status})`,
40
+ "PARSE_ERROR"
41
+ );
42
+ }
43
+
44
+ if (!response.ok) {
45
+ throw new AuthApiError(
46
+ (data as Record<string, Record<string, string>>).error?.message || "Request failed",
47
+ (data as Record<string, Record<string, string>>).error?.code || "UNKNOWN_ERROR"
48
+ );
49
+ }
50
+
51
+ return data as T;
52
+ }
53
+
54
+ /**
55
+ * Wrapper for fetch that catches generic network failures (like server down)
56
+ * and translates them to an AuthApiError.
57
+ */
58
+ async function fetchWithHandling(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
59
+ try {
60
+ return await fetch(input, init);
61
+ } catch (error: unknown) {
62
+ if (error instanceof TypeError && error.message.includes("Failed to fetch")) {
63
+ throw new AuthApiError(
64
+ "Failed to connect to the backend server. The backend might be down or failed to initialize (e.g., database connection timeout).",
65
+ "NETWORK_ERROR"
66
+ );
67
+ }
68
+ throw new AuthApiError("Network error: " + (error instanceof Error ? error.message : String(error)), "NETWORK_ERROR");
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Register a new user with email/password
74
+ */
75
+ export async function register(
76
+ email: string,
77
+ password: string,
78
+ displayName?: string
79
+ ): Promise<AuthResponse> {
80
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/register`, {
81
+ method: "POST",
82
+ headers: { "Content-Type": "application/json" },
83
+ body: JSON.stringify({ email, password, displayName })
84
+ });
85
+
86
+ return handleResponse<AuthResponse>(response);
87
+ }
88
+
89
+ /**
90
+ * Login with email/password
91
+ */
92
+ export async function login(email: string, password: string): Promise<AuthResponse> {
93
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/login`, {
94
+ method: "POST",
95
+ headers: { "Content-Type": "application/json" },
96
+ body: JSON.stringify({ email, password })
97
+ });
98
+
99
+ return handleResponse<AuthResponse>(response);
100
+ }
101
+
102
+ /**
103
+ * Login with Google ID token
104
+ */
105
+ export async function googleLogin(idToken: string): Promise<AuthResponse> {
106
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/google`, {
107
+ method: "POST",
108
+ headers: { "Content-Type": "application/json" },
109
+ body: JSON.stringify({ idToken })
110
+ });
111
+
112
+ return handleResponse<AuthResponse>(response);
113
+ }
114
+
115
+ /**
116
+ * Refresh access token using refresh token
117
+ */
118
+ export async function refreshAccessToken(refreshToken: string): Promise<RefreshResponse> {
119
+ console.log("[AUTH-API] Calling refresh endpoint...");
120
+
121
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/refresh`, {
122
+ method: "POST",
123
+ headers: { "Content-Type": "application/json" },
124
+ body: JSON.stringify({ refreshToken })
125
+ });
126
+
127
+ console.log("[AUTH-API] Refresh response status:", response.status);
128
+ return handleResponse<RefreshResponse>(response);
129
+ }
130
+
131
+ /**
132
+ * Logout and invalidate refresh token
133
+ */
134
+ export async function logout(refreshToken?: string): Promise<void> {
135
+ await fetchWithHandling(`${baseApiUrl}/api/auth/logout`, {
136
+ method: "POST",
137
+ headers: { "Content-Type": "application/json" },
138
+ body: JSON.stringify({ refreshToken })
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Get current user info
144
+ */
145
+ export async function getCurrentUser(accessToken: string): Promise<{ user: UserInfo }> {
146
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/me`, {
147
+ method: "GET",
148
+ headers: {
149
+ "Content-Type": "application/json",
150
+ "Authorization": `Bearer ${accessToken}`
151
+ }
152
+ });
153
+
154
+ return handleResponse<{ user: UserInfo }>(response);
155
+ }
156
+
157
+ /**
158
+ * Request password reset email
159
+ */
160
+ export async function forgotPassword(email: string): Promise<{ success: boolean; message: string }> {
161
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/forgot-password`, {
162
+ method: "POST",
163
+ headers: { "Content-Type": "application/json" },
164
+ body: JSON.stringify({ email })
165
+ });
166
+
167
+ return handleResponse<{ success: boolean; message: string }>(response);
168
+ }
169
+
170
+ /**
171
+ * Reset password using token from email
172
+ */
173
+ export async function resetPassword(token: string, password: string): Promise<{ success: boolean; message: string }> {
174
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/reset-password`, {
175
+ method: "POST",
176
+ headers: { "Content-Type": "application/json" },
177
+ body: JSON.stringify({ token, password })
178
+ });
179
+
180
+ return handleResponse<{ success: boolean; message: string }>(response);
181
+ }
182
+
183
+ /**
184
+ * Change password for authenticated user
185
+ */
186
+ export async function changePassword(
187
+ accessToken: string,
188
+ oldPassword: string,
189
+ newPassword: string
190
+ ): Promise<{ success: boolean; message: string }> {
191
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/change-password`, {
192
+ method: "POST",
193
+ headers: {
194
+ "Content-Type": "application/json",
195
+ "Authorization": `Bearer ${accessToken}`
196
+ },
197
+ body: JSON.stringify({ oldPassword, newPassword })
198
+ });
199
+
200
+ return handleResponse<{ success: boolean; message: string }>(response);
201
+ }
202
+
203
+ /**
204
+ * Send email verification link
205
+ */
206
+ export async function sendVerificationEmail(accessToken: string): Promise<{ success: boolean; message: string }> {
207
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/send-verification`, {
208
+ method: "POST",
209
+ headers: {
210
+ "Content-Type": "application/json",
211
+ "Authorization": `Bearer ${accessToken}`
212
+ }
213
+ });
214
+
215
+ return handleResponse<{ success: boolean; message: string }>(response);
216
+ }
217
+
218
+ /**
219
+ * Verify email address using token
220
+ */
221
+ export async function verifyEmail(token: string): Promise<{ success: boolean; message: string }> {
222
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/verify-email?token=${encodeURIComponent(token)}`, {
223
+ method: "GET",
224
+ headers: { "Content-Type": "application/json" }
225
+ });
226
+
227
+ return handleResponse<{ success: boolean; message: string }>(response);
228
+ }
229
+
230
+ /**
231
+ * Update current user profile
232
+ */
233
+ export async function updateProfile(
234
+ accessToken: string,
235
+ displayName?: string,
236
+ photoURL?: string
237
+ ): Promise<{ user: UserInfo }> {
238
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/me`, {
239
+ method: "PATCH",
240
+ headers: {
241
+ "Content-Type": "application/json",
242
+ "Authorization": `Bearer ${accessToken}`
243
+ },
244
+ body: JSON.stringify({ displayName, photoURL })
245
+ });
246
+
247
+ return handleResponse<{ user: UserInfo }>(response);
248
+ }
249
+
250
+ /**
251
+ * Fetch active sessions for current user
252
+ */
253
+ export async function fetchSessions(accessToken: string, currentRefreshToken?: string): Promise<{ sessions: Session[] }> {
254
+ const headers: Record<string, string> = {
255
+ "Content-Type": "application/json",
256
+ "Authorization": `Bearer ${accessToken}`
257
+ };
258
+ if (currentRefreshToken) {
259
+ headers["X-Refresh-Token"] = currentRefreshToken;
260
+ }
261
+
262
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/sessions`, {
263
+ method: "GET",
264
+ headers
265
+ });
266
+
267
+ return handleResponse<{ sessions: Session[] }>(response);
268
+ }
269
+
270
+ /**
271
+ * Revoke a specific session
272
+ */
273
+ export async function revokeSession(accessToken: string, sessionId: string): Promise<{ success: boolean; message: string }> {
274
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/sessions/${sessionId}`, {
275
+ method: "DELETE",
276
+ headers: {
277
+ "Content-Type": "application/json",
278
+ "Authorization": `Bearer ${accessToken}`
279
+ }
280
+ });
281
+
282
+ return handleResponse<{ success: boolean; message: string }>(response);
283
+ }
284
+
285
+ /**
286
+ * Revoke all sessions for current user
287
+ */
288
+ export async function revokeAllSessions(accessToken: string): Promise<{ success: boolean; message: string }> {
289
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/sessions`, {
290
+ method: "DELETE",
291
+ headers: {
292
+ "Content-Type": "application/json",
293
+ "Authorization": `Bearer ${accessToken}`
294
+ }
295
+ });
296
+
297
+ return handleResponse<{ success: boolean; message: string }>(response);
298
+ }
299
+
300
+ /**
301
+ * Auth config response from the backend
302
+ */
303
+ export interface AuthConfigResponse {
304
+ /** True when there are no users in the system and first user setup is needed */
305
+ needsSetup: boolean;
306
+ /** Whether new user registration is enabled */
307
+ registrationEnabled: boolean;
308
+ /** Whether Google OAuth is configured */
309
+ googleEnabled: boolean;
310
+ /** Whether email service is configured */
311
+ emailServiceEnabled: boolean;
312
+ }
313
+
314
+ /**
315
+ * Fetch auth configuration / status from the backend
316
+ * This is an unauthenticated endpoint used to detect bootstrap mode
317
+ */
318
+ export async function fetchAuthConfig(): Promise<AuthConfigResponse> {
319
+ const response = await fetchWithHandling(`${baseApiUrl}/api/auth/config`, {
320
+ method: "GET",
321
+ headers: { "Content-Type": "application/json" }
322
+ });
323
+
324
+ return handleResponse<AuthConfigResponse>(response);
325
+ }
326
+
327
+ export { AuthApiError };
328
+