@snokam/auth 2.2.22-alpha.0 → 2.3.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +374 -1041
  2. package/package.json +36 -48
  3. package/src/app-router/components/AuthComplete.styled.tsx +17 -0
  4. package/src/app-router/components/AuthComplete.tsx +52 -0
  5. package/src/app-router/components/Login.tsx +120 -0
  6. package/src/app-router/components/Provider/index.tsx +55 -0
  7. package/src/app-router/index.tsx +56 -0
  8. package/src/app-router/server.ts +93 -0
  9. package/src/build-proxy.tsx +160 -0
  10. package/src/components/Authenticate.tsx +38 -0
  11. package/src/components/Authenticated.tsx +11 -0
  12. package/src/components/Consent/index.tsx +35 -0
  13. package/src/components/EmployeeGate/index.tsx +59 -0
  14. package/src/components/Error/index.tsx +90 -0
  15. package/src/components/Error/styled.tsx +63 -0
  16. package/src/components/InactiveEmployee/index.tsx +18 -0
  17. package/src/components/LoggedOut/index.tsx +23 -0
  18. package/src/components/NotProvisioned/index.tsx +19 -0
  19. package/src/components/Unauthenticated.tsx +11 -0
  20. package/src/config.ts +9 -0
  21. package/src/handoff/handlers.ts +146 -0
  22. package/src/handoff/return-to.ts +40 -0
  23. package/src/handoff/ticket.ts +97 -0
  24. package/src/hooks.ts +146 -0
  25. package/src/impersonation/context.tsx +185 -0
  26. package/src/impersonation/cookies.ts +124 -0
  27. package/src/impersonation/handlers.ts +234 -0
  28. package/src/impersonation/routes.ts +30 -0
  29. package/src/impersonation/ticket.ts +79 -0
  30. package/src/index.tsx +31 -0
  31. package/src/options.tsx +389 -0
  32. package/src/page-router/components/Login.tsx +50 -0
  33. package/src/page-router/components/Provider/index.tsx +45 -0
  34. package/src/page-router/index.tsx +22 -0
  35. package/src/page-router/server.ts +6 -0
  36. package/src/password/api.ts +183 -0
  37. package/src/roles/context.tsx +54 -0
  38. package/src/route-handler/token.ts +124 -0
  39. package/src/route-handler.ts +52 -0
  40. package/src/scopes/context.tsx +127 -0
  41. package/src/scopes/matcher.ts +47 -0
  42. package/src/server/auth.tsx +30 -0
  43. package/src/server/handler.tsx +68 -0
  44. package/src/server/token.tsx +10 -0
  45. package/{lib/server.d.ts → src/server.tsx} +7 -4
  46. package/src/sso/exchange.ts +64 -0
  47. package/src/sso/tenant-cookie.ts +18 -0
  48. package/src/theme/employee-theme-sync.tsx +55 -0
  49. package/src/token/claims.ts +12 -0
  50. package/src/utils/callback-url.ts +17 -0
  51. package/src/utils/consent.ts +22 -0
  52. package/src/utils/cookies.ts +75 -0
  53. package/src/utils/cors.ts +68 -0
  54. package/src/utils/params.ts +15 -0
  55. package/src/utils/types.ts +52 -0
  56. package/src/utils/url.ts +19 -0
  57. package/lib/components/Authenticate.d.ts +0 -2
  58. package/lib/components/Authenticated.d.ts +0 -6
  59. package/lib/components/Error/index.d.ts +0 -5
  60. package/lib/components/Login.d.ts +0 -2
  61. package/lib/components/Provider/index.d.ts +0 -7
  62. package/lib/components/Unauthenticated.d.ts +0 -6
  63. package/lib/hooks.d.ts +0 -10
  64. package/lib/index.d.ts +0 -14
  65. package/lib/index.js +0 -42
  66. package/lib/index.js.map +0 -1
  67. package/lib/middleware.d.ts +0 -7
  68. package/lib/middleware.js +0 -2
  69. package/lib/middleware.js.map +0 -1
  70. package/lib/options.d.ts +0 -5
  71. package/lib/options.js +0 -2
  72. package/lib/options.js.map +0 -1
  73. package/lib/server/auth.d.ts +0 -11
  74. package/lib/server/handler.d.ts +0 -14
  75. package/lib/server/token.d.ts +0 -8
  76. package/lib/server.js +0 -2
  77. package/lib/server.js.map +0 -1
  78. package/lib/utils/cookies.d.ts +0 -5
  79. package/lib/utils/params.d.ts +0 -1
  80. package/lib/utils/types.d.ts +0 -39
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@snokam/auth",
3
3
  "description": "Snøkam auth components",
4
- "version": "2.2.22-alpha.0",
5
- "main": "lib/index.js",
6
- "typings": "lib/index.d.ts",
4
+ "version": "2.3.0",
5
+ "main": "src/index.tsx",
6
+ "types": "src/index.tsx",
7
7
  "files": [
8
- "lib",
8
+ "src",
9
9
  "package.json",
10
10
  "CHANGELOG.md"
11
11
  ],
@@ -14,58 +14,46 @@
14
14
  "url": "ssh://git@github.com:snokam/components.git",
15
15
  "directory": "@snokam/auth"
16
16
  },
17
- "scripts": {
18
- "start": "webpack --watch --config=../../../webpack/webpack.dev.js",
19
- "build": "webpack --config=../../../webpack/webpack.prod.js",
20
- "build:doc": "webpack --config=../../../webpack/webpack.dev.js",
21
- "clean": "rm -rf lib"
22
- },
23
17
  "publishConfig": {
24
18
  "access": "public"
25
19
  },
26
20
  "dependencies": {
27
- "@azure/msal-node": "^3.7.3",
21
+ "@azure/msal-node": "^5.4.2",
28
22
  "@emotion/react": "^11.14.0",
29
23
  "@emotion/styled": "^11.14.1",
30
- "@mui/material": "^7.3.2",
31
- "@snokam/core": "^1.4.5",
32
- "next-auth": "^4.24.11",
33
- "path-to-regexp": "^8.3.0"
24
+ "@mui/material": "^9.2.0",
25
+ "next-auth": "4.24.15",
26
+ "path-to-regexp": "^8.4.2",
27
+ "@snokam/employees-client": "1.0.0",
28
+ "@snokam/identity": "1.1.0",
29
+ "@snokam/config": "1.1.0",
30
+ "@snokam/platform-client": "1.0.0",
31
+ "@snokam/react-query": "1.1.0",
32
+ "@snokam/ui": "1.1.0"
34
33
  },
35
34
  "peerDependencies": {
36
- "next": "*",
37
- "react": "*",
38
- "react-dom": "*"
35
+ "next": "16.2.11",
36
+ "react": "19.2.8",
37
+ "react-dom": "19.2.8"
39
38
  },
40
- "exports": {
41
- ".": {
42
- "import": "./lib/index.js",
43
- "require": "./lib/index.js",
44
- "types": "./lib/index.d.ts"
45
- },
46
- "./server": {
47
- "import": "./lib/server.js",
48
- "require": "./lib/server.js",
49
- "types": "./lib/server.d.ts"
50
- },
51
- "./middleware": {
52
- "import": "./lib/middleware.js",
53
- "require": "./lib/middleware.js",
54
- "types": "./lib/middleware.d.ts"
55
- }
39
+ "devDependencies": {
40
+ "@types/react": "^19.2.17",
41
+ "@types/react-dom": "^19.2.3"
56
42
  },
57
- "typesVersions": {
58
- "*": {
59
- "*": [
60
- "lib/index.d.ts"
61
- ],
62
- "server": [
63
- "lib/server.d.ts"
64
- ],
65
- "middleware": [
66
- "lib/middleware.d.ts"
67
- ]
68
- }
43
+ "exports": {
44
+ ".": "./src/index.tsx",
45
+ "./config": "./src/config.ts",
46
+ "./server": "./src/server.tsx",
47
+ "./middleware": "./src/build-proxy.tsx",
48
+ "./route-handler": "./src/route-handler.ts",
49
+ "./page-router": "./src/page-router/index.tsx",
50
+ "./page-router/server": "./src/page-router/server.ts",
51
+ "./app-router": "./src/app-router/index.tsx",
52
+ "./app-router/server": "./src/app-router/server.ts",
53
+ "./password": "./src/password/api.ts",
54
+ "./sso-tenant": "./src/sso/tenant-cookie.ts"
69
55
  },
70
- "gitHead": "97d0b6543286e059de634fd63fc25d5da612d7a0"
71
- }
56
+ "scripts": {
57
+ "lint": "eslint ."
58
+ }
59
+ }
@@ -0,0 +1,17 @@
1
+ import styled from "@emotion/styled";
2
+
3
+ export const RetryLink = styled.a`
4
+ display: inline-flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ padding: 0.6rem 1.4rem;
8
+ border-radius: 999px;
9
+ background: var(--o-cyan);
10
+ color: var(--o-cyan-ink);
11
+ font-weight: 600;
12
+ text-decoration: none;
13
+
14
+ &:hover {
15
+ filter: brightness(0.95);
16
+ }
17
+ `;
@@ -0,0 +1,52 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { useSearchParams } from "next/navigation";
5
+ import { signIn } from "next-auth/react";
6
+ import { PageLoader } from "@snokam/ui";
7
+ import Error from "../../components/Error";
8
+ import { RetryLink } from "./AuthComplete.styled";
9
+
10
+ export const AuthComplete = () => {
11
+ const params = useSearchParams();
12
+ const done = useRef(false);
13
+ const [failed, setFailed] = useState(false);
14
+
15
+ useEffect(() => {
16
+ if (done.current) {
17
+ return;
18
+ }
19
+ const ticket = params.get("ticket");
20
+ if (!ticket) {
21
+ return;
22
+ }
23
+ done.current = true;
24
+ signIn("handoff", {
25
+ ticket,
26
+ callbackUrl: params.get("callbackUrl") || "/",
27
+ redirect: false,
28
+ }).then((result) => {
29
+ if (result?.ok && result.url) {
30
+ window.location.href = result.url;
31
+ } else {
32
+ setFailed(true);
33
+ }
34
+ });
35
+ }, [params]);
36
+
37
+ if (failed) {
38
+ return (
39
+ <Error
40
+ heading="Innlogging mislyktes"
41
+ message="Vi klarte ikke å fullføre innloggingen. Prøv igjen, eller ta kontakt med support hvis problemet vedvarer."
42
+ action={
43
+ <RetryLink href="/auth/login?callbackUrl=%2F">Prøv igjen</RetryLink>
44
+ }
45
+ />
46
+ );
47
+ }
48
+
49
+ return <PageLoader />;
50
+ };
51
+
52
+ export default AuthComplete;
@@ -0,0 +1,120 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState, useRef } from "react";
4
+ import { useRouter, useSearchParams } from "next/navigation";
5
+ import { useAuth } from "../../hooks";
6
+ import { signIn } from "next-auth/react";
7
+ import { PageLoader } from "@snokam/ui";
8
+ import { useOptionalConfig } from "@snokam/config";
9
+ import { safeCallbackTarget } from "../../utils/callback-url";
10
+ import Error from "../../components/Error";
11
+ import Consent from "../../components/Consent";
12
+ import LoggedOut from "../../components/LoggedOut";
13
+
14
+ export const Login = () => {
15
+ const { inProgress, isAuthenticated } = useAuth();
16
+ const [callbackUrl, setCallbackUrl] = useState<string | null>(null);
17
+ const [authParams, setAuthParams] = useState<Record<string, string>>({});
18
+ const [errorKind, setErrorKind] = useState<string | null>(null);
19
+ const [loggedOut, setLoggedOut] = useState(false);
20
+ const hasSignedIn = useRef(false);
21
+ const router = useRouter();
22
+ const searchParams = useSearchParams();
23
+ const config = useOptionalConfig();
24
+ const central = config?.NEXT_PUBLIC_CENTRAL_AUTH_URL;
25
+ const isError = !!errorKind;
26
+
27
+ useEffect(() => {
28
+ const rawCallbackUrl = searchParams.get("callbackUrl");
29
+ setCallbackUrl(rawCallbackUrl ? safeCallbackTarget(rawCallbackUrl) : null);
30
+ setErrorKind(searchParams.get("error"));
31
+ setLoggedOut(searchParams.get("loggedOut") === "1");
32
+
33
+ const authParamsObj: Record<string, string> = {};
34
+ searchParams.forEach((value, key) => {
35
+ if (key !== "error" && key !== "callbackUrl" && key !== "loggedOut") {
36
+ authParamsObj[key] = value;
37
+ }
38
+ });
39
+ setAuthParams(authParamsObj);
40
+ }, [searchParams]);
41
+
42
+ const startSignIn = (extraParams: Record<string, string> = {}) => {
43
+ const target = callbackUrl || "/";
44
+ const onLocalhost = window.location.hostname === "localhost";
45
+ const onCentral =
46
+ onLocalhost || (central && central.startsWith(window.location.origin));
47
+ if (central && !onCentral) {
48
+ const returnTo = `${window.location.origin}${target}`;
49
+ const prompt = extraParams.prompt
50
+ ? `&prompt=${encodeURIComponent(extraParams.prompt)}`
51
+ : "";
52
+ window.location.href = `${central}/handoff/start?returnTo=${encodeURIComponent(
53
+ returnTo
54
+ )}${prompt}`;
55
+ } else {
56
+ signIn(
57
+ "azure-ad",
58
+ { callbackUrl: target },
59
+ { ...authParams, ...extraParams }
60
+ );
61
+ }
62
+ };
63
+
64
+ useEffect(() => {
65
+ if (!callbackUrl) {
66
+ return;
67
+ }
68
+ if (isError) {
69
+ return;
70
+ }
71
+ if (hasSignedIn.current) {
72
+ return;
73
+ }
74
+ if (inProgress) {
75
+ return;
76
+ }
77
+
78
+ if (isAuthenticated) {
79
+ router.replace(callbackUrl || "/");
80
+ } else if (!loggedOut) {
81
+ hasSignedIn.current = true;
82
+ startSignIn();
83
+ }
84
+ // eslint-disable-next-line react-hooks/exhaustive-deps
85
+ }, [
86
+ inProgress,
87
+ isAuthenticated,
88
+ callbackUrl,
89
+ router,
90
+ isError,
91
+ central,
92
+ loggedOut,
93
+ ]);
94
+
95
+ if (loggedOut && !isAuthenticated && !isError) {
96
+ return (
97
+ <LoggedOut onLogin={() => startSignIn({ prompt: "select_account" })} />
98
+ );
99
+ }
100
+
101
+ if (errorKind === "consent") {
102
+ if (
103
+ typeof window !== "undefined" &&
104
+ !sessionStorage.getItem("auth-consent-retried")
105
+ ) {
106
+ sessionStorage.setItem("auth-consent-retried", "1");
107
+ signIn("azure-ad", { callbackUrl: callbackUrl || "/" }, authParams);
108
+ return <PageLoader />;
109
+ }
110
+ return <Consent />;
111
+ }
112
+
113
+ if (isError) {
114
+ return <Error />;
115
+ }
116
+
117
+ return <PageLoader />;
118
+ };
119
+
120
+ export default Login;
@@ -0,0 +1,55 @@
1
+ "use client";
2
+
3
+ import { memo, ReactNode, Suspense, useEffect } from "react";
4
+ import { useSearchParams } from "next/navigation";
5
+ import { Auth } from "../../../utils/types";
6
+ import { Authenticate } from "../../../components/Authenticate";
7
+ import { SessionProvider } from "next-auth/react";
8
+ import { useAuth } from "../../../hooks";
9
+ import { EmployeeThemeSync } from "../../../theme/employee-theme-sync";
10
+
11
+ export const Provider = memo((props: Auth.Provider.Props) => {
12
+ const { session, children } = props;
13
+
14
+ return (
15
+ <SessionProvider session={session ?? undefined}>
16
+ <EmployeeThemeSync />
17
+ <Suspense fallback={<>{children}</>}>
18
+ <ProviderInner {...props}>{children}</ProviderInner>
19
+ </Suspense>
20
+ </SessionProvider>
21
+ );
22
+ });
23
+
24
+ const ProviderInner = (props: Auth.Provider.Props) => {
25
+ const { children, enforceLogin } = props;
26
+ const searchParams = useSearchParams();
27
+ const enforceLoginParam = searchParams?.get("enforceLogin");
28
+
29
+ if (enforceLoginParam || enforceLogin) {
30
+ return (
31
+ <Authenticate provider="azure-ad" {...props}>
32
+ {children}
33
+ </Authenticate>
34
+ );
35
+ }
36
+
37
+ return <Wrapper>{children}</Wrapper>;
38
+ };
39
+
40
+ const Wrapper = ({ children }: { children: ReactNode }) => {
41
+ const { signOut } = useAuth();
42
+ const searchParams = useSearchParams();
43
+
44
+ useEffect(() => {
45
+ if (searchParams?.get("logout") === "true") {
46
+ signOut({
47
+ callbackUrl: "/auth/login?callbackUrl=/",
48
+ });
49
+ }
50
+ }, [searchParams, signOut]);
51
+
52
+ return <>{children}</>;
53
+ };
54
+
55
+ export default Provider;
@@ -0,0 +1,56 @@
1
+ "use client";
2
+
3
+ import { Provider } from "./components/Provider";
4
+ import { Authenticate } from "../components/Authenticate";
5
+ import { Login } from "./components/Login";
6
+ import { AuthComplete } from "./components/AuthComplete";
7
+ import { Error } from "../components/Error";
8
+ import { NotProvisioned } from "../components/NotProvisioned";
9
+ import { EmployeeGate } from "../components/EmployeeGate";
10
+
11
+ export { Unauthenticated } from "../components/Unauthenticated";
12
+ export { Authenticated } from "../components/Authenticated";
13
+ export { useAuth } from "../hooks";
14
+ export { EmployeeThemeSync } from "../theme/employee-theme-sync";
15
+ export { ScopesProvider, useScopes, useHasScope } from "../scopes/context";
16
+ export {
17
+ usePlatformRoles,
18
+ useHasPlatformRole,
19
+ PLATFORM_ADMIN,
20
+ PLATFORM_OPERATOR,
21
+ } from "../roles/context";
22
+ export type { RolesState } from "../roles/context";
23
+ export type { ScopesState } from "../scopes/context";
24
+ export {
25
+ scopeGranted,
26
+ resolveEmployeeScopes,
27
+ type ScopeAccess,
28
+ } from "../scopes/matcher";
29
+ export {
30
+ ImpersonationProvider,
31
+ useImpersonation,
32
+ } from "../impersonation/context";
33
+ export type { ImpersonationActive } from "../impersonation/cookies";
34
+ export { signIn, SessionProvider } from "next-auth/react";
35
+ export {
36
+ Login,
37
+ AuthComplete,
38
+ Error,
39
+ Provider,
40
+ Authenticate,
41
+ NotProvisioned,
42
+ EmployeeGate,
43
+ };
44
+
45
+ export default {
46
+ Login,
47
+ AuthComplete,
48
+ Error,
49
+ Provider,
50
+ Authenticate,
51
+ NotProvisioned,
52
+ EmployeeGate,
53
+ };
54
+
55
+ export type { Session } from "next-auth";
56
+ export type { Auth } from "../utils/types";
@@ -0,0 +1,93 @@
1
+ import {
2
+ getServerSession as nextAuthGetServerSession,
3
+ type Session,
4
+ } from "next-auth";
5
+ import type { NextAuthOptions } from "next-auth";
6
+ import { headers } from "next/headers";
7
+ import { getTenantDomains } from "@snokam/config";
8
+ import { getManagedBearer } from "@snokam/identity";
9
+
10
+ export { getServerToken } from "../server/token";
11
+ export { getNextAuthOptions } from "../options";
12
+ export type { Auth } from "../utils/types";
13
+
14
+ export async function serverTenantToken(): Promise<string | undefined> {
15
+ const scope = serverApiScope();
16
+ if (!scope) return undefined;
17
+ return getManagedBearer(scope);
18
+ }
19
+
20
+ export async function resolveServerApiBase(service: string): Promise<string> {
21
+ if (!service) return "";
22
+ const envKey = `NEXT_PUBLIC_${service.toUpperCase().replace(/-/g, "_")}_API_BASE_URL`;
23
+ const override = process.env[envKey];
24
+ if (
25
+ override &&
26
+ (override.includes("localhost") || override.includes("127.0.0.1"))
27
+ ) {
28
+ return override;
29
+ }
30
+
31
+ const requestHeaders = await headers();
32
+ const host =
33
+ requestHeaders.get("x-forwarded-host") ?? requestHeaders.get("host");
34
+ const domains = await getTenantDomains({
35
+ host,
36
+ platformApiBase: process.env.NEXT_PUBLIC_PLATFORM_API_BASE_URL ?? "",
37
+ authToken: await serverTenantToken(),
38
+ });
39
+ const custom = domains.find(
40
+ (domain) => domain.type === "backend" && domain.env === envKey
41
+ );
42
+ if (custom?.url) return custom.url;
43
+
44
+ const bare = host?.split(":")[0];
45
+ const tenantHost = (
46
+ bare === "localhost" || bare === "127.0.0.1"
47
+ ? process.env.NEXT_PUBLIC_TENANT_HOST
48
+ : host
49
+ )?.split(":")[0];
50
+ return tenantHost ? `https://${tenantHost}/api/${service}` : (override ?? "");
51
+ }
52
+
53
+ export function serverApiScope(): string {
54
+ const clientId = process.env.NEXT_PUBLIC_AZURE_AD_CLIENT_ID ?? "";
55
+ return clientId ? `api://${clientId}/user_impersonation` : "";
56
+ }
57
+
58
+ export async function resolveServerAppUrl(app: string): Promise<string> {
59
+ const requestHeaders = await headers();
60
+ const host =
61
+ requestHeaders.get("x-forwarded-host") ?? requestHeaders.get("host");
62
+ const domains = await getTenantDomains({
63
+ host,
64
+ platformApiBase: process.env.NEXT_PUBLIC_PLATFORM_API_BASE_URL ?? "",
65
+ authToken: await serverTenantToken(),
66
+ });
67
+ const match = domains.find(
68
+ (domain) => domain.type === "frontend" && domain.app === app
69
+ );
70
+ if (match?.url) return match.url;
71
+
72
+ const bare = host?.split(":")[0];
73
+ const effective =
74
+ !bare || bare === "localhost" || bare === "127.0.0.1"
75
+ ? process.env.NEXT_PUBLIC_TENANT_HOST?.split(":")[0]
76
+ : host;
77
+ return effective ? `https://${effective}` : "";
78
+ }
79
+
80
+ export async function getServerSession(
81
+ authOptions: NextAuthOptions
82
+ ): Promise<Session | null> {
83
+ return nextAuthGetServerSession(authOptions);
84
+ }
85
+
86
+ export async function getAuth(authOptions: NextAuthOptions) {
87
+ const session = await getServerSession(authOptions);
88
+ return {
89
+ isAuthenticated: !!session,
90
+ session,
91
+ user: session?.user,
92
+ };
93
+ }
@@ -0,0 +1,160 @@
1
+ import { match } from "path-to-regexp";
2
+ import { getToken } from "next-auth/jwt";
3
+ import { type NextRequestWithAuth } from "next-auth/middleware";
4
+ import { NextResponse, NextFetchEvent } from "next/server";
5
+ import { Auth } from "./utils/types";
6
+ import { getSessionCookieSettings } from "./utils/cookies";
7
+ import { addCorsHeaders } from "./utils/cors";
8
+ import { getAuthUrl } from "./utils/url";
9
+
10
+ export function buildAuthMiddleware({
11
+ matcher = [],
12
+ callbacks = {},
13
+ protectAllRoutes,
14
+ cors = {},
15
+ ...rest
16
+ }: Auth.Middleware.Props = {}): Auth.Middleware.Builder {
17
+ const requireLogin = process.env.IS_PR === "true";
18
+ const { name } = getSessionCookieSettings(getAuthUrl());
19
+
20
+ const shouldProtectPath = (pathname: string): boolean => {
21
+ if (pathname.startsWith("/api/healthy")) {
22
+ return false;
23
+ }
24
+
25
+ if (pathname.startsWith("/api/ready")) {
26
+ return false;
27
+ }
28
+
29
+ if (pathname.startsWith("/auth/")) {
30
+ return false;
31
+ }
32
+
33
+ if (requireLogin) {
34
+ return !pathname.startsWith("/_next") && !/\.[^/]+$/.test(pathname);
35
+ }
36
+
37
+ if (protectAllRoutes && matcher?.length === 0) {
38
+ return true;
39
+ }
40
+
41
+ return matcher.some((pattern) => {
42
+ if (!pattern) {
43
+ return false;
44
+ }
45
+
46
+ if (pattern.startsWith("/") && pattern.endsWith(")")) {
47
+ const cleanPattern = pattern.substring(1);
48
+ let fixedPattern = cleanPattern;
49
+ if (cleanPattern.startsWith("((?!") && !cleanPattern.startsWith("^")) {
50
+ fixedPattern = "^" + cleanPattern;
51
+ }
52
+
53
+ const regex = new RegExp(fixedPattern);
54
+ const cleanPathname = pathname.startsWith("/")
55
+ ? pathname.substring(1)
56
+ : pathname;
57
+ return regex.test(cleanPathname);
58
+ }
59
+
60
+ if (pattern.endsWith(":path*")) {
61
+ const base = pattern.replace(/\/:path\*$/, "");
62
+ return pathname.startsWith(base);
63
+ }
64
+
65
+ try {
66
+ return match(pattern, { end: true })(pathname) !== false;
67
+ } catch (error) {
68
+ console.warn(`Invalid path pattern in auth middleware: ${error}`);
69
+ return false;
70
+ }
71
+ });
72
+ };
73
+
74
+ const signInPage = "/auth/login";
75
+ const errorPage = "/api/auth/error";
76
+ const publicPaths = ["/api/auth", "/_next", "/favicon.ico"];
77
+
78
+ const authorized =
79
+ callbacks.authorized ??
80
+ (({ req, token }: { req: NextRequestWithAuth; token: unknown }) => {
81
+ return shouldProtectPath(req.nextUrl.pathname) ? !!token : true;
82
+ });
83
+
84
+ const authMiddleware = async (req: NextRequestWithAuth) => {
85
+ const { pathname, search, origin, basePath } = req.nextUrl;
86
+
87
+ if (
88
+ [signInPage, errorPage].includes(pathname) ||
89
+ publicPaths.some((p) => pathname.startsWith(p))
90
+ ) {
91
+ return;
92
+ }
93
+
94
+ const secret =
95
+ rest.secret ?? process.env.NEXTAUTH_SECRET ?? process.env.AUTH_SECRET;
96
+ if (!secret) {
97
+ console.error("[next-auth][error][NO_SECRET]");
98
+ const errorUrl = new URL(`${basePath}${errorPage}`, origin);
99
+ errorUrl.searchParams.append("error", "Configuration");
100
+ return NextResponse.redirect(errorUrl);
101
+ }
102
+
103
+ const token = await getToken({
104
+ req,
105
+ decode: rest.jwt?.decode,
106
+ cookieName: rest.cookies?.sessionToken?.name ?? name,
107
+ secret,
108
+ });
109
+
110
+ const isAuthorized = (await authorized({ req, token })) ?? !!token;
111
+ if (isAuthorized) {
112
+ req.nextauth = { token };
113
+ return;
114
+ }
115
+
116
+ const signInUrl = new URL(`${basePath}${signInPage}`, origin);
117
+ signInUrl.searchParams.append(
118
+ "callbackUrl",
119
+ `${basePath}${pathname}${search}`
120
+ );
121
+ return NextResponse.redirect(signInUrl);
122
+ };
123
+
124
+ const corsEnabled = cors.enabled !== false;
125
+ const corsAllRoutes = cors.allRoutes === true;
126
+
127
+ const middleware = async (
128
+ request: NextRequestWithAuth,
129
+ _event: NextFetchEvent
130
+ ) => {
131
+ const origin = request.headers.get("origin");
132
+ const isApiRoute = request.nextUrl.pathname.startsWith("/api/");
133
+ const applyCors = corsEnabled && (isApiRoute || corsAllRoutes);
134
+
135
+ if (applyCors && request.method === "OPTIONS") {
136
+ const response = new NextResponse(null, { status: 204 });
137
+ return addCorsHeaders(response, origin, cors);
138
+ }
139
+
140
+ const authResponse = await authMiddleware(request);
141
+
142
+ if (applyCors) {
143
+ const response =
144
+ authResponse instanceof NextResponse
145
+ ? authResponse
146
+ : NextResponse.next();
147
+ return addCorsHeaders(response, origin, cors);
148
+ }
149
+
150
+ return authResponse;
151
+ };
152
+
153
+ const config = matcher?.length > 0 ? { matcher } : undefined;
154
+
155
+ return { proxy: middleware, config };
156
+ }
157
+
158
+ const { proxy, config } = buildAuthMiddleware();
159
+ export { config };
160
+ export default proxy;