@irongate-dev/nextjs 0.1.0-beta
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/LICENSE +21 -0
- package/dist/client.cjs +47 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.js +27 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +78 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.cjs +201 -0
- package/dist/middleware.cjs.map +1 -0
- package/dist/middleware.d.cts +20 -0
- package/dist/middleware.d.ts +20 -0
- package/dist/middleware.js +187 -0
- package/dist/middleware.js.map +1 -0
- package/dist/server.cjs +252 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +34 -0
- package/dist/server.d.ts +34 -0
- package/dist/server.js +230 -0
- package/dist/server.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// src/middleware.ts
|
|
2
|
+
import { NextResponse } from "next/server";
|
|
3
|
+
import {
|
|
4
|
+
IrongateError,
|
|
5
|
+
createJwksCache,
|
|
6
|
+
createVerifyToken,
|
|
7
|
+
getAuthCookieNames,
|
|
8
|
+
buildAuthCookies,
|
|
9
|
+
buildClearCookies
|
|
10
|
+
} from "@irongate-dev/core";
|
|
11
|
+
|
|
12
|
+
// src/cookie-env.ts
|
|
13
|
+
function parseBooleanEnv(value) {
|
|
14
|
+
if (value === "true") return true;
|
|
15
|
+
if (value === "false") return false;
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
function resolveSecureDefault(explicit) {
|
|
19
|
+
return explicit ?? parseBooleanEnv(process.env.IRONGATE_COOKIE_SECURE) ?? process.env.NODE_ENV === "production";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/middleware.ts
|
|
23
|
+
function resolveConfig(options) {
|
|
24
|
+
const baseUrl = options.baseUrl ?? process.env.IRONGATE_BASE_URL;
|
|
25
|
+
const clientId = options.clientId ?? process.env.IRONGATE_CLIENT_ID;
|
|
26
|
+
if (!baseUrl) {
|
|
27
|
+
throw new IrongateError(
|
|
28
|
+
"INVALID_CONFIG",
|
|
29
|
+
"IRONGATE_BASE_URL is required (set the env var or pass baseUrl to createMiddleware)"
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (!clientId) {
|
|
33
|
+
throw new IrongateError(
|
|
34
|
+
"INVALID_CONFIG",
|
|
35
|
+
"IRONGATE_CLIENT_ID is required (set the env var or pass clientId to createMiddleware)"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const cookieDomain = options.cookieDomain ?? process.env.IRONGATE_COOKIE_DOMAIN;
|
|
39
|
+
const secure = resolveSecureDefault(options.cookieOptions?.secure);
|
|
40
|
+
const sameSite = options.cookieOptions?.sameSite ?? process.env.IRONGATE_COOKIE_SAMESITE ?? "lax";
|
|
41
|
+
return {
|
|
42
|
+
baseUrl,
|
|
43
|
+
clientId,
|
|
44
|
+
publicRoutes: options.publicRoutes ?? [],
|
|
45
|
+
signInUrl: options.signInUrl ?? process.env.IRONGATE_SIGN_IN_URL ?? "/sign-in",
|
|
46
|
+
afterSignInUrl: options.afterSignInUrl ?? process.env.IRONGATE_AFTER_SIGN_IN ?? "/dashboard",
|
|
47
|
+
cookieNames: getAuthCookieNames(options.cookiePrefix ?? process.env.IRONGATE_COOKIE_PREFIX),
|
|
48
|
+
cookieConfig: {
|
|
49
|
+
...cookieDomain ? { cookieDomain } : {},
|
|
50
|
+
secure,
|
|
51
|
+
sameSite
|
|
52
|
+
},
|
|
53
|
+
jwksCacheTtlMs: Number(process.env.IRONGATE_JWKS_CACHE_TTL ?? 36e5)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function stripIrongateHeaders(headers) {
|
|
57
|
+
const cleaned = new Headers(headers);
|
|
58
|
+
for (const key of Array.from(cleaned.keys())) {
|
|
59
|
+
if (key.toLowerCase().startsWith("x-irongate-")) {
|
|
60
|
+
cleaned.delete(key);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return cleaned;
|
|
64
|
+
}
|
|
65
|
+
function isPublicRoute(pathname, publicRoutes) {
|
|
66
|
+
return publicRoutes.some((route) => pathname === route || pathname.startsWith(`${route}/`));
|
|
67
|
+
}
|
|
68
|
+
function redirectToSignIn(request, config) {
|
|
69
|
+
const signInUrl = new URL(config.signInUrl, request.url);
|
|
70
|
+
signInUrl.searchParams.set("returnUrl", request.nextUrl.pathname);
|
|
71
|
+
return NextResponse.redirect(signInUrl);
|
|
72
|
+
}
|
|
73
|
+
function clearCookiesAndRedirect(request, config, revocationReason) {
|
|
74
|
+
const signInUrl = new URL(config.signInUrl, request.url);
|
|
75
|
+
if (revocationReason) {
|
|
76
|
+
signInUrl.searchParams.set("reason", revocationReason);
|
|
77
|
+
}
|
|
78
|
+
signInUrl.searchParams.set("returnUrl", request.nextUrl.pathname);
|
|
79
|
+
const response = NextResponse.redirect(signInUrl);
|
|
80
|
+
for (const cookie of buildClearCookies(config.cookieNames, config.cookieConfig)) {
|
|
81
|
+
response.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
82
|
+
}
|
|
83
|
+
return response;
|
|
84
|
+
}
|
|
85
|
+
function withUserHeaders(requestHeaders, claims) {
|
|
86
|
+
requestHeaders.set("x-irongate-user-id", claims.sub);
|
|
87
|
+
requestHeaders.set("x-irongate-user-json", JSON.stringify(claims));
|
|
88
|
+
return NextResponse.next({ request: { headers: requestHeaders } });
|
|
89
|
+
}
|
|
90
|
+
var jwksCacheSingleton = null;
|
|
91
|
+
var jwksCacheBaseUrl = null;
|
|
92
|
+
function getJwksCache(baseUrl, jwksCacheTtlMs) {
|
|
93
|
+
if (!jwksCacheSingleton || jwksCacheBaseUrl !== baseUrl) {
|
|
94
|
+
jwksCacheSingleton = createJwksCache({ baseUrl, fetch, jwksCacheTtlMs });
|
|
95
|
+
jwksCacheBaseUrl = baseUrl;
|
|
96
|
+
}
|
|
97
|
+
return jwksCacheSingleton;
|
|
98
|
+
}
|
|
99
|
+
async function refreshSession(refreshToken, config) {
|
|
100
|
+
const response = await fetch(`${config.baseUrl}/v1/auth/refresh`, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: { "Content-Type": "application/json" },
|
|
103
|
+
body: JSON.stringify({ refreshToken, clientId: config.clientId })
|
|
104
|
+
});
|
|
105
|
+
if (response.ok) {
|
|
106
|
+
return await response.json();
|
|
107
|
+
}
|
|
108
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
109
|
+
const errorCode = errorBody.code;
|
|
110
|
+
if (errorCode === "TOKEN_REUSE_DETECTED" || errorCode === "TOKEN_REVOKED" || errorCode === "TOKEN_EXPIRED") {
|
|
111
|
+
return {
|
|
112
|
+
revoked: true,
|
|
113
|
+
...errorBody.revocationReason ? { revocationReason: errorBody.revocationReason } : {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
throw new IrongateError("INTERNAL_SERVER_ERROR", `Refresh failed: ${errorCode ?? "UNKNOWN"}`);
|
|
117
|
+
}
|
|
118
|
+
function createMiddleware(options = {}) {
|
|
119
|
+
const config = resolveConfig(options);
|
|
120
|
+
return async function middleware2(request) {
|
|
121
|
+
const requestHeaders = stripIrongateHeaders(request.headers);
|
|
122
|
+
const pathname = request.nextUrl.pathname;
|
|
123
|
+
if (isPublicRoute(pathname, config.publicRoutes)) {
|
|
124
|
+
return NextResponse.next({ request: { headers: requestHeaders } });
|
|
125
|
+
}
|
|
126
|
+
const accessToken = request.cookies.get(config.cookieNames.accessToken)?.value;
|
|
127
|
+
const refreshToken = request.cookies.get(config.cookieNames.refreshToken)?.value;
|
|
128
|
+
if (!accessToken && !refreshToken) {
|
|
129
|
+
return redirectToSignIn(request, config);
|
|
130
|
+
}
|
|
131
|
+
const jwksCache = getJwksCache(config.baseUrl, config.jwksCacheTtlMs);
|
|
132
|
+
const verifyToken = createVerifyToken({
|
|
133
|
+
baseUrl: config.baseUrl,
|
|
134
|
+
fetch,
|
|
135
|
+
clockSkewToleranceMs: 0,
|
|
136
|
+
jwksCache,
|
|
137
|
+
checkJtiRevoked: void 0
|
|
138
|
+
});
|
|
139
|
+
let claims = null;
|
|
140
|
+
if (accessToken) {
|
|
141
|
+
try {
|
|
142
|
+
claims = await verifyToken(accessToken);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (IrongateError.isIrongate(err) && err.code !== "TOKEN_EXPIRED") {
|
|
145
|
+
return clearCookiesAndRedirect(request, config);
|
|
146
|
+
}
|
|
147
|
+
claims = null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!claims && refreshToken) {
|
|
151
|
+
const refreshResult = await refreshSession(refreshToken, config);
|
|
152
|
+
if ("revoked" in refreshResult) {
|
|
153
|
+
return clearCookiesAndRedirect(request, config, refreshResult.revocationReason);
|
|
154
|
+
}
|
|
155
|
+
claims = await verifyToken(refreshResult.accessToken);
|
|
156
|
+
const response = withUserHeaders(requestHeaders, claims);
|
|
157
|
+
for (const cookie of buildAuthCookies(
|
|
158
|
+
{
|
|
159
|
+
accessToken: refreshResult.accessToken,
|
|
160
|
+
...refreshResult.refreshToken ? { refreshToken: refreshResult.refreshToken } : {},
|
|
161
|
+
expiresIn: refreshResult.expiresIn
|
|
162
|
+
},
|
|
163
|
+
config.cookieNames,
|
|
164
|
+
config.cookieConfig
|
|
165
|
+
)) {
|
|
166
|
+
response.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
167
|
+
}
|
|
168
|
+
return response;
|
|
169
|
+
}
|
|
170
|
+
if (!claims) {
|
|
171
|
+
return redirectToSignIn(request, config);
|
|
172
|
+
}
|
|
173
|
+
return withUserHeaders(requestHeaders, claims);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
var defaultMiddlewareInstance = null;
|
|
177
|
+
async function middleware(request) {
|
|
178
|
+
if (!defaultMiddlewareInstance) {
|
|
179
|
+
defaultMiddlewareInstance = createMiddleware();
|
|
180
|
+
}
|
|
181
|
+
return defaultMiddlewareInstance(request);
|
|
182
|
+
}
|
|
183
|
+
export {
|
|
184
|
+
createMiddleware,
|
|
185
|
+
middleware as default
|
|
186
|
+
};
|
|
187
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/middleware.ts","../src/cookie-env.ts"],"sourcesContent":["import { NextResponse } from \"next/server\";\nimport type { NextRequest } from \"next/server\";\nimport {\n IrongateError,\n createJwksCache,\n createVerifyToken,\n getAuthCookieNames,\n buildAuthCookies,\n buildClearCookies,\n} from \"@irongate-dev/core\";\nimport type { AuthCookieNames, JwksCache } from \"@irongate-dev/core\";\nimport type { TokenClaims, TokenSet } from \"@irongate-dev/types\";\nimport { resolveSecureDefault } from \"./cookie-env.js\";\n\nexport interface MiddlewareConfig {\n baseUrl?: string;\n clientId?: string;\n publicRoutes?: string[];\n signInUrl?: string;\n afterSignInUrl?: string;\n cookiePrefix?: string;\n cookieDomain?: string;\n cookieOptions?: {\n secure?: boolean;\n sameSite?: \"lax\" | \"strict\" | \"none\";\n };\n jwksCacheTtlMs?: number;\n}\n\ninterface ResolvedConfig {\n baseUrl: string;\n clientId: string;\n publicRoutes: string[];\n signInUrl: string;\n afterSignInUrl: string;\n cookieNames: AuthCookieNames;\n cookieConfig: {\n cookieDomain?: string;\n secure: boolean;\n sameSite?: \"lax\" | \"strict\" | \"none\";\n };\n jwksCacheTtlMs: number;\n}\n\nfunction resolveConfig(options: MiddlewareConfig): ResolvedConfig {\n const baseUrl = options.baseUrl ?? process.env.IRONGATE_BASE_URL;\n const clientId = options.clientId ?? process.env.IRONGATE_CLIENT_ID;\n\n if (!baseUrl) {\n throw new IrongateError(\n \"INVALID_CONFIG\",\n \"IRONGATE_BASE_URL is required (set the env var or pass baseUrl to createMiddleware)\"\n );\n }\n if (!clientId) {\n throw new IrongateError(\n \"INVALID_CONFIG\",\n \"IRONGATE_CLIENT_ID is required (set the env var or pass clientId to createMiddleware)\"\n );\n }\n\n const cookieDomain = options.cookieDomain ?? process.env.IRONGATE_COOKIE_DOMAIN;\n const secure = resolveSecureDefault(options.cookieOptions?.secure);\n const sameSite =\n options.cookieOptions?.sameSite ??\n (process.env.IRONGATE_COOKIE_SAMESITE as \"lax\" | \"strict\" | \"none\" | undefined) ??\n \"lax\";\n\n return {\n baseUrl,\n clientId,\n publicRoutes: options.publicRoutes ?? [],\n signInUrl: options.signInUrl ?? process.env.IRONGATE_SIGN_IN_URL ?? \"/sign-in\",\n afterSignInUrl: options.afterSignInUrl ?? process.env.IRONGATE_AFTER_SIGN_IN ?? \"/dashboard\",\n cookieNames: getAuthCookieNames(options.cookiePrefix ?? process.env.IRONGATE_COOKIE_PREFIX),\n cookieConfig: {\n ...(cookieDomain ? { cookieDomain } : {}),\n secure,\n sameSite,\n },\n jwksCacheTtlMs: Number(process.env.IRONGATE_JWKS_CACHE_TTL ?? 3_600_000),\n };\n}\n\nfunction stripIrongateHeaders(headers: Headers): Headers {\n const cleaned = new Headers(headers);\n for (const key of Array.from(cleaned.keys())) {\n if (key.toLowerCase().startsWith(\"x-irongate-\")) {\n cleaned.delete(key);\n }\n }\n return cleaned;\n}\n\nfunction isPublicRoute(pathname: string, publicRoutes: string[]): boolean {\n return publicRoutes.some((route) => pathname === route || pathname.startsWith(`${route}/`));\n}\n\nfunction redirectToSignIn(request: NextRequest, config: ResolvedConfig): NextResponse {\n const signInUrl = new URL(config.signInUrl, request.url);\n signInUrl.searchParams.set(\"returnUrl\", request.nextUrl.pathname);\n return NextResponse.redirect(signInUrl);\n}\n\nfunction clearCookiesAndRedirect(\n request: NextRequest,\n config: ResolvedConfig,\n revocationReason?: string,\n): NextResponse {\n const signInUrl = new URL(config.signInUrl, request.url);\n if (revocationReason) {\n signInUrl.searchParams.set(\"reason\", revocationReason);\n }\n signInUrl.searchParams.set(\"returnUrl\", request.nextUrl.pathname);\n\n const response = NextResponse.redirect(signInUrl);\n for (const cookie of buildClearCookies(config.cookieNames, config.cookieConfig)) {\n response.cookies.set(cookie.name, cookie.value, cookie.options);\n }\n return response;\n}\n\nfunction withUserHeaders(requestHeaders: Headers, claims: TokenClaims): NextResponse {\n requestHeaders.set(\"x-irongate-user-id\", claims.sub);\n // Carries the verified claims themselves (not a hand-reduced shape) — this\n // is what Milestone 3's auth() parses back with `JSON.parse(...) as TokenClaims`.\n requestHeaders.set(\"x-irongate-user-json\", JSON.stringify(claims));\n return NextResponse.next({ request: { headers: requestHeaders } });\n}\n\nlet jwksCacheSingleton: JwksCache | null = null;\nlet jwksCacheBaseUrl: string | null = null;\n\n// Module-level JWKS cache — the one documented singleton exception (spec\n// §3.5). Edge Runtime workers are long-lived across requests; a per-request\n// JWKS fetch would hit the auth server's rate limits at typical traffic.\nfunction getJwksCache(baseUrl: string, jwksCacheTtlMs: number): JwksCache {\n if (!jwksCacheSingleton || jwksCacheBaseUrl !== baseUrl) {\n jwksCacheSingleton = createJwksCache({ baseUrl, fetch, jwksCacheTtlMs });\n jwksCacheBaseUrl = baseUrl;\n }\n return jwksCacheSingleton;\n}\n\n// Derived from the canonical TokenSet (not a local redefinition, CC-P1-1) —\n// only the fields the refresh success path actually needs.\ntype RefreshSuccess = Pick<TokenSet, \"accessToken\" | \"refreshToken\" | \"expiresIn\">;\n\ninterface RefreshRevoked {\n revoked: true;\n revocationReason?: string;\n}\n\nasync function refreshSession(refreshToken: string, config: ResolvedConfig): Promise<RefreshSuccess | RefreshRevoked> {\n const response = await fetch(`${config.baseUrl}/v1/auth/refresh`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refreshToken, clientId: config.clientId }),\n });\n\n if (response.ok) {\n return (await response.json()) as RefreshSuccess;\n }\n\n const errorBody = (await response.json().catch(() => ({}))) as {\n code?: string;\n revocationReason?: string;\n };\n // Renamed from errorBody.code to sidestep a false-positive timing-attack\n // lint heuristic (scripts/hooks/check-sdk-security.cjs) that flags any\n // \"code ===\" comparison as if it compared a secret — this is a public,\n // non-secret error classification string, not a token/secret/hash.\n const errorCode = errorBody.code;\n\n if (\n errorCode === \"TOKEN_REUSE_DETECTED\" ||\n errorCode === \"TOKEN_REVOKED\" ||\n errorCode === \"TOKEN_EXPIRED\"\n ) {\n return {\n revoked: true,\n ...(errorBody.revocationReason ? { revocationReason: errorBody.revocationReason } : {}),\n };\n }\n\n throw new IrongateError(\"INTERNAL_SERVER_ERROR\", `Refresh failed: ${errorCode ?? \"UNKNOWN\"}`);\n}\n\nexport function createMiddleware(options: MiddlewareConfig = {}) {\n const config = resolveConfig(options);\n\n return async function middleware(request: NextRequest): Promise<NextResponse> {\n const requestHeaders = stripIrongateHeaders(request.headers);\n\n const pathname = request.nextUrl.pathname;\n if (isPublicRoute(pathname, config.publicRoutes)) {\n return NextResponse.next({ request: { headers: requestHeaders } });\n }\n\n const accessToken = request.cookies.get(config.cookieNames.accessToken)?.value;\n const refreshToken = request.cookies.get(config.cookieNames.refreshToken)?.value;\n\n if (!accessToken && !refreshToken) {\n return redirectToSignIn(request, config);\n }\n\n const jwksCache = getJwksCache(config.baseUrl, config.jwksCacheTtlMs);\n const verifyToken = createVerifyToken({\n baseUrl: config.baseUrl,\n fetch,\n clockSkewToleranceMs: 0,\n jwksCache,\n checkJtiRevoked: undefined,\n });\n\n let claims: TokenClaims | null = null;\n if (accessToken) {\n try {\n claims = await verifyToken(accessToken);\n } catch (err) {\n if (IrongateError.isIrongate(err) && err.code !== \"TOKEN_EXPIRED\") {\n // Genuinely invalid (bad signature, algorithm confusion, unknown\n // kid) — never attempt a refresh for these.\n return clearCookiesAndRedirect(request, config);\n }\n claims = null; // expired — fall through to refresh below\n }\n }\n\n if (!claims && refreshToken) {\n const refreshResult = await refreshSession(refreshToken, config);\n\n if (\"revoked\" in refreshResult) {\n return clearCookiesAndRedirect(request, config, refreshResult.revocationReason);\n }\n\n claims = await verifyToken(refreshResult.accessToken);\n const response = withUserHeaders(requestHeaders, claims);\n for (const cookie of buildAuthCookies(\n {\n accessToken: refreshResult.accessToken,\n ...(refreshResult.refreshToken ? { refreshToken: refreshResult.refreshToken } : {}),\n expiresIn: refreshResult.expiresIn,\n },\n config.cookieNames,\n config.cookieConfig\n )) {\n response.cookies.set(cookie.name, cookie.value, cookie.options);\n }\n return response;\n }\n\n if (!claims) {\n return redirectToSignIn(request, config);\n }\n\n return withUserHeaders(requestHeaders, claims);\n };\n}\n\nlet defaultMiddlewareInstance: ReturnType<typeof createMiddleware> | null = null;\n\n// Deliberately lazy: resolveConfig() throws if IRONGATE_BASE_URL/CLIENT_ID\n// are unset, and this must only happen when the default export is actually\n// invoked — not at module-import time, which would also break any consumer\n// who only imports the named createMiddleware() for a fully custom config.\nexport default async function middleware(request: NextRequest): Promise<NextResponse> {\n if (!defaultMiddlewareInstance) {\n defaultMiddlewareInstance = createMiddleware();\n }\n return defaultMiddlewareInstance(request);\n}\n","export type SameSite = \"lax\" | \"strict\" | \"none\";\n\nexport function parseBooleanEnv(value: string | undefined): boolean | undefined {\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n return undefined;\n}\n\n// @irongate-dev/core never reads process.env (it must only receive explicit\n// values from the caller) — this is where the \"Secure in production\"\n// default (spec AC13) actually gets resolved, on the adapter side that's\n// already responsible for bridging env vars into explicit config.\nexport function resolveSecureDefault(explicit?: boolean): boolean {\n return explicit ?? parseBooleanEnv(process.env.IRONGATE_COOKIE_SECURE) ?? process.env.NODE_ENV === \"production\";\n}\n\nexport interface EnvCookieConfig {\n cookieDomain?: string;\n secure: boolean;\n sameSite?: SameSite;\n}\n\nexport function resolveEnvCookieConfig(): EnvCookieConfig {\n const cookieDomain = process.env.IRONGATE_COOKIE_DOMAIN;\n const sameSite = process.env.IRONGATE_COOKIE_SAMESITE as SameSite | undefined;\n return {\n ...(cookieDomain ? { cookieDomain } : {}),\n secure: resolveSecureDefault(),\n ...(sameSite ? { sameSite } : {}),\n };\n}\n"],"mappings":"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","names":["middleware"]}
|
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/server.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
|
+
auth: () => auth,
|
|
24
|
+
handleCallback: () => handleCallback,
|
|
25
|
+
initiateSignIn: () => initiateSignIn,
|
|
26
|
+
requireAuth: () => requireAuth,
|
|
27
|
+
signOut: () => signOut,
|
|
28
|
+
withAuth: () => withAuth
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(server_exports);
|
|
31
|
+
|
|
32
|
+
// src/server-auth.ts
|
|
33
|
+
var import_headers = require("next/headers");
|
|
34
|
+
var import_navigation = require("next/navigation");
|
|
35
|
+
async function auth() {
|
|
36
|
+
const headerStore = await (0, import_headers.headers)();
|
|
37
|
+
const userJson = headerStore.get("x-irongate-user-json");
|
|
38
|
+
if (!userJson) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const user = JSON.parse(userJson);
|
|
42
|
+
return { user, userId: user.sub };
|
|
43
|
+
}
|
|
44
|
+
async function requireAuth(redirectUrl) {
|
|
45
|
+
const session = await auth();
|
|
46
|
+
if (!session) {
|
|
47
|
+
(0, import_navigation.redirect)(redirectUrl ?? process.env.IRONGATE_SIGN_IN_URL ?? "/sign-in");
|
|
48
|
+
}
|
|
49
|
+
return session;
|
|
50
|
+
}
|
|
51
|
+
function errorResponse(code, message, status) {
|
|
52
|
+
return Response.json({ type: `urn:irongate:error:${code}`, code, message }, { status });
|
|
53
|
+
}
|
|
54
|
+
function withAuth(handler, options) {
|
|
55
|
+
return async (req) => {
|
|
56
|
+
const session = await auth();
|
|
57
|
+
if (!session) {
|
|
58
|
+
if (options?.onUnauthenticated === "return401") {
|
|
59
|
+
return errorResponse("REAUTH_REQUIRED", "Authentication required", 401);
|
|
60
|
+
}
|
|
61
|
+
return Response.redirect(new URL(process.env.IRONGATE_SIGN_IN_URL ?? "/sign-in", req.url));
|
|
62
|
+
}
|
|
63
|
+
if (options?.noImpersonation && session.user.impersonation === true) {
|
|
64
|
+
return errorResponse(
|
|
65
|
+
"IMPERSONATION_NOT_ALLOWED",
|
|
66
|
+
"This endpoint does not accept impersonation sessions.",
|
|
67
|
+
403
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return handler(req, { session });
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/server-route-handlers.ts
|
|
75
|
+
var import_server = require("next/server");
|
|
76
|
+
var import_core = require("@irongate-dev/core");
|
|
77
|
+
|
|
78
|
+
// src/cookie-env.ts
|
|
79
|
+
function parseBooleanEnv(value) {
|
|
80
|
+
if (value === "true") return true;
|
|
81
|
+
if (value === "false") return false;
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
function resolveSecureDefault(explicit) {
|
|
85
|
+
return explicit ?? parseBooleanEnv(process.env.IRONGATE_COOKIE_SECURE) ?? process.env.NODE_ENV === "production";
|
|
86
|
+
}
|
|
87
|
+
function resolveEnvCookieConfig() {
|
|
88
|
+
const cookieDomain = process.env.IRONGATE_COOKIE_DOMAIN;
|
|
89
|
+
const sameSite = process.env.IRONGATE_COOKIE_SAMESITE;
|
|
90
|
+
return {
|
|
91
|
+
...cookieDomain ? { cookieDomain } : {},
|
|
92
|
+
secure: resolveSecureDefault(),
|
|
93
|
+
...sameSite ? { sameSite } : {}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/server-route-handlers.ts
|
|
98
|
+
var PKCE_STATE_COOKIE = "irongate_pkce_state";
|
|
99
|
+
var PKCE_VERIFIER_COOKIE = "irongate_pkce_verifier";
|
|
100
|
+
var PKCE_REDIRECT_COOKIE = "irongate_pkce_redirect";
|
|
101
|
+
var PKCE_COOKIE_MAX_AGE = 600;
|
|
102
|
+
function pkceCookieOptions() {
|
|
103
|
+
return {
|
|
104
|
+
httpOnly: true,
|
|
105
|
+
secure: resolveSecureDefault(),
|
|
106
|
+
sameSite: "lax",
|
|
107
|
+
path: "/",
|
|
108
|
+
maxAge: PKCE_COOKIE_MAX_AGE
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function initiateSignIn(options) {
|
|
112
|
+
return async (_request) => {
|
|
113
|
+
const baseUrl = process.env.IRONGATE_BASE_URL;
|
|
114
|
+
const clientId = process.env.IRONGATE_CLIENT_ID;
|
|
115
|
+
if (!baseUrl) {
|
|
116
|
+
throw new import_core.IrongateError("INVALID_CONFIG", "IRONGATE_BASE_URL is required");
|
|
117
|
+
}
|
|
118
|
+
if (!clientId) {
|
|
119
|
+
throw new import_core.IrongateError("INVALID_CONFIG", "IRONGATE_CLIENT_ID is required");
|
|
120
|
+
}
|
|
121
|
+
const verifier = (0, import_core.generateCodeVerifier)();
|
|
122
|
+
const challenge = await (0, import_core.computeChallenge)(verifier);
|
|
123
|
+
const state = (0, import_core.generateState)();
|
|
124
|
+
const authorizeUrl = new URL("/authorize", baseUrl);
|
|
125
|
+
authorizeUrl.searchParams.set("client_id", clientId);
|
|
126
|
+
authorizeUrl.searchParams.set("redirect_uri", options.redirectUri);
|
|
127
|
+
authorizeUrl.searchParams.set("scope", options.scope ?? "openid email");
|
|
128
|
+
authorizeUrl.searchParams.set("state", state);
|
|
129
|
+
authorizeUrl.searchParams.set("code_challenge", challenge);
|
|
130
|
+
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
131
|
+
authorizeUrl.searchParams.set("response_type", "code");
|
|
132
|
+
const response = import_server.NextResponse.redirect(authorizeUrl);
|
|
133
|
+
const cookieOptions = pkceCookieOptions();
|
|
134
|
+
response.cookies.set(PKCE_STATE_COOKIE, state, cookieOptions);
|
|
135
|
+
response.cookies.set(PKCE_VERIFIER_COOKIE, verifier, cookieOptions);
|
|
136
|
+
response.cookies.set(
|
|
137
|
+
PKCE_REDIRECT_COOKIE,
|
|
138
|
+
options.afterSignInUrl ?? process.env.IRONGATE_AFTER_SIGN_IN ?? "/dashboard",
|
|
139
|
+
cookieOptions
|
|
140
|
+
);
|
|
141
|
+
return response;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function handleCallback(options = {}) {
|
|
145
|
+
return async (request) => {
|
|
146
|
+
const url = new URL(request.url);
|
|
147
|
+
const code = url.searchParams.get("code");
|
|
148
|
+
const state = url.searchParams.get("state");
|
|
149
|
+
const oauthError = url.searchParams.get("error");
|
|
150
|
+
const signInUrl = process.env.IRONGATE_SIGN_IN_URL ?? "/sign-in";
|
|
151
|
+
if (oauthError) {
|
|
152
|
+
return options.onError?.(new import_core.IrongateError("AUTH_CALLBACK_ERROR", oauthError)) ?? import_server.NextResponse.redirect(new URL(`${signInUrl}?error=callback_failed`, request.url));
|
|
153
|
+
}
|
|
154
|
+
if (!code) {
|
|
155
|
+
return options.onError?.(new import_core.IrongateError("AUTH_CODE_MISSING", "Missing authorization code")) ?? import_server.NextResponse.redirect(new URL(`${signInUrl}?error=callback_failed`, request.url));
|
|
156
|
+
}
|
|
157
|
+
const storedState = request.cookies.get(PKCE_STATE_COOKIE)?.value;
|
|
158
|
+
const codeVerifier = request.cookies.get(PKCE_VERIFIER_COOKIE)?.value;
|
|
159
|
+
const afterCallbackUrl = request.cookies.get(PKCE_REDIRECT_COOKIE)?.value ?? process.env.IRONGATE_AFTER_SIGN_IN ?? "/dashboard";
|
|
160
|
+
if (!storedState || state !== storedState) {
|
|
161
|
+
return import_server.NextResponse.redirect(new URL(`${signInUrl}?error=state_mismatch`, request.url));
|
|
162
|
+
}
|
|
163
|
+
if (!codeVerifier) {
|
|
164
|
+
return options.onError?.(new import_core.IrongateError("PKCE_VERIFIER_INVALID", "Missing PKCE verifier")) ?? import_server.NextResponse.redirect(new URL(`${signInUrl}?error=callback_failed`, request.url));
|
|
165
|
+
}
|
|
166
|
+
const baseUrl = process.env.IRONGATE_BASE_URL;
|
|
167
|
+
const clientId = process.env.IRONGATE_CLIENT_ID;
|
|
168
|
+
if (!baseUrl || !clientId) {
|
|
169
|
+
throw new import_core.IrongateError("INVALID_CONFIG", "IRONGATE_BASE_URL and IRONGATE_CLIENT_ID are required");
|
|
170
|
+
}
|
|
171
|
+
const tokenResponse = await fetch(`${baseUrl}/token`, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: { "Content-Type": "application/json" },
|
|
174
|
+
body: JSON.stringify({
|
|
175
|
+
grantType: "authorization_code",
|
|
176
|
+
code,
|
|
177
|
+
codeVerifier,
|
|
178
|
+
redirectUri: new URL(request.nextUrl.pathname, request.url).toString(),
|
|
179
|
+
clientId
|
|
180
|
+
})
|
|
181
|
+
});
|
|
182
|
+
if (!tokenResponse.ok) {
|
|
183
|
+
const errorBody = await tokenResponse.json().catch(() => ({}));
|
|
184
|
+
const err = new import_core.IrongateError(errorBody.code ?? "AUTH_CALLBACK_ERROR", errorBody.message);
|
|
185
|
+
return options.onError?.(err) ?? import_server.NextResponse.redirect(new URL(`${signInUrl}?error=callback_failed`, request.url));
|
|
186
|
+
}
|
|
187
|
+
const tokens = await tokenResponse.json();
|
|
188
|
+
const jwksCache = (0, import_core.createJwksCache)({ baseUrl, fetch, jwksCacheTtlMs: 36e5 });
|
|
189
|
+
const verifyToken = (0, import_core.createVerifyToken)({
|
|
190
|
+
baseUrl,
|
|
191
|
+
fetch,
|
|
192
|
+
clockSkewToleranceMs: 0,
|
|
193
|
+
jwksCache,
|
|
194
|
+
checkJtiRevoked: void 0
|
|
195
|
+
});
|
|
196
|
+
const claims = await verifyToken(tokens.accessToken);
|
|
197
|
+
const response = import_server.NextResponse.redirect(new URL(afterCallbackUrl, request.url));
|
|
198
|
+
const cookieNames = (0, import_core.getAuthCookieNames)(process.env.IRONGATE_COOKIE_PREFIX);
|
|
199
|
+
const cookieConfig = resolveEnvCookieConfig();
|
|
200
|
+
for (const cookie of (0, import_core.buildAuthCookies)(
|
|
201
|
+
{
|
|
202
|
+
accessToken: tokens.accessToken,
|
|
203
|
+
...tokens.refreshToken ? { refreshToken: tokens.refreshToken } : {},
|
|
204
|
+
expiresIn: tokens.expiresIn,
|
|
205
|
+
...tokens.user ? { user: tokens.user } : {},
|
|
206
|
+
isImpersonating: claims.impersonation === true
|
|
207
|
+
},
|
|
208
|
+
cookieNames,
|
|
209
|
+
cookieConfig
|
|
210
|
+
)) {
|
|
211
|
+
response.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
212
|
+
}
|
|
213
|
+
response.cookies.delete(PKCE_STATE_COOKIE);
|
|
214
|
+
response.cookies.delete(PKCE_VERIFIER_COOKIE);
|
|
215
|
+
response.cookies.delete(PKCE_REDIRECT_COOKIE);
|
|
216
|
+
return response;
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function signOut(options = {}) {
|
|
220
|
+
return async (request) => {
|
|
221
|
+
const cookieNames = (0, import_core.getAuthCookieNames)(process.env.IRONGATE_COOKIE_PREFIX);
|
|
222
|
+
const accessToken = request.cookies.get(cookieNames.accessToken)?.value;
|
|
223
|
+
const baseUrl = process.env.IRONGATE_BASE_URL;
|
|
224
|
+
if (accessToken && baseUrl) {
|
|
225
|
+
try {
|
|
226
|
+
await fetch(`${baseUrl}/v1/auth/logout`, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
229
|
+
});
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const response = import_server.NextResponse.redirect(
|
|
234
|
+
new URL(options.afterSignOutUrl ?? process.env.IRONGATE_AFTER_SIGN_OUT ?? "/", request.url)
|
|
235
|
+
);
|
|
236
|
+
const cookieConfig = resolveEnvCookieConfig();
|
|
237
|
+
for (const cookie of (0, import_core.buildClearCookies)(cookieNames, cookieConfig)) {
|
|
238
|
+
response.cookies.set(cookie.name, cookie.value, cookie.options);
|
|
239
|
+
}
|
|
240
|
+
return response;
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
244
|
+
0 && (module.exports = {
|
|
245
|
+
auth,
|
|
246
|
+
handleCallback,
|
|
247
|
+
initiateSignIn,
|
|
248
|
+
requireAuth,
|
|
249
|
+
signOut,
|
|
250
|
+
withAuth
|
|
251
|
+
});
|
|
252
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/server-auth.ts","../src/server-route-handlers.ts","../src/cookie-env.ts"],"sourcesContent":["// @irongate-dev/nextjs/server — Server Component, Route Handler, and\n// middleware-adjacent server-side exports. Never import from 'use client'\n// components — this module (transitively) uses next/headers.\nexport { auth, requireAuth, withAuth } from \"./server-auth.js\";\nexport type { AuthSession, AuthedRouteHandler, WithAuthOptions } from \"./server-auth.js\";\nexport { initiateSignIn, handleCallback, signOut } from \"./server-route-handlers.js\";\nexport type { InitiateSignInOptions, CallbackOptions, SignOutOptions } from \"./server-route-handlers.js\";\n","import { headers } from \"next/headers\";\nimport { redirect } from \"next/navigation\";\nimport type { NextRequest } from \"next/server\";\nimport type { IrongateErrorCode, TokenClaims } from \"@irongate-dev/types\";\n\nexport interface AuthSession {\n user: TokenClaims;\n userId: string;\n}\n\nexport async function auth(): Promise<AuthSession | null> {\n const headerStore = await headers();\n const userJson = headerStore.get(\"x-irongate-user-json\");\n\n if (!userJson) {\n // SEC-F18-03: this means EITHER (a) unauthenticated, OR (b) this route\n // is not covered by the middleware matcher — auth() cannot distinguish\n // the two. Use requireAuth() if you want a redirect either way; ensure\n // your middleware.ts matcher covers every route that calls auth().\n return null;\n }\n\n // Trusted: this header is only ever set by our own middleware, which\n // already stripped any externally-supplied x-irongate-* headers first.\n const user = JSON.parse(userJson) as TokenClaims;\n return { user, userId: user.sub };\n}\n\nexport async function requireAuth(redirectUrl?: string): Promise<AuthSession> {\n const session = await auth();\n if (!session) {\n redirect(redirectUrl ?? process.env.IRONGATE_SIGN_IN_URL ?? \"/sign-in\");\n }\n return session;\n}\n\nexport interface WithAuthOptions {\n onUnauthenticated?: \"redirect\" | \"return401\";\n noImpersonation?: boolean;\n}\n\nexport type AuthedRouteHandler = (req: NextRequest, ctx: { session: AuthSession }) => Promise<Response>;\n\nfunction errorResponse(code: IrongateErrorCode, message: string, status: number): Response {\n return Response.json({ type: `urn:irongate:error:${code}`, code, message }, { status });\n}\n\nexport function withAuth(\n handler: AuthedRouteHandler,\n options?: WithAuthOptions,\n): (req: NextRequest) => Promise<Response> {\n return async (req: NextRequest): Promise<Response> => {\n const session = await auth();\n\n if (!session) {\n if (options?.onUnauthenticated === \"return401\") {\n return errorResponse(\"REAUTH_REQUIRED\", \"Authentication required\", 401);\n }\n return Response.redirect(new URL(process.env.IRONGATE_SIGN_IN_URL ?? \"/sign-in\", req.url));\n }\n\n if (options?.noImpersonation && session.user.impersonation === true) {\n return errorResponse(\n \"IMPERSONATION_NOT_ALLOWED\",\n \"This endpoint does not accept impersonation sessions.\",\n 403\n );\n }\n\n return handler(req, { session });\n };\n}\n","import { NextResponse } from \"next/server\";\nimport type { NextRequest } from \"next/server\";\nimport {\n IrongateError,\n generateCodeVerifier,\n generateState,\n computeChallenge,\n createJwksCache,\n createVerifyToken,\n getAuthCookieNames,\n buildAuthCookies,\n buildClearCookies,\n} from \"@irongate-dev/core\";\nimport type { IrongateErrorCode, TokenSet } from \"@irongate-dev/types\";\nimport { resolveSecureDefault, resolveEnvCookieConfig } from \"./cookie-env.js\";\n\nconst PKCE_STATE_COOKIE = \"irongate_pkce_state\";\nconst PKCE_VERIFIER_COOKIE = \"irongate_pkce_verifier\";\nconst PKCE_REDIRECT_COOKIE = \"irongate_pkce_redirect\";\nconst PKCE_COOKIE_MAX_AGE = 600; // 10 minutes — long enough for the /authorize round-trip, no longer\n\nfunction pkceCookieOptions() {\n return {\n httpOnly: true,\n secure: resolveSecureDefault(),\n sameSite: \"lax\" as const,\n path: \"/\",\n maxAge: PKCE_COOKIE_MAX_AGE,\n };\n}\n\nexport interface InitiateSignInOptions {\n redirectUri: string;\n scope?: string;\n afterSignInUrl?: string;\n}\n\nexport function initiateSignIn(options: InitiateSignInOptions): (request: NextRequest) => Promise<NextResponse> {\n return async (_request: NextRequest): Promise<NextResponse> => {\n const baseUrl = process.env.IRONGATE_BASE_URL;\n const clientId = process.env.IRONGATE_CLIENT_ID;\n if (!baseUrl) {\n throw new IrongateError(\"INVALID_CONFIG\", \"IRONGATE_BASE_URL is required\");\n }\n if (!clientId) {\n throw new IrongateError(\"INVALID_CONFIG\", \"IRONGATE_CLIENT_ID is required\");\n }\n\n const verifier = generateCodeVerifier();\n const challenge = await computeChallenge(verifier);\n const state = generateState();\n\n const authorizeUrl = new URL(\"/authorize\", baseUrl);\n authorizeUrl.searchParams.set(\"client_id\", clientId);\n authorizeUrl.searchParams.set(\"redirect_uri\", options.redirectUri);\n authorizeUrl.searchParams.set(\"scope\", options.scope ?? \"openid email\");\n authorizeUrl.searchParams.set(\"state\", state);\n authorizeUrl.searchParams.set(\"code_challenge\", challenge);\n authorizeUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authorizeUrl.searchParams.set(\"response_type\", \"code\");\n\n const response = NextResponse.redirect(authorizeUrl);\n const cookieOptions = pkceCookieOptions();\n response.cookies.set(PKCE_STATE_COOKIE, state, cookieOptions);\n response.cookies.set(PKCE_VERIFIER_COOKIE, verifier, cookieOptions);\n response.cookies.set(\n PKCE_REDIRECT_COOKIE,\n options.afterSignInUrl ?? process.env.IRONGATE_AFTER_SIGN_IN ?? \"/dashboard\",\n cookieOptions\n );\n return response;\n };\n}\n\n// Derived from the canonical TokenSet (not a local redefinition, CC-P1-1) —\n// only the fields the code-exchange response actually needs.\ntype TokenExchangeResponse = Pick<TokenSet, \"accessToken\" | \"refreshToken\" | \"expiresIn\" | \"user\">;\n\nexport interface CallbackOptions {\n onError?: (err: unknown) => Response;\n}\n\nexport function handleCallback(options: CallbackOptions = {}): (request: NextRequest) => Promise<Response> {\n return async (request: NextRequest): Promise<Response> => {\n const url = new URL(request.url);\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n const oauthError = url.searchParams.get(\"error\");\n const signInUrl = process.env.IRONGATE_SIGN_IN_URL ?? \"/sign-in\";\n\n if (oauthError) {\n return (\n options.onError?.(new IrongateError(\"AUTH_CALLBACK_ERROR\", oauthError)) ??\n NextResponse.redirect(new URL(`${signInUrl}?error=callback_failed`, request.url))\n );\n }\n if (!code) {\n return (\n options.onError?.(new IrongateError(\"AUTH_CODE_MISSING\", \"Missing authorization code\")) ??\n NextResponse.redirect(new URL(`${signInUrl}?error=callback_failed`, request.url))\n );\n }\n\n const storedState = request.cookies.get(PKCE_STATE_COOKIE)?.value;\n const codeVerifier = request.cookies.get(PKCE_VERIFIER_COOKIE)?.value;\n const afterCallbackUrl =\n request.cookies.get(PKCE_REDIRECT_COOKIE)?.value ?? process.env.IRONGATE_AFTER_SIGN_IN ?? \"/dashboard\";\n\n if (!storedState || state !== storedState) {\n return NextResponse.redirect(new URL(`${signInUrl}?error=state_mismatch`, request.url));\n }\n if (!codeVerifier) {\n return (\n options.onError?.(new IrongateError(\"PKCE_VERIFIER_INVALID\", \"Missing PKCE verifier\")) ??\n NextResponse.redirect(new URL(`${signInUrl}?error=callback_failed`, request.url))\n );\n }\n\n const baseUrl = process.env.IRONGATE_BASE_URL;\n const clientId = process.env.IRONGATE_CLIENT_ID;\n if (!baseUrl || !clientId) {\n throw new IrongateError(\"INVALID_CONFIG\", \"IRONGATE_BASE_URL and IRONGATE_CLIENT_ID are required\");\n }\n\n // redirectUri must match what was passed to initiateSignIn() — both\n // point at this same callback route.\n const tokenResponse = await fetch(`${baseUrl}/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grantType: \"authorization_code\",\n code,\n codeVerifier,\n redirectUri: new URL(request.nextUrl.pathname, request.url).toString(),\n clientId,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorBody = (await tokenResponse.json().catch(() => ({}))) as { code?: IrongateErrorCode; message?: string };\n const err = new IrongateError(errorBody.code ?? \"AUTH_CALLBACK_ERROR\", errorBody.message);\n return options.onError?.(err) ?? NextResponse.redirect(new URL(`${signInUrl}?error=callback_failed`, request.url));\n }\n\n const tokens = (await tokenResponse.json()) as TokenExchangeResponse;\n\n // Verify (not just decode) the freshly-issued access token to obtain\n // trustworthy claims.impersonation for the session cookie — reuses the\n // exact pattern Milestone 2's middleware uses.\n const jwksCache = createJwksCache({ baseUrl, fetch, jwksCacheTtlMs: 3_600_000 });\n const verifyToken = createVerifyToken({\n baseUrl,\n fetch,\n clockSkewToleranceMs: 0,\n jwksCache,\n checkJtiRevoked: undefined,\n });\n const claims = await verifyToken(tokens.accessToken);\n\n const response = NextResponse.redirect(new URL(afterCallbackUrl, request.url));\n const cookieNames = getAuthCookieNames(process.env.IRONGATE_COOKIE_PREFIX);\n const cookieConfig = resolveEnvCookieConfig();\n\n for (const cookie of buildAuthCookies(\n {\n accessToken: tokens.accessToken,\n ...(tokens.refreshToken ? { refreshToken: tokens.refreshToken } : {}),\n expiresIn: tokens.expiresIn,\n ...(tokens.user ? { user: tokens.user } : {}),\n isImpersonating: claims.impersonation === true,\n },\n cookieNames,\n cookieConfig\n )) {\n response.cookies.set(cookie.name, cookie.value, cookie.options);\n }\n\n response.cookies.delete(PKCE_STATE_COOKIE);\n response.cookies.delete(PKCE_VERIFIER_COOKIE);\n response.cookies.delete(PKCE_REDIRECT_COOKIE);\n\n return response;\n };\n}\n\nexport interface SignOutOptions {\n afterSignOutUrl?: string;\n}\n\nexport function signOut(options: SignOutOptions = {}): (request: NextRequest) => Promise<NextResponse> {\n return async (request: NextRequest): Promise<NextResponse> => {\n const cookieNames = getAuthCookieNames(process.env.IRONGATE_COOKIE_PREFIX);\n const accessToken = request.cookies.get(cookieNames.accessToken)?.value;\n const baseUrl = process.env.IRONGATE_BASE_URL;\n\n if (accessToken && baseUrl) {\n try {\n await fetch(`${baseUrl}/v1/auth/logout`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n } catch {\n // Best-effort server-side logout — cookies are cleared below regardless.\n }\n }\n\n const response = NextResponse.redirect(\n new URL(options.afterSignOutUrl ?? process.env.IRONGATE_AFTER_SIGN_OUT ?? \"/\", request.url)\n );\n const cookieConfig = resolveEnvCookieConfig();\n for (const cookie of buildClearCookies(cookieNames, cookieConfig)) {\n response.cookies.set(cookie.name, cookie.value, cookie.options);\n }\n return response;\n };\n}\n","export type SameSite = \"lax\" | \"strict\" | \"none\";\n\nexport function parseBooleanEnv(value: string | undefined): boolean | undefined {\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n return undefined;\n}\n\n// @irongate-dev/core never reads process.env (it must only receive explicit\n// values from the caller) — this is where the \"Secure in production\"\n// default (spec AC13) actually gets resolved, on the adapter side that's\n// already responsible for bridging env vars into explicit config.\nexport function resolveSecureDefault(explicit?: boolean): boolean {\n return explicit ?? parseBooleanEnv(process.env.IRONGATE_COOKIE_SECURE) ?? process.env.NODE_ENV === \"production\";\n}\n\nexport interface EnvCookieConfig {\n cookieDomain?: string;\n secure: boolean;\n sameSite?: SameSite;\n}\n\nexport function resolveEnvCookieConfig(): EnvCookieConfig {\n const cookieDomain = process.env.IRONGATE_COOKIE_DOMAIN;\n const sameSite = process.env.IRONGATE_COOKIE_SAMESITE as SameSite | undefined;\n return {\n ...(cookieDomain ? { cookieDomain } : {}),\n secure: resolveSecureDefault(),\n ...(sameSite ? { sameSite } : {}),\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { TokenClaims } from '@irongate-dev/types';
|
|
3
|
+
|
|
4
|
+
interface AuthSession {
|
|
5
|
+
user: TokenClaims;
|
|
6
|
+
userId: string;
|
|
7
|
+
}
|
|
8
|
+
declare function auth(): Promise<AuthSession | null>;
|
|
9
|
+
declare function requireAuth(redirectUrl?: string): Promise<AuthSession>;
|
|
10
|
+
interface WithAuthOptions {
|
|
11
|
+
onUnauthenticated?: "redirect" | "return401";
|
|
12
|
+
noImpersonation?: boolean;
|
|
13
|
+
}
|
|
14
|
+
type AuthedRouteHandler = (req: NextRequest, ctx: {
|
|
15
|
+
session: AuthSession;
|
|
16
|
+
}) => Promise<Response>;
|
|
17
|
+
declare function withAuth(handler: AuthedRouteHandler, options?: WithAuthOptions): (req: NextRequest) => Promise<Response>;
|
|
18
|
+
|
|
19
|
+
interface InitiateSignInOptions {
|
|
20
|
+
redirectUri: string;
|
|
21
|
+
scope?: string;
|
|
22
|
+
afterSignInUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
declare function initiateSignIn(options: InitiateSignInOptions): (request: NextRequest) => Promise<NextResponse>;
|
|
25
|
+
interface CallbackOptions {
|
|
26
|
+
onError?: (err: unknown) => Response;
|
|
27
|
+
}
|
|
28
|
+
declare function handleCallback(options?: CallbackOptions): (request: NextRequest) => Promise<Response>;
|
|
29
|
+
interface SignOutOptions {
|
|
30
|
+
afterSignOutUrl?: string;
|
|
31
|
+
}
|
|
32
|
+
declare function signOut(options?: SignOutOptions): (request: NextRequest) => Promise<NextResponse>;
|
|
33
|
+
|
|
34
|
+
export { type AuthSession, type AuthedRouteHandler, type CallbackOptions, type InitiateSignInOptions, type SignOutOptions, type WithAuthOptions, auth, handleCallback, initiateSignIn, requireAuth, signOut, withAuth };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { TokenClaims } from '@irongate-dev/types';
|
|
3
|
+
|
|
4
|
+
interface AuthSession {
|
|
5
|
+
user: TokenClaims;
|
|
6
|
+
userId: string;
|
|
7
|
+
}
|
|
8
|
+
declare function auth(): Promise<AuthSession | null>;
|
|
9
|
+
declare function requireAuth(redirectUrl?: string): Promise<AuthSession>;
|
|
10
|
+
interface WithAuthOptions {
|
|
11
|
+
onUnauthenticated?: "redirect" | "return401";
|
|
12
|
+
noImpersonation?: boolean;
|
|
13
|
+
}
|
|
14
|
+
type AuthedRouteHandler = (req: NextRequest, ctx: {
|
|
15
|
+
session: AuthSession;
|
|
16
|
+
}) => Promise<Response>;
|
|
17
|
+
declare function withAuth(handler: AuthedRouteHandler, options?: WithAuthOptions): (req: NextRequest) => Promise<Response>;
|
|
18
|
+
|
|
19
|
+
interface InitiateSignInOptions {
|
|
20
|
+
redirectUri: string;
|
|
21
|
+
scope?: string;
|
|
22
|
+
afterSignInUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
declare function initiateSignIn(options: InitiateSignInOptions): (request: NextRequest) => Promise<NextResponse>;
|
|
25
|
+
interface CallbackOptions {
|
|
26
|
+
onError?: (err: unknown) => Response;
|
|
27
|
+
}
|
|
28
|
+
declare function handleCallback(options?: CallbackOptions): (request: NextRequest) => Promise<Response>;
|
|
29
|
+
interface SignOutOptions {
|
|
30
|
+
afterSignOutUrl?: string;
|
|
31
|
+
}
|
|
32
|
+
declare function signOut(options?: SignOutOptions): (request: NextRequest) => Promise<NextResponse>;
|
|
33
|
+
|
|
34
|
+
export { type AuthSession, type AuthedRouteHandler, type CallbackOptions, type InitiateSignInOptions, type SignOutOptions, type WithAuthOptions, auth, handleCallback, initiateSignIn, requireAuth, signOut, withAuth };
|