@tern-secure/nextjs 4.0.0 → 4.2.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/dist/cjs/app-router/client/TernSecureProvider.js +17 -2
- package/dist/cjs/app-router/client/TernSecureProvider.js.map +1 -1
- package/dist/cjs/app-router/client/actions.js +49 -49
- package/dist/cjs/app-router/client/actions.js.map +1 -1
- package/dist/cjs/app-router/route-handler/internal-route.js +17 -2
- package/dist/cjs/app-router/route-handler/internal-route.js.map +1 -1
- package/dist/cjs/app-router/server/auth.js +42 -28
- package/dist/cjs/app-router/server/auth.js.map +1 -1
- package/dist/cjs/app-router/server/edge-session.js +80 -0
- package/dist/cjs/app-router/server/edge-session.js.map +1 -0
- package/dist/cjs/app-router/server/index.js +4 -0
- package/dist/cjs/app-router/server/index.js.map +1 -1
- package/dist/cjs/app-router/server/jwt.js +141 -0
- package/dist/cjs/app-router/server/jwt.js.map +1 -0
- package/dist/cjs/app-router/server/sessionTernSecure.js +14 -9
- package/dist/cjs/app-router/server/sessionTernSecure.js.map +1 -1
- package/dist/cjs/app-router/server/ternSecureMiddleware.js +134 -13
- package/dist/cjs/app-router/server/ternSecureMiddleware.js.map +1 -1
- package/dist/cjs/boundary/TernSecureClientProvider.js +163 -40
- package/dist/cjs/boundary/TernSecureClientProvider.js.map +1 -1
- package/dist/cjs/boundary/TernSecureCtx.js.map +1 -1
- package/dist/cjs/boundary/hooks/useAuth.js +7 -8
- package/dist/cjs/boundary/hooks/useAuth.js.map +1 -1
- package/dist/cjs/components/sign-in.js +136 -45
- package/dist/cjs/components/sign-in.js.map +1 -1
- package/dist/cjs/components/sign-out-button.js +10 -1
- package/dist/cjs/components/sign-out-button.js.map +1 -1
- package/dist/cjs/components/sign-out.js +12 -3
- package/dist/cjs/components/sign-out.js.map +1 -1
- package/dist/cjs/components/sign-up.js +10 -5
- package/dist/cjs/components/sign-up.js.map +1 -1
- package/dist/cjs/errors.js +232 -5
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/index.js +0 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types.js +14 -0
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils/construct.js +50 -18
- package/dist/cjs/utils/construct.js.map +1 -1
- package/dist/cjs/utils/redirect.js +57 -0
- package/dist/cjs/utils/redirect.js.map +1 -0
- package/dist/esm/app-router/client/TernSecureProvider.js +17 -2
- package/dist/esm/app-router/client/TernSecureProvider.js.map +1 -1
- package/dist/esm/app-router/client/actions.js +59 -51
- package/dist/esm/app-router/client/actions.js.map +1 -1
- package/dist/esm/app-router/route-handler/internal-route.js +13 -1
- package/dist/esm/app-router/route-handler/internal-route.js.map +1 -1
- package/dist/esm/app-router/server/auth.js +40 -28
- package/dist/esm/app-router/server/auth.js.map +1 -1
- package/dist/esm/app-router/server/edge-session.js +56 -0
- package/dist/esm/app-router/server/edge-session.js.map +1 -0
- package/dist/esm/app-router/server/index.js +4 -2
- package/dist/esm/app-router/server/index.js.map +1 -1
- package/dist/esm/app-router/server/jwt.js +117 -0
- package/dist/esm/app-router/server/jwt.js.map +1 -0
- package/dist/esm/app-router/server/sessionTernSecure.js +14 -9
- package/dist/esm/app-router/server/sessionTernSecure.js.map +1 -1
- package/dist/esm/app-router/server/ternSecureMiddleware.js +132 -13
- package/dist/esm/app-router/server/ternSecureMiddleware.js.map +1 -1
- package/dist/esm/boundary/TernSecureClientProvider.js +164 -41
- package/dist/esm/boundary/TernSecureClientProvider.js.map +1 -1
- package/dist/esm/boundary/TernSecureCtx.js.map +1 -1
- package/dist/esm/boundary/hooks/useAuth.js +7 -8
- package/dist/esm/boundary/hooks/useAuth.js.map +1 -1
- package/dist/esm/components/sign-in.js +137 -46
- package/dist/esm/components/sign-in.js.map +1 -1
- package/dist/esm/components/sign-out-button.js +11 -2
- package/dist/esm/components/sign-out-button.js.map +1 -1
- package/dist/esm/components/sign-out.js +13 -4
- package/dist/esm/components/sign-out.js.map +1 -1
- package/dist/esm/components/sign-up.js +10 -5
- package/dist/esm/components/sign-up.js.map +1 -1
- package/dist/esm/errors.js +228 -4
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +0 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.js +6 -0
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils/construct.js +46 -17
- package/dist/esm/utils/construct.js.map +1 -1
- package/dist/esm/utils/redirect.js +32 -0
- package/dist/esm/utils/redirect.js.map +1 -0
- package/dist/types/app-router/client/TernSecureProvider.d.ts +14 -3
- package/dist/types/app-router/client/TernSecureProvider.d.ts.map +1 -1
- package/dist/types/app-router/client/actions.d.ts +23 -21
- package/dist/types/app-router/client/actions.d.ts.map +1 -1
- package/dist/types/app-router/route-handler/internal-route.d.ts +3 -0
- package/dist/types/app-router/route-handler/internal-route.d.ts.map +1 -1
- package/dist/types/app-router/server/auth.d.ts +13 -1
- package/dist/types/app-router/server/auth.d.ts.map +1 -1
- package/dist/types/app-router/server/edge-session.d.ts +15 -0
- package/dist/types/app-router/server/edge-session.d.ts.map +1 -0
- package/dist/types/app-router/server/index.d.ts +3 -2
- package/dist/types/app-router/server/index.d.ts.map +1 -1
- package/dist/types/app-router/server/jwt.d.ts +20 -0
- package/dist/types/app-router/server/jwt.d.ts.map +1 -0
- package/dist/types/app-router/server/sessionTernSecure.d.ts +4 -1
- package/dist/types/app-router/server/sessionTernSecure.d.ts.map +1 -1
- package/dist/types/app-router/server/ternSecureMiddleware.d.ts +17 -4
- package/dist/types/app-router/server/ternSecureMiddleware.d.ts.map +1 -1
- package/dist/types/boundary/TernSecureClientProvider.d.ts +17 -1
- package/dist/types/boundary/TernSecureClientProvider.d.ts.map +1 -1
- package/dist/types/boundary/TernSecureCtx.d.ts +3 -1
- package/dist/types/boundary/TernSecureCtx.d.ts.map +1 -1
- package/dist/types/boundary/hooks/useAuth.d.ts +4 -1
- package/dist/types/boundary/hooks/useAuth.d.ts.map +1 -1
- package/dist/types/components/sign-in.d.ts +1 -2
- package/dist/types/components/sign-in.d.ts.map +1 -1
- package/dist/types/components/sign-out-button.d.ts +2 -1
- package/dist/types/components/sign-out-button.d.ts.map +1 -1
- package/dist/types/components/sign-out.d.ts +2 -1
- package/dist/types/components/sign-out.d.ts.map +1 -1
- package/dist/types/components/sign-up.d.ts.map +1 -1
- package/dist/types/components/ui/alert.d.ts +1 -1
- package/dist/types/components/ui/button.d.ts +1 -1
- package/dist/types/errors.d.ts +36 -2
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types.d.ts +35 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/construct.d.ts +20 -4
- package/dist/types/utils/construct.d.ts.map +1 -1
- package/dist/types/utils/redirect.d.ts +9 -0
- package/dist/types/utils/redirect.d.ts.map +1 -0
- package/package.json +7 -6
- package/dist/cjs/boundary/hooks/useUser.js +0 -44
- package/dist/cjs/boundary/hooks/useUser.js.map +0 -1
- package/dist/esm/boundary/hooks/useUser.js +0 -20
- package/dist/esm/boundary/hooks/useUser.js.map +0 -1
- package/dist/types/boundary/hooks/useUser.d.ts +0 -7
- package/dist/types/boundary/hooks/useUser.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/app-router/server/sessionTernSecure.ts"],"sourcesContent":["'use server'\n\nimport { cookies } from 'next/headers';\nimport { adminTernSecureAuth as adminAuth } from '../../utils/admin-init';\n\ninterface FirebaseAuthError extends Error {\n code?: string;\n}\n\nexport interface User {\n uid: string | null;\n email: string | null;\n }\n\nexport interface Session {\n user: User | null;\n token: string | null;\n error: Error | null;\n}\n\nexport async function createSessionCookie(idToken: string) {\n try {\n const expiresIn = 60 * 60 * 24 * 5 * 1000;\n const sessionCookie = await adminAuth.createSessionCookie(idToken, { expiresIn });\n\n const cookieStore = await cookies();\n cookieStore.set('_session_cookie', sessionCookie, {\n maxAge: expiresIn,\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n path: '/',\n });\n return { success: true, message: 'Session created' };\n } catch (error) {\n return { success: false, message: 'Failed to create session' };\n }\n}\n\n\n\nexport async function getServerSessionCookie() {\n const cookieStore = await cookies();\n const sessionCookie = cookieStore.get('_session_cookie')?.value;\n\n if (!sessionCookie) {\n throw new Error('No session cookie found')\n }\n \n try {\n const decondeClaims = await adminAuth.verifySessionCookie(sessionCookie, true)\n return {\n token: sessionCookie,\n userId: decondeClaims.uid\n }\n } catch (error) {\n console.error('Error verifying session:', error)\n throw new Error('Invalid Session')\n }\n}\n\n\nexport async function getIdToken() {\n const cookieStore = await cookies();\n const token = cookieStore.get('_session_token')?.value;\n\n if (!token) {\n throw new Error('No session cookie found')\n }\n \n try {\n const decodedClaims = await adminAuth.verifyIdToken(token)\n return {\n token: token,\n userId: decodedClaims.uid\n }\n } catch (error) {\n console.error('Error verifying session:', error)\n throw new Error('Invalid Session')\n }\n}\n\nexport async function setServerSession(token: string) {\n const cookieStore = await cookies();\n cookieStore.set('
|
|
1
|
+
{"version":3,"sources":["../../../../src/app-router/server/sessionTernSecure.ts"],"sourcesContent":["'use server'\n\nimport { cookies } from 'next/headers';\nimport { adminTernSecureAuth as adminAuth } from '../../utils/admin-init';\n\ninterface FirebaseAuthError extends Error {\n code?: string;\n}\n\nexport interface User {\n uid: string | null;\n email: string | null;\n }\n\nexport interface Session {\n user: User | null;\n token: string | null;\n error: Error | null;\n}\n\nexport async function createSessionCookie(idToken: string) {\n try {\n const expiresIn = 60 * 60 * 24 * 5 * 1000;\n const sessionCookie = await adminAuth.createSessionCookie(idToken, { expiresIn });\n\n const cookieStore = await cookies();\n cookieStore.set('_session_cookie', sessionCookie, {\n maxAge: expiresIn,\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n path: '/',\n });\n return { success: true, message: 'Session created' };\n } catch (error) {\n return { success: false, message: 'Failed to create session' };\n }\n}\n\n\n\nexport async function getServerSessionCookie() {\n const cookieStore = await cookies();\n const sessionCookie = cookieStore.get('_session_cookie')?.value;\n\n if (!sessionCookie) {\n throw new Error('No session cookie found')\n }\n \n try {\n const decondeClaims = await adminAuth.verifySessionCookie(sessionCookie, true)\n return {\n token: sessionCookie,\n userId: decondeClaims.uid\n }\n } catch (error) {\n console.error('Error verifying session:', error)\n throw new Error('Invalid Session')\n }\n}\n\n\nexport async function getIdToken() {\n const cookieStore = await cookies();\n const token = cookieStore.get('_session_token')?.value;\n\n if (!token) {\n throw new Error('No session cookie found')\n }\n \n try {\n const decodedClaims = await adminAuth.verifyIdToken(token)\n return {\n token: token,\n userId: decodedClaims.uid\n }\n } catch (error) {\n console.error('Error verifying session:', error)\n throw new Error('Invalid Session')\n }\n}\n\nexport async function setServerSession(token: string) {\n try {\n const cookieStore = await cookies();\n cookieStore.set('_session_token', token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'strict',\n maxAge: 60 * 60, // 1 hour\n path: '/',\n });\n return { success: true, message: 'Session created' };\n } catch {\n return { success: false, message: 'Failed to create session' };\n }\n}\n\n export async function verifyTernIdToken(token: string): Promise<{ valid: boolean; uid?: string; error?: string }> {\n try {\n const decodedToken = await adminAuth.verifyIdToken(token, true);\n return { valid: true, uid: decodedToken.uid };\n } catch (error) {\n if (error instanceof Error) {\n const firebaseError = error as FirebaseAuthError;\n if (error.name === 'FirebaseAuthError') {\n // Handle specific Firebase Auth errors\n switch (firebaseError.code) {\n case 'auth/id-token-expired':\n return { valid: false, error: 'Token has expired' };\n case 'auth/id-token-revoked':\n return { valid: false, error: 'Token has been revoked' };\n case 'auth/user-disabled':\n return { valid: false, error: 'User account has been disabled' };\n default:\n return { valid: false, error: 'Invalid token' };\n }\n }\n }\n return { valid: false, error: 'Error verifying token' };\n }\n }\n \n\n export async function verifyTernSessionCookie(session: string): Promise<{ valid: boolean; uid?: any; error?: any }>{\n try {\n const res = await adminAuth.verifySessionCookie(session, true);\n if (res) {\n return { valid: true, uid: res.uid };\n } else {\n return { valid: false, error: 'Invalid session'};\n }\n } catch (error) {\n return {error: error, valid: false}\n }\n }\n\n\n export async function clearSessionCookie() {\n const cookieStore = await cookies()\n \n cookieStore.delete('_session_cookie')\n cookieStore.delete('_session_token')\n cookieStore.delete('_session')\n \n try {\n // Verify if there's an active session before revoking\n const sessionCookie = cookieStore.get('_session_cookie')?.value\n if (sessionCookie) {\n // Get the decoded claims to get the user's ID\n const decodedClaims = await adminAuth.verifySessionCookie(sessionCookie)\n \n // Revoke all sessions for the user\n await adminAuth.revokeRefreshTokens(decodedClaims.uid)\n }\n \n return { success: true, message: 'Session cleared successfully' }\n } catch (error) {\n console.error('Error clearing session:', error)\n // Still return success even if revoking fails, as cookies are cleared\n return { success: true, message: 'Session cookies cleared' }\n }\n }\n\n\n\n/*\n export async function GET(request: NextRequest) {\n const cookieStore = await cookies();\n const sessionCookie = cookieStore.get('session')?.value\n \n if (!sessionCookie) {\n return NextResponse.json({ isAuthenticated: false }, { status: 401 })\n }\n \n try {\n const decodedClaims = await adminAuth.verifySessionCookie(sessionCookie, true)\n return NextResponse.json({ isAuthenticated: true, user: decodedClaims }, { status: 200 })\n } catch (error) {\n console.error('Error verifying session cookie:', error)\n return NextResponse.json({ isAuthenticated: false }, { status: 401 })\n }\n }\n\n*/"],"mappings":"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","names":[]}
|
|
@@ -1,26 +1,145 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { cookies } from "next/headers";
|
|
3
|
+
import { verifySession } from "./edge-session";
|
|
4
|
+
import { verifyFirebaseToken } from "./jwt";
|
|
5
|
+
const runtime = "edge";
|
|
6
|
+
function createRouteMatcher(patterns) {
|
|
7
|
+
return (request) => {
|
|
8
|
+
const { pathname } = request.nextUrl;
|
|
9
|
+
return patterns.some((pattern) => {
|
|
10
|
+
const regexPattern = new RegExp(
|
|
11
|
+
`^${pattern.replace(/\*/g, ".*").replace(/\((.*)\)/, "(?:$1)?")}$`
|
|
12
|
+
);
|
|
13
|
+
return regexPattern.test(pathname);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async function edgeAuth(request) {
|
|
18
|
+
var _a, _b, _c, _d, _e, _f;
|
|
19
|
+
const cookieStore = await cookies();
|
|
20
|
+
async function protect() {
|
|
6
21
|
const { pathname } = request.nextUrl;
|
|
7
|
-
|
|
8
|
-
|
|
22
|
+
throw new Error("Unauthorized access");
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const sessionCookie = (_a = cookieStore.get("_session_cookie")) == null ? void 0 : _a.value;
|
|
26
|
+
if (sessionCookie) {
|
|
27
|
+
const userInfo = await verifyFirebaseToken(sessionCookie, true);
|
|
28
|
+
console.log("userInfo", userInfo);
|
|
29
|
+
if (userInfo.valid) {
|
|
30
|
+
return {
|
|
31
|
+
user: {
|
|
32
|
+
uid: (_b = userInfo.uid) != null ? _b : "",
|
|
33
|
+
email: (_c = userInfo.email) != null ? _c : null
|
|
34
|
+
},
|
|
35
|
+
token: sessionCookie,
|
|
36
|
+
protect: async () => {
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
9
40
|
}
|
|
41
|
+
const idToken = (_d = cookieStore.get("_session_token")) == null ? void 0 : _d.value;
|
|
42
|
+
if (idToken) {
|
|
43
|
+
const userInfo = await verifyFirebaseToken(idToken, false);
|
|
44
|
+
if (userInfo.valid) {
|
|
45
|
+
return {
|
|
46
|
+
user: {
|
|
47
|
+
uid: (_e = userInfo.uid) != null ? _e : "",
|
|
48
|
+
email: (_f = userInfo.email) != null ? _f : null
|
|
49
|
+
},
|
|
50
|
+
token: idToken,
|
|
51
|
+
protect: async () => {
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
user: null,
|
|
58
|
+
token: null,
|
|
59
|
+
protect
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return {
|
|
63
|
+
user: null,
|
|
64
|
+
token: null,
|
|
65
|
+
protect
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function edgeAuthSecond(request) {
|
|
70
|
+
var _a, _b;
|
|
71
|
+
async function protect() {
|
|
72
|
+
throw new Error("Unauthorized access");
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const sessionResult = await verifySession(request);
|
|
76
|
+
if (sessionResult.isAuthenticated && sessionResult.user) {
|
|
77
|
+
return {
|
|
78
|
+
user: sessionResult.user,
|
|
79
|
+
token: ((_a = request.cookies.get("_session_cookie")) == null ? void 0 : _a.value) || ((_b = request.cookies.get("_session_token")) == null ? void 0 : _b.value) || null,
|
|
80
|
+
protect: async () => {
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
user: null,
|
|
86
|
+
token: null,
|
|
87
|
+
protect
|
|
88
|
+
};
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error("Auth check error:", error);
|
|
91
|
+
return {
|
|
92
|
+
user: null,
|
|
93
|
+
token: null,
|
|
94
|
+
protect
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function ternSecureMiddleware(callback) {
|
|
99
|
+
return async function middleware(request) {
|
|
10
100
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
101
|
+
const auth = await edgeAuthSecond(request);
|
|
102
|
+
try {
|
|
103
|
+
await callback(auth, request);
|
|
104
|
+
const response = NextResponse.next();
|
|
105
|
+
if (auth.user) {
|
|
106
|
+
response.headers.set("x-user-id", auth.user.uid);
|
|
107
|
+
if (auth.user.email) {
|
|
108
|
+
response.headers.set("x-user-email", auth.user.email);
|
|
109
|
+
}
|
|
110
|
+
if (auth.user.emailVerified !== void 0) {
|
|
111
|
+
response.headers.set("x-email-verified", auth.user.emailVerified.toString());
|
|
112
|
+
}
|
|
113
|
+
if (auth.user.authTime) {
|
|
114
|
+
response.headers.set("x-auth-time", auth.user.authTime.toString());
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return response;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error instanceof Error && error.message === "Unauthorized access") {
|
|
120
|
+
const redirectUrl = new URL("/sign-in", request.url);
|
|
121
|
+
redirectUrl.searchParams.set("redirect", request.nextUrl.pathname);
|
|
122
|
+
return NextResponse.redirect(redirectUrl);
|
|
123
|
+
}
|
|
124
|
+
throw error;
|
|
14
125
|
}
|
|
15
|
-
const response = NextResponse.next();
|
|
16
|
-
return response;
|
|
17
126
|
} catch (error) {
|
|
18
|
-
console.error("
|
|
19
|
-
|
|
127
|
+
console.error("Middleware error:", {
|
|
128
|
+
error: error instanceof Error ? {
|
|
129
|
+
name: error.name,
|
|
130
|
+
message: error.message,
|
|
131
|
+
stack: error.stack
|
|
132
|
+
} : error,
|
|
133
|
+
path: request.nextUrl.pathname
|
|
134
|
+
});
|
|
135
|
+
const redirectUrl = new URL("/sign-in", request.url);
|
|
136
|
+
return NextResponse.redirect(redirectUrl);
|
|
20
137
|
}
|
|
21
138
|
};
|
|
22
139
|
}
|
|
23
140
|
export {
|
|
141
|
+
createRouteMatcher,
|
|
142
|
+
runtime,
|
|
24
143
|
ternSecureMiddleware
|
|
25
144
|
};
|
|
26
145
|
//# sourceMappingURL=ternSecureMiddleware.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/app-router/server/ternSecureMiddleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/app-router/server/ternSecureMiddleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { cookies } from 'next/headers'\nimport { verifySession, type UserInfo } from './edge-session'\nimport { verifyFirebaseToken } from './jwt';\n\nexport const runtime = \"edge\"\n\n\ninterface Auth {\n user: UserInfo | null\n token: string | null\n protect: () => Promise<void>\n}\n\ntype MiddlewareCallback = (\n auth: Auth,\n request: NextRequest\n) => Promise<void>\n\n\n/**\n * Create a route matcher function for public paths\n */\nexport function createRouteMatcher(patterns: string[]) {\n return (request: NextRequest): boolean => {\n const { pathname } = request.nextUrl\n return patterns.some(pattern => {\n // Convert route pattern to regex\n const regexPattern = new RegExp(\n `^${pattern.replace(/\\*/g, '.*').replace(/\\((.*)\\)/, '(?:$1)?')}$`\n )\n return regexPattern.test(pathname)\n })\n }\n}\n\n\n/**\n * Edge-compatible auth check\n */\nasync function edgeAuth(request: NextRequest): Promise<Auth>{\n const cookieStore = await cookies()\n\n async function protect() {\n const { pathname } = request.nextUrl\n throw new Error('Unauthorized access')\n }\n\n try {\n // First try session cookie\n const sessionCookie = cookieStore.get(\"_session_cookie\")?.value\n if (sessionCookie) {\n const userInfo = await verifyFirebaseToken(sessionCookie, true)\n console.log('userInfo', userInfo)\n if (userInfo.valid) {\n return { \n user: {\n uid: userInfo.uid ?? '',\n email: userInfo.email ?? null,\n },\n token: sessionCookie,\n protect: async () => {}\n }\n }\n }\n\n // Then try ID token\n const idToken = cookieStore.get(\"_session_token\")?.value\n if (idToken) {\n const userInfo = await verifyFirebaseToken(idToken, false)\n if (userInfo.valid) {\n return { \n user: {\n uid: userInfo.uid ?? '',\n email: userInfo.email ?? null,\n },\n token: idToken,\n protect: async () => {}\n }\n }\n }\n\n return {\n user: null,\n token: null,\n protect\n }\n } catch (error) {\n return {\n user: null,\n token: null,\n protect\n }\n }\n}\n\n/**\n * Edge-compatible auth check\n */\nasync function edgeAuthSecond(request: NextRequest): Promise<Auth> {\n async function protect() {\n throw new Error(\"Unauthorized access\")\n }\n\n try {\n const sessionResult = await verifySession(request)\n\n if (sessionResult.isAuthenticated && sessionResult.user) {\n return {\n user: sessionResult.user,\n token: request.cookies.get(\"_session_cookie\")?.value || request.cookies.get(\"_session_token\")?.value || null,\n protect: async () => {},\n }\n }\n\n return {\n user: null,\n token: null,\n protect,\n }\n } catch (error) {\n console.error(\"Auth check error:\", error)\n return {\n user: null,\n token: null,\n protect,\n }\n }\n}\n\n\n\n/**\n * Middleware factory that handles authentication and custom logic\n * @param customHandler Optional function for additional custom logic\n */\n\nexport function ternSecureMiddleware(callback: MiddlewareCallback) {\n return async function middleware(request: NextRequest) {\n try {\n const auth = await edgeAuthSecond(request)\n\n try {\n \n await callback(auth, request)\n\n const response = NextResponse.next()\n\n if (auth.user) {\n // Set auth headers\n response.headers.set(\"x-user-id\", auth.user.uid)\n if (auth.user.email) {\n response.headers.set(\"x-user-email\", auth.user.email)\n }\n if (auth.user.emailVerified !== undefined) {\n response.headers.set(\"x-email-verified\", auth.user.emailVerified.toString())\n }\n if (auth.user.authTime) {\n response.headers.set(\"x-auth-time\", auth.user.authTime.toString())\n }\n }\n\n return response\n } catch (error) {\n // Handle unauthorized access\n if (error instanceof Error && error.message === 'Unauthorized access') {\n const redirectUrl = new URL('/sign-in', request.url)\n redirectUrl.searchParams.set('redirect', request.nextUrl.pathname)\n return NextResponse.redirect(redirectUrl)\n }\n throw error\n }\n\n } catch (error) {\n console.error(\"Middleware error:\", {\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : error,\n path: request.nextUrl.pathname,\n })\n\n const redirectUrl = new URL(\"/sign-in\", request.url)\n return NextResponse.redirect(redirectUrl)\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,14 +4,20 @@ import { useState, useEffect, useMemo, useCallback } from "react";
|
|
|
4
4
|
import { ternSecureAuth } from "../utils/client-init";
|
|
5
5
|
import { onAuthStateChanged } from "firebase/auth";
|
|
6
6
|
import { TernSecureCtx } from "./TernSecureCtx";
|
|
7
|
-
import { useRouter } from "next/navigation";
|
|
7
|
+
import { useRouter, usePathname } from "next/navigation";
|
|
8
|
+
import { isBaseAuthRoute, isInternalRoute, isAuthRoute } from "../app-router/route-handler/internal-route";
|
|
9
|
+
import { hasRedirectLoop } from "../utils/construct";
|
|
8
10
|
function TernSecureClientProvider({
|
|
9
11
|
children,
|
|
10
|
-
loginPath = "/sign-in",
|
|
11
|
-
|
|
12
|
+
loginPath = process.env.NEXT_PUBLIC_SIGN_IN_PATH || "/sign-in",
|
|
13
|
+
signUpPath = process.env.NEXT_PUBLIC_SIGN_UP_PATH || "/sign-up",
|
|
14
|
+
loadingComponent,
|
|
15
|
+
requiresVerification
|
|
12
16
|
}) {
|
|
13
17
|
const auth = useMemo(() => ternSecureAuth, []);
|
|
14
18
|
const router = useRouter();
|
|
19
|
+
const pathname = usePathname();
|
|
20
|
+
const [isRedirecting, setIsRedirecting] = useState(false);
|
|
15
21
|
const [authState, setAuthState] = useState(() => ({
|
|
16
22
|
userId: null,
|
|
17
23
|
isLoaded: false,
|
|
@@ -20,9 +26,58 @@ function TernSecureClientProvider({
|
|
|
20
26
|
isVerified: false,
|
|
21
27
|
isAuthenticated: false,
|
|
22
28
|
token: null,
|
|
23
|
-
email: null
|
|
29
|
+
email: null,
|
|
30
|
+
status: "loading",
|
|
31
|
+
requiresVerification
|
|
24
32
|
}));
|
|
33
|
+
const constructUrlWithRedirect = useCallback(
|
|
34
|
+
(loginPath2, currentPath, loginPathParam, signUpPathParam) => {
|
|
35
|
+
const baseUrl = window.location.origin;
|
|
36
|
+
const signInUrl = new URL(loginPath2, baseUrl);
|
|
37
|
+
if (!currentPath.includes(loginPathParam) && !currentPath.includes(signUpPathParam)) {
|
|
38
|
+
signInUrl.searchParams.set("redirect", currentPath);
|
|
39
|
+
}
|
|
40
|
+
return signInUrl.toString();
|
|
41
|
+
},
|
|
42
|
+
[]
|
|
43
|
+
);
|
|
44
|
+
const shouldRedirect = useCallback(
|
|
45
|
+
(pathname2, isVerified) => {
|
|
46
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
47
|
+
if (isBaseAuthRoute(pathname2) && !searchParams.has("redirect")) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (isInternalRoute(pathname2)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (isAuthRoute(pathname2) && (!requiresVerification || isVerified)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
},
|
|
58
|
+
[requiresVerification]
|
|
59
|
+
);
|
|
60
|
+
const redirectToLogin = useCallback(
|
|
61
|
+
(currentPath) => {
|
|
62
|
+
const path = currentPath || pathname || "/";
|
|
63
|
+
if (isInternalRoute(path)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (hasRedirectLoop(path, loginPath)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
setIsRedirecting(true);
|
|
70
|
+
const loginUrl = constructUrlWithRedirect(loginPath, path, loginPath, signUpPath);
|
|
71
|
+
if (process.env.NODE_ENV === "production") {
|
|
72
|
+
window.location.href = loginUrl;
|
|
73
|
+
} else {
|
|
74
|
+
router.push(loginUrl);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
[router, loginPath, signUpPath, pathname, constructUrlWithRedirect]
|
|
78
|
+
);
|
|
25
79
|
const handleSignOut = useCallback(async (error) => {
|
|
80
|
+
const currentPath = window.location.pathname;
|
|
26
81
|
await auth.signOut();
|
|
27
82
|
setAuthState({
|
|
28
83
|
isLoaded: true,
|
|
@@ -32,56 +87,124 @@ function TernSecureClientProvider({
|
|
|
32
87
|
token: null,
|
|
33
88
|
email: null,
|
|
34
89
|
isVerified: false,
|
|
35
|
-
isAuthenticated: false
|
|
90
|
+
isAuthenticated: false,
|
|
91
|
+
status: "unauthenticated",
|
|
92
|
+
requiresVerification
|
|
36
93
|
});
|
|
37
|
-
|
|
38
|
-
}, [auth,
|
|
94
|
+
redirectToLogin(currentPath);
|
|
95
|
+
}, [auth, redirectToLogin, requiresVerification]);
|
|
39
96
|
const setEmail = useCallback((email) => {
|
|
40
97
|
setAuthState((prev) => ({
|
|
41
98
|
...prev,
|
|
42
99
|
email
|
|
43
100
|
}));
|
|
44
101
|
}, []);
|
|
102
|
+
const getAuthError = useCallback(() => {
|
|
103
|
+
if (authState.error) {
|
|
104
|
+
const error = authState.error;
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
message: error.message,
|
|
108
|
+
error: error.code,
|
|
109
|
+
user: null
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (authState.requiresVerification && authState.isValid && !authState.isVerified) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
message: "Email verification required",
|
|
116
|
+
error: "EMAIL_NOT_VERIFIED",
|
|
117
|
+
user: null
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (!authState.isAuthenticated && authState.status !== "loading") {
|
|
121
|
+
return {
|
|
122
|
+
success: false,
|
|
123
|
+
message: "User is not authenticated",
|
|
124
|
+
error: "AUTHENTICATED",
|
|
125
|
+
user: null
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
success: true,
|
|
130
|
+
user: ternSecureAuth.currentUser
|
|
131
|
+
};
|
|
132
|
+
}, [
|
|
133
|
+
authState.error,
|
|
134
|
+
authState.isValid,
|
|
135
|
+
authState.isVerified,
|
|
136
|
+
authState.isAuthenticated,
|
|
137
|
+
authState.status,
|
|
138
|
+
authState.requiresVerification
|
|
139
|
+
]);
|
|
45
140
|
useEffect(() => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
141
|
+
let mounted = true;
|
|
142
|
+
let initialLoad = true;
|
|
143
|
+
const unsubscribe = onAuthStateChanged(
|
|
144
|
+
auth,
|
|
145
|
+
async (user) => {
|
|
146
|
+
if (!mounted) return;
|
|
147
|
+
try {
|
|
148
|
+
if (user) {
|
|
149
|
+
const isValid = !!user.uid;
|
|
150
|
+
const isVerified = user.emailVerified;
|
|
151
|
+
const isAuthenticated = isValid && (!requiresVerification || isVerified);
|
|
152
|
+
setAuthState({
|
|
153
|
+
isLoaded: true,
|
|
154
|
+
userId: user.uid,
|
|
155
|
+
isValid,
|
|
156
|
+
isVerified,
|
|
157
|
+
isAuthenticated: isValid && isVerified,
|
|
158
|
+
token: user.getIdToken(),
|
|
159
|
+
error: null,
|
|
160
|
+
email: user.email,
|
|
161
|
+
status: isAuthenticated ? "authenticated" : "unverified",
|
|
162
|
+
requiresVerification
|
|
163
|
+
});
|
|
164
|
+
if (requiresVerification && !isVerified && shouldRedirect(pathname || "", isVerified)) {
|
|
165
|
+
if (initialLoad || !isRedirecting) {
|
|
166
|
+
redirectToLogin(pathname);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
setAuthState({
|
|
171
|
+
isLoaded: true,
|
|
172
|
+
userId: null,
|
|
173
|
+
isValid: false,
|
|
174
|
+
isVerified: false,
|
|
175
|
+
isAuthenticated: false,
|
|
176
|
+
token: null,
|
|
177
|
+
error: null,
|
|
178
|
+
email: null,
|
|
179
|
+
status: "unauthenticated",
|
|
180
|
+
requiresVerification
|
|
181
|
+
});
|
|
182
|
+
if (shouldRedirect(pathname || "", false) && initialLoad) {
|
|
183
|
+
redirectToLogin();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error("Auth state change error:", error);
|
|
188
|
+
if (mounted) {
|
|
189
|
+
handleSignOut(error instanceof Error ? error : new Error("Authentication error occurred"));
|
|
190
|
+
}
|
|
191
|
+
} finally {
|
|
192
|
+
initialLoad = false;
|
|
73
193
|
}
|
|
74
194
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
195
|
+
);
|
|
196
|
+
return () => {
|
|
197
|
+
mounted = false;
|
|
198
|
+
unsubscribe();
|
|
199
|
+
};
|
|
200
|
+
}, [auth, handleSignOut, redirectToLogin, requiresVerification, pathname, isRedirecting, shouldRedirect]);
|
|
80
201
|
const contextValue = useMemo(() => ({
|
|
81
202
|
...authState,
|
|
82
203
|
signOut: handleSignOut,
|
|
83
|
-
setEmail
|
|
84
|
-
|
|
204
|
+
setEmail,
|
|
205
|
+
getAuthError,
|
|
206
|
+
redirectToLogin
|
|
207
|
+
}), [authState, handleSignOut, setEmail, getAuthError, redirectToLogin]);
|
|
85
208
|
if (!authState.isLoaded) {
|
|
86
209
|
return /* @__PURE__ */ jsx(TernSecureCtx.Provider, { value: contextValue, children: loadingComponent || /* @__PURE__ */ jsx("div", { "aria-live": "polite", "aria-busy": "true", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading authentication state..." }) }) });
|
|
87
210
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\n\nimport React, { useState, useEffect, useMemo, useCallback } from 'react'\nimport { ternSecureAuth } from '../utils/client-init'\nimport { onAuthStateChanged, User } from \"firebase/auth\"\nimport { TernSecureCtx, TernSecureCtxValue } from './TernSecureCtx'\nimport { type TernSecureState } from '../types'\nimport { useRouter } from 'next/navigation'\n\ninterface TernSecureClientProviderProps {\n children: React.ReactNode;\n onUserChanged?: (user: User | null) => Promise<void>;\n loginPath?: string;\n loadingComponent?: React.ReactNode;\n}\n\nexport function TernSecureClientProvider({ \n children, \n loginPath = '/sign-in',\n loadingComponent\n}: TernSecureClientProviderProps) {\n const auth = useMemo(() => ternSecureAuth, []);\n const router = useRouter();\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\n userId: null,\n isLoaded: false,\n error: null,\n isValid: false,\n isVerified: false,\n isAuthenticated: false,\n token: null,\n email: null,\n }));\n\n const handleSignOut = useCallback(async (error?: Error) => {\n await auth.signOut();\n setAuthState({\n isLoaded: true,\n userId: null,\n error: error || null,\n isValid: false,\n token: null,\n email: null,\n isVerified: false,\n isAuthenticated: false,\n });\n router.push(loginPath);\n }, [auth, router, loginPath]);\n\n const setEmail = useCallback((email: string) => {\n setAuthState((prev) => ({\n ...prev,\n email,\n }))\n }, [])\n\nuseEffect(() => {\n const unsubscribe = onAuthStateChanged(auth, async (user: User | null) => {\n if (user) {\n const isValid = !!user.uid;\n const isVerified = user.emailVerified;\n setAuthState({\n isLoaded: true,\n userId: user.uid,\n isValid,\n isVerified,\n isAuthenticated: isValid && isVerified,\n token: user.getIdToken(),\n error: null,\n email: user.email,\n })\n } else {\n setAuthState({\n isLoaded: true,\n userId: null,\n isValid: false,\n isVerified: false,\n isAuthenticated: false,\n token: null,\n error: new Error('User is not authenticated'),\n email: null,\n })\n if (!window.location.pathname.includes(\"/sign-up\")) {\n router.push(loginPath)\n }\n }\n }, (error) => {\n handleSignOut(error instanceof Error ? error : new Error('Authentication error occurred'));\n })\n \n return () => unsubscribe()\n }, [auth, handleSignOut, router, loginPath])\n\n const contextValue: TernSecureCtxValue = useMemo(() => ({\n ...authState,\n signOut: handleSignOut,\n setEmail\n }), [authState, auth, handleSignOut, setEmail]);\n\n if (!authState.isLoaded) {\n return (\n <TernSecureCtx.Provider value={contextValue}>\n {loadingComponent || (\n <div aria-live=\"polite\" aria-busy=\"true\">\n <span className=\"sr-only\">Loading authentication state...</span>\n </div>\n )}\n </TernSecureCtx.Provider>\n );\n }\n\n return (\n <TernSecureCtx.Provider value={contextValue}>\n {children}\n </TernSecureCtx.Provider>\n )\n}"],"mappings":";AAwGY;AAtGZ,SAAgB,UAAU,WAAW,SAAS,mBAAmB;AACjE,SAAS,sBAAsB;AAC/B,SAAS,0BAAgC;AACzC,SAAS,qBAAyC;AAElD,SAAS,iBAAiB;AASnB,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA,YAAY;AAAA,EACZ;AACF,GAAkC;AAChC,QAAM,OAAO,QAAQ,MAAM,gBAAgB,CAAC,CAAC;AAC7C,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,SAA0B,OAAO;AAAA,IACjE,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,OAAO;AAAA,EACT,EAAE;AAEF,QAAM,gBAAgB,YAAY,OAAO,UAAkB;AACzD,UAAM,KAAK,QAAQ;AACnB,iBAAa;AAAA,MACX,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO,SAAS;AAAA,MAChB,SAAS;AAAA,MACT,OAAO;AAAA,MACP,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,iBAAiB;AAAA,IACnB,CAAC;AACD,WAAO,KAAK,SAAS;AAAA,EACvB,GAAG,CAAC,MAAM,QAAQ,SAAS,CAAC;AAE5B,QAAM,WAAW,YAAY,CAAC,UAAkB;AAC9C,iBAAa,CAAC,UAAU;AAAA,MACtB,GAAG;AAAA,MACH;AAAA,IACF,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEP,YAAU,MAAM;AACZ,UAAM,cAAc,mBAAmB,MAAM,OAAO,SAAsB;AACxE,UAAI,MAAM;AACR,cAAM,UAAU,CAAC,CAAC,KAAK;AACvB,cAAM,aAAa,KAAK;AACxB,qBAAa;AAAA,UACX,UAAU;AAAA,UACV,QAAQ,KAAK;AAAA,UACb;AAAA,UACA;AAAA,UACA,iBAAiB,WAAW;AAAA,UAC5B,OAAO,KAAK,WAAW;AAAA,UACvB,OAAO;AAAA,UACP,OAAO,KAAK;AAAA,QACd,CAAC;AAAA,MACH,OAAO;AACL,qBAAa;AAAA,UACX,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,iBAAiB;AAAA,UACjB,OAAO;AAAA,UACP,OAAO,IAAI,MAAM,2BAA2B;AAAA,UAC5C,OAAO;AAAA,QACT,CAAC;AACD,YAAI,CAAC,OAAO,SAAS,SAAS,SAAS,UAAU,GAAG;AAClD,iBAAO,KAAK,SAAS;AAAA,QACvB;AAAA,MACF;AAAA,IACF,GAAG,CAAC,UAAU;AACZ,oBAAc,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,+BAA+B,CAAC;AAAA,IAC3F,CAAC;AAED,WAAO,MAAM,YAAY;AAAA,EAC3B,GAAG,CAAC,MAAM,eAAe,QAAQ,SAAS,CAAC;AAE3C,QAAM,eAAmC,QAAQ,OAAO;AAAA,IACtD,GAAG;AAAA,IACH,SAAS;AAAA,IACT;AAAA,EACF,IAAI,CAAC,WAAW,MAAM,eAAe,QAAQ,CAAC;AAE9C,MAAI,CAAC,UAAU,UAAU;AACvB,WACE,oBAAC,cAAc,UAAd,EAAuB,OAAO,cAC5B,8BACC,oBAAC,SAAI,aAAU,UAAS,aAAU,QAChC,8BAAC,UAAK,WAAU,WAAU,6CAA+B,GAC3D,GAEJ;AAAA,EAEJ;AAEA,SACI,oBAAC,cAAc,UAAd,EAAuB,OAAO,cAC7B,UACF;AAEN;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\n\nimport React, { useState, useEffect, useMemo, useCallback } from 'react'\nimport { ternSecureAuth } from '../utils/client-init'\nimport { onAuthStateChanged, User } from \"firebase/auth\"\nimport { TernSecureCtx, TernSecureCtxValue } from './TernSecureCtx'\nimport type { TernSecureState, AuthError, SignInResponse } from \"../types\"\nimport type { ERRORS } from '../errors'\nimport { useRouter, usePathname } from 'next/navigation'\nimport { isBaseAuthRoute, isInternalRoute, isAuthRoute} from '../app-router/route-handler/internal-route'\nimport { hasRedirectLoop } from '../utils/construct'\n\n\n\n/**\n * @internal\n * Internal provider props - not meant for direct usage\n */\ninterface TernSecureClientProviderProps {\n children: React.ReactNode\n /** Callback when user state changes */\n onUserChanged?: (user: User | null) => Promise<void>\n /** Login page path */\n loginPath?: string\n /** Signup page path */\n signUpPath?: string\n /** Custom loading component */\n loadingComponent?: React.ReactNode\n /** Whether email verification is required */\n requiresVerification: boolean\n}\n\n/**\n * @internal\n * Internal provider component that handles authentication state\n * This is wrapped by the public TernSecureProvider\n */\n\nexport function TernSecureClientProvider({ \n children, \n loginPath = process.env.NEXT_PUBLIC_SIGN_IN_PATH || '/sign-in',\n signUpPath = process.env.NEXT_PUBLIC_SIGN_UP_PATH || '/sign-up',\n loadingComponent,\n requiresVerification,\n}: TernSecureClientProviderProps) {\n const auth = useMemo(() => ternSecureAuth, []);\n const router = useRouter();\n const pathname = usePathname() // Get current pathname\n const [isRedirecting, setIsRedirecting] = useState(false)\n\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\n userId: null,\n isLoaded: false,\n error: null,\n isValid: false,\n isVerified: false,\n isAuthenticated: false,\n token: null,\n email: null,\n status: \"loading\",\n requiresVerification,\n }));\n\n const constructUrlWithRedirect = useCallback(\n (loginPath: string, currentPath: string, loginPathParam: string, signUpPathParam: string): string => {\n const baseUrl = window.location.origin\n const signInUrl = new URL(loginPath, baseUrl)\n\n // Only add redirect if not already on login or signup page\n if (!currentPath.includes(loginPathParam) && !currentPath.includes(signUpPathParam)) {\n signInUrl.searchParams.set(\"redirect\", currentPath)\n }\n return signInUrl.toString()\n },\n [],\n )\n\n\n const shouldRedirect = useCallback(\n (pathname: string, isVerified: boolean) => {\n // Get current search params\n const searchParams = new URLSearchParams(window.location.search)\n\n // Don't redirect if we're on the base sign-in page with no redirect param\n if (isBaseAuthRoute(pathname) && !searchParams.has(\"redirect\")) {\n return false\n }\n\n // Don't redirect if we're on an internal route\n if (isInternalRoute(pathname)) {\n return false\n }\n\n // Don't redirect if we're in auth routes (except when handling verification)\n if (isAuthRoute(pathname) && (!requiresVerification || isVerified)) {\n return false\n }\n\n return true\n },\n [requiresVerification],\n )\n\n const redirectToLogin = useCallback(\n (currentPath?: string) => {\n const path = currentPath || pathname || \"/\"\n\n\n if (isInternalRoute(path)) { // Don't redirect if we're already on an internal route\n return\n }\n\n // Check for redirect loops\n if (hasRedirectLoop(path, loginPath)) {\n return\n }\n\n setIsRedirecting(true)\n\n const loginUrl = constructUrlWithRedirect(loginPath, path, loginPath, signUpPath)\n\n if (process.env.NODE_ENV === \"production\") {\n window.location.href = loginUrl\n } else {\n // Use router.push for development\n router.push(loginUrl)\n }\n }, \n [router, loginPath, signUpPath, pathname, constructUrlWithRedirect]\n)\n\n const handleSignOut = useCallback(async (error?: Error) => {\n const currentPath = window.location.pathname\n await auth.signOut();\n setAuthState({\n isLoaded: true,\n userId: null,\n error: error || null,\n isValid: false,\n token: null,\n email: null,\n isVerified: false,\n isAuthenticated: false,\n status: \"unauthenticated\",\n requiresVerification,\n })\n redirectToLogin(currentPath)\n }, [auth, redirectToLogin, requiresVerification])\n\n const setEmail = useCallback((email: string) => {\n setAuthState((prev) => ({\n ...prev,\n email,\n }))\n }, [])\n\n const getAuthError = useCallback((): SignInResponse => {\n if (authState.error) {\n const error = authState.error as AuthError;\n return {\n success: false,\n message: error.message,\n error: error.code as keyof typeof ERRORS,\n user: null,\n }\n }\n\n if (authState.requiresVerification && authState.isValid && !authState.isVerified) {\n return {\n success: false,\n message: 'Email verification required',\n error: 'EMAIL_NOT_VERIFIED',\n user: null,\n }\n }\n\n if (!authState.isAuthenticated && authState.status !== \"loading\") {\n return {\n success: false,\n message: 'User is not authenticated',\n error: 'AUTHENTICATED',\n user: null,\n }\n }\n\n return {\n success: true,\n user: ternSecureAuth.currentUser,\n }\n }, [\n authState.error,\n authState.isValid,\n authState.isVerified,\n authState.isAuthenticated,\n authState.status,\n authState.requiresVerification,\n ])\n\nuseEffect(() => {\n let mounted = true\n let initialLoad = true\n\n const unsubscribe = onAuthStateChanged(\n auth,\n async (user: User | null) => {\n if (!mounted) return\n try {\n if (user) {\n const isValid = !!user.uid;\n const isVerified = user.emailVerified;\n const isAuthenticated = isValid && (!requiresVerification || isVerified) // Consider user authenticated if verification is not required or if email is verified\n\n setAuthState({\n isLoaded: true,\n userId: user.uid,\n isValid,\n isVerified,\n isAuthenticated: isValid && isVerified,\n token: user.getIdToken(),\n error: null,\n email: user.email,\n status: isAuthenticated ? \"authenticated\" : \"unverified\",\n requiresVerification,\n })\n \n if (requiresVerification && !isVerified && shouldRedirect(pathname || \"\", isVerified)) {\n if(initialLoad || !isRedirecting) {\n redirectToLogin(pathname)\n }\n }\n } else {\n setAuthState({\n isLoaded: true,\n userId: null,\n isValid: false,\n isVerified: false,\n isAuthenticated: false,\n token: null,\n error: null,\n email: null,\n status: \"unauthenticated\",\n requiresVerification,\n })\n \n if (shouldRedirect(pathname || \"\", false) && initialLoad) {\n redirectToLogin()\n }\n }\n } catch (error){\n console.error(\"Auth state change error:\", error)\n if (mounted) {\n handleSignOut(error instanceof Error ? error : new Error(\"Authentication error occurred\"))\n }\n } finally {\n initialLoad = false\n }\n })\n \n return () => {\n mounted = false\n unsubscribe()\n }\n }, [auth, handleSignOut, redirectToLogin, requiresVerification, pathname, isRedirecting, shouldRedirect])\n\n const contextValue: TernSecureCtxValue = useMemo(() => ({\n ...authState,\n signOut: handleSignOut,\n setEmail,\n getAuthError,\n redirectToLogin,\n }), [authState, handleSignOut, setEmail, getAuthError, redirectToLogin]);\n\n if (!authState.isLoaded) {\n return (\n <TernSecureCtx.Provider value={contextValue}>\n {loadingComponent || (\n <div aria-live=\"polite\" aria-busy=\"true\">\n <span className=\"sr-only\">Loading authentication state...</span>\n </div>\n )}\n </TernSecureCtx.Provider>\n );\n }\n\n return (\n <TernSecureCtx.Provider value={contextValue}>\n {children}\n </TernSecureCtx.Provider>\n )\n}"],"mappings":"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","names":["loginPath","pathname"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/boundary/TernSecureCtx.tsx"],"sourcesContent":["\"use client\"\n\nimport { createContext, useContext } from 'react'\nimport { ternSecureAuth } from '../utils/client-init';\nimport { User } from 'firebase/auth';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/boundary/TernSecureCtx.tsx"],"sourcesContent":["\"use client\"\n\nimport { createContext, useContext } from 'react'\nimport { ternSecureAuth } from '../utils/client-init';\nimport { User } from 'firebase/auth';\nimport type { TernSecureState, SignInResponse } from '../types';\n\nexport const TernSecureUser = (): User | null => {\n return ternSecureAuth.currentUser;\n}\n\nexport interface TernSecureCtxValue extends TernSecureState {\n signOut: () => Promise<void>\n setEmail: (email: string) => void\n getAuthError: () => SignInResponse\n redirectToLogin: () => void\n}\n\nexport const TernSecureCtx = createContext<TernSecureCtxValue | null>(null)\n\nTernSecureCtx.displayName = 'TernSecureCtx'\n\nexport const useTernSecure = (hookName: string) => {\n const context = useContext(TernSecureCtx)\n \n if (!context) {\n throw new Error(\n `${hookName} must be used within TernSecureProvider`\n )\n }\n\n return context\n}\n\n"],"mappings":";AAEA,SAAS,eAAe,kBAAkB;AAC1C,SAAS,sBAAsB;AAIxB,MAAM,iBAAiB,MAAmB;AAC/C,SAAO,eAAe;AACxB;AASO,MAAM,gBAAgB,cAAyC,IAAI;AAE1E,cAAc,cAAc;AAErB,MAAM,gBAAgB,CAAC,aAAqB;AACjD,QAAM,UAAU,WAAW,aAAa;AAExC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,GAAG,QAAQ;AAAA,IACb;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -10,21 +10,18 @@ function useAuth() {
|
|
|
10
10
|
isVerified,
|
|
11
11
|
isAuthenticated,
|
|
12
12
|
token,
|
|
13
|
+
getAuthError,
|
|
14
|
+
status,
|
|
15
|
+
requiresVerification,
|
|
13
16
|
signOut
|
|
14
17
|
} = useTernSecure("useAuth");
|
|
15
18
|
const user = TernSecureUser();
|
|
16
|
-
const
|
|
17
|
-
if (error) return error;
|
|
18
|
-
if (isValid && !isVerified) {
|
|
19
|
-
return new Error("Email verification required");
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
};
|
|
19
|
+
const authResponse = getAuthError();
|
|
23
20
|
return {
|
|
24
21
|
user,
|
|
25
22
|
userId,
|
|
26
23
|
isLoaded,
|
|
27
|
-
error:
|
|
24
|
+
error: authResponse.success ? null : authResponse,
|
|
28
25
|
isValid,
|
|
29
26
|
// User is signed in
|
|
30
27
|
isVerified,
|
|
@@ -32,6 +29,8 @@ function useAuth() {
|
|
|
32
29
|
isAuthenticated,
|
|
33
30
|
// User is both signed in and verified
|
|
34
31
|
token,
|
|
32
|
+
status,
|
|
33
|
+
requiresVerification,
|
|
35
34
|
signOut
|
|
36
35
|
};
|
|
37
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/boundary/hooks/useAuth.ts"],"sourcesContent":["\"use client\"\n\nimport { useTernSecure } from '../TernSecureCtx'\nimport { User } from 'firebase/auth'\nimport { TernSecureUser } from '../TernSecureCtx'\n\nexport function useAuth() {\n const {\n userId,\n isLoaded,\n error,\n isValid,\n isVerified,\n isAuthenticated,\n token,\n signOut\n } = useTernSecure('useAuth')\n\n const user: User | null = TernSecureUser()\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/boundary/hooks/useAuth.ts"],"sourcesContent":["\"use client\"\n\nimport { useTernSecure } from '../TernSecureCtx'\nimport { User } from 'firebase/auth'\nimport { TernSecureUser } from '../TernSecureCtx'\nimport type { SignInResponse } from '../../types'\n\n\nexport function useAuth() {\n const {\n userId,\n isLoaded,\n error,\n isValid,\n isVerified,\n isAuthenticated,\n token,\n getAuthError,\n status,\n requiresVerification,\n signOut\n } = useTernSecure('useAuth')\n\n const user: User | null = TernSecureUser()\n const authResponse: SignInResponse = getAuthError()\n\n\n return {\n user,\n userId,\n isLoaded,\n error: authResponse.success ? null : authResponse,\n isValid, // User is signed in\n isVerified, // Email is verified\n isAuthenticated, // User is both signed in and verified\n token,\n status,\n requiresVerification,\n signOut\n }\n}\n"],"mappings":";AAEA,SAAS,qBAAqB;AAE9B,SAAS,sBAAsB;AAIxB,SAAS,UAAU;AACxB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,cAAc,SAAS;AAE3B,QAAM,OAAoB,eAAe;AACzC,QAAM,eAA+B,aAAa;AAGlD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,aAAa,UAAU,OAAO;AAAA,IACrC;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|