@instroc/auth 1.0.0 → 1.0.1

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/index.d.ts CHANGED
@@ -1,107 +1,159 @@
1
1
  interface AuthUser {
2
- id: string;
3
- email: string;
4
- email_verified: boolean;
5
- display_name: string | null;
6
- avatar_url: string | null;
7
- metadata: Record<string, unknown>;
8
- created_at: string;
2
+ id: string;
3
+ email: string;
4
+ email_verified: boolean;
5
+ display_name: string | null;
6
+ avatar_url: string | null;
7
+ metadata: Record<string, unknown>;
8
+ created_at: string;
9
9
  }
10
10
  interface AuthSession {
11
- access_token: string;
12
- refresh_token: string;
13
- expires_at: string;
11
+ access_token: string;
12
+ refresh_token: string;
13
+ expires_at: string;
14
14
  }
15
15
  interface AuthState {
16
- user: AuthUser | null;
17
- session: AuthSession | null;
18
- loading: boolean;
19
- error: string | null;
16
+ user: AuthUser | null;
17
+ session: AuthSession | null;
18
+ loading: boolean;
19
+ error: string | null;
20
20
  }
21
21
  interface LoginCredentials {
22
- email: string;
23
- password: string;
22
+ email: string;
23
+ password: string;
24
24
  }
25
25
  interface SignupCredentials {
26
- email: string;
27
- password: string;
28
- displayName?: string;
29
- metadata?: Record<string, unknown>;
26
+ email: string;
27
+ password: string;
28
+ displayName?: string;
29
+ metadata?: Record<string, unknown>;
30
30
  }
31
31
  type OAuthProvider = "google" | "github" | "microsoft" | "facebook";
32
32
  interface AuthConfig {
33
- emailAuthEnabled: boolean;
34
- googleAuthEnabled: boolean;
35
- githubAuthEnabled: boolean;
36
- microsoftAuthEnabled: boolean;
37
- facebookAuthEnabled: boolean;
38
- allowSignup: boolean;
39
- requireEmailVerification: boolean;
33
+ emailAuthEnabled: boolean;
34
+ googleAuthEnabled: boolean;
35
+ githubAuthEnabled: boolean;
36
+ microsoftAuthEnabled: boolean;
37
+ facebookAuthEnabled: boolean;
38
+ allowSignup: boolean;
39
+ requireEmailVerification: boolean;
40
40
  }
41
41
  interface VisibilityConfig {
42
- projectName: string;
43
- visibility: "public" | "private";
44
- requireLogin: boolean;
45
- logoUrl: string | null;
46
- welcomeMessage: string | null;
42
+ projectName: string;
43
+ visibility: "public" | "private";
44
+ requireLogin: boolean;
45
+ logoUrl: string | null;
46
+ welcomeMessage: string | null;
47
47
  }
48
+ /**
49
+ * Result of signup() — indicates what post-signup flow the caller should show.
50
+ *
51
+ * - `status: "authenticated"` — user is logged in, session is active. Redirect to app.
52
+ * - `status: "needs_verification"` — email verification OTP was sent. Show OTP screen.
53
+ * - `status: "needs_approval"` — account pending admin approval. Show waiting screen.
54
+ */
55
+ type SignupResult =
56
+ | {
57
+ status: "authenticated";
58
+ user: AuthUser;
59
+ }
60
+ | {
61
+ status: "needs_verification";
62
+ email: string;
63
+ }
64
+ | {
65
+ status: "needs_approval";
66
+ user: AuthUser;
67
+ };
48
68
  interface AuthContextValue extends AuthState {
49
- login: (credentials: LoginCredentials) => Promise<void>;
50
- signup: (credentials: SignupCredentials) => Promise<void>;
51
- logout: () => Promise<void>;
52
- signInWithOAuth: (provider: OAuthProvider) => void;
53
- verifyOTP: (email: string, code: string) => Promise<void>;
54
- resendOTP: (email: string) => Promise<void>;
55
- updateProfile: (data: UpdateProfileData) => Promise<void>;
56
- refreshSession: () => Promise<void>;
57
- forgotPassword: (email: string) => Promise<void>;
58
- resetPassword: (token: string, newPassword: string) => Promise<void>;
59
- setProjectId: (projectId: string) => void;
60
- projectId: string | null;
61
- authConfig: AuthConfig | null;
62
- visibilityConfig: VisibilityConfig | null;
69
+ login: (credentials: LoginCredentials) => Promise<void>;
70
+ signup: (credentials: SignupCredentials) => Promise<SignupResult>;
71
+ logout: () => Promise<void>;
72
+ signInWithOAuth: (provider: OAuthProvider) => void;
73
+ verifyOTP: (email: string, code: string) => Promise<void>;
74
+ resendOTP: (email: string) => Promise<void>;
75
+ updateProfile: (data: UpdateProfileData) => Promise<void>;
76
+ refreshSession: () => Promise<void>;
77
+ forgotPassword: (email: string) => Promise<void>;
78
+ resetPassword: (token: string, newPassword: string) => Promise<void>;
79
+ setProjectId: (projectId: string) => void;
80
+ projectId: string | null;
81
+ authConfig: AuthConfig | null;
82
+ visibilityConfig: VisibilityConfig | null;
63
83
  }
64
84
  interface UpdateProfileData {
65
- displayName?: string;
66
- avatarUrl?: string;
67
- metadata?: Record<string, unknown>;
85
+ displayName?: string;
86
+ avatarUrl?: string;
87
+ metadata?: Record<string, unknown>;
68
88
  }
69
89
  interface AuthProviderProps {
70
- children: React.ReactNode;
71
- projectId?: string;
72
- baseUrl?: string;
73
- persistSession?: boolean;
74
- onAuthStateChange?: (user: AuthUser | null) => void;
90
+ children: React.ReactNode;
91
+ projectId?: string;
92
+ baseUrl?: string;
93
+ persistSession?: boolean;
94
+ onAuthStateChange?: (user: AuthUser | null) => void;
75
95
  }
76
96
  interface AuthResponse {
77
- user: AuthUser;
78
- session: AuthSession;
97
+ user: AuthUser;
98
+ session: AuthSession;
99
+ /** Set when signup requires email verification — session is a placeholder with empty tokens. */
100
+ needsVerification?: boolean;
101
+ /** Set when signup succeeded but account is pending admin approval (private apps). */
102
+ needsApproval?: boolean;
79
103
  }
80
104
  interface RefreshResponse {
81
- access_token: string;
82
- expires_at: string;
105
+ access_token: string;
106
+ expires_at: string;
83
107
  }
84
108
 
85
- declare function AuthProvider({ children, projectId: initialProjectId, baseUrl, persistSession, onAuthStateChange, }: AuthProviderProps): JSX.Element;
109
+ declare function AuthProvider({
110
+ children,
111
+ projectId: initialProjectId,
112
+ baseUrl,
113
+ persistSession,
114
+ onAuthStateChange,
115
+ }: AuthProviderProps): JSX.Element;
86
116
  declare function useAuthContext(): AuthContextValue;
87
117
 
88
118
  declare function useAuth(): AuthContextValue;
89
119
  declare function useUser(): {
90
- user: AuthUser | null;
91
- loading: boolean;
120
+ user: AuthUser | null;
121
+ loading: boolean;
92
122
  };
93
123
  declare function useSession(): {
94
- session: AuthSession | null;
95
- loading: boolean;
96
- };
97
- declare function useAuthRequired(): {
98
- user: null;
99
- session: null;
100
- loading: boolean;
101
- } | {
102
- user: AuthUser;
103
- session: AuthSession | null;
104
- loading: boolean;
124
+ session: AuthSession | null;
125
+ loading: boolean;
105
126
  };
127
+ declare function useAuthRequired():
128
+ | {
129
+ user: null;
130
+ session: null;
131
+ loading: boolean;
132
+ }
133
+ | {
134
+ user: AuthUser;
135
+ session: AuthSession | null;
136
+ loading: boolean;
137
+ };
106
138
 
107
- export { type AuthConfig, type AuthContextValue, AuthProvider, type AuthProviderProps, type AuthResponse, type AuthSession, type AuthState, type AuthUser, type LoginCredentials, type OAuthProvider, type RefreshResponse, type SignupCredentials, type UpdateProfileData, type VisibilityConfig, useAuth, useAuthContext, useAuthRequired, useSession, useUser };
139
+ export {
140
+ type AuthConfig,
141
+ type AuthContextValue,
142
+ AuthProvider,
143
+ type AuthProviderProps,
144
+ type AuthResponse,
145
+ type AuthSession,
146
+ type AuthState,
147
+ type AuthUser,
148
+ type LoginCredentials,
149
+ type OAuthProvider,
150
+ type RefreshResponse,
151
+ type SignupCredentials,
152
+ type UpdateProfileData,
153
+ type VisibilityConfig,
154
+ useAuth,
155
+ useAuthContext,
156
+ useAuthRequired,
157
+ useSession,
158
+ useUser,
159
+ };
package/dist/index.js CHANGED
@@ -36,6 +36,10 @@ function AuthProvider({
36
36
  },
37
37
  [projectId, baseUrl]
38
38
  );
39
+ const authFetch = useCallback(
40
+ (url, init) => fetch(url, { ...init, credentials: "include" }),
41
+ []
42
+ );
39
43
  const saveSession = useCallback(
40
44
  (sessionData) => {
41
45
  if (persistSession && typeof window !== "undefined") {
@@ -74,7 +78,7 @@ function AuthProvider({
74
78
  try {
75
79
  await refreshSession();
76
80
  } catch (err) {
77
- console.error("Token refresh failed:", err);
81
+ console.warn("Token refresh failed:", err);
78
82
  setUser(null);
79
83
  setSession(null);
80
84
  saveSession(null);
@@ -105,7 +109,7 @@ function AuthProvider({
105
109
  return;
106
110
  }
107
111
  try {
108
- const response = await fetch(buildUrl("me"), {
112
+ const response = await authFetch(buildUrl("me"), {
109
113
  headers: {
110
114
  Authorization: `Bearer ${storedSession.access_token}`
111
115
  }
@@ -115,7 +119,7 @@ function AuthProvider({
115
119
  updateAuthState(data.user, storedSession);
116
120
  } else if (response.status === 401) {
117
121
  try {
118
- const refreshResponse = await fetch(buildUrl("refresh"), {
122
+ const refreshResponse = await authFetch(buildUrl("refresh"), {
119
123
  method: "POST",
120
124
  headers: { "Content-Type": "application/json" },
121
125
  body: JSON.stringify({ refreshToken: storedSession.refresh_token })
@@ -127,7 +131,7 @@ function AuthProvider({
127
131
  access_token: refreshData.access_token,
128
132
  expires_at: refreshData.expires_at
129
133
  };
130
- const userResponse = await fetch(buildUrl("me"), {
134
+ const userResponse = await authFetch(buildUrl("me"), {
131
135
  headers: {
132
136
  Authorization: `Bearer ${newSession.access_token}`
133
137
  }
@@ -171,7 +175,7 @@ function AuthProvider({
171
175
  setError(null);
172
176
  setLoading(true);
173
177
  try {
174
- const response = await fetch(buildUrl("login"), {
178
+ const response = await authFetch(buildUrl("login"), {
175
179
  method: "POST",
176
180
  headers: { "Content-Type": "application/json" },
177
181
  body: JSON.stringify(credentials)
@@ -197,7 +201,7 @@ function AuthProvider({
197
201
  setError(null);
198
202
  setLoading(true);
199
203
  try {
200
- const response = await fetch(buildUrl("signup"), {
204
+ const response = await authFetch(buildUrl("signup"), {
201
205
  method: "POST",
202
206
  headers: { "Content-Type": "application/json" },
203
207
  body: JSON.stringify(credentials)
@@ -207,11 +211,24 @@ function AuthProvider({
207
211
  throw new Error(data.error || "Signup failed");
208
212
  }
209
213
  const authResponse = data;
214
+ if (authResponse.needsVerification) {
215
+ return {
216
+ status: "needs_verification",
217
+ email: credentials.email
218
+ };
219
+ }
220
+ if (authResponse.needsApproval) {
221
+ updateAuthState(authResponse.user, authResponse.session);
222
+ return { status: "needs_approval", user: authResponse.user };
223
+ }
210
224
  if (authResponse.session.access_token) {
211
225
  updateAuthState(authResponse.user, authResponse.session);
212
- } else {
213
- setUser(authResponse.user);
226
+ return { status: "authenticated", user: authResponse.user };
214
227
  }
228
+ return {
229
+ status: "needs_verification",
230
+ email: credentials.email
231
+ };
215
232
  } catch (err) {
216
233
  const message = err instanceof Error ? err.message : "Signup failed";
217
234
  setError(message);
@@ -225,7 +242,7 @@ function AuthProvider({
225
242
  const logout = useCallback(async () => {
226
243
  try {
227
244
  if (session) {
228
- await fetch(buildUrl("logout"), {
245
+ await authFetch(buildUrl("logout"), {
229
246
  method: "POST",
230
247
  headers: {
231
248
  Authorization: `Bearer ${session.access_token}`
@@ -248,7 +265,7 @@ function AuthProvider({
248
265
  }
249
266
  setError(null);
250
267
  try {
251
- const response = await fetch(buildUrl("me"), {
268
+ const response = await authFetch(buildUrl("me"), {
252
269
  method: "PATCH",
253
270
  headers: {
254
271
  "Content-Type": "application/json",
@@ -270,10 +287,9 @@ function AuthProvider({
270
287
  [session, buildUrl]
271
288
  );
272
289
  const refreshSession = useCallback(async () => {
273
- if (!session) {
274
- throw new Error("No session to refresh");
275
- }
276
- const response = await fetch(buildUrl("refresh"), {
290
+ if (!session)
291
+ return;
292
+ const response = await authFetch(buildUrl("refresh"), {
277
293
  method: "POST",
278
294
  headers: { "Content-Type": "application/json" },
279
295
  body: JSON.stringify({ refreshToken: session.refresh_token })
@@ -294,7 +310,7 @@ function AuthProvider({
294
310
  async (email) => {
295
311
  setError(null);
296
312
  try {
297
- const response = await fetch(buildUrl("forgot-password"), {
313
+ const response = await authFetch(buildUrl("forgot-password"), {
298
314
  method: "POST",
299
315
  headers: { "Content-Type": "application/json" },
300
316
  body: JSON.stringify({ email })
@@ -315,7 +331,7 @@ function AuthProvider({
315
331
  async (token, newPassword) => {
316
332
  setError(null);
317
333
  try {
318
- const response = await fetch(buildUrl("reset-password"), {
334
+ const response = await authFetch(buildUrl("reset-password"), {
319
335
  method: "POST",
320
336
  headers: { "Content-Type": "application/json" },
321
337
  body: JSON.stringify({ token, password: newPassword })
@@ -349,7 +365,7 @@ function AuthProvider({
349
365
  setError(null);
350
366
  setLoading(true);
351
367
  try {
352
- const response = await fetch(buildUrl("verify-email"), {
368
+ const response = await authFetch(buildUrl("verify-email"), {
353
369
  method: "POST",
354
370
  headers: { "Content-Type": "application/json" },
355
371
  body: JSON.stringify({ email, otp: code })
@@ -375,7 +391,7 @@ function AuthProvider({
375
391
  async (email) => {
376
392
  setError(null);
377
393
  try {
378
- const response = await fetch(buildUrl("resend-verification"), {
394
+ const response = await authFetch(buildUrl("resend-verification"), {
379
395
  method: "POST",
380
396
  headers: { "Content-Type": "application/json" },
381
397
  body: JSON.stringify({ email })
@@ -396,9 +412,7 @@ function AuthProvider({
396
412
  if (!projectId)
397
413
  return;
398
414
  try {
399
- const response = await fetch(
400
- `${baseUrl}/${projectId}/auth/config`
401
- );
415
+ const response = await fetch(`${baseUrl}/${projectId}/auth/config`);
402
416
  if (response.ok) {
403
417
  const data = await response.json();
404
418
  setAuthConfig(data.config || null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instroc/auth",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Authentication hooks for Instroc Cloud — useAuth, useUser, AuthProvider",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,7 +11,9 @@
11
11
  "types": "./dist/index.d.ts"
12
12
  }
13
13
  },
14
- "files": ["dist"],
14
+ "files": [
15
+ "dist"
16
+ ],
15
17
  "scripts": {
16
18
  "build": "tsup",
17
19
  "dev": "tsup --watch"