@authgear/nextjs 0.1.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.
@@ -0,0 +1,48 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, ButtonHTMLAttributes } from 'react';
3
+ import { b as SessionState, U as UserInfo } from './types-Csfra4K2.js';
4
+ export { S as Session } from './types-Csfra4K2.js';
5
+
6
+ interface SignInOptions {
7
+ returnTo?: string;
8
+ loginPath?: string;
9
+ }
10
+ interface AuthgearProviderProps {
11
+ children: ReactNode;
12
+ /** Path to the userinfo API route. Defaults to "/api/auth/userinfo". */
13
+ userInfoPath?: string;
14
+ /** Path to the login route. Defaults to "/api/auth/login". */
15
+ loginPath?: string;
16
+ /** Path to the logout route. Defaults to "/api/auth/logout". */
17
+ logoutPath?: string;
18
+ }
19
+ declare function AuthgearProvider({ children, userInfoPath, loginPath, logoutPath, }: AuthgearProviderProps): react_jsx_runtime.JSX.Element;
20
+
21
+ interface SignInButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
22
+ signInOptions?: SignInOptions;
23
+ }
24
+ declare function SignInButton({ signInOptions, children, ...props }: SignInButtonProps): react_jsx_runtime.JSX.Element;
25
+
26
+ type SignOutButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
27
+ declare function SignOutButton({ children, ...props }: SignOutButtonProps): react_jsx_runtime.JSX.Element;
28
+
29
+ interface UseAuthgearReturn {
30
+ /** Current session state */
31
+ state: SessionState;
32
+ /** Current user info, null if not authenticated */
33
+ user: UserInfo | null;
34
+ /** Whether the initial session check has completed */
35
+ isLoaded: boolean;
36
+ /** Whether the user is currently authenticated */
37
+ isAuthenticated: boolean;
38
+ /** Navigate to the sign-in page */
39
+ signIn: (options?: SignInOptions) => void;
40
+ /** Navigate to the sign-out endpoint */
41
+ signOut: () => void;
42
+ }
43
+ declare function useAuthgear(): UseAuthgearReturn;
44
+
45
+ /** Returns the current user info, or null if not authenticated. */
46
+ declare function useUser(): UserInfo | null;
47
+
48
+ export { AuthgearProvider, type AuthgearProviderProps, SessionState, SignInButton, type SignInButtonProps, type SignInOptions, SignOutButton, type SignOutButtonProps, type UseAuthgearReturn, UserInfo, useAuthgear, useUser };
package/dist/client.js ADDED
@@ -0,0 +1,119 @@
1
+ "use client";
2
+ import {
3
+ SessionState
4
+ } from "./chunk-UY6NEM2T.js";
5
+
6
+ // src/components/AuthgearProvider.tsx
7
+ import {
8
+ createContext,
9
+ useContext,
10
+ useEffect,
11
+ useState,
12
+ useCallback
13
+ } from "react";
14
+ import { jsx } from "react/jsx-runtime";
15
+ var AuthgearContext = createContext(null);
16
+ function AuthgearProvider({
17
+ children,
18
+ userInfoPath = "/api/auth/userinfo",
19
+ loginPath = "/api/auth/login",
20
+ logoutPath = "/api/auth/logout"
21
+ }) {
22
+ const [state, setState] = useState("UNKNOWN" /* Unknown */);
23
+ const [user, setUser] = useState(null);
24
+ const [isLoaded, setIsLoaded] = useState(false);
25
+ useEffect(() => {
26
+ let cancelled = false;
27
+ async function fetchSession() {
28
+ try {
29
+ const res = await fetch(userInfoPath);
30
+ if (cancelled) return;
31
+ if (res.ok) {
32
+ const userInfo = await res.json();
33
+ setState("AUTHENTICATED" /* Authenticated */);
34
+ setUser(userInfo);
35
+ } else {
36
+ setState("NO_SESSION" /* NoSession */);
37
+ setUser(null);
38
+ }
39
+ } catch {
40
+ if (!cancelled) {
41
+ setState("NO_SESSION" /* NoSession */);
42
+ setUser(null);
43
+ }
44
+ } finally {
45
+ if (!cancelled) {
46
+ setIsLoaded(true);
47
+ }
48
+ }
49
+ }
50
+ fetchSession();
51
+ return () => {
52
+ cancelled = true;
53
+ };
54
+ }, [userInfoPath]);
55
+ const signIn = useCallback(
56
+ (options) => {
57
+ const path = options?.loginPath ?? loginPath;
58
+ const url = new URL(path, window.location.origin);
59
+ if (options?.returnTo) {
60
+ url.searchParams.set("returnTo", options.returnTo);
61
+ }
62
+ window.location.href = url.toString();
63
+ },
64
+ [loginPath]
65
+ );
66
+ const signOut = useCallback(() => {
67
+ window.location.href = logoutPath;
68
+ }, [logoutPath]);
69
+ return /* @__PURE__ */ jsx(AuthgearContext.Provider, { value: { state, user, isLoaded, signIn, signOut }, children });
70
+ }
71
+ function useAuthgearContext() {
72
+ const ctx = useContext(AuthgearContext);
73
+ if (!ctx) {
74
+ throw new Error("useAuthgearContext must be used within <AuthgearProvider>");
75
+ }
76
+ return ctx;
77
+ }
78
+
79
+ // src/components/SignInButton.tsx
80
+ import { jsx as jsx2 } from "react/jsx-runtime";
81
+ function SignInButton({ signInOptions, children = "Sign In", ...props }) {
82
+ const { signIn } = useAuthgearContext();
83
+ return /* @__PURE__ */ jsx2("button", { ...props, onClick: () => signIn(signInOptions), children });
84
+ }
85
+
86
+ // src/components/SignOutButton.tsx
87
+ import { jsx as jsx3 } from "react/jsx-runtime";
88
+ function SignOutButton({ children = "Sign Out", ...props }) {
89
+ const { signOut } = useAuthgearContext();
90
+ return /* @__PURE__ */ jsx3("button", { ...props, onClick: signOut, children });
91
+ }
92
+
93
+ // src/hooks/useAuthgear.ts
94
+ function useAuthgear() {
95
+ const { state, user, isLoaded, signIn, signOut } = useAuthgearContext();
96
+ return {
97
+ state,
98
+ user,
99
+ isLoaded,
100
+ isAuthenticated: state === "AUTHENTICATED" /* Authenticated */,
101
+ signIn,
102
+ signOut
103
+ };
104
+ }
105
+
106
+ // src/hooks/useUser.ts
107
+ function useUser() {
108
+ const { user } = useAuthgearContext();
109
+ return user;
110
+ }
111
+ export {
112
+ AuthgearProvider,
113
+ SessionState,
114
+ SignInButton,
115
+ SignOutButton,
116
+ useAuthgear,
117
+ useUser
118
+ };
119
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/AuthgearProvider.tsx","../src/components/SignInButton.tsx","../src/components/SignOutButton.tsx","../src/hooks/useAuthgear.ts","../src/hooks/useUser.ts"],"sourcesContent":["\"use client\";\n\nimport React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface AuthgearContextValue {\n state: SessionState;\n user: UserInfo | null;\n isLoaded: boolean;\n signIn: (options?: SignInOptions) => void;\n signOut: () => void;\n}\n\nexport interface SignInOptions {\n returnTo?: string;\n loginPath?: string;\n}\n\nconst AuthgearContext = createContext<AuthgearContextValue | null>(null);\n\nexport interface AuthgearProviderProps {\n children: ReactNode;\n /** Path to the userinfo API route. Defaults to \"/api/auth/userinfo\". */\n userInfoPath?: string;\n /** Path to the login route. Defaults to \"/api/auth/login\". */\n loginPath?: string;\n /** Path to the logout route. Defaults to \"/api/auth/logout\". */\n logoutPath?: string;\n}\n\nexport function AuthgearProvider({\n children,\n userInfoPath = \"/api/auth/userinfo\",\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n}: AuthgearProviderProps) {\n const [state, setState] = useState<SessionState>(SessionState.Unknown);\n const [user, setUser] = useState<UserInfo | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchSession() {\n try {\n const res = await fetch(userInfoPath);\n if (cancelled) return;\n\n if (res.ok) {\n const userInfo = (await res.json()) as UserInfo;\n setState(SessionState.Authenticated);\n setUser(userInfo);\n } else {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } catch {\n if (!cancelled) {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } finally {\n if (!cancelled) {\n setIsLoaded(true);\n }\n }\n }\n\n fetchSession();\n return () => { cancelled = true; };\n }, [userInfoPath]);\n\n const signIn = useCallback(\n (options?: SignInOptions) => {\n const path = options?.loginPath ?? loginPath;\n const url = new URL(path, window.location.origin);\n if (options?.returnTo) {\n url.searchParams.set(\"returnTo\", options.returnTo);\n }\n window.location.href = url.toString();\n },\n [loginPath],\n );\n\n const signOut = useCallback(() => {\n window.location.href = logoutPath;\n }, [logoutPath]);\n\n return (\n <AuthgearContext.Provider value={{ state, user, isLoaded, signIn, signOut }}>\n {children}\n </AuthgearContext.Provider>\n );\n}\n\nexport function useAuthgearContext(): AuthgearContextValue {\n const ctx = useContext(AuthgearContext);\n if (!ctx) {\n throw new Error(\"useAuthgearContext must be used within <AuthgearProvider>\");\n }\n return ctx;\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext, type SignInOptions } from \"./AuthgearProvider.js\";\n\nexport interface SignInButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n signInOptions?: SignInOptions;\n}\n\nexport function SignInButton({ signInOptions, children = \"Sign In\", ...props }: SignInButtonProps) {\n const { signIn } = useAuthgearContext();\n return (\n <button {...props} onClick={() => signIn(signInOptions)}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext } from \"./AuthgearProvider.js\";\n\nexport type SignOutButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport function SignOutButton({ children = \"Sign Out\", ...props }: SignOutButtonProps) {\n const { signOut } = useAuthgearContext();\n return (\n <button {...props} onClick={signOut}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport { useAuthgearContext, type SignInOptions } from \"../components/AuthgearProvider.js\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface UseAuthgearReturn {\n /** Current session state */\n state: SessionState;\n /** Current user info, null if not authenticated */\n user: UserInfo | null;\n /** Whether the initial session check has completed */\n isLoaded: boolean;\n /** Whether the user is currently authenticated */\n isAuthenticated: boolean;\n /** Navigate to the sign-in page */\n signIn: (options?: SignInOptions) => void;\n /** Navigate to the sign-out endpoint */\n signOut: () => void;\n}\n\nexport function useAuthgear(): UseAuthgearReturn {\n const { state, user, isLoaded, signIn, signOut } = useAuthgearContext();\n\n return {\n state,\n user,\n isLoaded,\n isAuthenticated: state === SessionState.Authenticated,\n signIn,\n signOut,\n };\n}\n","\"use client\";\n\nimport { useAuthgearContext } from \"../components/AuthgearProvider.js\";\nimport type { UserInfo } from \"../types.js\";\n\n/** Returns the current user info, or null if not authenticated. */\nexport function useUser(): UserInfo | null {\n const { user } = useAuthgearContext();\n return user;\n}\n"],"mappings":";;;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAuFH;AAvEJ,IAAM,kBAAkB,cAA2C,IAAI;AAYhE,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,aAAa;AACf,GAA0B;AACxB,QAAM,CAAC,OAAO,QAAQ,IAAI,gCAA2C;AACrE,QAAM,CAAC,MAAM,OAAO,IAAI,SAA0B,IAAI;AACtD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAE9C,YAAU,MAAM;AACd,QAAI,YAAY;AAEhB,mBAAe,eAAe;AAC5B,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,YAAY;AACpC,YAAI,UAAW;AAEf,YAAI,IAAI,IAAI;AACV,gBAAM,WAAY,MAAM,IAAI,KAAK;AACjC,sDAAmC;AACnC,kBAAQ,QAAQ;AAAA,QAClB,OAAO;AACL,+CAA+B;AAC/B,kBAAQ,IAAI;AAAA,QACd;AAAA,MACF,QAAQ;AACN,YAAI,CAAC,WAAW;AACd,+CAA+B;AAC/B,kBAAQ,IAAI;AAAA,QACd;AAAA,MACF,UAAE;AACA,YAAI,CAAC,WAAW;AACd,sBAAY,IAAI;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAEA,iBAAa;AACb,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAM;AAAA,EACnC,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,SAAS;AAAA,IACb,CAAC,YAA4B;AAC3B,YAAM,OAAO,SAAS,aAAa;AACnC,YAAM,MAAM,IAAI,IAAI,MAAM,OAAO,SAAS,MAAM;AAChD,UAAI,SAAS,UAAU;AACrB,YAAI,aAAa,IAAI,YAAY,QAAQ,QAAQ;AAAA,MACnD;AACA,aAAO,SAAS,OAAO,IAAI,SAAS;AAAA,IACtC;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,UAAU,YAAY,MAAM;AAChC,WAAO,SAAS,OAAO;AAAA,EACzB,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO,EAAE,OAAO,MAAM,UAAU,QAAQ,QAAQ,GACvE,UACH;AAEJ;AAEO,SAAS,qBAA2C;AACzD,QAAM,MAAM,WAAW,eAAe;AACtC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,2DAA2D;AAAA,EAC7E;AACA,SAAO;AACT;;;AChGI,gBAAAA,YAAA;AAHG,SAAS,aAAa,EAAE,eAAe,WAAW,WAAW,GAAG,MAAM,GAAsB;AACjG,QAAM,EAAE,OAAO,IAAI,mBAAmB;AACtC,SACE,gBAAAA,KAAC,YAAQ,GAAG,OAAO,SAAS,MAAM,OAAO,aAAa,GACnD,UACH;AAEJ;;;ACNI,gBAAAC,YAAA;AAHG,SAAS,cAAc,EAAE,WAAW,YAAY,GAAG,MAAM,GAAuB;AACrF,QAAM,EAAE,QAAQ,IAAI,mBAAmB;AACvC,SACE,gBAAAA,KAAC,YAAQ,GAAG,OAAO,SAAS,SACzB,UACH;AAEJ;;;ACMO,SAAS,cAAiC;AAC/C,QAAM,EAAE,OAAO,MAAM,UAAU,QAAQ,QAAQ,IAAI,mBAAmB;AAEtE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AACF;;;ACzBO,SAAS,UAA2B;AACzC,QAAM,EAAE,KAAK,IAAI,mBAAmB;AACpC,SAAO;AACT;","names":["jsx","jsx"]}
@@ -0,0 +1,34 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { A as AuthgearConfig } from './types-Csfra4K2.js';
3
+ export { D as DEFAULT_SCOPES, J as JWTPayload, O as OIDCConfiguration, P as Page, S as Session, a as SessionData, b as SessionState, T as TokenResponse, U as UserInfo } from './types-Csfra4K2.js';
4
+
5
+ /**
6
+ * Creates Next.js route handlers for all Authgear auth endpoints.
7
+ *
8
+ * Usage in `app/api/auth/[...authgear]/route.ts`:
9
+ * ```ts
10
+ * import { createAuthgearHandlers } from "@authgear/nextjs";
11
+ * export const { GET, POST } = createAuthgearHandlers(config);
12
+ * ```
13
+ *
14
+ * Routes handled:
15
+ * - GET /api/auth/login — Start OAuth flow
16
+ * - GET /api/auth/callback — Handle OAuth callback
17
+ * - GET /api/auth/logout — Logout and revoke tokens
18
+ * - POST /api/auth/refresh — Refresh access token
19
+ * - GET /api/auth/userinfo — Get current user info
20
+ */
21
+ declare function createAuthgearHandlers(config: AuthgearConfig): {
22
+ GET: (request: NextRequest, { params }: {
23
+ params: Promise<{
24
+ authgear: string[];
25
+ }>;
26
+ }) => Promise<NextResponse>;
27
+ POST: (request: NextRequest, { params }: {
28
+ params: Promise<{
29
+ authgear: string[];
30
+ }>;
31
+ }) => Promise<NextResponse>;
32
+ };
33
+
34
+ export { AuthgearConfig, createAuthgearHandlers };
package/dist/index.js ADDED
@@ -0,0 +1,294 @@
1
+ import {
2
+ parseUserInfo
3
+ } from "./chunk-3KVYAFQJ.js";
4
+ import {
5
+ buildClearCookie,
6
+ buildPKCECookie,
7
+ buildSessionCookie,
8
+ decryptPKCECookie,
9
+ decryptSession,
10
+ exchangeCode,
11
+ fetchOIDCConfiguration,
12
+ isTokenExpired,
13
+ refreshAccessToken,
14
+ resolveConfig,
15
+ revokeToken
16
+ } from "./chunk-MJD3XNUK.js";
17
+ import {
18
+ DEFAULT_SCOPES,
19
+ Page,
20
+ SessionState
21
+ } from "./chunk-UY6NEM2T.js";
22
+
23
+ // src/handlers/index.ts
24
+ import { NextResponse as NextResponse6 } from "next/server";
25
+
26
+ // src/handlers/login.ts
27
+ import { NextResponse } from "next/server";
28
+
29
+ // src/oauth/pkce.ts
30
+ import { randomBytes, createHash } from "crypto";
31
+ var VERIFIER_LENGTH = 64;
32
+ function generateCodeVerifier() {
33
+ return randomBytes(VERIFIER_LENGTH).toString("base64url").slice(0, VERIFIER_LENGTH);
34
+ }
35
+ function computeCodeChallenge(codeVerifier) {
36
+ return createHash("sha256").update(codeVerifier).digest("base64url");
37
+ }
38
+
39
+ // src/oauth/authorize.ts
40
+ import { randomBytes as randomBytes2 } from "crypto";
41
+ function generateState() {
42
+ return randomBytes2(32).toString("base64url");
43
+ }
44
+ function buildAuthorizeURL(oidcConfig, params) {
45
+ const url = new URL(oidcConfig.authorization_endpoint);
46
+ url.searchParams.set("response_type", "code");
47
+ url.searchParams.set("client_id", params.clientID);
48
+ url.searchParams.set("redirect_uri", params.redirectURI);
49
+ url.searchParams.set("scope", params.scopes.join(" "));
50
+ url.searchParams.set("code_challenge", computeCodeChallenge(params.codeVerifier));
51
+ url.searchParams.set("code_challenge_method", "S256");
52
+ url.searchParams.set("state", params.state);
53
+ return url.toString();
54
+ }
55
+
56
+ // src/handlers/login.ts
57
+ async function handleLogin(request, config) {
58
+ const resolved = resolveConfig(config);
59
+ const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);
60
+ const returnTo = request.nextUrl.searchParams.get("returnTo") ?? "/";
61
+ const codeVerifier = generateCodeVerifier();
62
+ const state = generateState();
63
+ const authorizeURL = buildAuthorizeURL(oidcConfig, {
64
+ clientID: resolved.clientID,
65
+ redirectURI: resolved.redirectURI,
66
+ scopes: resolved.scopes,
67
+ codeVerifier,
68
+ state
69
+ });
70
+ const pkceCookie = buildPKCECookie({ codeVerifier, state, returnTo }, resolved.sessionSecret);
71
+ const response = NextResponse.redirect(authorizeURL);
72
+ response.cookies.set(pkceCookie.name, pkceCookie.value, {
73
+ httpOnly: pkceCookie.httpOnly,
74
+ secure: pkceCookie.secure,
75
+ sameSite: pkceCookie.sameSite,
76
+ path: pkceCookie.path,
77
+ maxAge: pkceCookie.maxAge
78
+ });
79
+ return response;
80
+ }
81
+
82
+ // src/handlers/callback.ts
83
+ import { NextResponse as NextResponse2 } from "next/server";
84
+ async function handleCallback(request, config) {
85
+ const resolved = resolveConfig(config);
86
+ const code = request.nextUrl.searchParams.get("code");
87
+ const state = request.nextUrl.searchParams.get("state");
88
+ const error = request.nextUrl.searchParams.get("error");
89
+ const errorDescription = request.nextUrl.searchParams.get("error_description");
90
+ if (error) {
91
+ return NextResponse2.json(
92
+ { error, error_description: errorDescription },
93
+ { status: 400 }
94
+ );
95
+ }
96
+ if (!code || !state) {
97
+ return NextResponse2.json(
98
+ { error: "missing_params", error_description: "Missing code or state parameter" },
99
+ { status: 400 }
100
+ );
101
+ }
102
+ const pkceCookieValue = request.cookies.get("authgear.pkce")?.value;
103
+ if (!pkceCookieValue) {
104
+ return NextResponse2.json(
105
+ { error: "invalid_state", error_description: "Missing PKCE cookie" },
106
+ { status: 400 }
107
+ );
108
+ }
109
+ const pkceData = decryptPKCECookie(pkceCookieValue, resolved.sessionSecret);
110
+ if (!pkceData || pkceData.state !== state) {
111
+ return NextResponse2.json(
112
+ { error: "invalid_state", error_description: "State mismatch" },
113
+ { status: 400 }
114
+ );
115
+ }
116
+ const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);
117
+ const tokenResponse = await exchangeCode(oidcConfig, {
118
+ code,
119
+ codeVerifier: pkceData.codeVerifier,
120
+ clientID: resolved.clientID,
121
+ clientSecret: resolved.clientSecret || void 0,
122
+ redirectURI: resolved.redirectURI
123
+ });
124
+ const sessionCookie = buildSessionCookie(
125
+ resolved.cookieName,
126
+ {
127
+ accessToken: tokenResponse.access_token,
128
+ refreshToken: tokenResponse.refresh_token ?? null,
129
+ idToken: tokenResponse.id_token ?? null,
130
+ expiresAt: Math.floor(Date.now() / 1e3) + tokenResponse.expires_in
131
+ },
132
+ resolved.sessionSecret
133
+ );
134
+ const returnTo = pkceData.returnTo || "/";
135
+ const redirectURL = new URL(returnTo, request.nextUrl.origin);
136
+ const response = NextResponse2.redirect(redirectURL);
137
+ response.cookies.set(sessionCookie.name, sessionCookie.value, {
138
+ httpOnly: sessionCookie.httpOnly,
139
+ secure: sessionCookie.secure,
140
+ sameSite: sessionCookie.sameSite,
141
+ path: sessionCookie.path,
142
+ maxAge: sessionCookie.maxAge
143
+ });
144
+ response.cookies.set("authgear.pkce", "", { maxAge: 0, path: "/" });
145
+ return response;
146
+ }
147
+
148
+ // src/handlers/logout.ts
149
+ import { NextResponse as NextResponse3 } from "next/server";
150
+ async function handleLogout(request, config) {
151
+ const resolved = resolveConfig(config);
152
+ const sessionCookie = request.cookies.get(resolved.cookieName)?.value;
153
+ if (sessionCookie) {
154
+ const session = decryptSession(sessionCookie, resolved.sessionSecret);
155
+ if (session?.refreshToken) {
156
+ const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);
157
+ try {
158
+ await revokeToken(oidcConfig, session.refreshToken);
159
+ } catch {
160
+ }
161
+ }
162
+ }
163
+ const clearCookie = buildClearCookie(resolved.cookieName);
164
+ const redirectURL = new URL(resolved.postLogoutRedirectURI, request.nextUrl.origin);
165
+ const response = NextResponse3.redirect(redirectURL);
166
+ response.cookies.set(clearCookie.name, clearCookie.value, {
167
+ httpOnly: clearCookie.httpOnly,
168
+ secure: clearCookie.secure,
169
+ sameSite: clearCookie.sameSite,
170
+ path: clearCookie.path,
171
+ maxAge: clearCookie.maxAge
172
+ });
173
+ return response;
174
+ }
175
+
176
+ // src/handlers/refresh.ts
177
+ import { NextResponse as NextResponse4 } from "next/server";
178
+ async function handleRefresh(request, config) {
179
+ const resolved = resolveConfig(config);
180
+ const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;
181
+ if (!sessionCookieValue) {
182
+ return NextResponse4.json({ error: "no_session" }, { status: 401 });
183
+ }
184
+ const session = decryptSession(sessionCookieValue, resolved.sessionSecret);
185
+ if (!session?.refreshToken) {
186
+ return NextResponse4.json({ error: "no_refresh_token" }, { status: 401 });
187
+ }
188
+ const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);
189
+ const tokenResponse = await refreshAccessToken(oidcConfig, {
190
+ refreshToken: session.refreshToken,
191
+ clientID: resolved.clientID,
192
+ clientSecret: resolved.clientSecret || void 0
193
+ });
194
+ const newSession = {
195
+ accessToken: tokenResponse.access_token,
196
+ refreshToken: tokenResponse.refresh_token ?? session.refreshToken,
197
+ idToken: tokenResponse.id_token ?? session.idToken,
198
+ expiresAt: Math.floor(Date.now() / 1e3) + tokenResponse.expires_in
199
+ };
200
+ const sessionCookie = buildSessionCookie(resolved.cookieName, newSession, resolved.sessionSecret);
201
+ const response = NextResponse4.json({ ok: true });
202
+ response.cookies.set(sessionCookie.name, sessionCookie.value, {
203
+ httpOnly: sessionCookie.httpOnly,
204
+ secure: sessionCookie.secure,
205
+ sameSite: sessionCookie.sameSite,
206
+ path: sessionCookie.path,
207
+ maxAge: sessionCookie.maxAge
208
+ });
209
+ return response;
210
+ }
211
+
212
+ // src/handlers/userinfo.ts
213
+ import { NextResponse as NextResponse5 } from "next/server";
214
+ async function handleUserInfo(request, config) {
215
+ const resolved = resolveConfig(config);
216
+ const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;
217
+ if (!sessionCookieValue) {
218
+ return NextResponse5.json({ error: "no_session" }, { status: 401 });
219
+ }
220
+ let session = decryptSession(sessionCookieValue, resolved.sessionSecret);
221
+ if (!session) {
222
+ return NextResponse5.json({ error: "invalid_session" }, { status: 401 });
223
+ }
224
+ const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);
225
+ if (isTokenExpired(session.expiresAt) && session.refreshToken) {
226
+ const tokenResponse = await refreshAccessToken(oidcConfig, {
227
+ refreshToken: session.refreshToken,
228
+ clientID: resolved.clientID,
229
+ clientSecret: resolved.clientSecret || void 0
230
+ });
231
+ session = {
232
+ accessToken: tokenResponse.access_token,
233
+ refreshToken: tokenResponse.refresh_token ?? session.refreshToken,
234
+ idToken: tokenResponse.id_token ?? session.idToken,
235
+ expiresAt: Math.floor(Date.now() / 1e3) + tokenResponse.expires_in
236
+ };
237
+ }
238
+ const userinfoRes = await fetch(oidcConfig.userinfo_endpoint, {
239
+ headers: { Authorization: `Bearer ${session.accessToken}` }
240
+ });
241
+ if (!userinfoRes.ok) {
242
+ return NextResponse5.json({ error: "userinfo_failed" }, { status: userinfoRes.status });
243
+ }
244
+ const raw = await userinfoRes.json();
245
+ const userInfo = parseUserInfo(raw);
246
+ const response = NextResponse5.json(userInfo);
247
+ const newCookie = buildSessionCookie(resolved.cookieName, session, resolved.sessionSecret);
248
+ response.cookies.set(newCookie.name, newCookie.value, {
249
+ httpOnly: newCookie.httpOnly,
250
+ secure: newCookie.secure,
251
+ sameSite: newCookie.sameSite,
252
+ path: newCookie.path,
253
+ maxAge: newCookie.maxAge
254
+ });
255
+ return response;
256
+ }
257
+
258
+ // src/handlers/index.ts
259
+ function createAuthgearHandlers(config) {
260
+ async function GET(request, { params }) {
261
+ const { authgear } = await params;
262
+ const action = authgear?.[0];
263
+ switch (action) {
264
+ case "login":
265
+ return handleLogin(request, config);
266
+ case "callback":
267
+ return handleCallback(request, config);
268
+ case "logout":
269
+ return handleLogout(request, config);
270
+ case "userinfo":
271
+ return handleUserInfo(request, config);
272
+ default:
273
+ return NextResponse6.json({ error: "not_found" }, { status: 404 });
274
+ }
275
+ }
276
+ async function POST(request, { params }) {
277
+ const { authgear } = await params;
278
+ const action = authgear?.[0];
279
+ switch (action) {
280
+ case "refresh":
281
+ return handleRefresh(request, config);
282
+ default:
283
+ return NextResponse6.json({ error: "not_found" }, { status: 404 });
284
+ }
285
+ }
286
+ return { GET, POST };
287
+ }
288
+ export {
289
+ DEFAULT_SCOPES,
290
+ Page,
291
+ SessionState,
292
+ createAuthgearHandlers
293
+ };
294
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers/index.ts","../src/handlers/login.ts","../src/oauth/pkce.ts","../src/oauth/authorize.ts","../src/handlers/callback.ts","../src/handlers/logout.ts","../src/handlers/refresh.ts","../src/handlers/userinfo.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { handleLogin } from \"./login.js\";\nimport { handleCallback } from \"./callback.js\";\nimport { handleLogout } from \"./logout.js\";\nimport { handleRefresh } from \"./refresh.js\";\nimport { handleUserInfo } from \"./userinfo.js\";\n\n/**\n * Creates Next.js route handlers for all Authgear auth endpoints.\n *\n * Usage in `app/api/auth/[...authgear]/route.ts`:\n * ```ts\n * import { createAuthgearHandlers } from \"@authgear/nextjs\";\n * export const { GET, POST } = createAuthgearHandlers(config);\n * ```\n *\n * Routes handled:\n * - GET /api/auth/login — Start OAuth flow\n * - GET /api/auth/callback — Handle OAuth callback\n * - GET /api/auth/logout — Logout and revoke tokens\n * - POST /api/auth/refresh — Refresh access token\n * - GET /api/auth/userinfo — Get current user info\n */\nexport function createAuthgearHandlers(config: AuthgearConfig) {\n async function GET(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"login\":\n return handleLogin(request, config);\n case \"callback\":\n return handleCallback(request, config);\n case \"logout\":\n return handleLogout(request, config);\n case \"userinfo\":\n return handleUserInfo(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n async function POST(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"refresh\":\n return handleRefresh(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n return { GET, POST };\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { generateCodeVerifier } from \"../oauth/pkce.js\";\nimport { buildAuthorizeURL, generateState } from \"../oauth/authorize.js\";\nimport { buildPKCECookie } from \"../session/cookie.js\";\n\nexport async function handleLogin(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n const returnTo = request.nextUrl.searchParams.get(\"returnTo\") ?? \"/\";\n const codeVerifier = generateCodeVerifier();\n const state = generateState();\n\n const authorizeURL = buildAuthorizeURL(oidcConfig, {\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n scopes: resolved.scopes,\n codeVerifier,\n state,\n });\n\n const pkceCookie = buildPKCECookie({ codeVerifier, state, returnTo }, resolved.sessionSecret);\n\n const response = NextResponse.redirect(authorizeURL);\n response.cookies.set(pkceCookie.name, pkceCookie.value, {\n httpOnly: pkceCookie.httpOnly,\n secure: pkceCookie.secure,\n sameSite: pkceCookie.sameSite,\n path: pkceCookie.path,\n maxAge: pkceCookie.maxAge,\n });\n\n return response;\n}\n","import { randomBytes, createHash } from \"node:crypto\";\n\nconst VERIFIER_LENGTH = 64;\n\nexport function generateCodeVerifier(): string {\n return randomBytes(VERIFIER_LENGTH)\n .toString(\"base64url\")\n .slice(0, VERIFIER_LENGTH);\n}\n\nexport function computeCodeChallenge(codeVerifier: string): string {\n return createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n}\n","import { randomBytes } from \"node:crypto\";\nimport type { OIDCConfiguration } from \"../types.js\";\nimport { computeCodeChallenge } from \"./pkce.js\";\n\n/**\n * Build the URL to open an Authgear page (e.g. /settings) with the user\n * already authenticated via an app session token.\n */\nexport function buildOpenURL(\n oidcConfig: OIDCConfiguration,\n params: {\n clientID: string;\n appSessionToken: string;\n targetPath: string; // e.g. \"/settings\"\n },\n): string {\n const authorizationEndpoint = new URL(oidcConfig.authorization_endpoint);\n const settingsURL = `${authorizationEndpoint.origin}${params.targetPath}`;\n const loginHint = `https://authgear.com/login_hint?type=app_session_token&app_session_token=${encodeURIComponent(params.appSessionToken)}`;\n\n const url = new URL(oidcConfig.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"none\");\n url.searchParams.set(\"client_id\", params.clientID);\n url.searchParams.set(\"redirect_uri\", settingsURL);\n url.searchParams.set(\"prompt\", \"none\");\n url.searchParams.set(\"login_hint\", loginHint);\n return url.toString();\n}\n\nexport interface AuthorizeParams {\n clientID: string;\n redirectURI: string;\n scopes: string[];\n codeVerifier: string;\n state: string;\n}\n\nexport function generateState(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nexport function buildAuthorizeURL(\n oidcConfig: OIDCConfiguration,\n params: AuthorizeParams,\n): string {\n const url = new URL(oidcConfig.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", params.clientID);\n url.searchParams.set(\"redirect_uri\", params.redirectURI);\n url.searchParams.set(\"scope\", params.scopes.join(\" \"));\n url.searchParams.set(\"code_challenge\", computeCodeChallenge(params.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"state\", params.state);\n return url.toString();\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { exchangeCode } from \"../oauth/token.js\";\nimport { decryptPKCECookie, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleCallback(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const code = request.nextUrl.searchParams.get(\"code\");\n const state = request.nextUrl.searchParams.get(\"state\");\n const error = request.nextUrl.searchParams.get(\"error\");\n const errorDescription = request.nextUrl.searchParams.get(\"error_description\");\n\n if (error) {\n return NextResponse.json(\n { error, error_description: errorDescription },\n { status: 400 },\n );\n }\n\n if (!code || !state) {\n return NextResponse.json(\n { error: \"missing_params\", error_description: \"Missing code or state parameter\" },\n { status: 400 },\n );\n }\n\n // Validate PKCE state\n const pkceCookieValue = request.cookies.get(\"authgear.pkce\")?.value;\n if (!pkceCookieValue) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"Missing PKCE cookie\" },\n { status: 400 },\n );\n }\n\n const pkceData = decryptPKCECookie(pkceCookieValue, resolved.sessionSecret);\n if (!pkceData || pkceData.state !== state) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"State mismatch\" },\n { status: 400 },\n );\n }\n\n // Exchange code for tokens\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await exchangeCode(oidcConfig, {\n code,\n codeVerifier: pkceData.codeVerifier,\n clientID: resolved.clientID,\n clientSecret: resolved.clientSecret || undefined,\n redirectURI: resolved.redirectURI,\n });\n\n // Build session cookie\n const sessionCookie = buildSessionCookie(\n resolved.cookieName,\n {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? null,\n idToken: tokenResponse.id_token ?? null,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n },\n resolved.sessionSecret,\n );\n\n const returnTo = pkceData.returnTo || \"/\";\n const redirectURL = new URL(returnTo, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n // Set session cookie\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n // Clear PKCE cookie\n response.cookies.set(\"authgear.pkce\", \"\", { maxAge: 0, path: \"/\" });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { revokeToken } from \"../oauth/token.js\";\nimport { decryptSession, buildClearCookie } from \"../session/cookie.js\";\n\nexport async function handleLogout(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n // Revoke refresh token if present\n const sessionCookie = request.cookies.get(resolved.cookieName)?.value;\n if (sessionCookie) {\n const session = decryptSession(sessionCookie, resolved.sessionSecret);\n if (session?.refreshToken) {\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n try {\n await revokeToken(oidcConfig, session.refreshToken);\n } catch {\n // Best-effort revocation\n }\n }\n }\n\n const clearCookie = buildClearCookie(resolved.cookieName);\n const redirectURL = new URL(resolved.postLogoutRedirectURI, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n response.cookies.set(clearCookie.name, clearCookie.value, {\n httpOnly: clearCookie.httpOnly,\n secure: clearCookie.secure,\n sameSite: clearCookie.sameSite,\n path: clearCookie.path,\n maxAge: clearCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { decryptSession, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleRefresh(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n const session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session?.refreshToken) {\n return NextResponse.json({ error: \"no_refresh_token\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n clientSecret: resolved.clientSecret || undefined,\n });\n\n const newSession = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n\n const sessionCookie = buildSessionCookie(resolved.cookieName, newSession, resolved.sessionSecret);\n const response = NextResponse.json({ ok: true });\n\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig, UserInfo } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { decryptSession } from \"../session/cookie.js\";\nimport { isTokenExpired } from \"../session/state.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { buildSessionCookie } from \"../session/cookie.js\";\nimport { parseUserInfo } from \"../user.js\";\n\nexport async function handleUserInfo(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n let session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session) {\n return NextResponse.json({ error: \"invalid_session\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n // Auto-refresh if access token is expired\n if (isTokenExpired(session.expiresAt) && session.refreshToken) {\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n clientSecret: resolved.clientSecret || undefined,\n });\n session = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n }\n\n // Fetch user info from Authgear\n const userinfoRes = await fetch(oidcConfig.userinfo_endpoint, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n });\n\n if (!userinfoRes.ok) {\n return NextResponse.json({ error: \"userinfo_failed\" }, { status: userinfoRes.status });\n }\n\n const raw = (await userinfoRes.json()) as Record<string, unknown>;\n const userInfo: UserInfo = parseUserInfo(raw);\n\n const response = NextResponse.json(userInfo);\n\n // Update session cookie if tokens were refreshed\n const newCookie = buildSessionCookie(resolved.cookieName, session, resolved.sessionSecret);\n response.cookies.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n\n return response;\n}\n"],"mappings":"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","names":["NextResponse","randomBytes","randomBytes","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse"]}
@@ -0,0 +1,32 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { A as AuthgearConfig } from './types-Csfra4K2.js';
3
+
4
+ interface AuthgearProxyOptions extends AuthgearConfig {
5
+ /**
6
+ * Paths that require authentication. Unauthenticated requests are redirected to login.
7
+ * Supports exact paths and prefix patterns ending with `*` (e.g. "/dashboard/*").
8
+ */
9
+ protectedPaths?: string[];
10
+ /**
11
+ * Paths that are always public (never redirected to login).
12
+ * Takes precedence over protectedPaths.
13
+ * Defaults to ["/api/auth/*"].
14
+ */
15
+ publicPaths?: string[];
16
+ /**
17
+ * URL to redirect unauthenticated users. Defaults to "/api/auth/login".
18
+ */
19
+ loginPath?: string;
20
+ }
21
+ /**
22
+ * Create a Next.js 16 proxy function for Authgear authentication.
23
+ *
24
+ * Usage in `proxy.ts`:
25
+ * ```ts
26
+ * import { createAuthgearProxy } from "@authgear/nextjs/proxy";
27
+ * export const proxy = createAuthgearProxy({ ...config, protectedPaths: ["/dashboard/*"] });
28
+ * ```
29
+ */
30
+ declare function createAuthgearProxy(options: AuthgearProxyOptions): (request: NextRequest) => Promise<NextResponse>;
31
+
32
+ export { type AuthgearProxyOptions, createAuthgearProxy };
package/dist/proxy.js ADDED
@@ -0,0 +1,75 @@
1
+ import {
2
+ buildSessionCookie,
3
+ decryptSession,
4
+ fetchOIDCConfiguration,
5
+ isTokenExpired,
6
+ refreshAccessToken,
7
+ resolveConfig
8
+ } from "./chunk-MJD3XNUK.js";
9
+ import "./chunk-UY6NEM2T.js";
10
+
11
+ // src/proxy.ts
12
+ import { NextResponse } from "next/server";
13
+ function matchesPath(pathname, patterns) {
14
+ return patterns.some((pattern) => {
15
+ if (pattern.endsWith("*")) {
16
+ return pathname.startsWith(pattern.slice(0, -1));
17
+ }
18
+ return pathname === pattern;
19
+ });
20
+ }
21
+ function createAuthgearProxy(options) {
22
+ const resolved = resolveConfig(options);
23
+ const protectedPaths = options.protectedPaths ?? [];
24
+ const publicPaths = options.publicPaths ?? ["/api/auth/*"];
25
+ const loginPath = options.loginPath ?? "/api/auth/login";
26
+ return async function proxy(request) {
27
+ const { pathname } = request.nextUrl;
28
+ if (matchesPath(pathname, publicPaths)) {
29
+ return NextResponse.next();
30
+ }
31
+ const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;
32
+ let sessionData = sessionCookieValue ? decryptSession(sessionCookieValue, resolved.sessionSecret) : null;
33
+ if (sessionData && isTokenExpired(sessionData.expiresAt) && sessionData.refreshToken) {
34
+ try {
35
+ const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);
36
+ const tokenResponse = await refreshAccessToken(oidcConfig, {
37
+ refreshToken: sessionData.refreshToken,
38
+ clientID: resolved.clientID,
39
+ clientSecret: resolved.clientSecret || void 0
40
+ });
41
+ sessionData = {
42
+ accessToken: tokenResponse.access_token,
43
+ refreshToken: tokenResponse.refresh_token ?? sessionData.refreshToken,
44
+ idToken: tokenResponse.id_token ?? sessionData.idToken,
45
+ expiresAt: Math.floor(Date.now() / 1e3) + tokenResponse.expires_in
46
+ };
47
+ } catch {
48
+ sessionData = null;
49
+ }
50
+ }
51
+ if (!sessionData && matchesPath(pathname, protectedPaths)) {
52
+ const loginURL = new URL(loginPath, request.nextUrl.origin);
53
+ loginURL.searchParams.set("returnTo", pathname);
54
+ return NextResponse.redirect(loginURL);
55
+ }
56
+ const response = NextResponse.next();
57
+ if (sessionData) {
58
+ const requestHeaders = new Headers(request.headers);
59
+ requestHeaders.set("Authorization", `Bearer ${sessionData.accessToken}`);
60
+ const newCookie = buildSessionCookie(resolved.cookieName, sessionData, resolved.sessionSecret);
61
+ response.cookies.set(newCookie.name, newCookie.value, {
62
+ httpOnly: newCookie.httpOnly,
63
+ secure: newCookie.secure,
64
+ sameSite: newCookie.sameSite,
65
+ path: newCookie.path,
66
+ maxAge: newCookie.maxAge
67
+ });
68
+ }
69
+ return response;
70
+ };
71
+ }
72
+ export {
73
+ createAuthgearProxy
74
+ };
75
+ //# sourceMappingURL=proxy.js.map