next-api-layer 0.1.7 → 0.1.9
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/README.md +77 -2
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -150,9 +150,15 @@ const { data, success } = await api.get('users/profile');
|
|
|
150
150
|
// POST with body
|
|
151
151
|
const result = await api.post('projects', { body: { name: 'New Project' } });
|
|
152
152
|
|
|
153
|
-
// Per-request
|
|
153
|
+
// Per-request sanitization control
|
|
154
154
|
const rawHtml = await api.get('editor/content', { skipSanitize: true });
|
|
155
155
|
|
|
156
|
+
// Skip specific fields only (sanitize others)
|
|
157
|
+
const post = await api.post('blog/create', formData, {
|
|
158
|
+
isFormData: true,
|
|
159
|
+
skipSanitizeFields: ['content', 'raw_html'],
|
|
160
|
+
});
|
|
161
|
+
|
|
156
162
|
// With query params
|
|
157
163
|
const users = await api.get('users', { params: { page: 1, limit: 20 } });
|
|
158
164
|
```
|
|
@@ -413,6 +419,8 @@ interface AuthProxyConfig {
|
|
|
413
419
|
protectedRoutes?: string[]; // Routes requiring auth
|
|
414
420
|
authRoutes?: string[]; // Routes for non-auth users (login, register)
|
|
415
421
|
publicRoutes?: string[]; // Completely public routes
|
|
422
|
+
// Note: Locale prefix is automatically stripped before matching
|
|
423
|
+
// e.g., '/tr/login' matches config '/login' when i18n is enabled
|
|
416
424
|
};
|
|
417
425
|
|
|
418
426
|
cache?: {
|
|
@@ -420,6 +428,13 @@ interface AuthProxyConfig {
|
|
|
420
428
|
maxSize?: number; // Default: 100 tokens
|
|
421
429
|
};
|
|
422
430
|
|
|
431
|
+
i18n?: {
|
|
432
|
+
enabled?: boolean; // Enable locale detection from URL
|
|
433
|
+
locales?: string[]; // Valid locale codes ['en', 'tr', 'ar']
|
|
434
|
+
defaultLocale?: string; // Fallback locale
|
|
435
|
+
middleware?: (req: NextRequest) => NextResponse; // i18n middleware (e.g., next-intl)
|
|
436
|
+
};
|
|
437
|
+
|
|
423
438
|
// ======== Composability Hooks ========
|
|
424
439
|
|
|
425
440
|
// Runs BEFORE auth validation. Return NextResponse to bypass auth.
|
|
@@ -538,6 +553,13 @@ interface ApiClientConfig {
|
|
|
538
553
|
|
|
539
554
|
methodSpoofing?: boolean; // Default: false (for Laravel)
|
|
540
555
|
}
|
|
556
|
+
|
|
557
|
+
// Per-request options
|
|
558
|
+
interface RequestOptions {
|
|
559
|
+
isFormData?: boolean; // Send as FormData
|
|
560
|
+
skipSanitize?: boolean; // Skip all sanitization for this request
|
|
561
|
+
skipSanitizeFields?: string[]; // Skip sanitization for specific fields
|
|
562
|
+
}
|
|
541
563
|
```
|
|
542
564
|
|
|
543
565
|
## i18n Integration
|
|
@@ -593,9 +615,62 @@ API Client: Reads x-locale header → Appends ?lang=tr
|
|
|
593
615
|
Backend: GET /api/posts?lang=tr
|
|
594
616
|
```
|
|
595
617
|
|
|
618
|
+
### Route Matching with Locale Prefix
|
|
619
|
+
|
|
620
|
+
When using `localePrefix: 'always'` (e.g., with next-intl), the library automatically strips the locale prefix before matching routes:
|
|
621
|
+
|
|
622
|
+
```ts
|
|
623
|
+
createAuthProxy({
|
|
624
|
+
// ...
|
|
625
|
+
access: {
|
|
626
|
+
protectedRoutes: ['/dashboard', '/profile'],
|
|
627
|
+
authRoutes: ['/login', '/register'],
|
|
628
|
+
},
|
|
629
|
+
i18n: {
|
|
630
|
+
enabled: true,
|
|
631
|
+
locales: ['en', 'tr'],
|
|
632
|
+
defaultLocale: 'en',
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
// These all match correctly:
|
|
637
|
+
// /login → matches authRoutes '/login'
|
|
638
|
+
// /tr/login → matches authRoutes '/login' (locale stripped)
|
|
639
|
+
// /en/dashboard → matches protectedRoutes '/dashboard' (locale stripped)
|
|
640
|
+
```
|
|
641
|
+
|
|
596
642
|
### With next-intl
|
|
597
643
|
|
|
598
|
-
|
|
644
|
+
The easiest way to integrate with next-intl is using the `middleware` option:
|
|
645
|
+
|
|
646
|
+
```ts
|
|
647
|
+
import { createAuthProxy } from 'next-api-layer';
|
|
648
|
+
import createIntlMiddleware from 'next-intl/middleware';
|
|
649
|
+
import { routing } from './i18n/routing';
|
|
650
|
+
|
|
651
|
+
const intlMiddleware = createIntlMiddleware(routing);
|
|
652
|
+
|
|
653
|
+
export default createAuthProxy({
|
|
654
|
+
apiBaseUrl: process.env.API_URL!,
|
|
655
|
+
cookies: { user: 'userToken', guest: 'guestToken' },
|
|
656
|
+
|
|
657
|
+
i18n: {
|
|
658
|
+
enabled: true,
|
|
659
|
+
locales: ['en', 'tr', 'ar'],
|
|
660
|
+
defaultLocale: 'en',
|
|
661
|
+
middleware: intlMiddleware, // Library handles response merging automatically
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
The library automatically:
|
|
667
|
+
- Calls your i18n middleware internally
|
|
668
|
+
- Preserves `x-locale`, `x-auth-user`, and `x-refreshed-token` headers
|
|
669
|
+
- Copies all auth cookies to the merged response
|
|
670
|
+
|
|
671
|
+
#### Advanced: Manual Middleware Control
|
|
672
|
+
|
|
673
|
+
If you need more control, use the `afterAuth` hook instead:
|
|
599
674
|
|
|
600
675
|
```ts
|
|
601
676
|
import { createAuthProxy } from 'next-api-layer';
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';var chunkJEEL6S4O_cjs=require('./chunk-JEEL6S4O.cjs'),chunk6ENVQMWQ_cjs=require('./chunk-6ENVQMWQ.cjs'),server=require('next/server');function ue(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID()+crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2)}`}function le(e){return `rl:${e.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||e.headers.get("x-real-ip")||"unknown"}`}function Q(e){if(!e.apiBaseUrl)throw new Error("next-api-layer: apiBaseUrl is required");if(!e.cookies?.user||!e.cookies?.guest)throw new Error("next-api-layer: cookies.user and cookies.guest are required");let t=e.apiBaseUrl.endsWith("/")?e.apiBaseUrl:`${e.apiBaseUrl}/`,f={...chunk6ENVQMWQ_cjs.a,...e.cookies.options},r={...chunk6ENVQMWQ_cjs.b,...e.endpoints},p={enabled:e.csrf?.enabled??false,strategy:e.csrf?.strategy??chunk6ENVQMWQ_cjs.g.strategy,secret:e.csrf?.secret??ue(),cookieName:e.csrf?.cookieName??chunk6ENVQMWQ_cjs.g.cookieName,headerName:e.csrf?.headerName??chunk6ENVQMWQ_cjs.g.headerName,ignoreMethods:e.csrf?.ignoreMethods??chunk6ENVQMWQ_cjs.g.ignoreMethods,trustSameSite:e.csrf?.trustSameSite??chunk6ENVQMWQ_cjs.g.trustSameSite},R={enabled:e.rateLimit?.enabled??false,windowMs:e.rateLimit?.windowMs??chunk6ENVQMWQ_cjs.h.windowMs,maxRequests:e.rateLimit?.maxRequests??chunk6ENVQMWQ_cjs.h.maxRequests,keyFn:e.rateLimit?.keyFn??le,skipRoutes:e.rateLimit?.skipRoutes??chunk6ENVQMWQ_cjs.h.skipRoutes,onRateLimited:e.rateLimit?.onRateLimited},l={enabled:e.audit?.enabled??false,events:e.audit?.events??[...chunk6ENVQMWQ_cjs.i.events],logger:e.audit?.logger};return {...e,apiBaseUrl:t,_resolved:{cookieOptions:f,endpoints:r,csrf:p,rateLimit:R,audit:l}}}var $={parseAuthMe:e=>{let t=e;return !t?.success||!t?.data?null:{isValid:true,tokenType:t.data.type||"user",exp:t.data.exp||null,userData:t.data}},parseRefreshToken:e=>{let t=e;return t?.success&&t?.data?.accessToken?t.data.accessToken:null},parseGuestToken:e=>e?.data?.accessToken||null};function j(e){let{apiBaseUrl:t,_resolved:f,responseMappers:r}=e,{endpoints:p}=f,R={parseAuthMe:r?.parseAuthMe||$.parseAuthMe,parseRefreshToken:r?.parseRefreshToken||$.parseRefreshToken,parseGuestToken:r?.parseGuestToken||$.parseGuestToken};async function l(i){let u={isValid:false,tokenType:null,exp:null,userData:null};try{let s=await fetch(`${t}${p.validate}`,{headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"},cache:"no-store"});if(!s.ok)return u;let n=await s.json().catch(()=>null),d=R.parseAuthMe(n);return !d||!d.isValid?u:d}catch{return u}}async function c(i){return l(i)}async function m(i){try{let u=await fetch(`${t}${p.refresh}`,{method:"POST",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"},cache:"no-store"});if(!u.ok)return {success:!1,newToken:null};let s=await u.json().catch(()=>null),n=R.parseRefreshToken(s);return n?{success:!0,newToken:n}:{success:!1,newToken:null}}catch{return {success:false,newToken:null}}}async function a(){let i=e.guestToken;if(!i?.enabled||!i.credentials)return null;try{let u=await fetch(`${t}${p.guest}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:i.credentials.username,password:i.credentials.password}),cache:"no-store"});if(!u.ok)return null;let s=await u.json().catch(()=>null);return R.parseGuestToken(s)}catch{return null}}return {validateToken:l,getTokenInfo:c,refreshToken:m,createGuestToken:a}}function ee(e,t){if(!t?.enabled)return null;let f=t.locales??[],r=t.defaultLocale,R=e.split("/").filter(Boolean)[0];return R&&f.includes(R)?R:r??null}function z(e,t){let{cookies:f,guestToken:r,access:p,i18n:R,_resolved:l}=e,{cookieOptions:c}=l;function m(o,x,h){o.cookies.get(h)?.value&&x.cookies.delete(h);}function a(o,x){return m(o,x,f.guest),m(o,x,f.user),x}function i(o,x=500){return new server.NextResponse(JSON.stringify({success:false,message:o}),{status:x,headers:{"Content-Type":"application/json"}})}function u(o){return (p?.authRoutes??[]).some(h=>o===h||o.startsWith(`${h}/`))}function s(o){return p?.protectedByDefault?!n(o)&&!u(o):(p?.protectedRoutes??[]).some(h=>o===h||o.startsWith(`${h}/`))}function n(o){return (p?.publicRoutes??[]).some(h=>o===h||o.startsWith(`${h}/`))}function d(o){let x=p?.allowedTokenTypes;return !x||x.length===0?true:o?x.includes(o):false}async function N(o,x){let{origin:h}=o.nextUrl;if(r?.enabled){let y=await t.createGuestToken();if(y){let E;return x?E=server.NextResponse.next():s(o.nextUrl.pathname)?E=server.NextResponse.redirect(new URL("/login",h)):E=server.NextResponse.next(),E.cookies.set(f.guest,y,{...c,maxAge:3600}),E}}return x?i("Token bulunamad\u0131",401):s(o.nextUrl.pathname)?server.NextResponse.redirect(new URL("/login",h)):server.NextResponse.next()}async function L(o,x,h,y,E){let{pathname:k,origin:g}=o.nextUrl,{isValid:S,tokenType:A,userData:w}=x,v=A===chunk6ENVQMWQ_cjs.c.GUEST;if(!S){if(h&&y){let H=await t.refreshToken(y);if(H.success&&H.newToken){let O=await t.getTokenInfo(H.newToken);if(O.isValid){let _=new Headers(o.headers);O.userData&&_.set(chunk6ENVQMWQ_cjs.e.AUTH_USER,JSON.stringify(O.userData)),_.set(chunk6ENVQMWQ_cjs.e.REFRESHED_TOKEN,H.newToken);let Z=ee(k,R);Z&&_.set(chunk6ENVQMWQ_cjs.e.LOCALE,Z);let D;return u(k)?D=server.NextResponse.redirect(new URL("/",g)):D=server.NextResponse.next({request:{headers:_}}),D.cookies.set(f.user,H.newToken,{...c,maxAge:c.maxAge}),m(o,D,f.guest),D}}}let P=await N(o,E);return P.cookies.get(f.guest)?.value?m(o,P,f.user):a(o,P),P}let U=new Headers(o.headers);w&&U.set(chunk6ENVQMWQ_cjs.e.AUTH_USER,JSON.stringify(w));let J=ee(k,R);if(J&&U.set(chunk6ENVQMWQ_cjs.e.LOCALE,J),!v&&!d(A)){if(E){let Y=i("Bu i\u015Flem i\xE7in yetkiniz yok",403);return a(o,Y)}let P=server.NextResponse.redirect(new URL("/login",g));return a(o,P)}if(v)return E?server.NextResponse.next({request:{headers:U}}):s(k)?server.NextResponse.redirect(new URL("/login",g)):server.NextResponse.next({request:{headers:U}});if(u(k))return server.NextResponse.redirect(new URL("/",g));let X=server.NextResponse.next({request:{headers:U}});return m(o,X,f.guest),X}return {deleteAllAuthCookies:a,jsonError:i,isAuthPage:u,isProtectedRoute:s,isPublicRoute:n,isTokenTypeAllowed:d,handleNoToken:N,handleValidationResult:L}}function K(e){async function t(l){let c=ce();return `${await de(e.secret,l,c)}.${c}`}function f(l){let c=l.method.toUpperCase();if(e.ignoreMethods.includes(c))return {valid:true};let m=e.strategy;if(m==="fetch-metadata"||m==="both"){let a=r(l);if(m==="fetch-metadata"||a.valid||a.reason!=="missing-headers")return a}return m==="double-submit"||m==="both"?p(l):{valid:true}}function r(l){let c=l.headers.get("sec-fetch-site");if(!c)return {valid:false,reason:"missing-headers"};if(c==="same-origin")return {valid:true};if(c==="none"){let m=l.method.toUpperCase();return e.ignoreMethods.includes(m)?{valid:true}:{valid:false,reason:"direct-navigation-unsafe-method"}}return c==="same-site"?e.trustSameSite?{valid:true}:{valid:false,reason:"same-site-not-trusted"}:c==="cross-site"?{valid:false,reason:"cross-site-request"}:{valid:false,reason:"unknown-sec-fetch-site"}}function p(l){let c=l.cookies.get(e.cookieName)?.value;if(!c)return {valid:false,reason:"missing-cookie-token"};let m=l.headers.get(e.headerName);return m?fe(c,m)?c.split(".").length!==2?{valid:false,reason:"invalid-token-format"}:{valid:true}:{valid:false,reason:"token-mismatch"}:{valid:false,reason:"missing-header-token"}}async function R(l,c){let m=await t(c);return l.cookies.set(e.cookieName,m,{httpOnly:false,secure:process.env.NODE_ENV==="production",sameSite:"strict",path:"/"}),l}return {validateRequest:f,generateToken:t,attachCsrfCookie:R}}function ce(){if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}return Math.random().toString(36).substring(2)+Date.now().toString(36)}async function de(e,t,f){let r=`${t.length}!${t}!${f.length}!${f}`;if(typeof crypto<"u"&&crypto.subtle){let l=new TextEncoder,c=l.encode(e),m=l.encode(r),a=await crypto.subtle.importKey("raw",c,{name:"HMAC",hash:"SHA-256"},false,["sign"]),i=await crypto.subtle.sign("HMAC",a,m);return Array.from(new Uint8Array(i),u=>u.toString(16).padStart(2,"0")).join("")}let p=0,R=e+r;for(let l=0;l<R.length;l++){let c=R.charCodeAt(l);p=(p<<5)-p+c,p=p&p;}return Math.abs(p).toString(16)}function fe(e,t){if(e.length!==t.length)return false;let f=0;for(let r=0;r<e.length;r++)f|=e.charCodeAt(r)^t.charCodeAt(r);return f===0}function W(e){let t=new Map,f=setInterval(()=>{let i=Date.now();for(let[u,s]of t)s.resetAt<=i&&t.delete(u);},e.windowMs);typeof process<"u"&&process.on&&process.on("beforeExit",()=>clearInterval(f));function r(i){return e.skipRoutes.some(u=>u.endsWith("*")?i.startsWith(u.slice(0,-1)):u.endsWith("**")?i.startsWith(u.slice(0,-2)):i===u)}function p(i){let u=i.nextUrl.pathname;if(r(u))return {allowed:true,remaining:e.maxRequests,resetAt:0,limit:e.maxRequests};let s=e.keyFn(i),n=Date.now(),d=t.get(s);if(!d||d.resetAt<=n)return d={count:1,resetAt:n+e.windowMs},t.set(s,d),{allowed:true,remaining:e.maxRequests-1,resetAt:d.resetAt,limit:e.maxRequests};d.count++;let N=Math.max(0,e.maxRequests-d.count);return {allowed:d.count<=e.maxRequests,remaining:N,resetAt:d.resetAt,limit:e.maxRequests}}function R(i,u){return i.headers.set("X-RateLimit-Limit",u.limit.toString()),i.headers.set("X-RateLimit-Remaining",u.remaining.toString()),i.headers.set("X-RateLimit-Reset",Math.ceil(u.resetAt/1e3).toString()),i}function l(i,u){if(e.onRateLimited){let n=e.onRateLimited(i);return R(n,u)}let s=server.NextResponse.json({success:false,message:"Too many requests. Please try again later.",retryAfter:Math.ceil((u.resetAt-Date.now())/1e3)},{status:429});return s.headers.set("Retry-After",Math.ceil((u.resetAt-Date.now())/1e3).toString()),R(s,u)}function c(i){t.delete(i);}function m(){t.clear();}function a(){return t.size}return {check:p,applyHeaders:R,createLimitedResponse:l,shouldSkip:r,reset:c,clear:m,size:a}}function q(e){function t(s){return e.enabled&&e.events.includes(s)}function f(s){return s.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||s.headers.get("x-real-ip")||null}async function r(s,n,d){if(!t(s))return;let N={type:s,timestamp:new Date,ip:f(n),userId:d.userId,path:n.nextUrl.pathname,method:n.method,success:d.success,metadata:d.metadata};if(e.logger)try{await e.logger(N);}catch(L){console.error("[next-api-layer] Audit logger error:",L);}}function p(s,n,d){return r("auth:success",s,{success:true,userId:n,metadata:d})}function R(s,n){return r("auth:fail",s,{success:false,metadata:n})}function l(s,n,d){return r("auth:refresh",s,{success:true,userId:n,metadata:d})}function c(s,n){return r("auth:guest",s,{success:true,metadata:n})}function m(s,n,d){return r("access:denied",s,{success:false,userId:n,metadata:d})}function a(s,n){return r("csrf:fail",s,{success:false,metadata:n})}function i(s,n){return r("rateLimit:exceeded",s,{success:false,metadata:n})}function u(s,n,d){return r("error",s,{success:false,metadata:{...d,error:n.message,stack:n.stack}})}return {emit:r,isEnabled:t,authSuccess:p,authFail:R,authRefresh:l,authGuest:c,accessDenied:m,csrfFail:a,rateLimitExceeded:i,error:u}}function te(e,t){let f=[chunk6ENVQMWQ_cjs.e.LOCALE,chunk6ENVQMWQ_cjs.e.AUTH_USER,chunk6ENVQMWQ_cjs.e.REFRESHED_TOKEN];for(let r of f){let p=e.headers.get(r);p&&!t.headers.has(r)&&t.headers.set(r,p);}return e.cookies.getAll().forEach(r=>{t.cookies.get(r.name)||t.cookies.set(r.name,r.value);}),t}function se(e){let t=Q(e),f=j(t),r=z(t,f),p=K(t._resolved.csrf),R=W(t._resolved.rateLimit),l=q(t._resolved.audit);async function c(a){let{pathname:i,origin:u}=a.nextUrl,s=i.startsWith("/api");if(t._resolved.rateLimit.enabled){let g=R.check(a);if(!g.allowed)return await l.rateLimitExceeded(a,{limit:g.limit,resetAt:g.resetAt}),R.createLimitedResponse(a,g)}if(t._resolved.csrf.enabled){let g=p.validateRequest(a);if(!g.valid)return await l.csrfFail(a,{reason:g.reason}),server.NextResponse.json({success:false,message:"CSRF validation failed"},{status:403})}if(t.blockBrowserApiAccess&&s&&(a.headers.get("accept")||"").includes("text/html"))return server.NextResponse.redirect(new URL("/",u));if(t.beforeAuth){let g=await t.beforeAuth(a);if(g)return g}if((t.excludedPaths??[]).some(g=>i.startsWith(g)))return m(a,server.NextResponse.next(),{isAuthenticated:false,isGuest:false,tokenType:null,user:null});if(["/api/auth/login","/api/auth/logout","/api/auth/me","/api/auth/refresh","/api/auth/register"].includes(i))return m(a,server.NextResponse.next(),{isAuthenticated:false,isGuest:false,tokenType:null,user:null});let N=a.cookies?.get(t.cookies.user)?.value,L=a.cookies?.get(t.cookies.guest)?.value,o=N||L,x=!!N;if(!o){await l.authFail(a,{reason:"no-token"});let g=await r.handleNoToken(a,s);return m(a,g,{isAuthenticated:false,isGuest:false,tokenType:null,user:null})}let h=await f.getTokenInfo(o),y={isAuthenticated:h.isValid&&h.tokenType!=="guest",isGuest:h.isValid&&h.tokenType==="guest",tokenType:h.tokenType,user:h.userData};if(h.isValid)if(h.tokenType==="guest")await l.authGuest(a);else {let g=h.userData?.id?.toString();await l.authSuccess(a,g,{tokenType:h.tokenType});}else await l.authFail(a,{reason:"invalid-token"});let E=await r.handleValidationResult(a,h,x,o,s),k=await m(a,E,y);if(t._resolved.csrf.enabled&&y.isAuthenticated){let g=h.userData?.id?.toString()||o.slice(0,32);k=await p.attachCsrfCookie(k,g);}if(t._resolved.rateLimit.enabled){let g=R.check(a);k=R.applyHeaders(k,g);}return k}async function m(a,i,u){let s=i;if(t.i18n?.middleware){let n=await Promise.resolve(t.i18n.middleware(a));s=te(i,n);}if(t.afterAuth){let n=await t.afterAuth(a,s,u);s=te(s,n);}return s}return c.config=t,c.csrf=p,c.rateLimiter=R,c.audit=l,c}function me(e,t){if(!t||t.length===0)return false;let f=e.replace(/^\/api\//,"").replace(/^\//,"");return t.some(r=>{if(r===f)return true;if(r.includes("*")){let p=r.replace(/\*\*/g,"<<<DOUBLE>>>").replace(/\*/g,"[^/]+").replace(/<<<DOUBLE>>>/g,".+");return new RegExp(`^${p}$`).test(f)}return false})}function re(e){let{apiBaseUrl:t,userCookieName:f="auth_token",guestCookieName:r="guest_token",skipAuthByDefault:p=false,publicEndpoints:R=[],forwardHeaders:l=["content-type","accept","accept-language","x-requested-with"],excludeHeaders:c=["host","connection","cookie"],transformRequest:m,transformResponse:a}=e,i=t.replace(/\/$/,"");function u(n,d){return n.headers.get(chunk6ENVQMWQ_cjs.e.SKIP_AUTH)==="true"||me(d,R)?true:p}async function s(n){try{let d=new URL(n.url),N=d.pathname.replace(/^\/api\/?/,""),L=new URL(`${i}/${N}`);L.search=d.search;let o=new Headers;if(l.forEach(A=>{let w=n.headers.get(A);w&&o.set(A,w);}),n.headers.forEach((A,w)=>{let v=w.toLowerCase();!c.includes(v)&&!o.has(w)&&o.set(w,A);}),!u(n,N)){let A=n.cookies.get(f)?.value,w=n.cookies.get(r)?.value,v=A||w;v&&o.set(chunk6ENVQMWQ_cjs.e.AUTHORIZATION,`Bearer ${v}`);}o.delete(chunk6ENVQMWQ_cjs.e.SKIP_AUTH);let x=m?await m(n,o):o,h=null;if(n.method!=="GET"&&n.method!=="HEAD"){let A=n.headers.get("content-type")||"";A.includes("application/json")?h=await n.text():A.includes("multipart/form-data")?h=await n.formData():h=await n.text();}let y=await fetch(L.toString(),{method:n.method,headers:x,body:h}),E=y.headers.get("content-type")||"",k;E.includes("application/json")?k=await y.text():k=await y.arrayBuffer();let g=new Headers;y.headers.forEach((A,w)=>{let v=w.toLowerCase();["transfer-encoding","connection","keep-alive"].includes(v)||g.set(w,A);});let S=new server.NextResponse(k,{status:y.status,statusText:y.statusText,headers:g});return a&&(S=await a(S)),S}catch(d){return console.error("[Proxy Error]",d),server.NextResponse.json({success:false,message:"Proxy error: Unable to connect to backend",error:d instanceof Error?d.message:"Unknown error"},{status:502})}}return s.config=e,s}
|
|
2
|
-
Object.defineProperty(exports,"createApiClient",{enumerable:true,get:function(){return chunkJEEL6S4O_cjs.d}});Object.defineProperty(exports,"CSRF_SAFE_METHODS",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.f}});Object.defineProperty(exports,"DEFAULT_AUDIT_CONFIG",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.i}});Object.defineProperty(exports,"DEFAULT_COOKIE_OPTIONS",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.a}});Object.defineProperty(exports,"DEFAULT_CSRF_CONFIG",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.g}});Object.defineProperty(exports,"DEFAULT_ENDPOINTS",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.b}});Object.defineProperty(exports,"DEFAULT_RATE_LIMIT_CONFIG",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.h}});Object.defineProperty(exports,"ERROR_MESSAGES",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.d}});Object.defineProperty(exports,"HEADERS",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.e}});Object.defineProperty(exports,"TOKEN_TYPES",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.c}});exports.createAuditLogger=
|
|
1
|
+
'use strict';var chunkJEEL6S4O_cjs=require('./chunk-JEEL6S4O.cjs'),chunk6ENVQMWQ_cjs=require('./chunk-6ENVQMWQ.cjs'),server=require('next/server');function le(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID()+crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2)}`}function ce(e){return `rl:${e.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||e.headers.get("x-real-ip")||"unknown"}`}function te(e){if(!e.apiBaseUrl)throw new Error("next-api-layer: apiBaseUrl is required");if(!e.cookies?.user||!e.cookies?.guest)throw new Error("next-api-layer: cookies.user and cookies.guest are required");let t=e.apiBaseUrl.endsWith("/")?e.apiBaseUrl:`${e.apiBaseUrl}/`,d={...chunk6ENVQMWQ_cjs.a,...e.cookies.options},r={...chunk6ENVQMWQ_cjs.b,...e.endpoints},c={enabled:e.csrf?.enabled??false,strategy:e.csrf?.strategy??chunk6ENVQMWQ_cjs.g.strategy,secret:e.csrf?.secret??le(),cookieName:e.csrf?.cookieName??chunk6ENVQMWQ_cjs.g.cookieName,headerName:e.csrf?.headerName??chunk6ENVQMWQ_cjs.g.headerName,ignoreMethods:e.csrf?.ignoreMethods??chunk6ENVQMWQ_cjs.g.ignoreMethods,trustSameSite:e.csrf?.trustSameSite??chunk6ENVQMWQ_cjs.g.trustSameSite},m={enabled:e.rateLimit?.enabled??false,windowMs:e.rateLimit?.windowMs??chunk6ENVQMWQ_cjs.h.windowMs,maxRequests:e.rateLimit?.maxRequests??chunk6ENVQMWQ_cjs.h.maxRequests,keyFn:e.rateLimit?.keyFn??ce,skipRoutes:e.rateLimit?.skipRoutes??chunk6ENVQMWQ_cjs.h.skipRoutes,onRateLimited:e.rateLimit?.onRateLimited},l={enabled:e.audit?.enabled??false,events:e.audit?.events??[...chunk6ENVQMWQ_cjs.i.events],logger:e.audit?.logger};return {...e,apiBaseUrl:t,_resolved:{cookieOptions:d,endpoints:r,csrf:c,rateLimit:m,audit:l}}}var j={parseAuthMe:e=>{let t=e;return !t?.success||!t?.data?null:{isValid:true,tokenType:t.data.type||"user",exp:t.data.exp||null,userData:t.data}},parseRefreshToken:e=>{let t=e;return t?.success&&t?.data?.accessToken?t.data.accessToken:null},parseGuestToken:e=>e?.data?.accessToken||null};function z(e){let{apiBaseUrl:t,_resolved:d,responseMappers:r}=e,{endpoints:c}=d,m={parseAuthMe:r?.parseAuthMe||j.parseAuthMe,parseRefreshToken:r?.parseRefreshToken||j.parseRefreshToken,parseGuestToken:r?.parseGuestToken||j.parseGuestToken};async function l(a){let u={isValid:false,tokenType:null,exp:null,userData:null};try{let n=await fetch(`${t}${c.validate}`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},cache:"no-store"});if(!n.ok)return u;let s=await n.json().catch(()=>null),p=m.parseAuthMe(s);return !p||!p.isValid?u:p}catch{return u}}async function f(a){return l(a)}async function h(a){try{let u=await fetch(`${t}${c.refresh}`,{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},cache:"no-store"});if(!u.ok)return {success:!1,newToken:null};let n=await u.json().catch(()=>null),s=m.parseRefreshToken(n);return s?{success:!0,newToken:s}:{success:!1,newToken:null}}catch{return {success:false,newToken:null}}}async function o(){let a=e.guestToken;if(!a?.enabled||!a.credentials)return null;try{let u=await fetch(`${t}${c.guest}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:a.credentials.username,password:a.credentials.password}),cache:"no-store"});if(!u.ok)return null;let n=await u.json().catch(()=>null);return m.parseGuestToken(n)}catch{return null}}return {validateToken:l,getTokenInfo:f,refreshToken:h,createGuestToken:o}}function O(e,t){if(!t?.enabled)return null;let d=t.locales??[],r=t.defaultLocale,m=e.split("/").filter(Boolean)[0];return m&&d.includes(m)?m:r??null}function K(e,t){if(!t?.enabled)return e;let d=t.locales??[],r=e.split("/").filter(Boolean),c=r[0];return c&&d.includes(c)?"/"+r.slice(1).join("/")||"/":e}function W(e,t){let{cookies:d,guestToken:r,access:c,i18n:m,_resolved:l}=e,{cookieOptions:f}=l;function h(i,R,x){i.cookies.get(x)?.value&&R.cookies.delete(x);}function o(i,R){return h(i,R,d.guest),h(i,R,d.user),R}function a(i,R=500){return new server.NextResponse(JSON.stringify({success:false,message:i}),{status:R,headers:{"Content-Type":"application/json"}})}function u(i){let R=K(i,m);return (c?.authRoutes??[]).some(y=>R===y||R.startsWith(`${y}/`))}function n(i){let R=K(i,m);return c?.protectedByDefault?!s(i)&&!u(i):(c?.protectedRoutes??[]).some(y=>R===y||R.startsWith(`${y}/`))}function s(i){let R=K(i,m);return (c?.publicRoutes??[]).some(y=>R===y||R.startsWith(`${y}/`))}function p(i){let R=c?.allowedTokenTypes;return !R||R.length===0?true:i?R.includes(i):false}async function N(i,R){let{origin:x}=i.nextUrl;if(r?.enabled){let y=await t.createGuestToken();if(y){let L;return R?L=server.NextResponse.next():n(i.nextUrl.pathname)?L=server.NextResponse.redirect(new URL("/login",x)):L=server.NextResponse.next(),L.cookies.set(d.guest,y,{...f,maxAge:3600}),L}}return R?a("Token bulunamad\u0131",401):n(i.nextUrl.pathname)?server.NextResponse.redirect(new URL("/login",x)):server.NextResponse.next()}async function E(i,R,x,y,L){let{pathname:T,origin:g}=i.nextUrl,{isValid:S,tokenType:A,userData:w}=R,v=A===chunk6ENVQMWQ_cjs.c.GUEST;if(!S){if(x&&y){let H=await t.refreshToken(y);if(H.success&&H.newToken){let F=await t.getTokenInfo(H.newToken);if(F.isValid){let _=new Headers(i.headers);F.userData&&_.set(chunk6ENVQMWQ_cjs.e.AUTH_USER,JSON.stringify(F.userData)),_.set(chunk6ENVQMWQ_cjs.e.REFRESHED_TOKEN,H.newToken);let ee=O(T,m);ee&&_.set(chunk6ENVQMWQ_cjs.e.LOCALE,ee);let D;return u(T)?D=server.NextResponse.redirect(new URL("/",g)):D=server.NextResponse.next({request:{headers:_}}),D.cookies.set(d.user,H.newToken,{...f,maxAge:f.maxAge}),h(i,D,d.guest),D}}}let b=await N(i,L);return b.cookies.get(d.guest)?.value?h(i,b,d.user):o(i,b),b}let U=new Headers(i.headers);w&&U.set(chunk6ENVQMWQ_cjs.e.AUTH_USER,JSON.stringify(w));let Y=O(T,m);if(Y&&U.set(chunk6ENVQMWQ_cjs.e.LOCALE,Y),!v&&!p(A)){if(L){let Q=a("Bu i\u015Flem i\xE7in yetkiniz yok",403);return o(i,Q)}let b=server.NextResponse.redirect(new URL("/login",g));return o(i,b)}if(v)return L?server.NextResponse.next({request:{headers:U}}):n(T)?server.NextResponse.redirect(new URL("/login",g)):server.NextResponse.next({request:{headers:U}});if(u(T))return server.NextResponse.redirect(new URL("/",g));let Z=server.NextResponse.next({request:{headers:U}});return h(i,Z,d.guest),Z}return {deleteAllAuthCookies:o,jsonError:a,isAuthPage:u,isProtectedRoute:n,isPublicRoute:s,isTokenTypeAllowed:p,handleNoToken:N,handleValidationResult:E}}function q(e){async function t(l){let f=de();return `${await fe(e.secret,l,f)}.${f}`}function d(l){let f=l.method.toUpperCase();if(e.ignoreMethods.includes(f))return {valid:true};let h=e.strategy;if(h==="fetch-metadata"||h==="both"){let o=r(l);if(h==="fetch-metadata"||o.valid||o.reason!=="missing-headers")return o}return h==="double-submit"||h==="both"?c(l):{valid:true}}function r(l){let f=l.headers.get("sec-fetch-site");if(!f)return {valid:false,reason:"missing-headers"};if(f==="same-origin")return {valid:true};if(f==="none"){let h=l.method.toUpperCase();return e.ignoreMethods.includes(h)?{valid:true}:{valid:false,reason:"direct-navigation-unsafe-method"}}return f==="same-site"?e.trustSameSite?{valid:true}:{valid:false,reason:"same-site-not-trusted"}:f==="cross-site"?{valid:false,reason:"cross-site-request"}:{valid:false,reason:"unknown-sec-fetch-site"}}function c(l){let f=l.cookies.get(e.cookieName)?.value;if(!f)return {valid:false,reason:"missing-cookie-token"};let h=l.headers.get(e.headerName);return h?pe(f,h)?f.split(".").length!==2?{valid:false,reason:"invalid-token-format"}:{valid:true}:{valid:false,reason:"token-mismatch"}:{valid:false,reason:"missing-header-token"}}async function m(l,f){let h=await t(f);return l.cookies.set(e.cookieName,h,{httpOnly:false,secure:process.env.NODE_ENV==="production",sameSite:"strict",path:"/"}),l}return {validateRequest:d,generateToken:t,attachCsrfCookie:m}}function de(){if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}return Math.random().toString(36).substring(2)+Date.now().toString(36)}async function fe(e,t,d){let r=`${t.length}!${t}!${d.length}!${d}`;if(typeof crypto<"u"&&crypto.subtle){let l=new TextEncoder,f=l.encode(e),h=l.encode(r),o=await crypto.subtle.importKey("raw",f,{name:"HMAC",hash:"SHA-256"},false,["sign"]),a=await crypto.subtle.sign("HMAC",o,h);return Array.from(new Uint8Array(a),u=>u.toString(16).padStart(2,"0")).join("")}let c=0,m=e+r;for(let l=0;l<m.length;l++){let f=m.charCodeAt(l);c=(c<<5)-c+f,c=c&c;}return Math.abs(c).toString(16)}function pe(e,t){if(e.length!==t.length)return false;let d=0;for(let r=0;r<e.length;r++)d|=e.charCodeAt(r)^t.charCodeAt(r);return d===0}function J(e){let t=new Map,d=setInterval(()=>{let a=Date.now();for(let[u,n]of t)n.resetAt<=a&&t.delete(u);},e.windowMs);typeof process<"u"&&process.on&&process.on("beforeExit",()=>clearInterval(d));function r(a){return e.skipRoutes.some(u=>u.endsWith("*")?a.startsWith(u.slice(0,-1)):u.endsWith("**")?a.startsWith(u.slice(0,-2)):a===u)}function c(a){let u=a.nextUrl.pathname;if(r(u))return {allowed:true,remaining:e.maxRequests,resetAt:0,limit:e.maxRequests};let n=e.keyFn(a),s=Date.now(),p=t.get(n);if(!p||p.resetAt<=s)return p={count:1,resetAt:s+e.windowMs},t.set(n,p),{allowed:true,remaining:e.maxRequests-1,resetAt:p.resetAt,limit:e.maxRequests};p.count++;let N=Math.max(0,e.maxRequests-p.count);return {allowed:p.count<=e.maxRequests,remaining:N,resetAt:p.resetAt,limit:e.maxRequests}}function m(a,u){return a.headers.set("X-RateLimit-Limit",u.limit.toString()),a.headers.set("X-RateLimit-Remaining",u.remaining.toString()),a.headers.set("X-RateLimit-Reset",Math.ceil(u.resetAt/1e3).toString()),a}function l(a,u){if(e.onRateLimited){let s=e.onRateLimited(a);return m(s,u)}let n=server.NextResponse.json({success:false,message:"Too many requests. Please try again later.",retryAfter:Math.ceil((u.resetAt-Date.now())/1e3)},{status:429});return n.headers.set("Retry-After",Math.ceil((u.resetAt-Date.now())/1e3).toString()),m(n,u)}function f(a){t.delete(a);}function h(){t.clear();}function o(){return t.size}return {check:c,applyHeaders:m,createLimitedResponse:l,shouldSkip:r,reset:f,clear:h,size:o}}function X(e){function t(n){return e.enabled&&e.events.includes(n)}function d(n){return n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||n.headers.get("x-real-ip")||null}async function r(n,s,p){if(!t(n))return;let N={type:n,timestamp:new Date,ip:d(s),userId:p.userId,path:s.nextUrl.pathname,method:s.method,success:p.success,metadata:p.metadata};if(e.logger)try{await e.logger(N);}catch(E){console.error("[next-api-layer] Audit logger error:",E);}}function c(n,s,p){return r("auth:success",n,{success:true,userId:s,metadata:p})}function m(n,s){return r("auth:fail",n,{success:false,metadata:s})}function l(n,s,p){return r("auth:refresh",n,{success:true,userId:s,metadata:p})}function f(n,s){return r("auth:guest",n,{success:true,metadata:s})}function h(n,s,p){return r("access:denied",n,{success:false,userId:s,metadata:p})}function o(n,s){return r("csrf:fail",n,{success:false,metadata:s})}function a(n,s){return r("rateLimit:exceeded",n,{success:false,metadata:s})}function u(n,s,p){return r("error",n,{success:false,metadata:{...p,error:s.message,stack:s.stack}})}return {emit:r,isEnabled:t,authSuccess:c,authFail:m,authRefresh:l,authGuest:f,accessDenied:h,csrfFail:o,rateLimitExceeded:a,error:u}}function se(e,t){let d=[chunk6ENVQMWQ_cjs.e.LOCALE,chunk6ENVQMWQ_cjs.e.AUTH_USER,chunk6ENVQMWQ_cjs.e.REFRESHED_TOKEN];for(let r of d){let c=e.headers.get(r);c&&!t.headers.has(r)&&t.headers.set(r,c);}return e.cookies.getAll().forEach(r=>{t.cookies.get(r.name)||t.cookies.set(r.name,r.value);}),t}function ne(e){let t=te(e),d=z(t),r=W(t,d),c=q(t._resolved.csrf),m=J(t._resolved.rateLimit),l=X(t._resolved.audit);async function f(o){let{pathname:a,origin:u}=o.nextUrl,n=a.startsWith("/api");if(t._resolved.rateLimit.enabled){let g=m.check(o);if(!g.allowed)return await l.rateLimitExceeded(o,{limit:g.limit,resetAt:g.resetAt}),m.createLimitedResponse(o,g)}if(t._resolved.csrf.enabled){let g=c.validateRequest(o);if(!g.valid)return await l.csrfFail(o,{reason:g.reason}),server.NextResponse.json({success:false,message:"CSRF validation failed"},{status:403})}if(t.blockBrowserApiAccess&&n&&(o.headers.get("accept")||"").includes("text/html"))return server.NextResponse.redirect(new URL("/",u));if(t.beforeAuth){let g=await t.beforeAuth(o);if(g)return g}if((t.excludedPaths??[]).some(g=>a.startsWith(g)))return h(o,server.NextResponse.next(),{isAuthenticated:false,isGuest:false,tokenType:null,user:null});if(["/api/auth/login","/api/auth/logout","/api/auth/me","/api/auth/refresh","/api/auth/register"].includes(a))return h(o,server.NextResponse.next(),{isAuthenticated:false,isGuest:false,tokenType:null,user:null});let N=o.cookies?.get(t.cookies.user)?.value,E=o.cookies?.get(t.cookies.guest)?.value,i=N||E,R=!!N;if(!i){await l.authFail(o,{reason:"no-token"});let g=await r.handleNoToken(o,n);return h(o,g,{isAuthenticated:false,isGuest:false,tokenType:null,user:null})}let x=await d.getTokenInfo(i),y={isAuthenticated:x.isValid&&x.tokenType!=="guest",isGuest:x.isValid&&x.tokenType==="guest",tokenType:x.tokenType,user:x.userData};if(x.isValid)if(x.tokenType==="guest")await l.authGuest(o);else {let g=x.userData?.id?.toString();await l.authSuccess(o,g,{tokenType:x.tokenType});}else await l.authFail(o,{reason:"invalid-token"});let L=await r.handleValidationResult(o,x,R,i,n),T=await h(o,L,y);if(t._resolved.csrf.enabled&&y.isAuthenticated){let g=x.userData?.id?.toString()||i.slice(0,32);T=await c.attachCsrfCookie(T,g);}if(t._resolved.rateLimit.enabled){let g=m.check(o);T=m.applyHeaders(T,g);}return T}async function h(o,a,u){let n=a;if(t.i18n?.middleware){let s=await Promise.resolve(t.i18n.middleware(o));n=se(a,s);}if(t.i18n?.enabled){let s=O(o.nextUrl.pathname,t.i18n);s&&n.headers.set(chunk6ENVQMWQ_cjs.e.LOCALE,s);}if(t.afterAuth){let s=await t.afterAuth(o,n,u);n=se(n,s);}return n}return f.config=t,f.csrf=c,f.rateLimiter=m,f.audit=l,f}function he(e,t){if(!t||t.length===0)return false;let d=e.replace(/^\/api\//,"").replace(/^\//,"");return t.some(r=>{if(r===d)return true;if(r.includes("*")){let c=r.replace(/\*\*/g,"<<<DOUBLE>>>").replace(/\*/g,"[^/]+").replace(/<<<DOUBLE>>>/g,".+");return new RegExp(`^${c}$`).test(d)}return false})}function oe(e){let{apiBaseUrl:t,userCookieName:d="auth_token",guestCookieName:r="guest_token",skipAuthByDefault:c=false,publicEndpoints:m=[],forwardHeaders:l=["content-type","accept","accept-language","x-requested-with"],excludeHeaders:f=["host","connection","cookie"],transformRequest:h,transformResponse:o}=e,a=t.replace(/\/$/,"");function u(s,p){return s.headers.get(chunk6ENVQMWQ_cjs.e.SKIP_AUTH)==="true"||he(p,m)?true:c}async function n(s){try{let p=new URL(s.url),N=p.pathname.replace(/^\/api\/?/,""),E=new URL(`${a}/${N}`);E.search=p.search;let i=new Headers;if(l.forEach(A=>{let w=s.headers.get(A);w&&i.set(A,w);}),s.headers.forEach((A,w)=>{let v=w.toLowerCase();!f.includes(v)&&!i.has(w)&&i.set(w,A);}),!u(s,N)){let A=s.cookies.get(d)?.value,w=s.cookies.get(r)?.value,v=A||w;v&&i.set(chunk6ENVQMWQ_cjs.e.AUTHORIZATION,`Bearer ${v}`);}i.delete(chunk6ENVQMWQ_cjs.e.SKIP_AUTH);let R=h?await h(s,i):i,x=null;if(s.method!=="GET"&&s.method!=="HEAD"){let A=s.headers.get("content-type")||"";A.includes("application/json")?x=await s.text():A.includes("multipart/form-data")?x=await s.formData():x=await s.text();}let y=await fetch(E.toString(),{method:s.method,headers:R,body:x}),L=y.headers.get("content-type")||"",T;L.includes("application/json")?T=await y.text():T=await y.arrayBuffer();let g=new Headers;y.headers.forEach((A,w)=>{let v=w.toLowerCase();["transfer-encoding","connection","keep-alive"].includes(v)||g.set(w,A);});let S=new server.NextResponse(T,{status:y.status,statusText:y.statusText,headers:g});return o&&(S=await o(S)),S}catch(p){return console.error("[Proxy Error]",p),server.NextResponse.json({success:false,message:"Proxy error: Unable to connect to backend",error:p instanceof Error?p.message:"Unknown error"},{status:502})}}return n.config=e,n}
|
|
2
|
+
Object.defineProperty(exports,"createApiClient",{enumerable:true,get:function(){return chunkJEEL6S4O_cjs.d}});Object.defineProperty(exports,"CSRF_SAFE_METHODS",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.f}});Object.defineProperty(exports,"DEFAULT_AUDIT_CONFIG",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.i}});Object.defineProperty(exports,"DEFAULT_COOKIE_OPTIONS",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.a}});Object.defineProperty(exports,"DEFAULT_CSRF_CONFIG",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.g}});Object.defineProperty(exports,"DEFAULT_ENDPOINTS",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.b}});Object.defineProperty(exports,"DEFAULT_RATE_LIMIT_CONFIG",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.h}});Object.defineProperty(exports,"ERROR_MESSAGES",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.d}});Object.defineProperty(exports,"HEADERS",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.e}});Object.defineProperty(exports,"TOKEN_TYPES",{enumerable:true,get:function(){return chunk6ENVQMWQ_cjs.c}});exports.createAuditLogger=X;exports.createAuthProxy=ne;exports.createCsrfValidator=q;exports.createHandlers=W;exports.createProxyHandler=oe;exports.createRateLimiter=J;exports.createTokenValidation=z;//# sourceMappingURL=index.cjs.map
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/config.ts","../src/proxy/tokenValidation.ts","../src/proxy/handlers.ts","../src/proxy/csrf.ts","../src/proxy/rateLimit.ts","../src/proxy/audit.ts","../src/proxy/createAuthProxy.ts","../src/proxy/createProxyHandler.ts"],"names":["generateCsrfSecret","defaultRateLimitKeyFn","req","resolveProxyConfig","config","apiBaseUrl","cookieOptions","DEFAULT_COOKIE_OPTIONS","endpoints","DEFAULT_ENDPOINTS","csrf","DEFAULT_CSRF_CONFIG","rateLimit","DEFAULT_RATE_LIMIT_CONFIG","audit","DEFAULT_AUDIT_CONFIG","defaultMappers","response","res","createTokenValidation","_resolved","responseMappers","mappers","validateToken","token","invalidResult","rawResponse","parsed","getTokenInfo","refreshToken","oldToken","newToken","createGuestToken","guestConfig","extractLocale","pathname","i18n","locales","defaultLocale","firstSegment","createHandlers","validation","cookies","guestToken","access","safeDeleteCookie","cookieName","deleteAllAuthCookies","jsonError","message","status","NextResponse","isAuthPage","route","isProtectedRoute","isPublicRoute","isTokenTypeAllowed","tokenType","allowedTypes","handleNoToken","isApiRoute","origin","guestAccessToken","handleValidationResult","tokenInfo","isUserToken","currentToken","isValid","userData","isGuest","TOKEN_TYPES","refreshResult","newTokenInfo","requestHeaders","HEADERS","locale","createCsrfValidator","generateToken","sessionId","randomValue","generateRandomValue","computeHmac","validateRequest","method","strategy","fetchResult","validateFetchMetadata","validateDoubleSubmit","secFetchSite","cookieToken","headerToken","constantTimeEqual","attachCsrfCookie","array","b","secret","encoder","keyData","messageData","key","signature","hash","str","i","char","a","result","createRateLimiter","store","cleanupInterval","now","entry","shouldSkip","pattern","check","remaining","applyHeaders","createLimitedResponse","reset","clear","size","createAuditLogger","isEnabled","type","getIp","emit","options","event","error","authSuccess","userId","metadata","authFail","authRefresh","authGuest","accessDenied","csrfFail","rateLimitExceeded","err","mergeResponses","source","target","criticalHeaders","header","value","cookie","createAuthProxy","userConfig","handlers","rateLimiter","authProxy","rateLimitResult","csrfResult","beforeResult","path","applyMiddlewaresAndHooks","userToken","authResult","finalResponse","intlResponse","hookResponse","matchesPattern","endpoint","patterns","normalizedEndpoint","regexPattern","createProxyHandler","userCookieName","guestCookieName","skipAuthByDefault","publicEndpoints","forwardHeaders","excludeHeaders","transformRequest","transformResponse","baseUrl","shouldSkipAuth","handler","url","backendUrl","headers","headerName","lowerKey","finalHeaders","body","contentType","backendResponse","responseBody","responseHeaders"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Config Resolver\r\n * Resolves and validates configuration with defaults\r\n */\r\n\r\nimport type { NextRequest } from 'next/server';\r\nimport type {\r\n AuthProxyConfig,\r\n InternalProxyConfig,\r\n ApiClientConfig,\r\n ResolvedCookieOptions,\r\n EndpointConfig,\r\n ResolvedCsrfConfig,\r\n ResolvedRateLimitConfig,\r\n ResolvedAuditConfig,\r\n AuditEventType,\r\n} from './types';\r\n\r\nimport {\r\n DEFAULT_COOKIE_OPTIONS,\r\n DEFAULT_ENDPOINTS,\r\n DEFAULT_CSRF_CONFIG,\r\n DEFAULT_RATE_LIMIT_CONFIG,\r\n DEFAULT_AUDIT_CONFIG,\r\n} from './constants';\r\n\r\n/**\r\n * Generates a random secret for CSRF HMAC signing\r\n */\r\nfunction generateCsrfSecret(): string {\r\n // Use crypto if available (Node.js)\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID() + crypto.randomUUID();\r\n }\r\n // Fallback: timestamp + random\r\n return `${Date.now()}-${Math.random().toString(36).substring(2)}`;\r\n}\r\n\r\n/**\r\n * Default rate limit key function (IP-based)\r\n */\r\nfunction defaultRateLimitKeyFn(req: NextRequest): string {\r\n const forwarded = req.headers.get('x-forwarded-for');\r\n const ip = forwarded?.split(',')[0]?.trim() || \r\n req.headers.get('x-real-ip') || \r\n 'unknown';\r\n return `rl:${ip}`;\r\n}\r\n\r\n/**\r\n * Resolves proxy configuration with defaults\r\n */\r\nexport function resolveProxyConfig(config: AuthProxyConfig): InternalProxyConfig {\r\n // Validate required fields\r\n if (!config.apiBaseUrl) {\r\n throw new Error('next-api-layer: apiBaseUrl is required');\r\n }\r\n \r\n if (!config.cookies?.user || !config.cookies?.guest) {\r\n throw new Error('next-api-layer: cookies.user and cookies.guest are required');\r\n }\r\n\r\n // Ensure apiBaseUrl ends with /\r\n const apiBaseUrl = config.apiBaseUrl.endsWith('/')\r\n ? config.apiBaseUrl\r\n : `${config.apiBaseUrl}/`;\r\n\r\n // Resolve cookie options\r\n const cookieOptions: ResolvedCookieOptions = {\r\n ...DEFAULT_COOKIE_OPTIONS,\r\n ...config.cookies.options,\r\n };\r\n\r\n // Resolve endpoints\r\n const endpoints: Required<EndpointConfig> = {\r\n ...DEFAULT_ENDPOINTS,\r\n ...config.endpoints,\r\n };\r\n\r\n // Resolve CSRF config\r\n const csrf: ResolvedCsrfConfig = {\r\n enabled: config.csrf?.enabled ?? false,\r\n strategy: config.csrf?.strategy ?? DEFAULT_CSRF_CONFIG.strategy,\r\n secret: config.csrf?.secret ?? generateCsrfSecret(),\r\n cookieName: config.csrf?.cookieName ?? DEFAULT_CSRF_CONFIG.cookieName,\r\n headerName: config.csrf?.headerName ?? DEFAULT_CSRF_CONFIG.headerName,\r\n ignoreMethods: config.csrf?.ignoreMethods ?? DEFAULT_CSRF_CONFIG.ignoreMethods,\r\n trustSameSite: config.csrf?.trustSameSite ?? DEFAULT_CSRF_CONFIG.trustSameSite,\r\n };\r\n\r\n // Resolve rate limit config\r\n const rateLimit: ResolvedRateLimitConfig = {\r\n enabled: config.rateLimit?.enabled ?? false,\r\n windowMs: config.rateLimit?.windowMs ?? DEFAULT_RATE_LIMIT_CONFIG.windowMs,\r\n maxRequests: config.rateLimit?.maxRequests ?? DEFAULT_RATE_LIMIT_CONFIG.maxRequests,\r\n keyFn: config.rateLimit?.keyFn ?? defaultRateLimitKeyFn,\r\n skipRoutes: config.rateLimit?.skipRoutes ?? DEFAULT_RATE_LIMIT_CONFIG.skipRoutes,\r\n onRateLimited: config.rateLimit?.onRateLimited,\r\n };\r\n\r\n // Resolve audit config\r\n const audit: ResolvedAuditConfig = {\r\n enabled: config.audit?.enabled ?? false,\r\n events: config.audit?.events ?? [...DEFAULT_AUDIT_CONFIG.events] as AuditEventType[],\r\n logger: config.audit?.logger,\r\n };\r\n\r\n return {\r\n ...config,\r\n apiBaseUrl,\r\n _resolved: {\r\n cookieOptions,\r\n endpoints,\r\n csrf,\r\n rateLimit,\r\n audit,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Resolves API client configuration with defaults\r\n */\r\nexport function resolveApiClientConfig(config: ApiClientConfig = {}) {\r\n return {\r\n sanitization: {\r\n enabled: config.sanitization?.enabled ?? true,\r\n allowedTags: config.sanitization?.allowedTags,\r\n skipFields: config.sanitization?.skipFields ?? [],\r\n skipEndpoints: config.sanitization?.skipEndpoints ?? [],\r\n },\r\n i18n: {\r\n enabled: config.i18n?.enabled ?? false,\r\n paramName: config.i18n?.paramName ?? 'lang',\r\n locales: config.i18n?.locales ?? [],\r\n defaultLocale: config.i18n?.defaultLocale ?? 'en',\r\n },\r\n auth: {\r\n skipByDefault: config.auth?.skipByDefault ?? false,\r\n publicEndpoints: config.auth?.publicEndpoints ?? [],\r\n },\r\n methodSpoofing: config.methodSpoofing ?? false,\r\n errorMessages: {\r\n noToken: config.errorMessages?.noToken ?? 'Token bulunamadı.',\r\n connectionError: config.errorMessages?.connectionError ?? 'Bağlantı hatası oluştu.',\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Type guard to check if a value is defined\r\n */\r\nexport function isDefined<T>(value: T | undefined | null): value is T {\r\n return value !== undefined && value !== null;\r\n}\r\n","/**\r\n * Token Validation\r\n * Handles token validation and refresh with backend API\r\n */\r\n\r\nimport type { \r\n TokenInfo, \r\n RefreshResult, \r\n AuthMeResponse, \r\n GuestTokenResponse,\r\n InternalProxyConfig,\r\n ResponseMappers,\r\n} from '../shared/types';\r\n\r\n/**\r\n * Default response parsers (standard format)\r\n */\r\nconst defaultMappers: Required<ResponseMappers> = {\r\n // Default: { success: true, data: { type, exp, ...user } }\r\n parseAuthMe: (response: unknown): TokenInfo | null => {\r\n const res = response as AuthMeResponse | null;\r\n if (!res?.success || !res?.data) {\r\n return null;\r\n }\r\n return {\r\n isValid: true,\r\n tokenType: res.data.type || 'user',\r\n exp: res.data.exp || null,\r\n userData: res.data,\r\n };\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseRefreshToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.success && res?.data?.accessToken ? res.data.accessToken : null;\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseGuestToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.data?.accessToken || null;\r\n },\r\n};\r\n\r\n/**\r\n * Creates token validation functions\r\n */\r\nexport function createTokenValidation(\r\n config: InternalProxyConfig\r\n) {\r\n const { apiBaseUrl, _resolved, responseMappers } = config;\r\n const { endpoints } = _resolved;\r\n \r\n // Merge custom mappers with defaults\r\n const mappers: Required<ResponseMappers> = {\r\n parseAuthMe: responseMappers?.parseAuthMe || defaultMappers.parseAuthMe,\r\n parseRefreshToken: responseMappers?.parseRefreshToken || defaultMappers.parseRefreshToken,\r\n parseGuestToken: responseMappers?.parseGuestToken || defaultMappers.parseGuestToken,\r\n };\r\n\r\n /**\r\n * Validates a token against the backend\r\n */\r\n async function validateToken(token: string): Promise<TokenInfo> {\r\n const invalidResult: TokenInfo = { isValid: false, tokenType: null, exp: null, userData: null };\r\n \r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.validate}`, {\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return invalidResult;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const parsed = mappers.parseAuthMe(rawResponse);\r\n \r\n if (!parsed || !parsed.isValid) {\r\n return invalidResult;\r\n }\r\n\r\n return parsed;\r\n } catch {\r\n return invalidResult;\r\n }\r\n }\r\n\r\n /**\r\n * Gets token info (validates with backend)\r\n */\r\n async function getTokenInfo(token: string): Promise<TokenInfo> {\r\n return validateToken(token);\r\n }\r\n\r\n /**\r\n * Refreshes a token\r\n */\r\n async function refreshToken(oldToken: string): Promise<RefreshResult> {\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.refresh}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Authorization': `Bearer ${oldToken}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const newToken = mappers.parseRefreshToken(rawResponse);\r\n\r\n if (!newToken) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n return { success: true, newToken };\r\n } catch {\r\n return { success: false, newToken: null };\r\n }\r\n }\r\n\r\n /**\r\n * Creates a guest token\r\n */\r\n async function createGuestToken(): Promise<string | null> {\r\n const guestConfig = config.guestToken;\r\n \r\n if (!guestConfig?.enabled || !guestConfig.credentials) {\r\n return null;\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.guest}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n username: guestConfig.credentials.username,\r\n password: guestConfig.credentials.password,\r\n }),\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return null;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n return mappers.parseGuestToken(rawResponse);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n validateToken,\r\n getTokenInfo,\r\n refreshToken,\r\n createGuestToken,\r\n };\r\n}\r\n\r\nexport type TokenValidation = ReturnType<typeof createTokenValidation>;\r\n","/**\r\n * Proxy Handlers\r\n * Request handling logic for different scenarios\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { InternalProxyConfig, TokenInfo } from '../shared/types';\r\nimport { HEADERS, TOKEN_TYPES } from '../shared/constants';\r\nimport type { TokenValidation } from './tokenValidation';\r\n\r\n/**\r\n * Extracts locale from pathname based on i18n config\r\n * e.g., /en/dashboard → 'en', /dashboard → defaultLocale or null\r\n */\r\nfunction extractLocale(pathname: string, i18n?: InternalProxyConfig['i18n']): string | null {\r\n if (!i18n?.enabled) return null;\r\n \r\n const locales = i18n.locales ?? [];\r\n const defaultLocale = i18n.defaultLocale;\r\n \r\n // Extract first path segment\r\n const segments = pathname.split('/').filter(Boolean);\r\n const firstSegment = segments[0];\r\n \r\n // Check if it's a valid locale\r\n if (firstSegment && locales.includes(firstSegment)) {\r\n return firstSegment;\r\n }\r\n \r\n // Return default locale if provided\r\n return defaultLocale ?? null;\r\n}\r\n\r\n/**\r\n * Creates proxy handlers\r\n */\r\nexport function createHandlers(\r\n config: InternalProxyConfig,\r\n validation: TokenValidation\r\n) {\r\n const { cookies, guestToken, access, i18n, _resolved } = config;\r\n const { cookieOptions } = _resolved;\r\n\r\n /**\r\n * Safely deletes a cookie only if it exists in the request.\r\n * Prevents empty-value cookies from being created when deleting non-existent cookies.\r\n */\r\n function safeDeleteCookie(req: NextRequest, response: NextResponse, cookieName: string): void {\r\n if (req.cookies.get(cookieName)?.value) {\r\n response.cookies.delete(cookieName);\r\n }\r\n }\r\n\r\n /**\r\n * Deletes all auth cookies from response (only if they exist)\r\n */\r\n function deleteAllAuthCookies(req: NextRequest, response: NextResponse): NextResponse {\r\n safeDeleteCookie(req, response, cookies.guest);\r\n safeDeleteCookie(req, response, cookies.user);\r\n return response;\r\n }\r\n\r\n /**\r\n * Creates a JSON error response\r\n */\r\n function jsonError(message: string, status = 500): NextResponse {\r\n return new NextResponse(\r\n JSON.stringify({ success: false, message }),\r\n { status, headers: { 'Content-Type': 'application/json' } }\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is an auth page\r\n */\r\n function isAuthPage(pathname: string): boolean {\r\n const authRoutes = access?.authRoutes ?? [];\r\n return authRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is a protected route\r\n */\r\n function isProtectedRoute(pathname: string): boolean {\r\n // If protectedByDefault is true, everything is protected except public/auth routes\r\n if (access?.protectedByDefault) {\r\n return !isPublicRoute(pathname) && !isAuthPage(pathname);\r\n }\r\n \r\n const protectedRoutes = access?.protectedRoutes ?? [];\r\n return protectedRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is explicitly public\r\n */\r\n function isPublicRoute(pathname: string): boolean {\r\n const publicRoutes = access?.publicRoutes ?? [];\r\n return publicRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if token type is allowed\r\n */\r\n function isTokenTypeAllowed(tokenType: string | null): boolean {\r\n const allowedTypes = access?.allowedTokenTypes;\r\n \r\n // If no restriction, all types allowed\r\n if (!allowedTypes || allowedTypes.length === 0) {\r\n return true;\r\n }\r\n \r\n return tokenType ? allowedTypes.includes(tokenType) : false;\r\n }\r\n\r\n /**\r\n * Handles request when no token is present\r\n */\r\n async function handleNoToken(\r\n req: NextRequest,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { origin } = req.nextUrl;\r\n \r\n // Try to create guest token\r\n if (guestToken?.enabled) {\r\n const guestAccessToken = await validation.createGuestToken();\r\n \r\n if (guestAccessToken) {\r\n let response: NextResponse;\r\n \r\n if (isApiRoute) {\r\n response = NextResponse.next();\r\n } else if (isProtectedRoute(req.nextUrl.pathname)) {\r\n // Redirect to login if protected route\r\n response = NextResponse.redirect(new URL('/login', origin));\r\n } else {\r\n response = NextResponse.next();\r\n }\r\n \r\n response.cookies.set(cookies.guest, guestAccessToken, {\r\n ...cookieOptions,\r\n maxAge: 3600, // 1 hour default for guest tokens\r\n });\r\n \r\n return response;\r\n }\r\n }\r\n \r\n // No guest token - just continue or redirect\r\n if (isApiRoute) {\r\n return jsonError('Token bulunamadı', 401);\r\n }\r\n \r\n if (isProtectedRoute(req.nextUrl.pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n \r\n return NextResponse.next();\r\n }\r\n\r\n /**\r\n * Handles token validation result\r\n */\r\n async function handleValidationResult(\r\n req: NextRequest,\r\n tokenInfo: TokenInfo,\r\n isUserToken: boolean,\r\n currentToken: string,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const { isValid, tokenType, userData } = tokenInfo;\r\n const isGuest = tokenType === TOKEN_TYPES.GUEST;\r\n\r\n // ===== TOKEN INVALID =====\r\n if (!isValid) {\r\n // Try refresh if user token\r\n if (isUserToken && currentToken) {\r\n const refreshResult = await validation.refreshToken(currentToken);\r\n \r\n if (refreshResult.success && refreshResult.newToken) {\r\n const newTokenInfo = await validation.getTokenInfo(refreshResult.newToken);\r\n \r\n if (newTokenInfo.isValid) {\r\n // Successful refresh\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (newTokenInfo.userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(newTokenInfo.userData));\r\n }\r\n requestHeaders.set(HEADERS.REFRESHED_TOKEN, refreshResult.newToken);\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n let response: NextResponse;\r\n \r\n if (isAuthPage(pathname)) {\r\n response = NextResponse.redirect(new URL('/', origin));\r\n } else {\r\n response = NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n response.cookies.set(cookies.user, refreshResult.newToken, {\r\n ...cookieOptions,\r\n maxAge: cookieOptions.maxAge,\r\n });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n\r\n return response;\r\n }\r\n }\r\n }\r\n\r\n // Refresh failed or no user token - handle as no token\r\n const response = await handleNoToken(req, isApiRoute);\r\n \r\n // Check if handleNoToken created a new guest token\r\n const hasNewGuestToken = response.cookies.get(cookies.guest)?.value;\r\n \r\n if (!hasNewGuestToken) {\r\n // No new guest token created - delete all auth cookies\r\n deleteAllAuthCookies(req, response);\r\n } else {\r\n // New guest token created - only delete the invalid user cookie\r\n safeDeleteCookie(req, response, cookies.user);\r\n }\r\n \r\n return response;\r\n }\r\n\r\n // ===== TOKEN VALID =====\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(userData));\r\n }\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n // Check if token type is allowed\r\n if (!isGuest && !isTokenTypeAllowed(tokenType)) {\r\n if (isApiRoute) {\r\n const response = jsonError('Bu işlem için yetkiniz yok', 403);\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n \r\n const response = NextResponse.redirect(new URL('/login', origin));\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n\r\n // Guest token handling\r\n if (isGuest) {\r\n if (isApiRoute) {\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // Protected routes require login\r\n if (isProtectedRoute(pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // User token - block auth pages\r\n if (isAuthPage(pathname)) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n\r\n // Normal access\r\n const response = NextResponse.next({ request: { headers: requestHeaders } });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n \r\n return response;\r\n }\r\n\r\n return {\r\n deleteAllAuthCookies,\r\n jsonError,\r\n isAuthPage,\r\n isProtectedRoute,\r\n isPublicRoute,\r\n isTokenTypeAllowed,\r\n handleNoToken,\r\n handleValidationResult,\r\n };\r\n}\r\n\r\nexport type Handlers = ReturnType<typeof createHandlers>;\r\n","/**\r\n * CSRF Protection\r\n * \r\n * Implements OWASP recommended CSRF protection:\r\n * - Fetch Metadata (Sec-Fetch-Site header) for modern browsers (98%+ coverage)\r\n * - Signed HMAC Double-Submit Cookie as fallback\r\n * \r\n * @see https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedCsrfConfig } from '../shared/types';\r\n\r\nexport interface CsrfValidationResult {\r\n valid: boolean;\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Creates CSRF validator functions\r\n */\r\nexport function createCsrfValidator(config: ResolvedCsrfConfig) {\r\n /**\r\n * Generate HMAC-signed CSRF token\r\n * Format: hmac.randomValue\r\n */\r\n async function generateToken(sessionId: string): Promise<string> {\r\n const randomValue = generateRandomValue();\r\n const hmac = await computeHmac(config.secret, sessionId, randomValue);\r\n return `${hmac}.${randomValue}`;\r\n }\r\n\r\n /**\r\n * Validate CSRF request using configured strategy\r\n */\r\n function validateRequest(req: NextRequest): CsrfValidationResult {\r\n const method = req.method.toUpperCase();\r\n \r\n // Skip safe methods\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n\r\n const strategy = config.strategy;\r\n \r\n // Fetch Metadata validation (primary, modern browsers)\r\n if (strategy === 'fetch-metadata' || strategy === 'both') {\r\n const fetchResult = validateFetchMetadata(req);\r\n \r\n if (strategy === 'fetch-metadata') {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata passes, we're good\r\n if (fetchResult.valid) {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata fails or not available, try double-submit\r\n if (fetchResult.reason === 'missing-headers') {\r\n // Fall through to double-submit\r\n } else {\r\n // Explicit cross-site rejection from Fetch Metadata\r\n return fetchResult;\r\n }\r\n }\r\n\r\n // Double-Submit Cookie validation (fallback)\r\n if (strategy === 'double-submit' || strategy === 'both') {\r\n return validateDoubleSubmit(req);\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Validate using Fetch Metadata headers (Sec-Fetch-Site)\r\n * @see https://web.dev/fetch-metadata/\r\n */\r\n function validateFetchMetadata(req: NextRequest): CsrfValidationResult {\r\n const secFetchSite = req.headers.get('sec-fetch-site');\r\n \r\n // No Fetch Metadata headers (older browser or stripped by proxy)\r\n if (!secFetchSite) {\r\n return { valid: false, reason: 'missing-headers' };\r\n }\r\n\r\n // Same-origin requests are always trusted\r\n if (secFetchSite === 'same-origin') {\r\n return { valid: true };\r\n }\r\n\r\n // None = direct navigation (bookmark, typed URL) - allow for GET\r\n if (secFetchSite === 'none') {\r\n const method = req.method.toUpperCase();\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n // Non-safe method from direct navigation is suspicious\r\n return { valid: false, reason: 'direct-navigation-unsafe-method' };\r\n }\r\n\r\n // Same-site: trust based on config\r\n if (secFetchSite === 'same-site') {\r\n if (config.trustSameSite) {\r\n return { valid: true };\r\n }\r\n // Conservative: don't trust same-site by default (subdomain takeover risk)\r\n return { valid: false, reason: 'same-site-not-trusted' };\r\n }\r\n\r\n // Cross-site: reject state-changing requests\r\n if (secFetchSite === 'cross-site') {\r\n return { valid: false, reason: 'cross-site-request' };\r\n }\r\n\r\n // Unknown value - be conservative\r\n return { valid: false, reason: 'unknown-sec-fetch-site' };\r\n }\r\n\r\n /**\r\n * Validate using Double-Submit Cookie pattern with HMAC\r\n */\r\n function validateDoubleSubmit(req: NextRequest): CsrfValidationResult {\r\n // Get token from cookie\r\n const cookieToken = req.cookies.get(config.cookieName)?.value;\r\n \r\n if (!cookieToken) {\r\n return { valid: false, reason: 'missing-cookie-token' };\r\n }\r\n\r\n // Get token from header (or form field)\r\n const headerToken = req.headers.get(config.headerName);\r\n \r\n if (!headerToken) {\r\n return { valid: false, reason: 'missing-header-token' };\r\n }\r\n\r\n // Tokens must match (constant-time comparison to prevent timing attacks)\r\n if (!constantTimeEqual(cookieToken, headerToken)) {\r\n return { valid: false, reason: 'token-mismatch' };\r\n }\r\n\r\n // Validate HMAC structure\r\n const parts = cookieToken.split('.');\r\n if (parts.length !== 2) {\r\n return { valid: false, reason: 'invalid-token-format' };\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Create response with CSRF cookie set\r\n */\r\n async function attachCsrfCookie(\r\n response: NextResponse, \r\n sessionId: string\r\n ): Promise<NextResponse> {\r\n const token = await generateToken(sessionId);\r\n \r\n response.cookies.set(config.cookieName, token, {\r\n httpOnly: false, // Must be readable by JS\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/',\r\n });\r\n\r\n return response;\r\n }\r\n\r\n return {\r\n validateRequest,\r\n generateToken,\r\n attachCsrfCookie,\r\n };\r\n}\r\n\r\n// ==================== Helper Functions ====================\r\n\r\n/**\r\n * Generate cryptographically random value\r\n */\r\nfunction generateRandomValue(): string {\r\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n return Array.from(array, b => b.toString(16).padStart(2, '0')).join('');\r\n }\r\n // Fallback (less secure, for edge cases)\r\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\r\n}\r\n\r\n/**\r\n * Compute HMAC-SHA256\r\n */\r\nasync function computeHmac(\r\n secret: string, \r\n sessionId: string, \r\n randomValue: string\r\n): Promise<string> {\r\n const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`;\r\n \r\n if (typeof crypto !== 'undefined' && crypto.subtle) {\r\n const encoder = new TextEncoder();\r\n const keyData = encoder.encode(secret);\r\n const messageData = encoder.encode(message);\r\n \r\n const key = await crypto.subtle.importKey(\r\n 'raw',\r\n keyData,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign']\r\n );\r\n \r\n const signature = await crypto.subtle.sign('HMAC', key, messageData);\r\n return Array.from(new Uint8Array(signature), b => \r\n b.toString(16).padStart(2, '0')\r\n ).join('');\r\n }\r\n \r\n // Fallback (less secure)\r\n let hash = 0;\r\n const str = secret + message;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = ((hash << 5) - hash) + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(16);\r\n}\r\n\r\n/**\r\n * Constant-time string comparison (prevents timing attacks)\r\n */\r\nfunction constantTimeEqual(a: string, b: string): boolean {\r\n if (a.length !== b.length) {\r\n return false;\r\n }\r\n \r\n let result = 0;\r\n for (let i = 0; i < a.length; i++) {\r\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\r\n }\r\n \r\n return result === 0;\r\n}\r\n\r\nexport type CsrfValidator = ReturnType<typeof createCsrfValidator>;\r\n","/**\r\n * Rate Limiting\r\n * \r\n * Implements token bucket algorithm for rate limiting.\r\n * In-memory store by default, designed for single-instance deployments.\r\n * \r\n * For horizontal scaling (multiple instances), use a custom store\r\n * with Redis or similar distributed cache.\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedRateLimitConfig } from '../shared/types';\r\n\r\ninterface RateLimitEntry {\r\n count: number;\r\n resetAt: number;\r\n}\r\n\r\nexport interface RateLimitResult {\r\n allowed: boolean;\r\n remaining: number;\r\n resetAt: number;\r\n limit: number;\r\n}\r\n\r\n/**\r\n * Creates a rate limiter with in-memory store\r\n */\r\nexport function createRateLimiter(config: ResolvedRateLimitConfig) {\r\n // In-memory store\r\n const store = new Map<string, RateLimitEntry>();\r\n \r\n // Cleanup expired entries periodically\r\n const cleanupInterval = setInterval(() => {\r\n const now = Date.now();\r\n for (const [key, entry] of store) {\r\n if (entry.resetAt <= now) {\r\n store.delete(key);\r\n }\r\n }\r\n }, config.windowMs);\r\n\r\n // Prevent memory leak in long-running processes\r\n if (typeof process !== 'undefined' && process.on) {\r\n process.on('beforeExit', () => clearInterval(cleanupInterval));\r\n }\r\n\r\n /**\r\n * Check if route should skip rate limiting\r\n */\r\n function shouldSkip(pathname: string): boolean {\r\n return config.skipRoutes.some(pattern => {\r\n // Simple glob matching\r\n if (pattern.endsWith('*')) {\r\n return pathname.startsWith(pattern.slice(0, -1));\r\n }\r\n if (pattern.endsWith('**')) {\r\n return pathname.startsWith(pattern.slice(0, -2));\r\n }\r\n return pathname === pattern;\r\n });\r\n }\r\n\r\n /**\r\n * Check rate limit for request\r\n */\r\n function check(req: NextRequest): RateLimitResult {\r\n const pathname = req.nextUrl.pathname;\r\n \r\n // Skip if route matches skip patterns\r\n if (shouldSkip(pathname)) {\r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests,\r\n resetAt: 0,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n const key = config.keyFn(req);\r\n const now = Date.now();\r\n \r\n let entry = store.get(key);\r\n \r\n // New window or expired\r\n if (!entry || entry.resetAt <= now) {\r\n entry = {\r\n count: 1,\r\n resetAt: now + config.windowMs,\r\n };\r\n store.set(key, entry);\r\n \r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests - 1,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n // Existing window\r\n entry.count++;\r\n \r\n const remaining = Math.max(0, config.maxRequests - entry.count);\r\n const allowed = entry.count <= config.maxRequests;\r\n \r\n return {\r\n allowed,\r\n remaining,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n /**\r\n * Apply rate limit headers to response\r\n */\r\n function applyHeaders(response: NextResponse, result: RateLimitResult): NextResponse {\r\n response.headers.set('X-RateLimit-Limit', result.limit.toString());\r\n response.headers.set('X-RateLimit-Remaining', result.remaining.toString());\r\n response.headers.set('X-RateLimit-Reset', Math.ceil(result.resetAt / 1000).toString());\r\n return response;\r\n }\r\n\r\n /**\r\n * Create rate limited response (429 Too Many Requests)\r\n */\r\n function createLimitedResponse(req: NextRequest, result: RateLimitResult): NextResponse {\r\n // User-provided handler\r\n if (config.onRateLimited) {\r\n const response = config.onRateLimited(req);\r\n return applyHeaders(response, result);\r\n }\r\n\r\n // Default response\r\n const response = NextResponse.json(\r\n {\r\n success: false,\r\n message: 'Too many requests. Please try again later.',\r\n retryAfter: Math.ceil((result.resetAt - Date.now()) / 1000),\r\n },\r\n { status: 429 }\r\n );\r\n\r\n response.headers.set('Retry-After', Math.ceil((result.resetAt - Date.now()) / 1000).toString());\r\n return applyHeaders(response, result);\r\n }\r\n\r\n /**\r\n * Reset rate limit for a key (useful for testing)\r\n */\r\n function reset(key: string): void {\r\n store.delete(key);\r\n }\r\n\r\n /**\r\n * Clear all rate limit entries\r\n */\r\n function clear(): void {\r\n store.clear();\r\n }\r\n\r\n /**\r\n * Get current store size (for monitoring)\r\n */\r\n function size(): number {\r\n return store.size;\r\n }\r\n\r\n return {\r\n check,\r\n applyHeaders,\r\n createLimitedResponse,\r\n shouldSkip,\r\n reset,\r\n clear,\r\n size,\r\n };\r\n}\r\n\r\nexport type RateLimiter = ReturnType<typeof createRateLimiter>;\r\n","/**\r\n * Audit Logging\r\n * \r\n * Event-based security audit logging system.\r\n * Emits events for authentication, access control, and security violations.\r\n */\r\n\r\nimport { NextRequest } from 'next/server';\r\nimport type { ResolvedAuditConfig, AuditEvent, AuditEventType } from '../shared/types';\r\n\r\n/**\r\n * Creates an audit logger instance\r\n */\r\nexport function createAuditLogger(config: ResolvedAuditConfig) {\r\n /**\r\n * Check if event type is enabled\r\n */\r\n function isEnabled(type: AuditEventType): boolean {\r\n return config.enabled && config.events.includes(type);\r\n }\r\n\r\n /**\r\n * Extract IP address from request\r\n */\r\n function getIp(req: NextRequest): string | null {\r\n return (\r\n req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||\r\n req.headers.get('x-real-ip') ||\r\n null\r\n );\r\n }\r\n\r\n /**\r\n * Emit an audit event\r\n */\r\n async function emit(\r\n type: AuditEventType,\r\n req: NextRequest,\r\n options: {\r\n success: boolean;\r\n userId?: string;\r\n metadata?: Record<string, unknown>;\r\n }\r\n ): Promise<void> {\r\n if (!isEnabled(type)) {\r\n return;\r\n }\r\n\r\n const event: AuditEvent = {\r\n type,\r\n timestamp: new Date(),\r\n ip: getIp(req),\r\n userId: options.userId,\r\n path: req.nextUrl.pathname,\r\n method: req.method,\r\n success: options.success,\r\n metadata: options.metadata,\r\n };\r\n\r\n // Call user's logger\r\n if (config.logger) {\r\n try {\r\n await config.logger(event);\r\n } catch (error) {\r\n // Silently fail - don't break the request flow for logging errors\r\n console.error('[next-api-layer] Audit logger error:', error);\r\n }\r\n }\r\n }\r\n\r\n // ==================== Convenience Methods ====================\r\n\r\n /**\r\n * Log successful authentication\r\n */\r\n function authSuccess(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:success', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log failed authentication\r\n */\r\n function authFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log token refresh\r\n */\r\n function authRefresh(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:refresh', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log guest token creation\r\n */\r\n function authGuest(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:guest', req, { success: true, metadata });\r\n }\r\n\r\n /**\r\n * Log access denied\r\n */\r\n function accessDenied(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('access:denied', req, { success: false, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log CSRF validation failure\r\n */\r\n function csrfFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('csrf:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log rate limit exceeded\r\n */\r\n function rateLimitExceeded(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('rateLimit:exceeded', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log general error\r\n */\r\n function error(req: NextRequest, err: Error, metadata?: Record<string, unknown>) {\r\n return emit('error', req, { \r\n success: false, \r\n metadata: { \r\n ...metadata, \r\n error: err.message,\r\n stack: err.stack,\r\n } \r\n });\r\n }\r\n\r\n return {\r\n emit,\r\n isEnabled,\r\n // Convenience methods\r\n authSuccess,\r\n authFail,\r\n authRefresh,\r\n authGuest,\r\n accessDenied,\r\n csrfFail,\r\n rateLimitExceeded,\r\n error,\r\n };\r\n}\r\n\r\nexport type AuditLogger = ReturnType<typeof createAuditLogger>;\r\n","/**\r\n * createAuthProxy\r\n * Factory function to create Next.js middleware for external JWT authentication\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { AuthProxyConfig, AuthResult } from '../shared/types';\r\nimport { resolveProxyConfig } from '../shared/config';\r\nimport { createTokenValidation } from './tokenValidation';\r\nimport { createHandlers } from './handlers';\r\nimport { createCsrfValidator } from './csrf';\r\nimport { createRateLimiter } from './rateLimit';\r\nimport { createAuditLogger } from './audit';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\n/**\r\n * Merge two NextResponse objects, preserving headers and cookies from both\r\n * Target response takes priority for conflicts\r\n */\r\nfunction mergeResponses(source: NextResponse, target: NextResponse): NextResponse {\r\n // Copy critical headers from source to target (if not already set)\r\n const criticalHeaders = [HEADERS.LOCALE, HEADERS.AUTH_USER, HEADERS.REFRESHED_TOKEN];\r\n \r\n for (const header of criticalHeaders) {\r\n const value = source.headers.get(header);\r\n if (value && !target.headers.has(header)) {\r\n target.headers.set(header, value);\r\n }\r\n }\r\n \r\n // Copy cookies from source to target (if not already set)\r\n source.cookies.getAll().forEach(cookie => {\r\n if (!target.cookies.get(cookie.name)) {\r\n target.cookies.set(cookie.name, cookie.value);\r\n }\r\n });\r\n \r\n return target;\r\n}\r\n\r\n/**\r\n * Creates an authentication proxy middleware for Next.js\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts\r\n * import { createAuthProxy } from 'next-api-layer';\r\n * \r\n * const authProxy = createAuthProxy({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * cookies: {\r\n * user: 'userAuthToken',\r\n * guest: 'guestAuthToken',\r\n * },\r\n * guestToken: {\r\n * enabled: true,\r\n * credentials: {\r\n * username: process.env.GUEST_USERNAME!,\r\n * password: process.env.GUEST_PASSWORD!,\r\n * },\r\n * },\r\n * access: {\r\n * protectedRoutes: ['/dashboard', '/profile'],\r\n * authRoutes: ['/login', '/register'],\r\n * },\r\n * // Security features\r\n * csrf: { enabled: true },\r\n * rateLimit: { enabled: true, maxRequests: 100 },\r\n * audit: { \r\n * enabled: true, \r\n * logger: (event) => console.log('[AUDIT]', event) \r\n * },\r\n * });\r\n * \r\n * export default authProxy;\r\n * \r\n * export const config = {\r\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\r\n * };\r\n * ```\r\n */\r\nexport function createAuthProxy(userConfig: AuthProxyConfig) {\r\n // Resolve config with defaults\r\n const config = resolveProxyConfig(userConfig);\r\n \r\n // Create validation functions\r\n const validation = createTokenValidation(config);\r\n \r\n // Create handlers\r\n const handlers = createHandlers(config, validation);\r\n\r\n // Create security modules\r\n const csrf = createCsrfValidator(config._resolved.csrf);\r\n const rateLimiter = createRateLimiter(config._resolved.rateLimit);\r\n const audit = createAuditLogger(config._resolved.audit);\r\n\r\n /**\r\n * The middleware function\r\n */\r\n async function authProxy(req: NextRequest): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const isApiRoute = pathname.startsWith('/api');\r\n\r\n // ============ Rate Limiting ============\r\n // Check early to protect against DoS\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n \r\n if (!rateLimitResult.allowed) {\r\n await audit.rateLimitExceeded(req, { \r\n limit: rateLimitResult.limit,\r\n resetAt: rateLimitResult.resetAt,\r\n });\r\n return rateLimiter.createLimitedResponse(req, rateLimitResult);\r\n }\r\n }\r\n\r\n // ============ CSRF Protection ============\r\n // Check before any state-changing operations\r\n if (config._resolved.csrf.enabled) {\r\n const csrfResult = csrf.validateRequest(req);\r\n \r\n if (!csrfResult.valid) {\r\n await audit.csrfFail(req, { reason: csrfResult.reason });\r\n return NextResponse.json(\r\n { success: false, message: 'CSRF validation failed' },\r\n { status: 403 }\r\n );\r\n }\r\n }\r\n\r\n // ============ Block Browser API Access ============\r\n // Prevents direct browser access to API routes (when Accept: text/html)\r\n if (config.blockBrowserApiAccess && isApiRoute) {\r\n const acceptHeader = req.headers.get('accept') || '';\r\n if (acceptHeader.includes('text/html')) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n }\r\n\r\n // ============ beforeAuth Hook ============\r\n // Allows user to handle request before auth validation\r\n if (config.beforeAuth) {\r\n const beforeResult = await config.beforeAuth(req);\r\n if (beforeResult) {\r\n return beforeResult; // User handled the request\r\n }\r\n }\r\n\r\n // Skip excluded paths\r\n const excludedPaths = config.excludedPaths ?? [];\r\n if (excludedPaths.some(path => pathname.startsWith(path))) {\r\n return applyMiddlewaresAndHooks(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Skip auth API endpoints (they handle their own auth)\r\n const authApiPaths = [\r\n '/api/auth/login',\r\n '/api/auth/logout',\r\n '/api/auth/me',\r\n '/api/auth/refresh',\r\n '/api/auth/register',\r\n ];\r\n \r\n if (authApiPaths.includes(pathname)) {\r\n return applyMiddlewaresAndHooks(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Get tokens from cookies\r\n const userToken = req.cookies?.get(config.cookies.user)?.value;\r\n const guestToken = req.cookies?.get(config.cookies.guest)?.value;\r\n const currentToken = userToken || guestToken;\r\n const isUserToken = !!userToken;\r\n\r\n // No token - handle appropriately\r\n if (!currentToken) {\r\n await audit.authFail(req, { reason: 'no-token' });\r\n const response = await handlers.handleNoToken(req, isApiRoute);\r\n return applyMiddlewaresAndHooks(req, response, { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Validate token\r\n const tokenInfo = await validation.getTokenInfo(currentToken);\r\n \r\n // Build auth result for afterAuth hook\r\n const authResult: AuthResult = {\r\n isAuthenticated: tokenInfo.isValid && tokenInfo.tokenType !== 'guest',\r\n isGuest: tokenInfo.isValid && tokenInfo.tokenType === 'guest',\r\n tokenType: tokenInfo.tokenType,\r\n user: tokenInfo.userData,\r\n };\r\n\r\n // Audit logging based on validation result \r\n if (tokenInfo.isValid) {\r\n if (tokenInfo.tokenType === 'guest') {\r\n await audit.authGuest(req);\r\n } else {\r\n const userId = tokenInfo.userData?.id?.toString();\r\n await audit.authSuccess(req, userId, { tokenType: tokenInfo.tokenType });\r\n }\r\n } else {\r\n await audit.authFail(req, { reason: 'invalid-token' });\r\n }\r\n\r\n // Handle validation result\r\n const response = await handlers.handleValidationResult(\r\n req,\r\n tokenInfo,\r\n isUserToken,\r\n currentToken,\r\n isApiRoute\r\n );\r\n \r\n // Apply CSRF cookie if enabled (for authenticated requests)\r\n let finalResponse = await applyMiddlewaresAndHooks(req, response, authResult);\r\n \r\n if (config._resolved.csrf.enabled && authResult.isAuthenticated) {\r\n const sessionId = tokenInfo.userData?.id?.toString() || currentToken.slice(0, 32);\r\n finalResponse = await csrf.attachCsrfCookie(finalResponse, sessionId);\r\n }\r\n\r\n // Apply rate limit headers\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n finalResponse = rateLimiter.applyHeaders(finalResponse, rateLimitResult);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n \r\n /**\r\n * Helper to apply i18n middleware and afterAuth hook\r\n * Merges responses to preserve critical headers (x-locale, x-auth-user, etc.)\r\n */\r\n async function applyMiddlewaresAndHooks(req: NextRequest, response: NextResponse, authResult: AuthResult): Promise<NextResponse> {\r\n let finalResponse = response;\r\n \r\n // Apply i18n middleware if configured\r\n if (config.i18n?.middleware) {\r\n const intlResponse = await Promise.resolve(config.i18n.middleware(req));\r\n // Merge library's response headers into i18n response\r\n finalResponse = mergeResponses(response, intlResponse);\r\n }\r\n \r\n // Apply afterAuth hook if configured\r\n if (config.afterAuth) {\r\n const hookResponse = await config.afterAuth(req, finalResponse, authResult);\r\n // Merge previous response headers into hook's response\r\n finalResponse = mergeResponses(finalResponse, hookResponse);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n\r\n // Attach instances for debugging/testing\r\n authProxy.config = config;\r\n authProxy.csrf = csrf;\r\n authProxy.rateLimiter = rateLimiter;\r\n authProxy.audit = audit;\r\n\r\n return authProxy;\r\n}\r\n\r\nexport type AuthProxy = ReturnType<typeof createAuthProxy>;\r\n\r\n","/**\r\n * createProxyHandler\r\n * Factory function to create Next.js API route handlers that proxy requests to backend\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\nexport interface ProxyHandlerConfig {\r\n /** Base URL of the backend API */\r\n apiBaseUrl: string;\r\n /** Cookie name for user auth token */\r\n userCookieName?: string;\r\n /** Cookie name for guest auth token */\r\n guestCookieName?: string;\r\n /** \r\n * Default behavior for auth - if true, all requests skip auth by default\r\n * Individual requests can override with X-Skip-Auth header\r\n */\r\n skipAuthByDefault?: boolean;\r\n /**\r\n * Public endpoints that should never include auth token (glob patterns)\r\n * e.g., ['news/*', 'public/**', 'categories']\r\n */\r\n publicEndpoints?: string[];\r\n /** Headers to forward from client request */\r\n forwardHeaders?: string[];\r\n /** Headers to exclude from forwarding */\r\n excludeHeaders?: string[];\r\n /** Custom request transformer */\r\n transformRequest?: (req: NextRequest, headers: Headers) => Headers | Promise<Headers>;\r\n /** Custom response transformer */\r\n transformResponse?: (response: Response) => Response | Promise<Response>;\r\n}\r\n\r\n/**\r\n * Check if endpoint matches any patterns (glob support)\r\n */\r\nfunction matchesPattern(endpoint: string, patterns: string[]): boolean {\r\n if (!patterns || patterns.length === 0) return false;\r\n \r\n // Normalize endpoint\r\n const normalizedEndpoint = endpoint\r\n .replace(/^\\/api\\//, '')\r\n .replace(/^\\//, '');\r\n \r\n return patterns.some(pattern => {\r\n if (pattern === normalizedEndpoint) return true;\r\n \r\n if (pattern.includes('*')) {\r\n const regexPattern = pattern\r\n .replace(/\\*\\*/g, '<<<DOUBLE>>>')\r\n .replace(/\\*/g, '[^/]+')\r\n .replace(/<<<DOUBLE>>>/g, '.+');\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(normalizedEndpoint);\r\n }\r\n \r\n return false;\r\n });\r\n}\r\n\r\n/**\r\n * Creates a proxy handler for Next.js API routes\r\n * \r\n * @example\r\n * ```ts\r\n * // app/api/[...path]/route.ts\r\n * import { createProxyHandler } from 'next-api-layer';\r\n * \r\n * const handler = createProxyHandler({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * userCookieName: 'auth_token',\r\n * guestCookieName: 'guest_token',\r\n * publicEndpoints: ['news/*', 'categories', 'public/**'],\r\n * });\r\n * \r\n * export const GET = handler;\r\n * export const POST = handler;\r\n * export const PUT = handler;\r\n * export const PATCH = handler;\r\n * export const DELETE = handler;\r\n * ```\r\n */\r\nexport function createProxyHandler(config: ProxyHandlerConfig) {\r\n const {\r\n apiBaseUrl,\r\n userCookieName = 'auth_token',\r\n guestCookieName = 'guest_token',\r\n skipAuthByDefault = false,\r\n publicEndpoints = [],\r\n forwardHeaders = ['content-type', 'accept', 'accept-language', 'x-requested-with'],\r\n excludeHeaders = ['host', 'connection', 'cookie'],\r\n transformRequest,\r\n transformResponse,\r\n } = config;\r\n\r\n // Normalize base URL (remove trailing slash)\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n /**\r\n * Determine if auth should be skipped for this request\r\n */\r\n function shouldSkipAuth(req: NextRequest, endpoint: string): boolean {\r\n // Check X-Skip-Auth header from client\r\n const skipAuthHeader = req.headers.get(HEADERS.SKIP_AUTH);\r\n if (skipAuthHeader === 'true') {\r\n return true;\r\n }\r\n \r\n // Check if endpoint matches public patterns\r\n if (matchesPattern(endpoint, publicEndpoints)) {\r\n return true;\r\n }\r\n \r\n // Default behavior\r\n return skipAuthByDefault;\r\n }\r\n\r\n /**\r\n * The proxy handler function\r\n */\r\n async function handler(req: NextRequest): Promise<NextResponse> {\r\n try {\r\n // Extract endpoint from URL path (remove /api/ prefix)\r\n const url = new URL(req.url);\r\n const endpoint = url.pathname.replace(/^\\/api\\/?/, '');\r\n \r\n // Build backend URL\r\n const backendUrl = new URL(`${baseUrl}/${endpoint}`);\r\n backendUrl.search = url.search; // Forward query params\r\n\r\n // Build headers for backend request\r\n const headers = new Headers();\r\n \r\n // Forward allowed headers from original request\r\n forwardHeaders.forEach(headerName => {\r\n const value = req.headers.get(headerName);\r\n if (value) {\r\n headers.set(headerName, value);\r\n }\r\n });\r\n\r\n // Forward all headers except excluded ones\r\n req.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n if (!excludeHeaders.includes(lowerKey) && !headers.has(key)) {\r\n headers.set(key, value);\r\n }\r\n });\r\n\r\n // Add Authorization header if auth is not skipped\r\n if (!shouldSkipAuth(req, endpoint)) {\r\n const userToken = req.cookies.get(userCookieName)?.value;\r\n const guestToken = req.cookies.get(guestCookieName)?.value;\r\n const token = userToken || guestToken;\r\n \r\n if (token) {\r\n headers.set(HEADERS.AUTHORIZATION, `Bearer ${token}`);\r\n }\r\n }\r\n\r\n // Remove skip-auth header (internal use only)\r\n headers.delete(HEADERS.SKIP_AUTH);\r\n\r\n // Allow custom request transformation\r\n const finalHeaders = transformRequest \r\n ? await transformRequest(req, headers) \r\n : headers;\r\n\r\n // Get request body if present\r\n let body: BodyInit | null = null;\r\n if (req.method !== 'GET' && req.method !== 'HEAD') {\r\n const contentType = req.headers.get('content-type') || '';\r\n \r\n if (contentType.includes('application/json')) {\r\n body = await req.text();\r\n } else if (contentType.includes('multipart/form-data')) {\r\n body = await req.formData();\r\n } else {\r\n body = await req.text();\r\n }\r\n }\r\n\r\n // Make request to backend\r\n const backendResponse = await fetch(backendUrl.toString(), {\r\n method: req.method,\r\n headers: finalHeaders,\r\n body,\r\n });\r\n\r\n // Get response body\r\n const contentType = backendResponse.headers.get('content-type') || '';\r\n let responseBody: ArrayBuffer | string;\r\n \r\n if (contentType.includes('application/json')) {\r\n responseBody = await backendResponse.text();\r\n } else {\r\n responseBody = await backendResponse.arrayBuffer();\r\n }\r\n\r\n // Build response headers (forward relevant ones)\r\n const responseHeaders = new Headers();\r\n backendResponse.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n // Skip hop-by-hop headers\r\n if (!['transfer-encoding', 'connection', 'keep-alive'].includes(lowerKey)) {\r\n responseHeaders.set(key, value);\r\n }\r\n });\r\n\r\n // Create response\r\n let response = new NextResponse(responseBody, {\r\n status: backendResponse.status,\r\n statusText: backendResponse.statusText,\r\n headers: responseHeaders,\r\n });\r\n\r\n // Allow custom response transformation\r\n if (transformResponse) {\r\n response = await transformResponse(response) as NextResponse;\r\n }\r\n\r\n return response;\r\n } catch (error) {\r\n console.error('[Proxy Error]', error);\r\n \r\n return NextResponse.json(\r\n { \r\n success: false, \r\n message: 'Proxy error: Unable to connect to backend',\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n },\r\n { status: 502 }\r\n );\r\n }\r\n }\r\n\r\n // Attach config for debugging\r\n handler.config = config;\r\n\r\n return handler;\r\n}\r\n\r\nexport type ProxyHandler = ReturnType<typeof createProxyHandler>;\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/config.ts","../src/proxy/tokenValidation.ts","../src/proxy/handlers.ts","../src/proxy/csrf.ts","../src/proxy/rateLimit.ts","../src/proxy/audit.ts","../src/proxy/createAuthProxy.ts","../src/proxy/createProxyHandler.ts"],"names":["generateCsrfSecret","defaultRateLimitKeyFn","req","resolveProxyConfig","config","apiBaseUrl","cookieOptions","DEFAULT_COOKIE_OPTIONS","endpoints","DEFAULT_ENDPOINTS","csrf","DEFAULT_CSRF_CONFIG","rateLimit","DEFAULT_RATE_LIMIT_CONFIG","audit","DEFAULT_AUDIT_CONFIG","defaultMappers","response","res","createTokenValidation","_resolved","responseMappers","mappers","validateToken","token","invalidResult","rawResponse","parsed","getTokenInfo","refreshToken","oldToken","newToken","createGuestToken","guestConfig","extractLocale","pathname","i18n","locales","defaultLocale","firstSegment","stripLocale","segments","createHandlers","validation","cookies","guestToken","access","safeDeleteCookie","cookieName","deleteAllAuthCookies","jsonError","message","status","NextResponse","isAuthPage","cleanPath","route","isProtectedRoute","isPublicRoute","isTokenTypeAllowed","tokenType","allowedTypes","handleNoToken","isApiRoute","origin","guestAccessToken","handleValidationResult","tokenInfo","isUserToken","currentToken","isValid","userData","isGuest","TOKEN_TYPES","refreshResult","newTokenInfo","requestHeaders","HEADERS","locale","createCsrfValidator","generateToken","sessionId","randomValue","generateRandomValue","computeHmac","validateRequest","method","strategy","fetchResult","validateFetchMetadata","validateDoubleSubmit","secFetchSite","cookieToken","headerToken","constantTimeEqual","attachCsrfCookie","array","b","secret","encoder","keyData","messageData","key","signature","hash","str","i","char","a","result","createRateLimiter","store","cleanupInterval","now","entry","shouldSkip","pattern","check","remaining","applyHeaders","createLimitedResponse","reset","clear","size","createAuditLogger","isEnabled","type","getIp","emit","options","event","error","authSuccess","userId","metadata","authFail","authRefresh","authGuest","accessDenied","csrfFail","rateLimitExceeded","err","mergeResponses","source","target","criticalHeaders","header","value","cookie","createAuthProxy","userConfig","handlers","rateLimiter","authProxy","rateLimitResult","csrfResult","beforeResult","path","applyMiddlewaresAndHooks","userToken","authResult","finalResponse","intlResponse","hookResponse","matchesPattern","endpoint","patterns","normalizedEndpoint","regexPattern","createProxyHandler","userCookieName","guestCookieName","skipAuthByDefault","publicEndpoints","forwardHeaders","excludeHeaders","transformRequest","transformResponse","baseUrl","shouldSkipAuth","handler","url","backendUrl","headers","headerName","lowerKey","finalHeaders","body","contentType","backendResponse","responseBody","responseHeaders"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Config Resolver\r\n * Resolves and validates configuration with defaults\r\n */\r\n\r\nimport type { NextRequest } from 'next/server';\r\nimport type {\r\n AuthProxyConfig,\r\n InternalProxyConfig,\r\n ApiClientConfig,\r\n ResolvedCookieOptions,\r\n EndpointConfig,\r\n ResolvedCsrfConfig,\r\n ResolvedRateLimitConfig,\r\n ResolvedAuditConfig,\r\n AuditEventType,\r\n} from './types';\r\n\r\nimport {\r\n DEFAULT_COOKIE_OPTIONS,\r\n DEFAULT_ENDPOINTS,\r\n DEFAULT_CSRF_CONFIG,\r\n DEFAULT_RATE_LIMIT_CONFIG,\r\n DEFAULT_AUDIT_CONFIG,\r\n} from './constants';\r\n\r\n/**\r\n * Generates a random secret for CSRF HMAC signing\r\n */\r\nfunction generateCsrfSecret(): string {\r\n // Use crypto if available (Node.js)\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID() + crypto.randomUUID();\r\n }\r\n // Fallback: timestamp + random\r\n return `${Date.now()}-${Math.random().toString(36).substring(2)}`;\r\n}\r\n\r\n/**\r\n * Default rate limit key function (IP-based)\r\n */\r\nfunction defaultRateLimitKeyFn(req: NextRequest): string {\r\n const forwarded = req.headers.get('x-forwarded-for');\r\n const ip = forwarded?.split(',')[0]?.trim() || \r\n req.headers.get('x-real-ip') || \r\n 'unknown';\r\n return `rl:${ip}`;\r\n}\r\n\r\n/**\r\n * Resolves proxy configuration with defaults\r\n */\r\nexport function resolveProxyConfig(config: AuthProxyConfig): InternalProxyConfig {\r\n // Validate required fields\r\n if (!config.apiBaseUrl) {\r\n throw new Error('next-api-layer: apiBaseUrl is required');\r\n }\r\n \r\n if (!config.cookies?.user || !config.cookies?.guest) {\r\n throw new Error('next-api-layer: cookies.user and cookies.guest are required');\r\n }\r\n\r\n // Ensure apiBaseUrl ends with /\r\n const apiBaseUrl = config.apiBaseUrl.endsWith('/')\r\n ? config.apiBaseUrl\r\n : `${config.apiBaseUrl}/`;\r\n\r\n // Resolve cookie options\r\n const cookieOptions: ResolvedCookieOptions = {\r\n ...DEFAULT_COOKIE_OPTIONS,\r\n ...config.cookies.options,\r\n };\r\n\r\n // Resolve endpoints\r\n const endpoints: Required<EndpointConfig> = {\r\n ...DEFAULT_ENDPOINTS,\r\n ...config.endpoints,\r\n };\r\n\r\n // Resolve CSRF config\r\n const csrf: ResolvedCsrfConfig = {\r\n enabled: config.csrf?.enabled ?? false,\r\n strategy: config.csrf?.strategy ?? DEFAULT_CSRF_CONFIG.strategy,\r\n secret: config.csrf?.secret ?? generateCsrfSecret(),\r\n cookieName: config.csrf?.cookieName ?? DEFAULT_CSRF_CONFIG.cookieName,\r\n headerName: config.csrf?.headerName ?? DEFAULT_CSRF_CONFIG.headerName,\r\n ignoreMethods: config.csrf?.ignoreMethods ?? DEFAULT_CSRF_CONFIG.ignoreMethods,\r\n trustSameSite: config.csrf?.trustSameSite ?? DEFAULT_CSRF_CONFIG.trustSameSite,\r\n };\r\n\r\n // Resolve rate limit config\r\n const rateLimit: ResolvedRateLimitConfig = {\r\n enabled: config.rateLimit?.enabled ?? false,\r\n windowMs: config.rateLimit?.windowMs ?? DEFAULT_RATE_LIMIT_CONFIG.windowMs,\r\n maxRequests: config.rateLimit?.maxRequests ?? DEFAULT_RATE_LIMIT_CONFIG.maxRequests,\r\n keyFn: config.rateLimit?.keyFn ?? defaultRateLimitKeyFn,\r\n skipRoutes: config.rateLimit?.skipRoutes ?? DEFAULT_RATE_LIMIT_CONFIG.skipRoutes,\r\n onRateLimited: config.rateLimit?.onRateLimited,\r\n };\r\n\r\n // Resolve audit config\r\n const audit: ResolvedAuditConfig = {\r\n enabled: config.audit?.enabled ?? false,\r\n events: config.audit?.events ?? [...DEFAULT_AUDIT_CONFIG.events] as AuditEventType[],\r\n logger: config.audit?.logger,\r\n };\r\n\r\n return {\r\n ...config,\r\n apiBaseUrl,\r\n _resolved: {\r\n cookieOptions,\r\n endpoints,\r\n csrf,\r\n rateLimit,\r\n audit,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Resolves API client configuration with defaults\r\n */\r\nexport function resolveApiClientConfig(config: ApiClientConfig = {}) {\r\n return {\r\n sanitization: {\r\n enabled: config.sanitization?.enabled ?? true,\r\n allowedTags: config.sanitization?.allowedTags,\r\n skipFields: config.sanitization?.skipFields ?? [],\r\n skipEndpoints: config.sanitization?.skipEndpoints ?? [],\r\n },\r\n i18n: {\r\n enabled: config.i18n?.enabled ?? false,\r\n paramName: config.i18n?.paramName ?? 'lang',\r\n locales: config.i18n?.locales ?? [],\r\n defaultLocale: config.i18n?.defaultLocale ?? 'en',\r\n },\r\n auth: {\r\n skipByDefault: config.auth?.skipByDefault ?? false,\r\n publicEndpoints: config.auth?.publicEndpoints ?? [],\r\n },\r\n methodSpoofing: config.methodSpoofing ?? false,\r\n errorMessages: {\r\n noToken: config.errorMessages?.noToken ?? 'Token bulunamadı.',\r\n connectionError: config.errorMessages?.connectionError ?? 'Bağlantı hatası oluştu.',\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Type guard to check if a value is defined\r\n */\r\nexport function isDefined<T>(value: T | undefined | null): value is T {\r\n return value !== undefined && value !== null;\r\n}\r\n","/**\r\n * Token Validation\r\n * Handles token validation and refresh with backend API\r\n */\r\n\r\nimport type { \r\n TokenInfo, \r\n RefreshResult, \r\n AuthMeResponse, \r\n GuestTokenResponse,\r\n InternalProxyConfig,\r\n ResponseMappers,\r\n} from '../shared/types';\r\n\r\n/**\r\n * Default response parsers (standard format)\r\n */\r\nconst defaultMappers: Required<ResponseMappers> = {\r\n // Default: { success: true, data: { type, exp, ...user } }\r\n parseAuthMe: (response: unknown): TokenInfo | null => {\r\n const res = response as AuthMeResponse | null;\r\n if (!res?.success || !res?.data) {\r\n return null;\r\n }\r\n return {\r\n isValid: true,\r\n tokenType: res.data.type || 'user',\r\n exp: res.data.exp || null,\r\n userData: res.data,\r\n };\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseRefreshToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.success && res?.data?.accessToken ? res.data.accessToken : null;\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseGuestToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.data?.accessToken || null;\r\n },\r\n};\r\n\r\n/**\r\n * Creates token validation functions\r\n */\r\nexport function createTokenValidation(\r\n config: InternalProxyConfig\r\n) {\r\n const { apiBaseUrl, _resolved, responseMappers } = config;\r\n const { endpoints } = _resolved;\r\n \r\n // Merge custom mappers with defaults\r\n const mappers: Required<ResponseMappers> = {\r\n parseAuthMe: responseMappers?.parseAuthMe || defaultMappers.parseAuthMe,\r\n parseRefreshToken: responseMappers?.parseRefreshToken || defaultMappers.parseRefreshToken,\r\n parseGuestToken: responseMappers?.parseGuestToken || defaultMappers.parseGuestToken,\r\n };\r\n\r\n /**\r\n * Validates a token against the backend\r\n */\r\n async function validateToken(token: string): Promise<TokenInfo> {\r\n const invalidResult: TokenInfo = { isValid: false, tokenType: null, exp: null, userData: null };\r\n \r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.validate}`, {\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return invalidResult;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const parsed = mappers.parseAuthMe(rawResponse);\r\n \r\n if (!parsed || !parsed.isValid) {\r\n return invalidResult;\r\n }\r\n\r\n return parsed;\r\n } catch {\r\n return invalidResult;\r\n }\r\n }\r\n\r\n /**\r\n * Gets token info (validates with backend)\r\n */\r\n async function getTokenInfo(token: string): Promise<TokenInfo> {\r\n return validateToken(token);\r\n }\r\n\r\n /**\r\n * Refreshes a token\r\n */\r\n async function refreshToken(oldToken: string): Promise<RefreshResult> {\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.refresh}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Authorization': `Bearer ${oldToken}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const newToken = mappers.parseRefreshToken(rawResponse);\r\n\r\n if (!newToken) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n return { success: true, newToken };\r\n } catch {\r\n return { success: false, newToken: null };\r\n }\r\n }\r\n\r\n /**\r\n * Creates a guest token\r\n */\r\n async function createGuestToken(): Promise<string | null> {\r\n const guestConfig = config.guestToken;\r\n \r\n if (!guestConfig?.enabled || !guestConfig.credentials) {\r\n return null;\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.guest}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n username: guestConfig.credentials.username,\r\n password: guestConfig.credentials.password,\r\n }),\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return null;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n return mappers.parseGuestToken(rawResponse);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n validateToken,\r\n getTokenInfo,\r\n refreshToken,\r\n createGuestToken,\r\n };\r\n}\r\n\r\nexport type TokenValidation = ReturnType<typeof createTokenValidation>;\r\n","/**\r\n * Proxy Handlers\r\n * Request handling logic for different scenarios\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { InternalProxyConfig, TokenInfo } from '../shared/types';\r\nimport { HEADERS, TOKEN_TYPES } from '../shared/constants';\r\nimport type { TokenValidation } from './tokenValidation';\r\n\r\n/**\r\n * Extracts locale from pathname based on i18n config\r\n * e.g., /en/dashboard → 'en', /dashboard → defaultLocale or null\r\n */\r\nexport function extractLocale(pathname: string, i18n?: InternalProxyConfig['i18n']): string | null {\r\n if (!i18n?.enabled) return null;\r\n \r\n const locales = i18n.locales ?? [];\r\n const defaultLocale = i18n.defaultLocale;\r\n \r\n // Extract first path segment\r\n const segments = pathname.split('/').filter(Boolean);\r\n const firstSegment = segments[0];\r\n \r\n // Check if it's a valid locale\r\n if (firstSegment && locales.includes(firstSegment)) {\r\n return firstSegment;\r\n }\r\n \r\n // Return default locale if provided\r\n return defaultLocale ?? null;\r\n}\r\n\r\n/**\r\n * Strips locale prefix from pathname for route matching\r\n * e.g., /tr/giris → /giris, /en/dashboard → /dashboard\r\n */\r\nexport function stripLocale(pathname: string, i18n?: InternalProxyConfig['i18n']): string {\r\n if (!i18n?.enabled) return pathname;\r\n \r\n const locales = i18n.locales ?? [];\r\n const segments = pathname.split('/').filter(Boolean);\r\n const firstSegment = segments[0];\r\n \r\n // If first segment is a valid locale, strip it\r\n if (firstSegment && locales.includes(firstSegment)) {\r\n const strippedPath = '/' + segments.slice(1).join('/');\r\n return strippedPath || '/';\r\n }\r\n \r\n return pathname;\r\n}\r\n\r\n/**\r\n * Creates proxy handlers\r\n */\r\nexport function createHandlers(\r\n config: InternalProxyConfig,\r\n validation: TokenValidation\r\n) {\r\n const { cookies, guestToken, access, i18n, _resolved } = config;\r\n const { cookieOptions } = _resolved;\r\n\r\n /**\r\n * Safely deletes a cookie only if it exists in the request.\r\n * Prevents empty-value cookies from being created when deleting non-existent cookies.\r\n */\r\n function safeDeleteCookie(req: NextRequest, response: NextResponse, cookieName: string): void {\r\n if (req.cookies.get(cookieName)?.value) {\r\n response.cookies.delete(cookieName);\r\n }\r\n }\r\n\r\n /**\r\n * Deletes all auth cookies from response (only if they exist)\r\n */\r\n function deleteAllAuthCookies(req: NextRequest, response: NextResponse): NextResponse {\r\n safeDeleteCookie(req, response, cookies.guest);\r\n safeDeleteCookie(req, response, cookies.user);\r\n return response;\r\n }\r\n\r\n /**\r\n * Creates a JSON error response\r\n */\r\n function jsonError(message: string, status = 500): NextResponse {\r\n return new NextResponse(\r\n JSON.stringify({ success: false, message }),\r\n { status, headers: { 'Content-Type': 'application/json' } }\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is an auth page\r\n */\r\n function isAuthPage(pathname: string): boolean {\r\n const cleanPath = stripLocale(pathname, i18n);\r\n const authRoutes = access?.authRoutes ?? [];\r\n return authRoutes.some(route => \r\n cleanPath === route || cleanPath.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is a protected route\r\n */\r\n function isProtectedRoute(pathname: string): boolean {\r\n const cleanPath = stripLocale(pathname, i18n);\r\n \r\n // If protectedByDefault is true, everything is protected except public/auth routes\r\n if (access?.protectedByDefault) {\r\n return !isPublicRoute(pathname) && !isAuthPage(pathname);\r\n }\r\n \r\n const protectedRoutes = access?.protectedRoutes ?? [];\r\n return protectedRoutes.some(route => \r\n cleanPath === route || cleanPath.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is explicitly public\r\n */\r\n function isPublicRoute(pathname: string): boolean {\r\n const cleanPath = stripLocale(pathname, i18n);\r\n const publicRoutes = access?.publicRoutes ?? [];\r\n return publicRoutes.some(route => \r\n cleanPath === route || cleanPath.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if token type is allowed\r\n */\r\n function isTokenTypeAllowed(tokenType: string | null): boolean {\r\n const allowedTypes = access?.allowedTokenTypes;\r\n \r\n // If no restriction, all types allowed\r\n if (!allowedTypes || allowedTypes.length === 0) {\r\n return true;\r\n }\r\n \r\n return tokenType ? allowedTypes.includes(tokenType) : false;\r\n }\r\n\r\n /**\r\n * Handles request when no token is present\r\n */\r\n async function handleNoToken(\r\n req: NextRequest,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { origin } = req.nextUrl;\r\n \r\n // Try to create guest token\r\n if (guestToken?.enabled) {\r\n const guestAccessToken = await validation.createGuestToken();\r\n \r\n if (guestAccessToken) {\r\n let response: NextResponse;\r\n \r\n if (isApiRoute) {\r\n response = NextResponse.next();\r\n } else if (isProtectedRoute(req.nextUrl.pathname)) {\r\n // Redirect to login if protected route\r\n response = NextResponse.redirect(new URL('/login', origin));\r\n } else {\r\n response = NextResponse.next();\r\n }\r\n \r\n response.cookies.set(cookies.guest, guestAccessToken, {\r\n ...cookieOptions,\r\n maxAge: 3600, // 1 hour default for guest tokens\r\n });\r\n \r\n return response;\r\n }\r\n }\r\n \r\n // No guest token - just continue or redirect\r\n if (isApiRoute) {\r\n return jsonError('Token bulunamadı', 401);\r\n }\r\n \r\n if (isProtectedRoute(req.nextUrl.pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n \r\n return NextResponse.next();\r\n }\r\n\r\n /**\r\n * Handles token validation result\r\n */\r\n async function handleValidationResult(\r\n req: NextRequest,\r\n tokenInfo: TokenInfo,\r\n isUserToken: boolean,\r\n currentToken: string,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const { isValid, tokenType, userData } = tokenInfo;\r\n const isGuest = tokenType === TOKEN_TYPES.GUEST;\r\n\r\n // ===== TOKEN INVALID =====\r\n if (!isValid) {\r\n // Try refresh if user token\r\n if (isUserToken && currentToken) {\r\n const refreshResult = await validation.refreshToken(currentToken);\r\n \r\n if (refreshResult.success && refreshResult.newToken) {\r\n const newTokenInfo = await validation.getTokenInfo(refreshResult.newToken);\r\n \r\n if (newTokenInfo.isValid) {\r\n // Successful refresh\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (newTokenInfo.userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(newTokenInfo.userData));\r\n }\r\n requestHeaders.set(HEADERS.REFRESHED_TOKEN, refreshResult.newToken);\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n let response: NextResponse;\r\n \r\n if (isAuthPage(pathname)) {\r\n response = NextResponse.redirect(new URL('/', origin));\r\n } else {\r\n response = NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n response.cookies.set(cookies.user, refreshResult.newToken, {\r\n ...cookieOptions,\r\n maxAge: cookieOptions.maxAge,\r\n });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n\r\n return response;\r\n }\r\n }\r\n }\r\n\r\n // Refresh failed or no user token - handle as no token\r\n const response = await handleNoToken(req, isApiRoute);\r\n \r\n // Check if handleNoToken created a new guest token\r\n const hasNewGuestToken = response.cookies.get(cookies.guest)?.value;\r\n \r\n if (!hasNewGuestToken) {\r\n // No new guest token created - delete all auth cookies\r\n deleteAllAuthCookies(req, response);\r\n } else {\r\n // New guest token created - only delete the invalid user cookie\r\n safeDeleteCookie(req, response, cookies.user);\r\n }\r\n \r\n return response;\r\n }\r\n\r\n // ===== TOKEN VALID =====\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(userData));\r\n }\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n // Check if token type is allowed\r\n if (!isGuest && !isTokenTypeAllowed(tokenType)) {\r\n if (isApiRoute) {\r\n const response = jsonError('Bu işlem için yetkiniz yok', 403);\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n \r\n const response = NextResponse.redirect(new URL('/login', origin));\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n\r\n // Guest token handling\r\n if (isGuest) {\r\n if (isApiRoute) {\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // Protected routes require login\r\n if (isProtectedRoute(pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // User token - block auth pages\r\n if (isAuthPage(pathname)) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n\r\n // Normal access\r\n const response = NextResponse.next({ request: { headers: requestHeaders } });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n \r\n return response;\r\n }\r\n\r\n return {\r\n deleteAllAuthCookies,\r\n jsonError,\r\n isAuthPage,\r\n isProtectedRoute,\r\n isPublicRoute,\r\n isTokenTypeAllowed,\r\n handleNoToken,\r\n handleValidationResult,\r\n };\r\n}\r\n\r\nexport type Handlers = ReturnType<typeof createHandlers>;\r\n","/**\r\n * CSRF Protection\r\n * \r\n * Implements OWASP recommended CSRF protection:\r\n * - Fetch Metadata (Sec-Fetch-Site header) for modern browsers (98%+ coverage)\r\n * - Signed HMAC Double-Submit Cookie as fallback\r\n * \r\n * @see https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedCsrfConfig } from '../shared/types';\r\n\r\nexport interface CsrfValidationResult {\r\n valid: boolean;\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Creates CSRF validator functions\r\n */\r\nexport function createCsrfValidator(config: ResolvedCsrfConfig) {\r\n /**\r\n * Generate HMAC-signed CSRF token\r\n * Format: hmac.randomValue\r\n */\r\n async function generateToken(sessionId: string): Promise<string> {\r\n const randomValue = generateRandomValue();\r\n const hmac = await computeHmac(config.secret, sessionId, randomValue);\r\n return `${hmac}.${randomValue}`;\r\n }\r\n\r\n /**\r\n * Validate CSRF request using configured strategy\r\n */\r\n function validateRequest(req: NextRequest): CsrfValidationResult {\r\n const method = req.method.toUpperCase();\r\n \r\n // Skip safe methods\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n\r\n const strategy = config.strategy;\r\n \r\n // Fetch Metadata validation (primary, modern browsers)\r\n if (strategy === 'fetch-metadata' || strategy === 'both') {\r\n const fetchResult = validateFetchMetadata(req);\r\n \r\n if (strategy === 'fetch-metadata') {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata passes, we're good\r\n if (fetchResult.valid) {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata fails or not available, try double-submit\r\n if (fetchResult.reason === 'missing-headers') {\r\n // Fall through to double-submit\r\n } else {\r\n // Explicit cross-site rejection from Fetch Metadata\r\n return fetchResult;\r\n }\r\n }\r\n\r\n // Double-Submit Cookie validation (fallback)\r\n if (strategy === 'double-submit' || strategy === 'both') {\r\n return validateDoubleSubmit(req);\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Validate using Fetch Metadata headers (Sec-Fetch-Site)\r\n * @see https://web.dev/fetch-metadata/\r\n */\r\n function validateFetchMetadata(req: NextRequest): CsrfValidationResult {\r\n const secFetchSite = req.headers.get('sec-fetch-site');\r\n \r\n // No Fetch Metadata headers (older browser or stripped by proxy)\r\n if (!secFetchSite) {\r\n return { valid: false, reason: 'missing-headers' };\r\n }\r\n\r\n // Same-origin requests are always trusted\r\n if (secFetchSite === 'same-origin') {\r\n return { valid: true };\r\n }\r\n\r\n // None = direct navigation (bookmark, typed URL) - allow for GET\r\n if (secFetchSite === 'none') {\r\n const method = req.method.toUpperCase();\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n // Non-safe method from direct navigation is suspicious\r\n return { valid: false, reason: 'direct-navigation-unsafe-method' };\r\n }\r\n\r\n // Same-site: trust based on config\r\n if (secFetchSite === 'same-site') {\r\n if (config.trustSameSite) {\r\n return { valid: true };\r\n }\r\n // Conservative: don't trust same-site by default (subdomain takeover risk)\r\n return { valid: false, reason: 'same-site-not-trusted' };\r\n }\r\n\r\n // Cross-site: reject state-changing requests\r\n if (secFetchSite === 'cross-site') {\r\n return { valid: false, reason: 'cross-site-request' };\r\n }\r\n\r\n // Unknown value - be conservative\r\n return { valid: false, reason: 'unknown-sec-fetch-site' };\r\n }\r\n\r\n /**\r\n * Validate using Double-Submit Cookie pattern with HMAC\r\n */\r\n function validateDoubleSubmit(req: NextRequest): CsrfValidationResult {\r\n // Get token from cookie\r\n const cookieToken = req.cookies.get(config.cookieName)?.value;\r\n \r\n if (!cookieToken) {\r\n return { valid: false, reason: 'missing-cookie-token' };\r\n }\r\n\r\n // Get token from header (or form field)\r\n const headerToken = req.headers.get(config.headerName);\r\n \r\n if (!headerToken) {\r\n return { valid: false, reason: 'missing-header-token' };\r\n }\r\n\r\n // Tokens must match (constant-time comparison to prevent timing attacks)\r\n if (!constantTimeEqual(cookieToken, headerToken)) {\r\n return { valid: false, reason: 'token-mismatch' };\r\n }\r\n\r\n // Validate HMAC structure\r\n const parts = cookieToken.split('.');\r\n if (parts.length !== 2) {\r\n return { valid: false, reason: 'invalid-token-format' };\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Create response with CSRF cookie set\r\n */\r\n async function attachCsrfCookie(\r\n response: NextResponse, \r\n sessionId: string\r\n ): Promise<NextResponse> {\r\n const token = await generateToken(sessionId);\r\n \r\n response.cookies.set(config.cookieName, token, {\r\n httpOnly: false, // Must be readable by JS\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/',\r\n });\r\n\r\n return response;\r\n }\r\n\r\n return {\r\n validateRequest,\r\n generateToken,\r\n attachCsrfCookie,\r\n };\r\n}\r\n\r\n// ==================== Helper Functions ====================\r\n\r\n/**\r\n * Generate cryptographically random value\r\n */\r\nfunction generateRandomValue(): string {\r\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n return Array.from(array, b => b.toString(16).padStart(2, '0')).join('');\r\n }\r\n // Fallback (less secure, for edge cases)\r\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\r\n}\r\n\r\n/**\r\n * Compute HMAC-SHA256\r\n */\r\nasync function computeHmac(\r\n secret: string, \r\n sessionId: string, \r\n randomValue: string\r\n): Promise<string> {\r\n const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`;\r\n \r\n if (typeof crypto !== 'undefined' && crypto.subtle) {\r\n const encoder = new TextEncoder();\r\n const keyData = encoder.encode(secret);\r\n const messageData = encoder.encode(message);\r\n \r\n const key = await crypto.subtle.importKey(\r\n 'raw',\r\n keyData,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign']\r\n );\r\n \r\n const signature = await crypto.subtle.sign('HMAC', key, messageData);\r\n return Array.from(new Uint8Array(signature), b => \r\n b.toString(16).padStart(2, '0')\r\n ).join('');\r\n }\r\n \r\n // Fallback (less secure)\r\n let hash = 0;\r\n const str = secret + message;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = ((hash << 5) - hash) + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(16);\r\n}\r\n\r\n/**\r\n * Constant-time string comparison (prevents timing attacks)\r\n */\r\nfunction constantTimeEqual(a: string, b: string): boolean {\r\n if (a.length !== b.length) {\r\n return false;\r\n }\r\n \r\n let result = 0;\r\n for (let i = 0; i < a.length; i++) {\r\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\r\n }\r\n \r\n return result === 0;\r\n}\r\n\r\nexport type CsrfValidator = ReturnType<typeof createCsrfValidator>;\r\n","/**\r\n * Rate Limiting\r\n * \r\n * Implements token bucket algorithm for rate limiting.\r\n * In-memory store by default, designed for single-instance deployments.\r\n * \r\n * For horizontal scaling (multiple instances), use a custom store\r\n * with Redis or similar distributed cache.\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedRateLimitConfig } from '../shared/types';\r\n\r\ninterface RateLimitEntry {\r\n count: number;\r\n resetAt: number;\r\n}\r\n\r\nexport interface RateLimitResult {\r\n allowed: boolean;\r\n remaining: number;\r\n resetAt: number;\r\n limit: number;\r\n}\r\n\r\n/**\r\n * Creates a rate limiter with in-memory store\r\n */\r\nexport function createRateLimiter(config: ResolvedRateLimitConfig) {\r\n // In-memory store\r\n const store = new Map<string, RateLimitEntry>();\r\n \r\n // Cleanup expired entries periodically\r\n const cleanupInterval = setInterval(() => {\r\n const now = Date.now();\r\n for (const [key, entry] of store) {\r\n if (entry.resetAt <= now) {\r\n store.delete(key);\r\n }\r\n }\r\n }, config.windowMs);\r\n\r\n // Prevent memory leak in long-running processes\r\n if (typeof process !== 'undefined' && process.on) {\r\n process.on('beforeExit', () => clearInterval(cleanupInterval));\r\n }\r\n\r\n /**\r\n * Check if route should skip rate limiting\r\n */\r\n function shouldSkip(pathname: string): boolean {\r\n return config.skipRoutes.some(pattern => {\r\n // Simple glob matching\r\n if (pattern.endsWith('*')) {\r\n return pathname.startsWith(pattern.slice(0, -1));\r\n }\r\n if (pattern.endsWith('**')) {\r\n return pathname.startsWith(pattern.slice(0, -2));\r\n }\r\n return pathname === pattern;\r\n });\r\n }\r\n\r\n /**\r\n * Check rate limit for request\r\n */\r\n function check(req: NextRequest): RateLimitResult {\r\n const pathname = req.nextUrl.pathname;\r\n \r\n // Skip if route matches skip patterns\r\n if (shouldSkip(pathname)) {\r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests,\r\n resetAt: 0,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n const key = config.keyFn(req);\r\n const now = Date.now();\r\n \r\n let entry = store.get(key);\r\n \r\n // New window or expired\r\n if (!entry || entry.resetAt <= now) {\r\n entry = {\r\n count: 1,\r\n resetAt: now + config.windowMs,\r\n };\r\n store.set(key, entry);\r\n \r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests - 1,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n // Existing window\r\n entry.count++;\r\n \r\n const remaining = Math.max(0, config.maxRequests - entry.count);\r\n const allowed = entry.count <= config.maxRequests;\r\n \r\n return {\r\n allowed,\r\n remaining,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n /**\r\n * Apply rate limit headers to response\r\n */\r\n function applyHeaders(response: NextResponse, result: RateLimitResult): NextResponse {\r\n response.headers.set('X-RateLimit-Limit', result.limit.toString());\r\n response.headers.set('X-RateLimit-Remaining', result.remaining.toString());\r\n response.headers.set('X-RateLimit-Reset', Math.ceil(result.resetAt / 1000).toString());\r\n return response;\r\n }\r\n\r\n /**\r\n * Create rate limited response (429 Too Many Requests)\r\n */\r\n function createLimitedResponse(req: NextRequest, result: RateLimitResult): NextResponse {\r\n // User-provided handler\r\n if (config.onRateLimited) {\r\n const response = config.onRateLimited(req);\r\n return applyHeaders(response, result);\r\n }\r\n\r\n // Default response\r\n const response = NextResponse.json(\r\n {\r\n success: false,\r\n message: 'Too many requests. Please try again later.',\r\n retryAfter: Math.ceil((result.resetAt - Date.now()) / 1000),\r\n },\r\n { status: 429 }\r\n );\r\n\r\n response.headers.set('Retry-After', Math.ceil((result.resetAt - Date.now()) / 1000).toString());\r\n return applyHeaders(response, result);\r\n }\r\n\r\n /**\r\n * Reset rate limit for a key (useful for testing)\r\n */\r\n function reset(key: string): void {\r\n store.delete(key);\r\n }\r\n\r\n /**\r\n * Clear all rate limit entries\r\n */\r\n function clear(): void {\r\n store.clear();\r\n }\r\n\r\n /**\r\n * Get current store size (for monitoring)\r\n */\r\n function size(): number {\r\n return store.size;\r\n }\r\n\r\n return {\r\n check,\r\n applyHeaders,\r\n createLimitedResponse,\r\n shouldSkip,\r\n reset,\r\n clear,\r\n size,\r\n };\r\n}\r\n\r\nexport type RateLimiter = ReturnType<typeof createRateLimiter>;\r\n","/**\r\n * Audit Logging\r\n * \r\n * Event-based security audit logging system.\r\n * Emits events for authentication, access control, and security violations.\r\n */\r\n\r\nimport { NextRequest } from 'next/server';\r\nimport type { ResolvedAuditConfig, AuditEvent, AuditEventType } from '../shared/types';\r\n\r\n/**\r\n * Creates an audit logger instance\r\n */\r\nexport function createAuditLogger(config: ResolvedAuditConfig) {\r\n /**\r\n * Check if event type is enabled\r\n */\r\n function isEnabled(type: AuditEventType): boolean {\r\n return config.enabled && config.events.includes(type);\r\n }\r\n\r\n /**\r\n * Extract IP address from request\r\n */\r\n function getIp(req: NextRequest): string | null {\r\n return (\r\n req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||\r\n req.headers.get('x-real-ip') ||\r\n null\r\n );\r\n }\r\n\r\n /**\r\n * Emit an audit event\r\n */\r\n async function emit(\r\n type: AuditEventType,\r\n req: NextRequest,\r\n options: {\r\n success: boolean;\r\n userId?: string;\r\n metadata?: Record<string, unknown>;\r\n }\r\n ): Promise<void> {\r\n if (!isEnabled(type)) {\r\n return;\r\n }\r\n\r\n const event: AuditEvent = {\r\n type,\r\n timestamp: new Date(),\r\n ip: getIp(req),\r\n userId: options.userId,\r\n path: req.nextUrl.pathname,\r\n method: req.method,\r\n success: options.success,\r\n metadata: options.metadata,\r\n };\r\n\r\n // Call user's logger\r\n if (config.logger) {\r\n try {\r\n await config.logger(event);\r\n } catch (error) {\r\n // Silently fail - don't break the request flow for logging errors\r\n console.error('[next-api-layer] Audit logger error:', error);\r\n }\r\n }\r\n }\r\n\r\n // ==================== Convenience Methods ====================\r\n\r\n /**\r\n * Log successful authentication\r\n */\r\n function authSuccess(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:success', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log failed authentication\r\n */\r\n function authFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log token refresh\r\n */\r\n function authRefresh(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:refresh', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log guest token creation\r\n */\r\n function authGuest(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:guest', req, { success: true, metadata });\r\n }\r\n\r\n /**\r\n * Log access denied\r\n */\r\n function accessDenied(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('access:denied', req, { success: false, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log CSRF validation failure\r\n */\r\n function csrfFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('csrf:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log rate limit exceeded\r\n */\r\n function rateLimitExceeded(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('rateLimit:exceeded', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log general error\r\n */\r\n function error(req: NextRequest, err: Error, metadata?: Record<string, unknown>) {\r\n return emit('error', req, { \r\n success: false, \r\n metadata: { \r\n ...metadata, \r\n error: err.message,\r\n stack: err.stack,\r\n } \r\n });\r\n }\r\n\r\n return {\r\n emit,\r\n isEnabled,\r\n // Convenience methods\r\n authSuccess,\r\n authFail,\r\n authRefresh,\r\n authGuest,\r\n accessDenied,\r\n csrfFail,\r\n rateLimitExceeded,\r\n error,\r\n };\r\n}\r\n\r\nexport type AuditLogger = ReturnType<typeof createAuditLogger>;\r\n","/**\r\n * createAuthProxy\r\n * Factory function to create Next.js middleware for external JWT authentication\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { AuthProxyConfig, AuthResult } from '../shared/types';\r\nimport { resolveProxyConfig } from '../shared/config';\r\nimport { createTokenValidation } from './tokenValidation';\r\nimport { createHandlers, extractLocale } from './handlers';\r\nimport { createCsrfValidator } from './csrf';\r\nimport { createRateLimiter } from './rateLimit';\r\nimport { createAuditLogger } from './audit';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\n/**\r\n * Merge two NextResponse objects, preserving headers and cookies from both\r\n * Target response takes priority for conflicts\r\n */\r\nfunction mergeResponses(source: NextResponse, target: NextResponse): NextResponse {\r\n // Copy critical headers from source to target (if not already set)\r\n const criticalHeaders = [HEADERS.LOCALE, HEADERS.AUTH_USER, HEADERS.REFRESHED_TOKEN];\r\n \r\n for (const header of criticalHeaders) {\r\n const value = source.headers.get(header);\r\n if (value && !target.headers.has(header)) {\r\n target.headers.set(header, value);\r\n }\r\n }\r\n \r\n // Copy cookies from source to target (if not already set)\r\n source.cookies.getAll().forEach(cookie => {\r\n if (!target.cookies.get(cookie.name)) {\r\n target.cookies.set(cookie.name, cookie.value);\r\n }\r\n });\r\n \r\n return target;\r\n}\r\n\r\n/**\r\n * Creates an authentication proxy middleware for Next.js\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts\r\n * import { createAuthProxy } from 'next-api-layer';\r\n * \r\n * const authProxy = createAuthProxy({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * cookies: {\r\n * user: 'userAuthToken',\r\n * guest: 'guestAuthToken',\r\n * },\r\n * guestToken: {\r\n * enabled: true,\r\n * credentials: {\r\n * username: process.env.GUEST_USERNAME!,\r\n * password: process.env.GUEST_PASSWORD!,\r\n * },\r\n * },\r\n * access: {\r\n * protectedRoutes: ['/dashboard', '/profile'],\r\n * authRoutes: ['/login', '/register'],\r\n * },\r\n * // Security features\r\n * csrf: { enabled: true },\r\n * rateLimit: { enabled: true, maxRequests: 100 },\r\n * audit: { \r\n * enabled: true, \r\n * logger: (event) => console.log('[AUDIT]', event) \r\n * },\r\n * });\r\n * \r\n * export default authProxy;\r\n * \r\n * export const config = {\r\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\r\n * };\r\n * ```\r\n */\r\nexport function createAuthProxy(userConfig: AuthProxyConfig) {\r\n // Resolve config with defaults\r\n const config = resolveProxyConfig(userConfig);\r\n \r\n // Create validation functions\r\n const validation = createTokenValidation(config);\r\n \r\n // Create handlers\r\n const handlers = createHandlers(config, validation);\r\n\r\n // Create security modules\r\n const csrf = createCsrfValidator(config._resolved.csrf);\r\n const rateLimiter = createRateLimiter(config._resolved.rateLimit);\r\n const audit = createAuditLogger(config._resolved.audit);\r\n\r\n /**\r\n * The middleware function\r\n */\r\n async function authProxy(req: NextRequest): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const isApiRoute = pathname.startsWith('/api');\r\n\r\n // ============ Rate Limiting ============\r\n // Check early to protect against DoS\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n \r\n if (!rateLimitResult.allowed) {\r\n await audit.rateLimitExceeded(req, { \r\n limit: rateLimitResult.limit,\r\n resetAt: rateLimitResult.resetAt,\r\n });\r\n return rateLimiter.createLimitedResponse(req, rateLimitResult);\r\n }\r\n }\r\n\r\n // ============ CSRF Protection ============\r\n // Check before any state-changing operations\r\n if (config._resolved.csrf.enabled) {\r\n const csrfResult = csrf.validateRequest(req);\r\n \r\n if (!csrfResult.valid) {\r\n await audit.csrfFail(req, { reason: csrfResult.reason });\r\n return NextResponse.json(\r\n { success: false, message: 'CSRF validation failed' },\r\n { status: 403 }\r\n );\r\n }\r\n }\r\n\r\n // ============ Block Browser API Access ============\r\n // Prevents direct browser access to API routes (when Accept: text/html)\r\n if (config.blockBrowserApiAccess && isApiRoute) {\r\n const acceptHeader = req.headers.get('accept') || '';\r\n if (acceptHeader.includes('text/html')) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n }\r\n\r\n // ============ beforeAuth Hook ============\r\n // Allows user to handle request before auth validation\r\n if (config.beforeAuth) {\r\n const beforeResult = await config.beforeAuth(req);\r\n if (beforeResult) {\r\n return beforeResult; // User handled the request\r\n }\r\n }\r\n\r\n // Skip excluded paths\r\n const excludedPaths = config.excludedPaths ?? [];\r\n if (excludedPaths.some(path => pathname.startsWith(path))) {\r\n return applyMiddlewaresAndHooks(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Skip auth API endpoints (they handle their own auth)\r\n const authApiPaths = [\r\n '/api/auth/login',\r\n '/api/auth/logout',\r\n '/api/auth/me',\r\n '/api/auth/refresh',\r\n '/api/auth/register',\r\n ];\r\n \r\n if (authApiPaths.includes(pathname)) {\r\n return applyMiddlewaresAndHooks(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Get tokens from cookies\r\n const userToken = req.cookies?.get(config.cookies.user)?.value;\r\n const guestToken = req.cookies?.get(config.cookies.guest)?.value;\r\n const currentToken = userToken || guestToken;\r\n const isUserToken = !!userToken;\r\n\r\n // No token - handle appropriately\r\n if (!currentToken) {\r\n await audit.authFail(req, { reason: 'no-token' });\r\n const response = await handlers.handleNoToken(req, isApiRoute);\r\n return applyMiddlewaresAndHooks(req, response, { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Validate token\r\n const tokenInfo = await validation.getTokenInfo(currentToken);\r\n \r\n // Build auth result for afterAuth hook\r\n const authResult: AuthResult = {\r\n isAuthenticated: tokenInfo.isValid && tokenInfo.tokenType !== 'guest',\r\n isGuest: tokenInfo.isValid && tokenInfo.tokenType === 'guest',\r\n tokenType: tokenInfo.tokenType,\r\n user: tokenInfo.userData,\r\n };\r\n\r\n // Audit logging based on validation result \r\n if (tokenInfo.isValid) {\r\n if (tokenInfo.tokenType === 'guest') {\r\n await audit.authGuest(req);\r\n } else {\r\n const userId = tokenInfo.userData?.id?.toString();\r\n await audit.authSuccess(req, userId, { tokenType: tokenInfo.tokenType });\r\n }\r\n } else {\r\n await audit.authFail(req, { reason: 'invalid-token' });\r\n }\r\n\r\n // Handle validation result\r\n const response = await handlers.handleValidationResult(\r\n req,\r\n tokenInfo,\r\n isUserToken,\r\n currentToken,\r\n isApiRoute\r\n );\r\n \r\n // Apply CSRF cookie if enabled (for authenticated requests)\r\n let finalResponse = await applyMiddlewaresAndHooks(req, response, authResult);\r\n \r\n if (config._resolved.csrf.enabled && authResult.isAuthenticated) {\r\n const sessionId = tokenInfo.userData?.id?.toString() || currentToken.slice(0, 32);\r\n finalResponse = await csrf.attachCsrfCookie(finalResponse, sessionId);\r\n }\r\n\r\n // Apply rate limit headers\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n finalResponse = rateLimiter.applyHeaders(finalResponse, rateLimitResult);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n \r\n /**\r\n * Helper to apply i18n middleware and afterAuth hook\r\n * Merges responses to preserve critical headers (x-locale, x-auth-user, etc.)\r\n */\r\n async function applyMiddlewaresAndHooks(req: NextRequest, response: NextResponse, authResult: AuthResult): Promise<NextResponse> {\r\n let finalResponse = response;\r\n \r\n // Apply i18n middleware if configured\r\n if (config.i18n?.middleware) {\r\n const intlResponse = await Promise.resolve(config.i18n.middleware(req));\r\n // Merge library's response headers into i18n response\r\n finalResponse = mergeResponses(response, intlResponse);\r\n }\r\n \r\n // Ensure x-locale is set as response header (not just request header)\r\n // This is needed because intl middleware creates a new response that loses request headers\r\n if (config.i18n?.enabled) {\r\n const locale = extractLocale(req.nextUrl.pathname, config.i18n);\r\n if (locale) {\r\n finalResponse.headers.set(HEADERS.LOCALE, locale);\r\n }\r\n }\r\n \r\n // Apply afterAuth hook if configured\r\n if (config.afterAuth) {\r\n const hookResponse = await config.afterAuth(req, finalResponse, authResult);\r\n // Merge previous response headers into hook's response\r\n finalResponse = mergeResponses(finalResponse, hookResponse);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n\r\n // Attach instances for debugging/testing\r\n authProxy.config = config;\r\n authProxy.csrf = csrf;\r\n authProxy.rateLimiter = rateLimiter;\r\n authProxy.audit = audit;\r\n\r\n return authProxy;\r\n}\r\n\r\nexport type AuthProxy = ReturnType<typeof createAuthProxy>;\r\n\r\n","/**\r\n * createProxyHandler\r\n * Factory function to create Next.js API route handlers that proxy requests to backend\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\nexport interface ProxyHandlerConfig {\r\n /** Base URL of the backend API */\r\n apiBaseUrl: string;\r\n /** Cookie name for user auth token */\r\n userCookieName?: string;\r\n /** Cookie name for guest auth token */\r\n guestCookieName?: string;\r\n /** \r\n * Default behavior for auth - if true, all requests skip auth by default\r\n * Individual requests can override with X-Skip-Auth header\r\n */\r\n skipAuthByDefault?: boolean;\r\n /**\r\n * Public endpoints that should never include auth token (glob patterns)\r\n * e.g., ['news/*', 'public/**', 'categories']\r\n */\r\n publicEndpoints?: string[];\r\n /** Headers to forward from client request */\r\n forwardHeaders?: string[];\r\n /** Headers to exclude from forwarding */\r\n excludeHeaders?: string[];\r\n /** Custom request transformer */\r\n transformRequest?: (req: NextRequest, headers: Headers) => Headers | Promise<Headers>;\r\n /** Custom response transformer */\r\n transformResponse?: (response: Response) => Response | Promise<Response>;\r\n}\r\n\r\n/**\r\n * Check if endpoint matches any patterns (glob support)\r\n */\r\nfunction matchesPattern(endpoint: string, patterns: string[]): boolean {\r\n if (!patterns || patterns.length === 0) return false;\r\n \r\n // Normalize endpoint\r\n const normalizedEndpoint = endpoint\r\n .replace(/^\\/api\\//, '')\r\n .replace(/^\\//, '');\r\n \r\n return patterns.some(pattern => {\r\n if (pattern === normalizedEndpoint) return true;\r\n \r\n if (pattern.includes('*')) {\r\n const regexPattern = pattern\r\n .replace(/\\*\\*/g, '<<<DOUBLE>>>')\r\n .replace(/\\*/g, '[^/]+')\r\n .replace(/<<<DOUBLE>>>/g, '.+');\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(normalizedEndpoint);\r\n }\r\n \r\n return false;\r\n });\r\n}\r\n\r\n/**\r\n * Creates a proxy handler for Next.js API routes\r\n * \r\n * @example\r\n * ```ts\r\n * // app/api/[...path]/route.ts\r\n * import { createProxyHandler } from 'next-api-layer';\r\n * \r\n * const handler = createProxyHandler({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * userCookieName: 'auth_token',\r\n * guestCookieName: 'guest_token',\r\n * publicEndpoints: ['news/*', 'categories', 'public/**'],\r\n * });\r\n * \r\n * export const GET = handler;\r\n * export const POST = handler;\r\n * export const PUT = handler;\r\n * export const PATCH = handler;\r\n * export const DELETE = handler;\r\n * ```\r\n */\r\nexport function createProxyHandler(config: ProxyHandlerConfig) {\r\n const {\r\n apiBaseUrl,\r\n userCookieName = 'auth_token',\r\n guestCookieName = 'guest_token',\r\n skipAuthByDefault = false,\r\n publicEndpoints = [],\r\n forwardHeaders = ['content-type', 'accept', 'accept-language', 'x-requested-with'],\r\n excludeHeaders = ['host', 'connection', 'cookie'],\r\n transformRequest,\r\n transformResponse,\r\n } = config;\r\n\r\n // Normalize base URL (remove trailing slash)\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n /**\r\n * Determine if auth should be skipped for this request\r\n */\r\n function shouldSkipAuth(req: NextRequest, endpoint: string): boolean {\r\n // Check X-Skip-Auth header from client\r\n const skipAuthHeader = req.headers.get(HEADERS.SKIP_AUTH);\r\n if (skipAuthHeader === 'true') {\r\n return true;\r\n }\r\n \r\n // Check if endpoint matches public patterns\r\n if (matchesPattern(endpoint, publicEndpoints)) {\r\n return true;\r\n }\r\n \r\n // Default behavior\r\n return skipAuthByDefault;\r\n }\r\n\r\n /**\r\n * The proxy handler function\r\n */\r\n async function handler(req: NextRequest): Promise<NextResponse> {\r\n try {\r\n // Extract endpoint from URL path (remove /api/ prefix)\r\n const url = new URL(req.url);\r\n const endpoint = url.pathname.replace(/^\\/api\\/?/, '');\r\n \r\n // Build backend URL\r\n const backendUrl = new URL(`${baseUrl}/${endpoint}`);\r\n backendUrl.search = url.search; // Forward query params\r\n\r\n // Build headers for backend request\r\n const headers = new Headers();\r\n \r\n // Forward allowed headers from original request\r\n forwardHeaders.forEach(headerName => {\r\n const value = req.headers.get(headerName);\r\n if (value) {\r\n headers.set(headerName, value);\r\n }\r\n });\r\n\r\n // Forward all headers except excluded ones\r\n req.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n if (!excludeHeaders.includes(lowerKey) && !headers.has(key)) {\r\n headers.set(key, value);\r\n }\r\n });\r\n\r\n // Add Authorization header if auth is not skipped\r\n if (!shouldSkipAuth(req, endpoint)) {\r\n const userToken = req.cookies.get(userCookieName)?.value;\r\n const guestToken = req.cookies.get(guestCookieName)?.value;\r\n const token = userToken || guestToken;\r\n \r\n if (token) {\r\n headers.set(HEADERS.AUTHORIZATION, `Bearer ${token}`);\r\n }\r\n }\r\n\r\n // Remove skip-auth header (internal use only)\r\n headers.delete(HEADERS.SKIP_AUTH);\r\n\r\n // Allow custom request transformation\r\n const finalHeaders = transformRequest \r\n ? await transformRequest(req, headers) \r\n : headers;\r\n\r\n // Get request body if present\r\n let body: BodyInit | null = null;\r\n if (req.method !== 'GET' && req.method !== 'HEAD') {\r\n const contentType = req.headers.get('content-type') || '';\r\n \r\n if (contentType.includes('application/json')) {\r\n body = await req.text();\r\n } else if (contentType.includes('multipart/form-data')) {\r\n body = await req.formData();\r\n } else {\r\n body = await req.text();\r\n }\r\n }\r\n\r\n // Make request to backend\r\n const backendResponse = await fetch(backendUrl.toString(), {\r\n method: req.method,\r\n headers: finalHeaders,\r\n body,\r\n });\r\n\r\n // Get response body\r\n const contentType = backendResponse.headers.get('content-type') || '';\r\n let responseBody: ArrayBuffer | string;\r\n \r\n if (contentType.includes('application/json')) {\r\n responseBody = await backendResponse.text();\r\n } else {\r\n responseBody = await backendResponse.arrayBuffer();\r\n }\r\n\r\n // Build response headers (forward relevant ones)\r\n const responseHeaders = new Headers();\r\n backendResponse.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n // Skip hop-by-hop headers\r\n if (!['transfer-encoding', 'connection', 'keep-alive'].includes(lowerKey)) {\r\n responseHeaders.set(key, value);\r\n }\r\n });\r\n\r\n // Create response\r\n let response = new NextResponse(responseBody, {\r\n status: backendResponse.status,\r\n statusText: backendResponse.statusText,\r\n headers: responseHeaders,\r\n });\r\n\r\n // Allow custom response transformation\r\n if (transformResponse) {\r\n response = await transformResponse(response) as NextResponse;\r\n }\r\n\r\n return response;\r\n } catch (error) {\r\n console.error('[Proxy Error]', error);\r\n \r\n return NextResponse.json(\r\n { \r\n success: false, \r\n message: 'Proxy error: Unable to connect to backend',\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n },\r\n { status: 502 }\r\n );\r\n }\r\n }\r\n\r\n // Attach config for debugging\r\n handler.config = config;\r\n\r\n return handler;\r\n}\r\n\r\nexport type ProxyHandler = ReturnType<typeof createProxyHandler>;\r\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export{d as createApiClient}from'./chunk-NDZA32WH.js';import {c,e,a,b,g,h,i}from'./chunk-XBAO7FJN.js';export{f as CSRF_SAFE_METHODS,i as DEFAULT_AUDIT_CONFIG,a as DEFAULT_COOKIE_OPTIONS,g as DEFAULT_CSRF_CONFIG,b as DEFAULT_ENDPOINTS,h as DEFAULT_RATE_LIMIT_CONFIG,d as ERROR_MESSAGES,e as HEADERS,c as TOKEN_TYPES}from'./chunk-XBAO7FJN.js';import {NextResponse}from'next/server';function ue(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID()+crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2)}`}function le(e){return `rl:${e.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||e.headers.get("x-real-ip")||"unknown"}`}function Q(e){if(!e.apiBaseUrl)throw new Error("next-api-layer: apiBaseUrl is required");if(!e.cookies?.user||!e.cookies?.guest)throw new Error("next-api-layer: cookies.user and cookies.guest are required");let t=e.apiBaseUrl.endsWith("/")?e.apiBaseUrl:`${e.apiBaseUrl}/`,f={...a,...e.cookies.options},r={...b,...e.endpoints},p={enabled:e.csrf?.enabled??false,strategy:e.csrf?.strategy??g.strategy,secret:e.csrf?.secret??ue(),cookieName:e.csrf?.cookieName??g.cookieName,headerName:e.csrf?.headerName??g.headerName,ignoreMethods:e.csrf?.ignoreMethods??g.ignoreMethods,trustSameSite:e.csrf?.trustSameSite??g.trustSameSite},R={enabled:e.rateLimit?.enabled??false,windowMs:e.rateLimit?.windowMs??h.windowMs,maxRequests:e.rateLimit?.maxRequests??h.maxRequests,keyFn:e.rateLimit?.keyFn??le,skipRoutes:e.rateLimit?.skipRoutes??h.skipRoutes,onRateLimited:e.rateLimit?.onRateLimited},l={enabled:e.audit?.enabled??false,events:e.audit?.events??[...i.events],logger:e.audit?.logger};return {...e,apiBaseUrl:t,_resolved:{cookieOptions:f,endpoints:r,csrf:p,rateLimit:R,audit:l}}}var $={parseAuthMe:e=>{let t=e;return !t?.success||!t?.data?null:{isValid:true,tokenType:t.data.type||"user",exp:t.data.exp||null,userData:t.data}},parseRefreshToken:e=>{let t=e;return t?.success&&t?.data?.accessToken?t.data.accessToken:null},parseGuestToken:e=>e?.data?.accessToken||null};function j(e){let{apiBaseUrl:t,_resolved:f,responseMappers:r}=e,{endpoints:p}=f,R={parseAuthMe:r?.parseAuthMe||$.parseAuthMe,parseRefreshToken:r?.parseRefreshToken||$.parseRefreshToken,parseGuestToken:r?.parseGuestToken||$.parseGuestToken};async function l(i){let u={isValid:false,tokenType:null,exp:null,userData:null};try{let s=await fetch(`${t}${p.validate}`,{headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"},cache:"no-store"});if(!s.ok)return u;let n=await s.json().catch(()=>null),d=R.parseAuthMe(n);return !d||!d.isValid?u:d}catch{return u}}async function c(i){return l(i)}async function m(i){try{let u=await fetch(`${t}${p.refresh}`,{method:"POST",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"},cache:"no-store"});if(!u.ok)return {success:!1,newToken:null};let s=await u.json().catch(()=>null),n=R.parseRefreshToken(s);return n?{success:!0,newToken:n}:{success:!1,newToken:null}}catch{return {success:false,newToken:null}}}async function a(){let i=e.guestToken;if(!i?.enabled||!i.credentials)return null;try{let u=await fetch(`${t}${p.guest}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:i.credentials.username,password:i.credentials.password}),cache:"no-store"});if(!u.ok)return null;let s=await u.json().catch(()=>null);return R.parseGuestToken(s)}catch{return null}}return {validateToken:l,getTokenInfo:c,refreshToken:m,createGuestToken:a}}function ee(e,t){if(!t?.enabled)return null;let f=t.locales??[],r=t.defaultLocale,R=e.split("/").filter(Boolean)[0];return R&&f.includes(R)?R:r??null}function z(e$1,t){let{cookies:f,guestToken:r,access:p,i18n:R,_resolved:l}=e$1,{cookieOptions:c$1}=l;function m(o,x,h){o.cookies.get(h)?.value&&x.cookies.delete(h);}function a(o,x){return m(o,x,f.guest),m(o,x,f.user),x}function i(o,x=500){return new NextResponse(JSON.stringify({success:false,message:o}),{status:x,headers:{"Content-Type":"application/json"}})}function u(o){return (p?.authRoutes??[]).some(h=>o===h||o.startsWith(`${h}/`))}function s(o){return p?.protectedByDefault?!n(o)&&!u(o):(p?.protectedRoutes??[]).some(h=>o===h||o.startsWith(`${h}/`))}function n(o){return (p?.publicRoutes??[]).some(h=>o===h||o.startsWith(`${h}/`))}function d(o){let x=p?.allowedTokenTypes;return !x||x.length===0?true:o?x.includes(o):false}async function N(o,x){let{origin:h}=o.nextUrl;if(r?.enabled){let y=await t.createGuestToken();if(y){let E;return x?E=NextResponse.next():s(o.nextUrl.pathname)?E=NextResponse.redirect(new URL("/login",h)):E=NextResponse.next(),E.cookies.set(f.guest,y,{...c$1,maxAge:3600}),E}}return x?i("Token bulunamad\u0131",401):s(o.nextUrl.pathname)?NextResponse.redirect(new URL("/login",h)):NextResponse.next()}async function L(o,x,h,y,E){let{pathname:k,origin:g}=o.nextUrl,{isValid:S,tokenType:A,userData:w}=x,v=A===c.GUEST;if(!S){if(h&&y){let H=await t.refreshToken(y);if(H.success&&H.newToken){let O=await t.getTokenInfo(H.newToken);if(O.isValid){let _=new Headers(o.headers);O.userData&&_.set(e.AUTH_USER,JSON.stringify(O.userData)),_.set(e.REFRESHED_TOKEN,H.newToken);let Z=ee(k,R);Z&&_.set(e.LOCALE,Z);let D;return u(k)?D=NextResponse.redirect(new URL("/",g)):D=NextResponse.next({request:{headers:_}}),D.cookies.set(f.user,H.newToken,{...c$1,maxAge:c$1.maxAge}),m(o,D,f.guest),D}}}let P=await N(o,E);return P.cookies.get(f.guest)?.value?m(o,P,f.user):a(o,P),P}let U=new Headers(o.headers);w&&U.set(e.AUTH_USER,JSON.stringify(w));let J=ee(k,R);if(J&&U.set(e.LOCALE,J),!v&&!d(A)){if(E){let Y=i("Bu i\u015Flem i\xE7in yetkiniz yok",403);return a(o,Y)}let P=NextResponse.redirect(new URL("/login",g));return a(o,P)}if(v)return E?NextResponse.next({request:{headers:U}}):s(k)?NextResponse.redirect(new URL("/login",g)):NextResponse.next({request:{headers:U}});if(u(k))return NextResponse.redirect(new URL("/",g));let X=NextResponse.next({request:{headers:U}});return m(o,X,f.guest),X}return {deleteAllAuthCookies:a,jsonError:i,isAuthPage:u,isProtectedRoute:s,isPublicRoute:n,isTokenTypeAllowed:d,handleNoToken:N,handleValidationResult:L}}function K(e){async function t(l){let c=ce();return `${await de(e.secret,l,c)}.${c}`}function f(l){let c=l.method.toUpperCase();if(e.ignoreMethods.includes(c))return {valid:true};let m=e.strategy;if(m==="fetch-metadata"||m==="both"){let a=r(l);if(m==="fetch-metadata"||a.valid||a.reason!=="missing-headers")return a}return m==="double-submit"||m==="both"?p(l):{valid:true}}function r(l){let c=l.headers.get("sec-fetch-site");if(!c)return {valid:false,reason:"missing-headers"};if(c==="same-origin")return {valid:true};if(c==="none"){let m=l.method.toUpperCase();return e.ignoreMethods.includes(m)?{valid:true}:{valid:false,reason:"direct-navigation-unsafe-method"}}return c==="same-site"?e.trustSameSite?{valid:true}:{valid:false,reason:"same-site-not-trusted"}:c==="cross-site"?{valid:false,reason:"cross-site-request"}:{valid:false,reason:"unknown-sec-fetch-site"}}function p(l){let c=l.cookies.get(e.cookieName)?.value;if(!c)return {valid:false,reason:"missing-cookie-token"};let m=l.headers.get(e.headerName);return m?fe(c,m)?c.split(".").length!==2?{valid:false,reason:"invalid-token-format"}:{valid:true}:{valid:false,reason:"token-mismatch"}:{valid:false,reason:"missing-header-token"}}async function R(l,c){let m=await t(c);return l.cookies.set(e.cookieName,m,{httpOnly:false,secure:process.env.NODE_ENV==="production",sameSite:"strict",path:"/"}),l}return {validateRequest:f,generateToken:t,attachCsrfCookie:R}}function ce(){if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}return Math.random().toString(36).substring(2)+Date.now().toString(36)}async function de(e,t,f){let r=`${t.length}!${t}!${f.length}!${f}`;if(typeof crypto<"u"&&crypto.subtle){let l=new TextEncoder,c=l.encode(e),m=l.encode(r),a=await crypto.subtle.importKey("raw",c,{name:"HMAC",hash:"SHA-256"},false,["sign"]),i=await crypto.subtle.sign("HMAC",a,m);return Array.from(new Uint8Array(i),u=>u.toString(16).padStart(2,"0")).join("")}let p=0,R=e+r;for(let l=0;l<R.length;l++){let c=R.charCodeAt(l);p=(p<<5)-p+c,p=p&p;}return Math.abs(p).toString(16)}function fe(e,t){if(e.length!==t.length)return false;let f=0;for(let r=0;r<e.length;r++)f|=e.charCodeAt(r)^t.charCodeAt(r);return f===0}function W(e){let t=new Map,f=setInterval(()=>{let i=Date.now();for(let[u,s]of t)s.resetAt<=i&&t.delete(u);},e.windowMs);typeof process<"u"&&process.on&&process.on("beforeExit",()=>clearInterval(f));function r(i){return e.skipRoutes.some(u=>u.endsWith("*")?i.startsWith(u.slice(0,-1)):u.endsWith("**")?i.startsWith(u.slice(0,-2)):i===u)}function p(i){let u=i.nextUrl.pathname;if(r(u))return {allowed:true,remaining:e.maxRequests,resetAt:0,limit:e.maxRequests};let s=e.keyFn(i),n=Date.now(),d=t.get(s);if(!d||d.resetAt<=n)return d={count:1,resetAt:n+e.windowMs},t.set(s,d),{allowed:true,remaining:e.maxRequests-1,resetAt:d.resetAt,limit:e.maxRequests};d.count++;let N=Math.max(0,e.maxRequests-d.count);return {allowed:d.count<=e.maxRequests,remaining:N,resetAt:d.resetAt,limit:e.maxRequests}}function R(i,u){return i.headers.set("X-RateLimit-Limit",u.limit.toString()),i.headers.set("X-RateLimit-Remaining",u.remaining.toString()),i.headers.set("X-RateLimit-Reset",Math.ceil(u.resetAt/1e3).toString()),i}function l(i,u){if(e.onRateLimited){let n=e.onRateLimited(i);return R(n,u)}let s=NextResponse.json({success:false,message:"Too many requests. Please try again later.",retryAfter:Math.ceil((u.resetAt-Date.now())/1e3)},{status:429});return s.headers.set("Retry-After",Math.ceil((u.resetAt-Date.now())/1e3).toString()),R(s,u)}function c(i){t.delete(i);}function m(){t.clear();}function a(){return t.size}return {check:p,applyHeaders:R,createLimitedResponse:l,shouldSkip:r,reset:c,clear:m,size:a}}function q(e){function t(s){return e.enabled&&e.events.includes(s)}function f(s){return s.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||s.headers.get("x-real-ip")||null}async function r(s,n,d){if(!t(s))return;let N={type:s,timestamp:new Date,ip:f(n),userId:d.userId,path:n.nextUrl.pathname,method:n.method,success:d.success,metadata:d.metadata};if(e.logger)try{await e.logger(N);}catch(L){console.error("[next-api-layer] Audit logger error:",L);}}function p(s,n,d){return r("auth:success",s,{success:true,userId:n,metadata:d})}function R(s,n){return r("auth:fail",s,{success:false,metadata:n})}function l(s,n,d){return r("auth:refresh",s,{success:true,userId:n,metadata:d})}function c(s,n){return r("auth:guest",s,{success:true,metadata:n})}function m(s,n,d){return r("access:denied",s,{success:false,userId:n,metadata:d})}function a(s,n){return r("csrf:fail",s,{success:false,metadata:n})}function i(s,n){return r("rateLimit:exceeded",s,{success:false,metadata:n})}function u(s,n,d){return r("error",s,{success:false,metadata:{...d,error:n.message,stack:n.stack}})}return {emit:r,isEnabled:t,authSuccess:p,authFail:R,authRefresh:l,authGuest:c,accessDenied:m,csrfFail:a,rateLimitExceeded:i,error:u}}function te(e$1,t){let f=[e.LOCALE,e.AUTH_USER,e.REFRESHED_TOKEN];for(let r of f){let p=e$1.headers.get(r);p&&!t.headers.has(r)&&t.headers.set(r,p);}return e$1.cookies.getAll().forEach(r=>{t.cookies.get(r.name)||t.cookies.set(r.name,r.value);}),t}function se(e){let t=Q(e),f=j(t),r=z(t,f),p=K(t._resolved.csrf),R=W(t._resolved.rateLimit),l=q(t._resolved.audit);async function c(a){let{pathname:i,origin:u}=a.nextUrl,s=i.startsWith("/api");if(t._resolved.rateLimit.enabled){let g=R.check(a);if(!g.allowed)return await l.rateLimitExceeded(a,{limit:g.limit,resetAt:g.resetAt}),R.createLimitedResponse(a,g)}if(t._resolved.csrf.enabled){let g=p.validateRequest(a);if(!g.valid)return await l.csrfFail(a,{reason:g.reason}),NextResponse.json({success:false,message:"CSRF validation failed"},{status:403})}if(t.blockBrowserApiAccess&&s&&(a.headers.get("accept")||"").includes("text/html"))return NextResponse.redirect(new URL("/",u));if(t.beforeAuth){let g=await t.beforeAuth(a);if(g)return g}if((t.excludedPaths??[]).some(g=>i.startsWith(g)))return m(a,NextResponse.next(),{isAuthenticated:false,isGuest:false,tokenType:null,user:null});if(["/api/auth/login","/api/auth/logout","/api/auth/me","/api/auth/refresh","/api/auth/register"].includes(i))return m(a,NextResponse.next(),{isAuthenticated:false,isGuest:false,tokenType:null,user:null});let N=a.cookies?.get(t.cookies.user)?.value,L=a.cookies?.get(t.cookies.guest)?.value,o=N||L,x=!!N;if(!o){await l.authFail(a,{reason:"no-token"});let g=await r.handleNoToken(a,s);return m(a,g,{isAuthenticated:false,isGuest:false,tokenType:null,user:null})}let h=await f.getTokenInfo(o),y={isAuthenticated:h.isValid&&h.tokenType!=="guest",isGuest:h.isValid&&h.tokenType==="guest",tokenType:h.tokenType,user:h.userData};if(h.isValid)if(h.tokenType==="guest")await l.authGuest(a);else {let g=h.userData?.id?.toString();await l.authSuccess(a,g,{tokenType:h.tokenType});}else await l.authFail(a,{reason:"invalid-token"});let E=await r.handleValidationResult(a,h,x,o,s),k=await m(a,E,y);if(t._resolved.csrf.enabled&&y.isAuthenticated){let g=h.userData?.id?.toString()||o.slice(0,32);k=await p.attachCsrfCookie(k,g);}if(t._resolved.rateLimit.enabled){let g=R.check(a);k=R.applyHeaders(k,g);}return k}async function m(a,i,u){let s=i;if(t.i18n?.middleware){let n=await Promise.resolve(t.i18n.middleware(a));s=te(i,n);}if(t.afterAuth){let n=await t.afterAuth(a,s,u);s=te(s,n);}return s}return c.config=t,c.csrf=p,c.rateLimiter=R,c.audit=l,c}function me(e,t){if(!t||t.length===0)return false;let f=e.replace(/^\/api\//,"").replace(/^\//,"");return t.some(r=>{if(r===f)return true;if(r.includes("*")){let p=r.replace(/\*\*/g,"<<<DOUBLE>>>").replace(/\*/g,"[^/]+").replace(/<<<DOUBLE>>>/g,".+");return new RegExp(`^${p}$`).test(f)}return false})}function re(e$1){let{apiBaseUrl:t,userCookieName:f="auth_token",guestCookieName:r="guest_token",skipAuthByDefault:p=false,publicEndpoints:R=[],forwardHeaders:l=["content-type","accept","accept-language","x-requested-with"],excludeHeaders:c=["host","connection","cookie"],transformRequest:m,transformResponse:a}=e$1,i=t.replace(/\/$/,"");function u(n,d){return n.headers.get(e.SKIP_AUTH)==="true"||me(d,R)?true:p}async function s(n){try{let d=new URL(n.url),N=d.pathname.replace(/^\/api\/?/,""),L=new URL(`${i}/${N}`);L.search=d.search;let o=new Headers;if(l.forEach(A=>{let w=n.headers.get(A);w&&o.set(A,w);}),n.headers.forEach((A,w)=>{let v=w.toLowerCase();!c.includes(v)&&!o.has(w)&&o.set(w,A);}),!u(n,N)){let A=n.cookies.get(f)?.value,w=n.cookies.get(r)?.value,v=A||w;v&&o.set(e.AUTHORIZATION,`Bearer ${v}`);}o.delete(e.SKIP_AUTH);let x=m?await m(n,o):o,h=null;if(n.method!=="GET"&&n.method!=="HEAD"){let A=n.headers.get("content-type")||"";A.includes("application/json")?h=await n.text():A.includes("multipart/form-data")?h=await n.formData():h=await n.text();}let y=await fetch(L.toString(),{method:n.method,headers:x,body:h}),E=y.headers.get("content-type")||"",k;E.includes("application/json")?k=await y.text():k=await y.arrayBuffer();let g=new Headers;y.headers.forEach((A,w)=>{let v=w.toLowerCase();["transfer-encoding","connection","keep-alive"].includes(v)||g.set(w,A);});let S=new NextResponse(k,{status:y.status,statusText:y.statusText,headers:g});return a&&(S=await a(S)),S}catch(d){return console.error("[Proxy Error]",d),NextResponse.json({success:false,message:"Proxy error: Unable to connect to backend",error:d instanceof Error?d.message:"Unknown error"},{status:502})}}return s.config=e$1,s}
|
|
2
|
-
export{
|
|
1
|
+
export{d as createApiClient}from'./chunk-NDZA32WH.js';import {e,c,a,b,g,h,i}from'./chunk-XBAO7FJN.js';export{f as CSRF_SAFE_METHODS,i as DEFAULT_AUDIT_CONFIG,a as DEFAULT_COOKIE_OPTIONS,g as DEFAULT_CSRF_CONFIG,b as DEFAULT_ENDPOINTS,h as DEFAULT_RATE_LIMIT_CONFIG,d as ERROR_MESSAGES,e as HEADERS,c as TOKEN_TYPES}from'./chunk-XBAO7FJN.js';import {NextResponse}from'next/server';function le(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID()+crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2)}`}function ce(e){return `rl:${e.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||e.headers.get("x-real-ip")||"unknown"}`}function te(e){if(!e.apiBaseUrl)throw new Error("next-api-layer: apiBaseUrl is required");if(!e.cookies?.user||!e.cookies?.guest)throw new Error("next-api-layer: cookies.user and cookies.guest are required");let t=e.apiBaseUrl.endsWith("/")?e.apiBaseUrl:`${e.apiBaseUrl}/`,d={...a,...e.cookies.options},r={...b,...e.endpoints},c={enabled:e.csrf?.enabled??false,strategy:e.csrf?.strategy??g.strategy,secret:e.csrf?.secret??le(),cookieName:e.csrf?.cookieName??g.cookieName,headerName:e.csrf?.headerName??g.headerName,ignoreMethods:e.csrf?.ignoreMethods??g.ignoreMethods,trustSameSite:e.csrf?.trustSameSite??g.trustSameSite},m={enabled:e.rateLimit?.enabled??false,windowMs:e.rateLimit?.windowMs??h.windowMs,maxRequests:e.rateLimit?.maxRequests??h.maxRequests,keyFn:e.rateLimit?.keyFn??ce,skipRoutes:e.rateLimit?.skipRoutes??h.skipRoutes,onRateLimited:e.rateLimit?.onRateLimited},l={enabled:e.audit?.enabled??false,events:e.audit?.events??[...i.events],logger:e.audit?.logger};return {...e,apiBaseUrl:t,_resolved:{cookieOptions:d,endpoints:r,csrf:c,rateLimit:m,audit:l}}}var j={parseAuthMe:e=>{let t=e;return !t?.success||!t?.data?null:{isValid:true,tokenType:t.data.type||"user",exp:t.data.exp||null,userData:t.data}},parseRefreshToken:e=>{let t=e;return t?.success&&t?.data?.accessToken?t.data.accessToken:null},parseGuestToken:e=>e?.data?.accessToken||null};function z(e){let{apiBaseUrl:t,_resolved:d,responseMappers:r}=e,{endpoints:c}=d,m={parseAuthMe:r?.parseAuthMe||j.parseAuthMe,parseRefreshToken:r?.parseRefreshToken||j.parseRefreshToken,parseGuestToken:r?.parseGuestToken||j.parseGuestToken};async function l(a){let u={isValid:false,tokenType:null,exp:null,userData:null};try{let n=await fetch(`${t}${c.validate}`,{headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},cache:"no-store"});if(!n.ok)return u;let s=await n.json().catch(()=>null),p=m.parseAuthMe(s);return !p||!p.isValid?u:p}catch{return u}}async function f(a){return l(a)}async function h(a){try{let u=await fetch(`${t}${c.refresh}`,{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},cache:"no-store"});if(!u.ok)return {success:!1,newToken:null};let n=await u.json().catch(()=>null),s=m.parseRefreshToken(n);return s?{success:!0,newToken:s}:{success:!1,newToken:null}}catch{return {success:false,newToken:null}}}async function o(){let a=e.guestToken;if(!a?.enabled||!a.credentials)return null;try{let u=await fetch(`${t}${c.guest}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:a.credentials.username,password:a.credentials.password}),cache:"no-store"});if(!u.ok)return null;let n=await u.json().catch(()=>null);return m.parseGuestToken(n)}catch{return null}}return {validateToken:l,getTokenInfo:f,refreshToken:h,createGuestToken:o}}function O(e,t){if(!t?.enabled)return null;let d=t.locales??[],r=t.defaultLocale,m=e.split("/").filter(Boolean)[0];return m&&d.includes(m)?m:r??null}function K(e,t){if(!t?.enabled)return e;let d=t.locales??[],r=e.split("/").filter(Boolean),c=r[0];return c&&d.includes(c)?"/"+r.slice(1).join("/")||"/":e}function W(e$1,t){let{cookies:d,guestToken:r,access:c$1,i18n:m,_resolved:l}=e$1,{cookieOptions:f}=l;function h(i,R,x){i.cookies.get(x)?.value&&R.cookies.delete(x);}function o(i,R){return h(i,R,d.guest),h(i,R,d.user),R}function a(i,R=500){return new NextResponse(JSON.stringify({success:false,message:i}),{status:R,headers:{"Content-Type":"application/json"}})}function u(i){let R=K(i,m);return (c$1?.authRoutes??[]).some(y=>R===y||R.startsWith(`${y}/`))}function n(i){let R=K(i,m);return c$1?.protectedByDefault?!s(i)&&!u(i):(c$1?.protectedRoutes??[]).some(y=>R===y||R.startsWith(`${y}/`))}function s(i){let R=K(i,m);return (c$1?.publicRoutes??[]).some(y=>R===y||R.startsWith(`${y}/`))}function p(i){let R=c$1?.allowedTokenTypes;return !R||R.length===0?true:i?R.includes(i):false}async function N(i,R){let{origin:x}=i.nextUrl;if(r?.enabled){let y=await t.createGuestToken();if(y){let L;return R?L=NextResponse.next():n(i.nextUrl.pathname)?L=NextResponse.redirect(new URL("/login",x)):L=NextResponse.next(),L.cookies.set(d.guest,y,{...f,maxAge:3600}),L}}return R?a("Token bulunamad\u0131",401):n(i.nextUrl.pathname)?NextResponse.redirect(new URL("/login",x)):NextResponse.next()}async function E(i,R,x,y,L){let{pathname:T,origin:g}=i.nextUrl,{isValid:S,tokenType:A,userData:w}=R,v=A===c.GUEST;if(!S){if(x&&y){let H=await t.refreshToken(y);if(H.success&&H.newToken){let F=await t.getTokenInfo(H.newToken);if(F.isValid){let _=new Headers(i.headers);F.userData&&_.set(e.AUTH_USER,JSON.stringify(F.userData)),_.set(e.REFRESHED_TOKEN,H.newToken);let ee=O(T,m);ee&&_.set(e.LOCALE,ee);let D;return u(T)?D=NextResponse.redirect(new URL("/",g)):D=NextResponse.next({request:{headers:_}}),D.cookies.set(d.user,H.newToken,{...f,maxAge:f.maxAge}),h(i,D,d.guest),D}}}let b=await N(i,L);return b.cookies.get(d.guest)?.value?h(i,b,d.user):o(i,b),b}let U=new Headers(i.headers);w&&U.set(e.AUTH_USER,JSON.stringify(w));let Y=O(T,m);if(Y&&U.set(e.LOCALE,Y),!v&&!p(A)){if(L){let Q=a("Bu i\u015Flem i\xE7in yetkiniz yok",403);return o(i,Q)}let b=NextResponse.redirect(new URL("/login",g));return o(i,b)}if(v)return L?NextResponse.next({request:{headers:U}}):n(T)?NextResponse.redirect(new URL("/login",g)):NextResponse.next({request:{headers:U}});if(u(T))return NextResponse.redirect(new URL("/",g));let Z=NextResponse.next({request:{headers:U}});return h(i,Z,d.guest),Z}return {deleteAllAuthCookies:o,jsonError:a,isAuthPage:u,isProtectedRoute:n,isPublicRoute:s,isTokenTypeAllowed:p,handleNoToken:N,handleValidationResult:E}}function q(e){async function t(l){let f=de();return `${await fe(e.secret,l,f)}.${f}`}function d(l){let f=l.method.toUpperCase();if(e.ignoreMethods.includes(f))return {valid:true};let h=e.strategy;if(h==="fetch-metadata"||h==="both"){let o=r(l);if(h==="fetch-metadata"||o.valid||o.reason!=="missing-headers")return o}return h==="double-submit"||h==="both"?c(l):{valid:true}}function r(l){let f=l.headers.get("sec-fetch-site");if(!f)return {valid:false,reason:"missing-headers"};if(f==="same-origin")return {valid:true};if(f==="none"){let h=l.method.toUpperCase();return e.ignoreMethods.includes(h)?{valid:true}:{valid:false,reason:"direct-navigation-unsafe-method"}}return f==="same-site"?e.trustSameSite?{valid:true}:{valid:false,reason:"same-site-not-trusted"}:f==="cross-site"?{valid:false,reason:"cross-site-request"}:{valid:false,reason:"unknown-sec-fetch-site"}}function c(l){let f=l.cookies.get(e.cookieName)?.value;if(!f)return {valid:false,reason:"missing-cookie-token"};let h=l.headers.get(e.headerName);return h?pe(f,h)?f.split(".").length!==2?{valid:false,reason:"invalid-token-format"}:{valid:true}:{valid:false,reason:"token-mismatch"}:{valid:false,reason:"missing-header-token"}}async function m(l,f){let h=await t(f);return l.cookies.set(e.cookieName,h,{httpOnly:false,secure:process.env.NODE_ENV==="production",sameSite:"strict",path:"/"}),l}return {validateRequest:d,generateToken:t,attachCsrfCookie:m}}function de(){if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}return Math.random().toString(36).substring(2)+Date.now().toString(36)}async function fe(e,t,d){let r=`${t.length}!${t}!${d.length}!${d}`;if(typeof crypto<"u"&&crypto.subtle){let l=new TextEncoder,f=l.encode(e),h=l.encode(r),o=await crypto.subtle.importKey("raw",f,{name:"HMAC",hash:"SHA-256"},false,["sign"]),a=await crypto.subtle.sign("HMAC",o,h);return Array.from(new Uint8Array(a),u=>u.toString(16).padStart(2,"0")).join("")}let c=0,m=e+r;for(let l=0;l<m.length;l++){let f=m.charCodeAt(l);c=(c<<5)-c+f,c=c&c;}return Math.abs(c).toString(16)}function pe(e,t){if(e.length!==t.length)return false;let d=0;for(let r=0;r<e.length;r++)d|=e.charCodeAt(r)^t.charCodeAt(r);return d===0}function J(e){let t=new Map,d=setInterval(()=>{let a=Date.now();for(let[u,n]of t)n.resetAt<=a&&t.delete(u);},e.windowMs);typeof process<"u"&&process.on&&process.on("beforeExit",()=>clearInterval(d));function r(a){return e.skipRoutes.some(u=>u.endsWith("*")?a.startsWith(u.slice(0,-1)):u.endsWith("**")?a.startsWith(u.slice(0,-2)):a===u)}function c(a){let u=a.nextUrl.pathname;if(r(u))return {allowed:true,remaining:e.maxRequests,resetAt:0,limit:e.maxRequests};let n=e.keyFn(a),s=Date.now(),p=t.get(n);if(!p||p.resetAt<=s)return p={count:1,resetAt:s+e.windowMs},t.set(n,p),{allowed:true,remaining:e.maxRequests-1,resetAt:p.resetAt,limit:e.maxRequests};p.count++;let N=Math.max(0,e.maxRequests-p.count);return {allowed:p.count<=e.maxRequests,remaining:N,resetAt:p.resetAt,limit:e.maxRequests}}function m(a,u){return a.headers.set("X-RateLimit-Limit",u.limit.toString()),a.headers.set("X-RateLimit-Remaining",u.remaining.toString()),a.headers.set("X-RateLimit-Reset",Math.ceil(u.resetAt/1e3).toString()),a}function l(a,u){if(e.onRateLimited){let s=e.onRateLimited(a);return m(s,u)}let n=NextResponse.json({success:false,message:"Too many requests. Please try again later.",retryAfter:Math.ceil((u.resetAt-Date.now())/1e3)},{status:429});return n.headers.set("Retry-After",Math.ceil((u.resetAt-Date.now())/1e3).toString()),m(n,u)}function f(a){t.delete(a);}function h(){t.clear();}function o(){return t.size}return {check:c,applyHeaders:m,createLimitedResponse:l,shouldSkip:r,reset:f,clear:h,size:o}}function X(e){function t(n){return e.enabled&&e.events.includes(n)}function d(n){return n.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||n.headers.get("x-real-ip")||null}async function r(n,s,p){if(!t(n))return;let N={type:n,timestamp:new Date,ip:d(s),userId:p.userId,path:s.nextUrl.pathname,method:s.method,success:p.success,metadata:p.metadata};if(e.logger)try{await e.logger(N);}catch(E){console.error("[next-api-layer] Audit logger error:",E);}}function c(n,s,p){return r("auth:success",n,{success:true,userId:s,metadata:p})}function m(n,s){return r("auth:fail",n,{success:false,metadata:s})}function l(n,s,p){return r("auth:refresh",n,{success:true,userId:s,metadata:p})}function f(n,s){return r("auth:guest",n,{success:true,metadata:s})}function h(n,s,p){return r("access:denied",n,{success:false,userId:s,metadata:p})}function o(n,s){return r("csrf:fail",n,{success:false,metadata:s})}function a(n,s){return r("rateLimit:exceeded",n,{success:false,metadata:s})}function u(n,s,p){return r("error",n,{success:false,metadata:{...p,error:s.message,stack:s.stack}})}return {emit:r,isEnabled:t,authSuccess:c,authFail:m,authRefresh:l,authGuest:f,accessDenied:h,csrfFail:o,rateLimitExceeded:a,error:u}}function se(e$1,t){let d=[e.LOCALE,e.AUTH_USER,e.REFRESHED_TOKEN];for(let r of d){let c=e$1.headers.get(r);c&&!t.headers.has(r)&&t.headers.set(r,c);}return e$1.cookies.getAll().forEach(r=>{t.cookies.get(r.name)||t.cookies.set(r.name,r.value);}),t}function ne(e$1){let t=te(e$1),d=z(t),r=W(t,d),c=q(t._resolved.csrf),m=J(t._resolved.rateLimit),l=X(t._resolved.audit);async function f(o){let{pathname:a,origin:u}=o.nextUrl,n=a.startsWith("/api");if(t._resolved.rateLimit.enabled){let g=m.check(o);if(!g.allowed)return await l.rateLimitExceeded(o,{limit:g.limit,resetAt:g.resetAt}),m.createLimitedResponse(o,g)}if(t._resolved.csrf.enabled){let g=c.validateRequest(o);if(!g.valid)return await l.csrfFail(o,{reason:g.reason}),NextResponse.json({success:false,message:"CSRF validation failed"},{status:403})}if(t.blockBrowserApiAccess&&n&&(o.headers.get("accept")||"").includes("text/html"))return NextResponse.redirect(new URL("/",u));if(t.beforeAuth){let g=await t.beforeAuth(o);if(g)return g}if((t.excludedPaths??[]).some(g=>a.startsWith(g)))return h(o,NextResponse.next(),{isAuthenticated:false,isGuest:false,tokenType:null,user:null});if(["/api/auth/login","/api/auth/logout","/api/auth/me","/api/auth/refresh","/api/auth/register"].includes(a))return h(o,NextResponse.next(),{isAuthenticated:false,isGuest:false,tokenType:null,user:null});let N=o.cookies?.get(t.cookies.user)?.value,E=o.cookies?.get(t.cookies.guest)?.value,i=N||E,R=!!N;if(!i){await l.authFail(o,{reason:"no-token"});let g=await r.handleNoToken(o,n);return h(o,g,{isAuthenticated:false,isGuest:false,tokenType:null,user:null})}let x=await d.getTokenInfo(i),y={isAuthenticated:x.isValid&&x.tokenType!=="guest",isGuest:x.isValid&&x.tokenType==="guest",tokenType:x.tokenType,user:x.userData};if(x.isValid)if(x.tokenType==="guest")await l.authGuest(o);else {let g=x.userData?.id?.toString();await l.authSuccess(o,g,{tokenType:x.tokenType});}else await l.authFail(o,{reason:"invalid-token"});let L=await r.handleValidationResult(o,x,R,i,n),T=await h(o,L,y);if(t._resolved.csrf.enabled&&y.isAuthenticated){let g=x.userData?.id?.toString()||i.slice(0,32);T=await c.attachCsrfCookie(T,g);}if(t._resolved.rateLimit.enabled){let g=m.check(o);T=m.applyHeaders(T,g);}return T}async function h(o,a,u){let n=a;if(t.i18n?.middleware){let s=await Promise.resolve(t.i18n.middleware(o));n=se(a,s);}if(t.i18n?.enabled){let s=O(o.nextUrl.pathname,t.i18n);s&&n.headers.set(e.LOCALE,s);}if(t.afterAuth){let s=await t.afterAuth(o,n,u);n=se(n,s);}return n}return f.config=t,f.csrf=c,f.rateLimiter=m,f.audit=l,f}function he(e,t){if(!t||t.length===0)return false;let d=e.replace(/^\/api\//,"").replace(/^\//,"");return t.some(r=>{if(r===d)return true;if(r.includes("*")){let c=r.replace(/\*\*/g,"<<<DOUBLE>>>").replace(/\*/g,"[^/]+").replace(/<<<DOUBLE>>>/g,".+");return new RegExp(`^${c}$`).test(d)}return false})}function oe(e$1){let{apiBaseUrl:t,userCookieName:d="auth_token",guestCookieName:r="guest_token",skipAuthByDefault:c=false,publicEndpoints:m=[],forwardHeaders:l=["content-type","accept","accept-language","x-requested-with"],excludeHeaders:f=["host","connection","cookie"],transformRequest:h,transformResponse:o}=e$1,a=t.replace(/\/$/,"");function u(s,p){return s.headers.get(e.SKIP_AUTH)==="true"||he(p,m)?true:c}async function n(s){try{let p=new URL(s.url),N=p.pathname.replace(/^\/api\/?/,""),E=new URL(`${a}/${N}`);E.search=p.search;let i=new Headers;if(l.forEach(A=>{let w=s.headers.get(A);w&&i.set(A,w);}),s.headers.forEach((A,w)=>{let v=w.toLowerCase();!f.includes(v)&&!i.has(w)&&i.set(w,A);}),!u(s,N)){let A=s.cookies.get(d)?.value,w=s.cookies.get(r)?.value,v=A||w;v&&i.set(e.AUTHORIZATION,`Bearer ${v}`);}i.delete(e.SKIP_AUTH);let R=h?await h(s,i):i,x=null;if(s.method!=="GET"&&s.method!=="HEAD"){let A=s.headers.get("content-type")||"";A.includes("application/json")?x=await s.text():A.includes("multipart/form-data")?x=await s.formData():x=await s.text();}let y=await fetch(E.toString(),{method:s.method,headers:R,body:x}),L=y.headers.get("content-type")||"",T;L.includes("application/json")?T=await y.text():T=await y.arrayBuffer();let g=new Headers;y.headers.forEach((A,w)=>{let v=w.toLowerCase();["transfer-encoding","connection","keep-alive"].includes(v)||g.set(w,A);});let S=new NextResponse(T,{status:y.status,statusText:y.statusText,headers:g});return o&&(S=await o(S)),S}catch(p){return console.error("[Proxy Error]",p),NextResponse.json({success:false,message:"Proxy error: Unable to connect to backend",error:p instanceof Error?p.message:"Unknown error"},{status:502})}}return n.config=e$1,n}
|
|
2
|
+
export{X as createAuditLogger,ne as createAuthProxy,q as createCsrfValidator,W as createHandlers,oe as createProxyHandler,J as createRateLimiter,z as createTokenValidation};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/config.ts","../src/proxy/tokenValidation.ts","../src/proxy/handlers.ts","../src/proxy/csrf.ts","../src/proxy/rateLimit.ts","../src/proxy/audit.ts","../src/proxy/createAuthProxy.ts","../src/proxy/createProxyHandler.ts"],"names":["generateCsrfSecret","defaultRateLimitKeyFn","req","resolveProxyConfig","config","apiBaseUrl","cookieOptions","DEFAULT_COOKIE_OPTIONS","endpoints","DEFAULT_ENDPOINTS","csrf","DEFAULT_CSRF_CONFIG","rateLimit","DEFAULT_RATE_LIMIT_CONFIG","audit","DEFAULT_AUDIT_CONFIG","defaultMappers","response","res","createTokenValidation","_resolved","responseMappers","mappers","validateToken","token","invalidResult","rawResponse","parsed","getTokenInfo","refreshToken","oldToken","newToken","createGuestToken","guestConfig","extractLocale","pathname","i18n","locales","defaultLocale","firstSegment","createHandlers","validation","cookies","guestToken","access","safeDeleteCookie","cookieName","deleteAllAuthCookies","jsonError","message","status","NextResponse","isAuthPage","route","isProtectedRoute","isPublicRoute","isTokenTypeAllowed","tokenType","allowedTypes","handleNoToken","isApiRoute","origin","guestAccessToken","handleValidationResult","tokenInfo","isUserToken","currentToken","isValid","userData","isGuest","TOKEN_TYPES","refreshResult","newTokenInfo","requestHeaders","HEADERS","locale","createCsrfValidator","generateToken","sessionId","randomValue","generateRandomValue","computeHmac","validateRequest","method","strategy","fetchResult","validateFetchMetadata","validateDoubleSubmit","secFetchSite","cookieToken","headerToken","constantTimeEqual","attachCsrfCookie","array","b","secret","encoder","keyData","messageData","key","signature","hash","str","i","char","a","result","createRateLimiter","store","cleanupInterval","now","entry","shouldSkip","pattern","check","remaining","applyHeaders","createLimitedResponse","reset","clear","size","createAuditLogger","isEnabled","type","getIp","emit","options","event","error","authSuccess","userId","metadata","authFail","authRefresh","authGuest","accessDenied","csrfFail","rateLimitExceeded","err","mergeResponses","source","target","criticalHeaders","header","value","cookie","createAuthProxy","userConfig","handlers","rateLimiter","authProxy","rateLimitResult","csrfResult","beforeResult","path","applyMiddlewaresAndHooks","userToken","authResult","finalResponse","intlResponse","hookResponse","matchesPattern","endpoint","patterns","normalizedEndpoint","regexPattern","createProxyHandler","userCookieName","guestCookieName","skipAuthByDefault","publicEndpoints","forwardHeaders","excludeHeaders","transformRequest","transformResponse","baseUrl","shouldSkipAuth","handler","url","backendUrl","headers","headerName","lowerKey","finalHeaders","body","contentType","backendResponse","responseBody","responseHeaders"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Config Resolver\r\n * Resolves and validates configuration with defaults\r\n */\r\n\r\nimport type { NextRequest } from 'next/server';\r\nimport type {\r\n AuthProxyConfig,\r\n InternalProxyConfig,\r\n ApiClientConfig,\r\n ResolvedCookieOptions,\r\n EndpointConfig,\r\n ResolvedCsrfConfig,\r\n ResolvedRateLimitConfig,\r\n ResolvedAuditConfig,\r\n AuditEventType,\r\n} from './types';\r\n\r\nimport {\r\n DEFAULT_COOKIE_OPTIONS,\r\n DEFAULT_ENDPOINTS,\r\n DEFAULT_CSRF_CONFIG,\r\n DEFAULT_RATE_LIMIT_CONFIG,\r\n DEFAULT_AUDIT_CONFIG,\r\n} from './constants';\r\n\r\n/**\r\n * Generates a random secret for CSRF HMAC signing\r\n */\r\nfunction generateCsrfSecret(): string {\r\n // Use crypto if available (Node.js)\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID() + crypto.randomUUID();\r\n }\r\n // Fallback: timestamp + random\r\n return `${Date.now()}-${Math.random().toString(36).substring(2)}`;\r\n}\r\n\r\n/**\r\n * Default rate limit key function (IP-based)\r\n */\r\nfunction defaultRateLimitKeyFn(req: NextRequest): string {\r\n const forwarded = req.headers.get('x-forwarded-for');\r\n const ip = forwarded?.split(',')[0]?.trim() || \r\n req.headers.get('x-real-ip') || \r\n 'unknown';\r\n return `rl:${ip}`;\r\n}\r\n\r\n/**\r\n * Resolves proxy configuration with defaults\r\n */\r\nexport function resolveProxyConfig(config: AuthProxyConfig): InternalProxyConfig {\r\n // Validate required fields\r\n if (!config.apiBaseUrl) {\r\n throw new Error('next-api-layer: apiBaseUrl is required');\r\n }\r\n \r\n if (!config.cookies?.user || !config.cookies?.guest) {\r\n throw new Error('next-api-layer: cookies.user and cookies.guest are required');\r\n }\r\n\r\n // Ensure apiBaseUrl ends with /\r\n const apiBaseUrl = config.apiBaseUrl.endsWith('/')\r\n ? config.apiBaseUrl\r\n : `${config.apiBaseUrl}/`;\r\n\r\n // Resolve cookie options\r\n const cookieOptions: ResolvedCookieOptions = {\r\n ...DEFAULT_COOKIE_OPTIONS,\r\n ...config.cookies.options,\r\n };\r\n\r\n // Resolve endpoints\r\n const endpoints: Required<EndpointConfig> = {\r\n ...DEFAULT_ENDPOINTS,\r\n ...config.endpoints,\r\n };\r\n\r\n // Resolve CSRF config\r\n const csrf: ResolvedCsrfConfig = {\r\n enabled: config.csrf?.enabled ?? false,\r\n strategy: config.csrf?.strategy ?? DEFAULT_CSRF_CONFIG.strategy,\r\n secret: config.csrf?.secret ?? generateCsrfSecret(),\r\n cookieName: config.csrf?.cookieName ?? DEFAULT_CSRF_CONFIG.cookieName,\r\n headerName: config.csrf?.headerName ?? DEFAULT_CSRF_CONFIG.headerName,\r\n ignoreMethods: config.csrf?.ignoreMethods ?? DEFAULT_CSRF_CONFIG.ignoreMethods,\r\n trustSameSite: config.csrf?.trustSameSite ?? DEFAULT_CSRF_CONFIG.trustSameSite,\r\n };\r\n\r\n // Resolve rate limit config\r\n const rateLimit: ResolvedRateLimitConfig = {\r\n enabled: config.rateLimit?.enabled ?? false,\r\n windowMs: config.rateLimit?.windowMs ?? DEFAULT_RATE_LIMIT_CONFIG.windowMs,\r\n maxRequests: config.rateLimit?.maxRequests ?? DEFAULT_RATE_LIMIT_CONFIG.maxRequests,\r\n keyFn: config.rateLimit?.keyFn ?? defaultRateLimitKeyFn,\r\n skipRoutes: config.rateLimit?.skipRoutes ?? DEFAULT_RATE_LIMIT_CONFIG.skipRoutes,\r\n onRateLimited: config.rateLimit?.onRateLimited,\r\n };\r\n\r\n // Resolve audit config\r\n const audit: ResolvedAuditConfig = {\r\n enabled: config.audit?.enabled ?? false,\r\n events: config.audit?.events ?? [...DEFAULT_AUDIT_CONFIG.events] as AuditEventType[],\r\n logger: config.audit?.logger,\r\n };\r\n\r\n return {\r\n ...config,\r\n apiBaseUrl,\r\n _resolved: {\r\n cookieOptions,\r\n endpoints,\r\n csrf,\r\n rateLimit,\r\n audit,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Resolves API client configuration with defaults\r\n */\r\nexport function resolveApiClientConfig(config: ApiClientConfig = {}) {\r\n return {\r\n sanitization: {\r\n enabled: config.sanitization?.enabled ?? true,\r\n allowedTags: config.sanitization?.allowedTags,\r\n skipFields: config.sanitization?.skipFields ?? [],\r\n skipEndpoints: config.sanitization?.skipEndpoints ?? [],\r\n },\r\n i18n: {\r\n enabled: config.i18n?.enabled ?? false,\r\n paramName: config.i18n?.paramName ?? 'lang',\r\n locales: config.i18n?.locales ?? [],\r\n defaultLocale: config.i18n?.defaultLocale ?? 'en',\r\n },\r\n auth: {\r\n skipByDefault: config.auth?.skipByDefault ?? false,\r\n publicEndpoints: config.auth?.publicEndpoints ?? [],\r\n },\r\n methodSpoofing: config.methodSpoofing ?? false,\r\n errorMessages: {\r\n noToken: config.errorMessages?.noToken ?? 'Token bulunamadı.',\r\n connectionError: config.errorMessages?.connectionError ?? 'Bağlantı hatası oluştu.',\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Type guard to check if a value is defined\r\n */\r\nexport function isDefined<T>(value: T | undefined | null): value is T {\r\n return value !== undefined && value !== null;\r\n}\r\n","/**\r\n * Token Validation\r\n * Handles token validation and refresh with backend API\r\n */\r\n\r\nimport type { \r\n TokenInfo, \r\n RefreshResult, \r\n AuthMeResponse, \r\n GuestTokenResponse,\r\n InternalProxyConfig,\r\n ResponseMappers,\r\n} from '../shared/types';\r\n\r\n/**\r\n * Default response parsers (standard format)\r\n */\r\nconst defaultMappers: Required<ResponseMappers> = {\r\n // Default: { success: true, data: { type, exp, ...user } }\r\n parseAuthMe: (response: unknown): TokenInfo | null => {\r\n const res = response as AuthMeResponse | null;\r\n if (!res?.success || !res?.data) {\r\n return null;\r\n }\r\n return {\r\n isValid: true,\r\n tokenType: res.data.type || 'user',\r\n exp: res.data.exp || null,\r\n userData: res.data,\r\n };\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseRefreshToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.success && res?.data?.accessToken ? res.data.accessToken : null;\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseGuestToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.data?.accessToken || null;\r\n },\r\n};\r\n\r\n/**\r\n * Creates token validation functions\r\n */\r\nexport function createTokenValidation(\r\n config: InternalProxyConfig\r\n) {\r\n const { apiBaseUrl, _resolved, responseMappers } = config;\r\n const { endpoints } = _resolved;\r\n \r\n // Merge custom mappers with defaults\r\n const mappers: Required<ResponseMappers> = {\r\n parseAuthMe: responseMappers?.parseAuthMe || defaultMappers.parseAuthMe,\r\n parseRefreshToken: responseMappers?.parseRefreshToken || defaultMappers.parseRefreshToken,\r\n parseGuestToken: responseMappers?.parseGuestToken || defaultMappers.parseGuestToken,\r\n };\r\n\r\n /**\r\n * Validates a token against the backend\r\n */\r\n async function validateToken(token: string): Promise<TokenInfo> {\r\n const invalidResult: TokenInfo = { isValid: false, tokenType: null, exp: null, userData: null };\r\n \r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.validate}`, {\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return invalidResult;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const parsed = mappers.parseAuthMe(rawResponse);\r\n \r\n if (!parsed || !parsed.isValid) {\r\n return invalidResult;\r\n }\r\n\r\n return parsed;\r\n } catch {\r\n return invalidResult;\r\n }\r\n }\r\n\r\n /**\r\n * Gets token info (validates with backend)\r\n */\r\n async function getTokenInfo(token: string): Promise<TokenInfo> {\r\n return validateToken(token);\r\n }\r\n\r\n /**\r\n * Refreshes a token\r\n */\r\n async function refreshToken(oldToken: string): Promise<RefreshResult> {\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.refresh}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Authorization': `Bearer ${oldToken}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const newToken = mappers.parseRefreshToken(rawResponse);\r\n\r\n if (!newToken) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n return { success: true, newToken };\r\n } catch {\r\n return { success: false, newToken: null };\r\n }\r\n }\r\n\r\n /**\r\n * Creates a guest token\r\n */\r\n async function createGuestToken(): Promise<string | null> {\r\n const guestConfig = config.guestToken;\r\n \r\n if (!guestConfig?.enabled || !guestConfig.credentials) {\r\n return null;\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.guest}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n username: guestConfig.credentials.username,\r\n password: guestConfig.credentials.password,\r\n }),\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return null;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n return mappers.parseGuestToken(rawResponse);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n validateToken,\r\n getTokenInfo,\r\n refreshToken,\r\n createGuestToken,\r\n };\r\n}\r\n\r\nexport type TokenValidation = ReturnType<typeof createTokenValidation>;\r\n","/**\r\n * Proxy Handlers\r\n * Request handling logic for different scenarios\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { InternalProxyConfig, TokenInfo } from '../shared/types';\r\nimport { HEADERS, TOKEN_TYPES } from '../shared/constants';\r\nimport type { TokenValidation } from './tokenValidation';\r\n\r\n/**\r\n * Extracts locale from pathname based on i18n config\r\n * e.g., /en/dashboard → 'en', /dashboard → defaultLocale or null\r\n */\r\nfunction extractLocale(pathname: string, i18n?: InternalProxyConfig['i18n']): string | null {\r\n if (!i18n?.enabled) return null;\r\n \r\n const locales = i18n.locales ?? [];\r\n const defaultLocale = i18n.defaultLocale;\r\n \r\n // Extract first path segment\r\n const segments = pathname.split('/').filter(Boolean);\r\n const firstSegment = segments[0];\r\n \r\n // Check if it's a valid locale\r\n if (firstSegment && locales.includes(firstSegment)) {\r\n return firstSegment;\r\n }\r\n \r\n // Return default locale if provided\r\n return defaultLocale ?? null;\r\n}\r\n\r\n/**\r\n * Creates proxy handlers\r\n */\r\nexport function createHandlers(\r\n config: InternalProxyConfig,\r\n validation: TokenValidation\r\n) {\r\n const { cookies, guestToken, access, i18n, _resolved } = config;\r\n const { cookieOptions } = _resolved;\r\n\r\n /**\r\n * Safely deletes a cookie only if it exists in the request.\r\n * Prevents empty-value cookies from being created when deleting non-existent cookies.\r\n */\r\n function safeDeleteCookie(req: NextRequest, response: NextResponse, cookieName: string): void {\r\n if (req.cookies.get(cookieName)?.value) {\r\n response.cookies.delete(cookieName);\r\n }\r\n }\r\n\r\n /**\r\n * Deletes all auth cookies from response (only if they exist)\r\n */\r\n function deleteAllAuthCookies(req: NextRequest, response: NextResponse): NextResponse {\r\n safeDeleteCookie(req, response, cookies.guest);\r\n safeDeleteCookie(req, response, cookies.user);\r\n return response;\r\n }\r\n\r\n /**\r\n * Creates a JSON error response\r\n */\r\n function jsonError(message: string, status = 500): NextResponse {\r\n return new NextResponse(\r\n JSON.stringify({ success: false, message }),\r\n { status, headers: { 'Content-Type': 'application/json' } }\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is an auth page\r\n */\r\n function isAuthPage(pathname: string): boolean {\r\n const authRoutes = access?.authRoutes ?? [];\r\n return authRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is a protected route\r\n */\r\n function isProtectedRoute(pathname: string): boolean {\r\n // If protectedByDefault is true, everything is protected except public/auth routes\r\n if (access?.protectedByDefault) {\r\n return !isPublicRoute(pathname) && !isAuthPage(pathname);\r\n }\r\n \r\n const protectedRoutes = access?.protectedRoutes ?? [];\r\n return protectedRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is explicitly public\r\n */\r\n function isPublicRoute(pathname: string): boolean {\r\n const publicRoutes = access?.publicRoutes ?? [];\r\n return publicRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if token type is allowed\r\n */\r\n function isTokenTypeAllowed(tokenType: string | null): boolean {\r\n const allowedTypes = access?.allowedTokenTypes;\r\n \r\n // If no restriction, all types allowed\r\n if (!allowedTypes || allowedTypes.length === 0) {\r\n return true;\r\n }\r\n \r\n return tokenType ? allowedTypes.includes(tokenType) : false;\r\n }\r\n\r\n /**\r\n * Handles request when no token is present\r\n */\r\n async function handleNoToken(\r\n req: NextRequest,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { origin } = req.nextUrl;\r\n \r\n // Try to create guest token\r\n if (guestToken?.enabled) {\r\n const guestAccessToken = await validation.createGuestToken();\r\n \r\n if (guestAccessToken) {\r\n let response: NextResponse;\r\n \r\n if (isApiRoute) {\r\n response = NextResponse.next();\r\n } else if (isProtectedRoute(req.nextUrl.pathname)) {\r\n // Redirect to login if protected route\r\n response = NextResponse.redirect(new URL('/login', origin));\r\n } else {\r\n response = NextResponse.next();\r\n }\r\n \r\n response.cookies.set(cookies.guest, guestAccessToken, {\r\n ...cookieOptions,\r\n maxAge: 3600, // 1 hour default for guest tokens\r\n });\r\n \r\n return response;\r\n }\r\n }\r\n \r\n // No guest token - just continue or redirect\r\n if (isApiRoute) {\r\n return jsonError('Token bulunamadı', 401);\r\n }\r\n \r\n if (isProtectedRoute(req.nextUrl.pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n \r\n return NextResponse.next();\r\n }\r\n\r\n /**\r\n * Handles token validation result\r\n */\r\n async function handleValidationResult(\r\n req: NextRequest,\r\n tokenInfo: TokenInfo,\r\n isUserToken: boolean,\r\n currentToken: string,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const { isValid, tokenType, userData } = tokenInfo;\r\n const isGuest = tokenType === TOKEN_TYPES.GUEST;\r\n\r\n // ===== TOKEN INVALID =====\r\n if (!isValid) {\r\n // Try refresh if user token\r\n if (isUserToken && currentToken) {\r\n const refreshResult = await validation.refreshToken(currentToken);\r\n \r\n if (refreshResult.success && refreshResult.newToken) {\r\n const newTokenInfo = await validation.getTokenInfo(refreshResult.newToken);\r\n \r\n if (newTokenInfo.isValid) {\r\n // Successful refresh\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (newTokenInfo.userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(newTokenInfo.userData));\r\n }\r\n requestHeaders.set(HEADERS.REFRESHED_TOKEN, refreshResult.newToken);\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n let response: NextResponse;\r\n \r\n if (isAuthPage(pathname)) {\r\n response = NextResponse.redirect(new URL('/', origin));\r\n } else {\r\n response = NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n response.cookies.set(cookies.user, refreshResult.newToken, {\r\n ...cookieOptions,\r\n maxAge: cookieOptions.maxAge,\r\n });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n\r\n return response;\r\n }\r\n }\r\n }\r\n\r\n // Refresh failed or no user token - handle as no token\r\n const response = await handleNoToken(req, isApiRoute);\r\n \r\n // Check if handleNoToken created a new guest token\r\n const hasNewGuestToken = response.cookies.get(cookies.guest)?.value;\r\n \r\n if (!hasNewGuestToken) {\r\n // No new guest token created - delete all auth cookies\r\n deleteAllAuthCookies(req, response);\r\n } else {\r\n // New guest token created - only delete the invalid user cookie\r\n safeDeleteCookie(req, response, cookies.user);\r\n }\r\n \r\n return response;\r\n }\r\n\r\n // ===== TOKEN VALID =====\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(userData));\r\n }\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n // Check if token type is allowed\r\n if (!isGuest && !isTokenTypeAllowed(tokenType)) {\r\n if (isApiRoute) {\r\n const response = jsonError('Bu işlem için yetkiniz yok', 403);\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n \r\n const response = NextResponse.redirect(new URL('/login', origin));\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n\r\n // Guest token handling\r\n if (isGuest) {\r\n if (isApiRoute) {\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // Protected routes require login\r\n if (isProtectedRoute(pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // User token - block auth pages\r\n if (isAuthPage(pathname)) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n\r\n // Normal access\r\n const response = NextResponse.next({ request: { headers: requestHeaders } });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n \r\n return response;\r\n }\r\n\r\n return {\r\n deleteAllAuthCookies,\r\n jsonError,\r\n isAuthPage,\r\n isProtectedRoute,\r\n isPublicRoute,\r\n isTokenTypeAllowed,\r\n handleNoToken,\r\n handleValidationResult,\r\n };\r\n}\r\n\r\nexport type Handlers = ReturnType<typeof createHandlers>;\r\n","/**\r\n * CSRF Protection\r\n * \r\n * Implements OWASP recommended CSRF protection:\r\n * - Fetch Metadata (Sec-Fetch-Site header) for modern browsers (98%+ coverage)\r\n * - Signed HMAC Double-Submit Cookie as fallback\r\n * \r\n * @see https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedCsrfConfig } from '../shared/types';\r\n\r\nexport interface CsrfValidationResult {\r\n valid: boolean;\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Creates CSRF validator functions\r\n */\r\nexport function createCsrfValidator(config: ResolvedCsrfConfig) {\r\n /**\r\n * Generate HMAC-signed CSRF token\r\n * Format: hmac.randomValue\r\n */\r\n async function generateToken(sessionId: string): Promise<string> {\r\n const randomValue = generateRandomValue();\r\n const hmac = await computeHmac(config.secret, sessionId, randomValue);\r\n return `${hmac}.${randomValue}`;\r\n }\r\n\r\n /**\r\n * Validate CSRF request using configured strategy\r\n */\r\n function validateRequest(req: NextRequest): CsrfValidationResult {\r\n const method = req.method.toUpperCase();\r\n \r\n // Skip safe methods\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n\r\n const strategy = config.strategy;\r\n \r\n // Fetch Metadata validation (primary, modern browsers)\r\n if (strategy === 'fetch-metadata' || strategy === 'both') {\r\n const fetchResult = validateFetchMetadata(req);\r\n \r\n if (strategy === 'fetch-metadata') {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata passes, we're good\r\n if (fetchResult.valid) {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata fails or not available, try double-submit\r\n if (fetchResult.reason === 'missing-headers') {\r\n // Fall through to double-submit\r\n } else {\r\n // Explicit cross-site rejection from Fetch Metadata\r\n return fetchResult;\r\n }\r\n }\r\n\r\n // Double-Submit Cookie validation (fallback)\r\n if (strategy === 'double-submit' || strategy === 'both') {\r\n return validateDoubleSubmit(req);\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Validate using Fetch Metadata headers (Sec-Fetch-Site)\r\n * @see https://web.dev/fetch-metadata/\r\n */\r\n function validateFetchMetadata(req: NextRequest): CsrfValidationResult {\r\n const secFetchSite = req.headers.get('sec-fetch-site');\r\n \r\n // No Fetch Metadata headers (older browser or stripped by proxy)\r\n if (!secFetchSite) {\r\n return { valid: false, reason: 'missing-headers' };\r\n }\r\n\r\n // Same-origin requests are always trusted\r\n if (secFetchSite === 'same-origin') {\r\n return { valid: true };\r\n }\r\n\r\n // None = direct navigation (bookmark, typed URL) - allow for GET\r\n if (secFetchSite === 'none') {\r\n const method = req.method.toUpperCase();\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n // Non-safe method from direct navigation is suspicious\r\n return { valid: false, reason: 'direct-navigation-unsafe-method' };\r\n }\r\n\r\n // Same-site: trust based on config\r\n if (secFetchSite === 'same-site') {\r\n if (config.trustSameSite) {\r\n return { valid: true };\r\n }\r\n // Conservative: don't trust same-site by default (subdomain takeover risk)\r\n return { valid: false, reason: 'same-site-not-trusted' };\r\n }\r\n\r\n // Cross-site: reject state-changing requests\r\n if (secFetchSite === 'cross-site') {\r\n return { valid: false, reason: 'cross-site-request' };\r\n }\r\n\r\n // Unknown value - be conservative\r\n return { valid: false, reason: 'unknown-sec-fetch-site' };\r\n }\r\n\r\n /**\r\n * Validate using Double-Submit Cookie pattern with HMAC\r\n */\r\n function validateDoubleSubmit(req: NextRequest): CsrfValidationResult {\r\n // Get token from cookie\r\n const cookieToken = req.cookies.get(config.cookieName)?.value;\r\n \r\n if (!cookieToken) {\r\n return { valid: false, reason: 'missing-cookie-token' };\r\n }\r\n\r\n // Get token from header (or form field)\r\n const headerToken = req.headers.get(config.headerName);\r\n \r\n if (!headerToken) {\r\n return { valid: false, reason: 'missing-header-token' };\r\n }\r\n\r\n // Tokens must match (constant-time comparison to prevent timing attacks)\r\n if (!constantTimeEqual(cookieToken, headerToken)) {\r\n return { valid: false, reason: 'token-mismatch' };\r\n }\r\n\r\n // Validate HMAC structure\r\n const parts = cookieToken.split('.');\r\n if (parts.length !== 2) {\r\n return { valid: false, reason: 'invalid-token-format' };\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Create response with CSRF cookie set\r\n */\r\n async function attachCsrfCookie(\r\n response: NextResponse, \r\n sessionId: string\r\n ): Promise<NextResponse> {\r\n const token = await generateToken(sessionId);\r\n \r\n response.cookies.set(config.cookieName, token, {\r\n httpOnly: false, // Must be readable by JS\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/',\r\n });\r\n\r\n return response;\r\n }\r\n\r\n return {\r\n validateRequest,\r\n generateToken,\r\n attachCsrfCookie,\r\n };\r\n}\r\n\r\n// ==================== Helper Functions ====================\r\n\r\n/**\r\n * Generate cryptographically random value\r\n */\r\nfunction generateRandomValue(): string {\r\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n return Array.from(array, b => b.toString(16).padStart(2, '0')).join('');\r\n }\r\n // Fallback (less secure, for edge cases)\r\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\r\n}\r\n\r\n/**\r\n * Compute HMAC-SHA256\r\n */\r\nasync function computeHmac(\r\n secret: string, \r\n sessionId: string, \r\n randomValue: string\r\n): Promise<string> {\r\n const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`;\r\n \r\n if (typeof crypto !== 'undefined' && crypto.subtle) {\r\n const encoder = new TextEncoder();\r\n const keyData = encoder.encode(secret);\r\n const messageData = encoder.encode(message);\r\n \r\n const key = await crypto.subtle.importKey(\r\n 'raw',\r\n keyData,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign']\r\n );\r\n \r\n const signature = await crypto.subtle.sign('HMAC', key, messageData);\r\n return Array.from(new Uint8Array(signature), b => \r\n b.toString(16).padStart(2, '0')\r\n ).join('');\r\n }\r\n \r\n // Fallback (less secure)\r\n let hash = 0;\r\n const str = secret + message;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = ((hash << 5) - hash) + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(16);\r\n}\r\n\r\n/**\r\n * Constant-time string comparison (prevents timing attacks)\r\n */\r\nfunction constantTimeEqual(a: string, b: string): boolean {\r\n if (a.length !== b.length) {\r\n return false;\r\n }\r\n \r\n let result = 0;\r\n for (let i = 0; i < a.length; i++) {\r\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\r\n }\r\n \r\n return result === 0;\r\n}\r\n\r\nexport type CsrfValidator = ReturnType<typeof createCsrfValidator>;\r\n","/**\r\n * Rate Limiting\r\n * \r\n * Implements token bucket algorithm for rate limiting.\r\n * In-memory store by default, designed for single-instance deployments.\r\n * \r\n * For horizontal scaling (multiple instances), use a custom store\r\n * with Redis or similar distributed cache.\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedRateLimitConfig } from '../shared/types';\r\n\r\ninterface RateLimitEntry {\r\n count: number;\r\n resetAt: number;\r\n}\r\n\r\nexport interface RateLimitResult {\r\n allowed: boolean;\r\n remaining: number;\r\n resetAt: number;\r\n limit: number;\r\n}\r\n\r\n/**\r\n * Creates a rate limiter with in-memory store\r\n */\r\nexport function createRateLimiter(config: ResolvedRateLimitConfig) {\r\n // In-memory store\r\n const store = new Map<string, RateLimitEntry>();\r\n \r\n // Cleanup expired entries periodically\r\n const cleanupInterval = setInterval(() => {\r\n const now = Date.now();\r\n for (const [key, entry] of store) {\r\n if (entry.resetAt <= now) {\r\n store.delete(key);\r\n }\r\n }\r\n }, config.windowMs);\r\n\r\n // Prevent memory leak in long-running processes\r\n if (typeof process !== 'undefined' && process.on) {\r\n process.on('beforeExit', () => clearInterval(cleanupInterval));\r\n }\r\n\r\n /**\r\n * Check if route should skip rate limiting\r\n */\r\n function shouldSkip(pathname: string): boolean {\r\n return config.skipRoutes.some(pattern => {\r\n // Simple glob matching\r\n if (pattern.endsWith('*')) {\r\n return pathname.startsWith(pattern.slice(0, -1));\r\n }\r\n if (pattern.endsWith('**')) {\r\n return pathname.startsWith(pattern.slice(0, -2));\r\n }\r\n return pathname === pattern;\r\n });\r\n }\r\n\r\n /**\r\n * Check rate limit for request\r\n */\r\n function check(req: NextRequest): RateLimitResult {\r\n const pathname = req.nextUrl.pathname;\r\n \r\n // Skip if route matches skip patterns\r\n if (shouldSkip(pathname)) {\r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests,\r\n resetAt: 0,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n const key = config.keyFn(req);\r\n const now = Date.now();\r\n \r\n let entry = store.get(key);\r\n \r\n // New window or expired\r\n if (!entry || entry.resetAt <= now) {\r\n entry = {\r\n count: 1,\r\n resetAt: now + config.windowMs,\r\n };\r\n store.set(key, entry);\r\n \r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests - 1,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n // Existing window\r\n entry.count++;\r\n \r\n const remaining = Math.max(0, config.maxRequests - entry.count);\r\n const allowed = entry.count <= config.maxRequests;\r\n \r\n return {\r\n allowed,\r\n remaining,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n /**\r\n * Apply rate limit headers to response\r\n */\r\n function applyHeaders(response: NextResponse, result: RateLimitResult): NextResponse {\r\n response.headers.set('X-RateLimit-Limit', result.limit.toString());\r\n response.headers.set('X-RateLimit-Remaining', result.remaining.toString());\r\n response.headers.set('X-RateLimit-Reset', Math.ceil(result.resetAt / 1000).toString());\r\n return response;\r\n }\r\n\r\n /**\r\n * Create rate limited response (429 Too Many Requests)\r\n */\r\n function createLimitedResponse(req: NextRequest, result: RateLimitResult): NextResponse {\r\n // User-provided handler\r\n if (config.onRateLimited) {\r\n const response = config.onRateLimited(req);\r\n return applyHeaders(response, result);\r\n }\r\n\r\n // Default response\r\n const response = NextResponse.json(\r\n {\r\n success: false,\r\n message: 'Too many requests. Please try again later.',\r\n retryAfter: Math.ceil((result.resetAt - Date.now()) / 1000),\r\n },\r\n { status: 429 }\r\n );\r\n\r\n response.headers.set('Retry-After', Math.ceil((result.resetAt - Date.now()) / 1000).toString());\r\n return applyHeaders(response, result);\r\n }\r\n\r\n /**\r\n * Reset rate limit for a key (useful for testing)\r\n */\r\n function reset(key: string): void {\r\n store.delete(key);\r\n }\r\n\r\n /**\r\n * Clear all rate limit entries\r\n */\r\n function clear(): void {\r\n store.clear();\r\n }\r\n\r\n /**\r\n * Get current store size (for monitoring)\r\n */\r\n function size(): number {\r\n return store.size;\r\n }\r\n\r\n return {\r\n check,\r\n applyHeaders,\r\n createLimitedResponse,\r\n shouldSkip,\r\n reset,\r\n clear,\r\n size,\r\n };\r\n}\r\n\r\nexport type RateLimiter = ReturnType<typeof createRateLimiter>;\r\n","/**\r\n * Audit Logging\r\n * \r\n * Event-based security audit logging system.\r\n * Emits events for authentication, access control, and security violations.\r\n */\r\n\r\nimport { NextRequest } from 'next/server';\r\nimport type { ResolvedAuditConfig, AuditEvent, AuditEventType } from '../shared/types';\r\n\r\n/**\r\n * Creates an audit logger instance\r\n */\r\nexport function createAuditLogger(config: ResolvedAuditConfig) {\r\n /**\r\n * Check if event type is enabled\r\n */\r\n function isEnabled(type: AuditEventType): boolean {\r\n return config.enabled && config.events.includes(type);\r\n }\r\n\r\n /**\r\n * Extract IP address from request\r\n */\r\n function getIp(req: NextRequest): string | null {\r\n return (\r\n req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||\r\n req.headers.get('x-real-ip') ||\r\n null\r\n );\r\n }\r\n\r\n /**\r\n * Emit an audit event\r\n */\r\n async function emit(\r\n type: AuditEventType,\r\n req: NextRequest,\r\n options: {\r\n success: boolean;\r\n userId?: string;\r\n metadata?: Record<string, unknown>;\r\n }\r\n ): Promise<void> {\r\n if (!isEnabled(type)) {\r\n return;\r\n }\r\n\r\n const event: AuditEvent = {\r\n type,\r\n timestamp: new Date(),\r\n ip: getIp(req),\r\n userId: options.userId,\r\n path: req.nextUrl.pathname,\r\n method: req.method,\r\n success: options.success,\r\n metadata: options.metadata,\r\n };\r\n\r\n // Call user's logger\r\n if (config.logger) {\r\n try {\r\n await config.logger(event);\r\n } catch (error) {\r\n // Silently fail - don't break the request flow for logging errors\r\n console.error('[next-api-layer] Audit logger error:', error);\r\n }\r\n }\r\n }\r\n\r\n // ==================== Convenience Methods ====================\r\n\r\n /**\r\n * Log successful authentication\r\n */\r\n function authSuccess(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:success', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log failed authentication\r\n */\r\n function authFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log token refresh\r\n */\r\n function authRefresh(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:refresh', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log guest token creation\r\n */\r\n function authGuest(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:guest', req, { success: true, metadata });\r\n }\r\n\r\n /**\r\n * Log access denied\r\n */\r\n function accessDenied(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('access:denied', req, { success: false, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log CSRF validation failure\r\n */\r\n function csrfFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('csrf:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log rate limit exceeded\r\n */\r\n function rateLimitExceeded(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('rateLimit:exceeded', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log general error\r\n */\r\n function error(req: NextRequest, err: Error, metadata?: Record<string, unknown>) {\r\n return emit('error', req, { \r\n success: false, \r\n metadata: { \r\n ...metadata, \r\n error: err.message,\r\n stack: err.stack,\r\n } \r\n });\r\n }\r\n\r\n return {\r\n emit,\r\n isEnabled,\r\n // Convenience methods\r\n authSuccess,\r\n authFail,\r\n authRefresh,\r\n authGuest,\r\n accessDenied,\r\n csrfFail,\r\n rateLimitExceeded,\r\n error,\r\n };\r\n}\r\n\r\nexport type AuditLogger = ReturnType<typeof createAuditLogger>;\r\n","/**\r\n * createAuthProxy\r\n * Factory function to create Next.js middleware for external JWT authentication\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { AuthProxyConfig, AuthResult } from '../shared/types';\r\nimport { resolveProxyConfig } from '../shared/config';\r\nimport { createTokenValidation } from './tokenValidation';\r\nimport { createHandlers } from './handlers';\r\nimport { createCsrfValidator } from './csrf';\r\nimport { createRateLimiter } from './rateLimit';\r\nimport { createAuditLogger } from './audit';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\n/**\r\n * Merge two NextResponse objects, preserving headers and cookies from both\r\n * Target response takes priority for conflicts\r\n */\r\nfunction mergeResponses(source: NextResponse, target: NextResponse): NextResponse {\r\n // Copy critical headers from source to target (if not already set)\r\n const criticalHeaders = [HEADERS.LOCALE, HEADERS.AUTH_USER, HEADERS.REFRESHED_TOKEN];\r\n \r\n for (const header of criticalHeaders) {\r\n const value = source.headers.get(header);\r\n if (value && !target.headers.has(header)) {\r\n target.headers.set(header, value);\r\n }\r\n }\r\n \r\n // Copy cookies from source to target (if not already set)\r\n source.cookies.getAll().forEach(cookie => {\r\n if (!target.cookies.get(cookie.name)) {\r\n target.cookies.set(cookie.name, cookie.value);\r\n }\r\n });\r\n \r\n return target;\r\n}\r\n\r\n/**\r\n * Creates an authentication proxy middleware for Next.js\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts\r\n * import { createAuthProxy } from 'next-api-layer';\r\n * \r\n * const authProxy = createAuthProxy({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * cookies: {\r\n * user: 'userAuthToken',\r\n * guest: 'guestAuthToken',\r\n * },\r\n * guestToken: {\r\n * enabled: true,\r\n * credentials: {\r\n * username: process.env.GUEST_USERNAME!,\r\n * password: process.env.GUEST_PASSWORD!,\r\n * },\r\n * },\r\n * access: {\r\n * protectedRoutes: ['/dashboard', '/profile'],\r\n * authRoutes: ['/login', '/register'],\r\n * },\r\n * // Security features\r\n * csrf: { enabled: true },\r\n * rateLimit: { enabled: true, maxRequests: 100 },\r\n * audit: { \r\n * enabled: true, \r\n * logger: (event) => console.log('[AUDIT]', event) \r\n * },\r\n * });\r\n * \r\n * export default authProxy;\r\n * \r\n * export const config = {\r\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\r\n * };\r\n * ```\r\n */\r\nexport function createAuthProxy(userConfig: AuthProxyConfig) {\r\n // Resolve config with defaults\r\n const config = resolveProxyConfig(userConfig);\r\n \r\n // Create validation functions\r\n const validation = createTokenValidation(config);\r\n \r\n // Create handlers\r\n const handlers = createHandlers(config, validation);\r\n\r\n // Create security modules\r\n const csrf = createCsrfValidator(config._resolved.csrf);\r\n const rateLimiter = createRateLimiter(config._resolved.rateLimit);\r\n const audit = createAuditLogger(config._resolved.audit);\r\n\r\n /**\r\n * The middleware function\r\n */\r\n async function authProxy(req: NextRequest): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const isApiRoute = pathname.startsWith('/api');\r\n\r\n // ============ Rate Limiting ============\r\n // Check early to protect against DoS\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n \r\n if (!rateLimitResult.allowed) {\r\n await audit.rateLimitExceeded(req, { \r\n limit: rateLimitResult.limit,\r\n resetAt: rateLimitResult.resetAt,\r\n });\r\n return rateLimiter.createLimitedResponse(req, rateLimitResult);\r\n }\r\n }\r\n\r\n // ============ CSRF Protection ============\r\n // Check before any state-changing operations\r\n if (config._resolved.csrf.enabled) {\r\n const csrfResult = csrf.validateRequest(req);\r\n \r\n if (!csrfResult.valid) {\r\n await audit.csrfFail(req, { reason: csrfResult.reason });\r\n return NextResponse.json(\r\n { success: false, message: 'CSRF validation failed' },\r\n { status: 403 }\r\n );\r\n }\r\n }\r\n\r\n // ============ Block Browser API Access ============\r\n // Prevents direct browser access to API routes (when Accept: text/html)\r\n if (config.blockBrowserApiAccess && isApiRoute) {\r\n const acceptHeader = req.headers.get('accept') || '';\r\n if (acceptHeader.includes('text/html')) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n }\r\n\r\n // ============ beforeAuth Hook ============\r\n // Allows user to handle request before auth validation\r\n if (config.beforeAuth) {\r\n const beforeResult = await config.beforeAuth(req);\r\n if (beforeResult) {\r\n return beforeResult; // User handled the request\r\n }\r\n }\r\n\r\n // Skip excluded paths\r\n const excludedPaths = config.excludedPaths ?? [];\r\n if (excludedPaths.some(path => pathname.startsWith(path))) {\r\n return applyMiddlewaresAndHooks(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Skip auth API endpoints (they handle their own auth)\r\n const authApiPaths = [\r\n '/api/auth/login',\r\n '/api/auth/logout',\r\n '/api/auth/me',\r\n '/api/auth/refresh',\r\n '/api/auth/register',\r\n ];\r\n \r\n if (authApiPaths.includes(pathname)) {\r\n return applyMiddlewaresAndHooks(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Get tokens from cookies\r\n const userToken = req.cookies?.get(config.cookies.user)?.value;\r\n const guestToken = req.cookies?.get(config.cookies.guest)?.value;\r\n const currentToken = userToken || guestToken;\r\n const isUserToken = !!userToken;\r\n\r\n // No token - handle appropriately\r\n if (!currentToken) {\r\n await audit.authFail(req, { reason: 'no-token' });\r\n const response = await handlers.handleNoToken(req, isApiRoute);\r\n return applyMiddlewaresAndHooks(req, response, { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Validate token\r\n const tokenInfo = await validation.getTokenInfo(currentToken);\r\n \r\n // Build auth result for afterAuth hook\r\n const authResult: AuthResult = {\r\n isAuthenticated: tokenInfo.isValid && tokenInfo.tokenType !== 'guest',\r\n isGuest: tokenInfo.isValid && tokenInfo.tokenType === 'guest',\r\n tokenType: tokenInfo.tokenType,\r\n user: tokenInfo.userData,\r\n };\r\n\r\n // Audit logging based on validation result \r\n if (tokenInfo.isValid) {\r\n if (tokenInfo.tokenType === 'guest') {\r\n await audit.authGuest(req);\r\n } else {\r\n const userId = tokenInfo.userData?.id?.toString();\r\n await audit.authSuccess(req, userId, { tokenType: tokenInfo.tokenType });\r\n }\r\n } else {\r\n await audit.authFail(req, { reason: 'invalid-token' });\r\n }\r\n\r\n // Handle validation result\r\n const response = await handlers.handleValidationResult(\r\n req,\r\n tokenInfo,\r\n isUserToken,\r\n currentToken,\r\n isApiRoute\r\n );\r\n \r\n // Apply CSRF cookie if enabled (for authenticated requests)\r\n let finalResponse = await applyMiddlewaresAndHooks(req, response, authResult);\r\n \r\n if (config._resolved.csrf.enabled && authResult.isAuthenticated) {\r\n const sessionId = tokenInfo.userData?.id?.toString() || currentToken.slice(0, 32);\r\n finalResponse = await csrf.attachCsrfCookie(finalResponse, sessionId);\r\n }\r\n\r\n // Apply rate limit headers\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n finalResponse = rateLimiter.applyHeaders(finalResponse, rateLimitResult);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n \r\n /**\r\n * Helper to apply i18n middleware and afterAuth hook\r\n * Merges responses to preserve critical headers (x-locale, x-auth-user, etc.)\r\n */\r\n async function applyMiddlewaresAndHooks(req: NextRequest, response: NextResponse, authResult: AuthResult): Promise<NextResponse> {\r\n let finalResponse = response;\r\n \r\n // Apply i18n middleware if configured\r\n if (config.i18n?.middleware) {\r\n const intlResponse = await Promise.resolve(config.i18n.middleware(req));\r\n // Merge library's response headers into i18n response\r\n finalResponse = mergeResponses(response, intlResponse);\r\n }\r\n \r\n // Apply afterAuth hook if configured\r\n if (config.afterAuth) {\r\n const hookResponse = await config.afterAuth(req, finalResponse, authResult);\r\n // Merge previous response headers into hook's response\r\n finalResponse = mergeResponses(finalResponse, hookResponse);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n\r\n // Attach instances for debugging/testing\r\n authProxy.config = config;\r\n authProxy.csrf = csrf;\r\n authProxy.rateLimiter = rateLimiter;\r\n authProxy.audit = audit;\r\n\r\n return authProxy;\r\n}\r\n\r\nexport type AuthProxy = ReturnType<typeof createAuthProxy>;\r\n\r\n","/**\r\n * createProxyHandler\r\n * Factory function to create Next.js API route handlers that proxy requests to backend\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\nexport interface ProxyHandlerConfig {\r\n /** Base URL of the backend API */\r\n apiBaseUrl: string;\r\n /** Cookie name for user auth token */\r\n userCookieName?: string;\r\n /** Cookie name for guest auth token */\r\n guestCookieName?: string;\r\n /** \r\n * Default behavior for auth - if true, all requests skip auth by default\r\n * Individual requests can override with X-Skip-Auth header\r\n */\r\n skipAuthByDefault?: boolean;\r\n /**\r\n * Public endpoints that should never include auth token (glob patterns)\r\n * e.g., ['news/*', 'public/**', 'categories']\r\n */\r\n publicEndpoints?: string[];\r\n /** Headers to forward from client request */\r\n forwardHeaders?: string[];\r\n /** Headers to exclude from forwarding */\r\n excludeHeaders?: string[];\r\n /** Custom request transformer */\r\n transformRequest?: (req: NextRequest, headers: Headers) => Headers | Promise<Headers>;\r\n /** Custom response transformer */\r\n transformResponse?: (response: Response) => Response | Promise<Response>;\r\n}\r\n\r\n/**\r\n * Check if endpoint matches any patterns (glob support)\r\n */\r\nfunction matchesPattern(endpoint: string, patterns: string[]): boolean {\r\n if (!patterns || patterns.length === 0) return false;\r\n \r\n // Normalize endpoint\r\n const normalizedEndpoint = endpoint\r\n .replace(/^\\/api\\//, '')\r\n .replace(/^\\//, '');\r\n \r\n return patterns.some(pattern => {\r\n if (pattern === normalizedEndpoint) return true;\r\n \r\n if (pattern.includes('*')) {\r\n const regexPattern = pattern\r\n .replace(/\\*\\*/g, '<<<DOUBLE>>>')\r\n .replace(/\\*/g, '[^/]+')\r\n .replace(/<<<DOUBLE>>>/g, '.+');\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(normalizedEndpoint);\r\n }\r\n \r\n return false;\r\n });\r\n}\r\n\r\n/**\r\n * Creates a proxy handler for Next.js API routes\r\n * \r\n * @example\r\n * ```ts\r\n * // app/api/[...path]/route.ts\r\n * import { createProxyHandler } from 'next-api-layer';\r\n * \r\n * const handler = createProxyHandler({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * userCookieName: 'auth_token',\r\n * guestCookieName: 'guest_token',\r\n * publicEndpoints: ['news/*', 'categories', 'public/**'],\r\n * });\r\n * \r\n * export const GET = handler;\r\n * export const POST = handler;\r\n * export const PUT = handler;\r\n * export const PATCH = handler;\r\n * export const DELETE = handler;\r\n * ```\r\n */\r\nexport function createProxyHandler(config: ProxyHandlerConfig) {\r\n const {\r\n apiBaseUrl,\r\n userCookieName = 'auth_token',\r\n guestCookieName = 'guest_token',\r\n skipAuthByDefault = false,\r\n publicEndpoints = [],\r\n forwardHeaders = ['content-type', 'accept', 'accept-language', 'x-requested-with'],\r\n excludeHeaders = ['host', 'connection', 'cookie'],\r\n transformRequest,\r\n transformResponse,\r\n } = config;\r\n\r\n // Normalize base URL (remove trailing slash)\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n /**\r\n * Determine if auth should be skipped for this request\r\n */\r\n function shouldSkipAuth(req: NextRequest, endpoint: string): boolean {\r\n // Check X-Skip-Auth header from client\r\n const skipAuthHeader = req.headers.get(HEADERS.SKIP_AUTH);\r\n if (skipAuthHeader === 'true') {\r\n return true;\r\n }\r\n \r\n // Check if endpoint matches public patterns\r\n if (matchesPattern(endpoint, publicEndpoints)) {\r\n return true;\r\n }\r\n \r\n // Default behavior\r\n return skipAuthByDefault;\r\n }\r\n\r\n /**\r\n * The proxy handler function\r\n */\r\n async function handler(req: NextRequest): Promise<NextResponse> {\r\n try {\r\n // Extract endpoint from URL path (remove /api/ prefix)\r\n const url = new URL(req.url);\r\n const endpoint = url.pathname.replace(/^\\/api\\/?/, '');\r\n \r\n // Build backend URL\r\n const backendUrl = new URL(`${baseUrl}/${endpoint}`);\r\n backendUrl.search = url.search; // Forward query params\r\n\r\n // Build headers for backend request\r\n const headers = new Headers();\r\n \r\n // Forward allowed headers from original request\r\n forwardHeaders.forEach(headerName => {\r\n const value = req.headers.get(headerName);\r\n if (value) {\r\n headers.set(headerName, value);\r\n }\r\n });\r\n\r\n // Forward all headers except excluded ones\r\n req.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n if (!excludeHeaders.includes(lowerKey) && !headers.has(key)) {\r\n headers.set(key, value);\r\n }\r\n });\r\n\r\n // Add Authorization header if auth is not skipped\r\n if (!shouldSkipAuth(req, endpoint)) {\r\n const userToken = req.cookies.get(userCookieName)?.value;\r\n const guestToken = req.cookies.get(guestCookieName)?.value;\r\n const token = userToken || guestToken;\r\n \r\n if (token) {\r\n headers.set(HEADERS.AUTHORIZATION, `Bearer ${token}`);\r\n }\r\n }\r\n\r\n // Remove skip-auth header (internal use only)\r\n headers.delete(HEADERS.SKIP_AUTH);\r\n\r\n // Allow custom request transformation\r\n const finalHeaders = transformRequest \r\n ? await transformRequest(req, headers) \r\n : headers;\r\n\r\n // Get request body if present\r\n let body: BodyInit | null = null;\r\n if (req.method !== 'GET' && req.method !== 'HEAD') {\r\n const contentType = req.headers.get('content-type') || '';\r\n \r\n if (contentType.includes('application/json')) {\r\n body = await req.text();\r\n } else if (contentType.includes('multipart/form-data')) {\r\n body = await req.formData();\r\n } else {\r\n body = await req.text();\r\n }\r\n }\r\n\r\n // Make request to backend\r\n const backendResponse = await fetch(backendUrl.toString(), {\r\n method: req.method,\r\n headers: finalHeaders,\r\n body,\r\n });\r\n\r\n // Get response body\r\n const contentType = backendResponse.headers.get('content-type') || '';\r\n let responseBody: ArrayBuffer | string;\r\n \r\n if (contentType.includes('application/json')) {\r\n responseBody = await backendResponse.text();\r\n } else {\r\n responseBody = await backendResponse.arrayBuffer();\r\n }\r\n\r\n // Build response headers (forward relevant ones)\r\n const responseHeaders = new Headers();\r\n backendResponse.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n // Skip hop-by-hop headers\r\n if (!['transfer-encoding', 'connection', 'keep-alive'].includes(lowerKey)) {\r\n responseHeaders.set(key, value);\r\n }\r\n });\r\n\r\n // Create response\r\n let response = new NextResponse(responseBody, {\r\n status: backendResponse.status,\r\n statusText: backendResponse.statusText,\r\n headers: responseHeaders,\r\n });\r\n\r\n // Allow custom response transformation\r\n if (transformResponse) {\r\n response = await transformResponse(response) as NextResponse;\r\n }\r\n\r\n return response;\r\n } catch (error) {\r\n console.error('[Proxy Error]', error);\r\n \r\n return NextResponse.json(\r\n { \r\n success: false, \r\n message: 'Proxy error: Unable to connect to backend',\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n },\r\n { status: 502 }\r\n );\r\n }\r\n }\r\n\r\n // Attach config for debugging\r\n handler.config = config;\r\n\r\n return handler;\r\n}\r\n\r\nexport type ProxyHandler = ReturnType<typeof createProxyHandler>;\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/config.ts","../src/proxy/tokenValidation.ts","../src/proxy/handlers.ts","../src/proxy/csrf.ts","../src/proxy/rateLimit.ts","../src/proxy/audit.ts","../src/proxy/createAuthProxy.ts","../src/proxy/createProxyHandler.ts"],"names":["generateCsrfSecret","defaultRateLimitKeyFn","req","resolveProxyConfig","config","apiBaseUrl","cookieOptions","DEFAULT_COOKIE_OPTIONS","endpoints","DEFAULT_ENDPOINTS","csrf","DEFAULT_CSRF_CONFIG","rateLimit","DEFAULT_RATE_LIMIT_CONFIG","audit","DEFAULT_AUDIT_CONFIG","defaultMappers","response","res","createTokenValidation","_resolved","responseMappers","mappers","validateToken","token","invalidResult","rawResponse","parsed","getTokenInfo","refreshToken","oldToken","newToken","createGuestToken","guestConfig","extractLocale","pathname","i18n","locales","defaultLocale","firstSegment","stripLocale","segments","createHandlers","validation","cookies","guestToken","access","safeDeleteCookie","cookieName","deleteAllAuthCookies","jsonError","message","status","NextResponse","isAuthPage","cleanPath","route","isProtectedRoute","isPublicRoute","isTokenTypeAllowed","tokenType","allowedTypes","handleNoToken","isApiRoute","origin","guestAccessToken","handleValidationResult","tokenInfo","isUserToken","currentToken","isValid","userData","isGuest","TOKEN_TYPES","refreshResult","newTokenInfo","requestHeaders","HEADERS","locale","createCsrfValidator","generateToken","sessionId","randomValue","generateRandomValue","computeHmac","validateRequest","method","strategy","fetchResult","validateFetchMetadata","validateDoubleSubmit","secFetchSite","cookieToken","headerToken","constantTimeEqual","attachCsrfCookie","array","b","secret","encoder","keyData","messageData","key","signature","hash","str","i","char","a","result","createRateLimiter","store","cleanupInterval","now","entry","shouldSkip","pattern","check","remaining","applyHeaders","createLimitedResponse","reset","clear","size","createAuditLogger","isEnabled","type","getIp","emit","options","event","error","authSuccess","userId","metadata","authFail","authRefresh","authGuest","accessDenied","csrfFail","rateLimitExceeded","err","mergeResponses","source","target","criticalHeaders","header","value","cookie","createAuthProxy","userConfig","handlers","rateLimiter","authProxy","rateLimitResult","csrfResult","beforeResult","path","applyMiddlewaresAndHooks","userToken","authResult","finalResponse","intlResponse","hookResponse","matchesPattern","endpoint","patterns","normalizedEndpoint","regexPattern","createProxyHandler","userCookieName","guestCookieName","skipAuthByDefault","publicEndpoints","forwardHeaders","excludeHeaders","transformRequest","transformResponse","baseUrl","shouldSkipAuth","handler","url","backendUrl","headers","headerName","lowerKey","finalHeaders","body","contentType","backendResponse","responseBody","responseHeaders"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Config Resolver\r\n * Resolves and validates configuration with defaults\r\n */\r\n\r\nimport type { NextRequest } from 'next/server';\r\nimport type {\r\n AuthProxyConfig,\r\n InternalProxyConfig,\r\n ApiClientConfig,\r\n ResolvedCookieOptions,\r\n EndpointConfig,\r\n ResolvedCsrfConfig,\r\n ResolvedRateLimitConfig,\r\n ResolvedAuditConfig,\r\n AuditEventType,\r\n} from './types';\r\n\r\nimport {\r\n DEFAULT_COOKIE_OPTIONS,\r\n DEFAULT_ENDPOINTS,\r\n DEFAULT_CSRF_CONFIG,\r\n DEFAULT_RATE_LIMIT_CONFIG,\r\n DEFAULT_AUDIT_CONFIG,\r\n} from './constants';\r\n\r\n/**\r\n * Generates a random secret for CSRF HMAC signing\r\n */\r\nfunction generateCsrfSecret(): string {\r\n // Use crypto if available (Node.js)\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID() + crypto.randomUUID();\r\n }\r\n // Fallback: timestamp + random\r\n return `${Date.now()}-${Math.random().toString(36).substring(2)}`;\r\n}\r\n\r\n/**\r\n * Default rate limit key function (IP-based)\r\n */\r\nfunction defaultRateLimitKeyFn(req: NextRequest): string {\r\n const forwarded = req.headers.get('x-forwarded-for');\r\n const ip = forwarded?.split(',')[0]?.trim() || \r\n req.headers.get('x-real-ip') || \r\n 'unknown';\r\n return `rl:${ip}`;\r\n}\r\n\r\n/**\r\n * Resolves proxy configuration with defaults\r\n */\r\nexport function resolveProxyConfig(config: AuthProxyConfig): InternalProxyConfig {\r\n // Validate required fields\r\n if (!config.apiBaseUrl) {\r\n throw new Error('next-api-layer: apiBaseUrl is required');\r\n }\r\n \r\n if (!config.cookies?.user || !config.cookies?.guest) {\r\n throw new Error('next-api-layer: cookies.user and cookies.guest are required');\r\n }\r\n\r\n // Ensure apiBaseUrl ends with /\r\n const apiBaseUrl = config.apiBaseUrl.endsWith('/')\r\n ? config.apiBaseUrl\r\n : `${config.apiBaseUrl}/`;\r\n\r\n // Resolve cookie options\r\n const cookieOptions: ResolvedCookieOptions = {\r\n ...DEFAULT_COOKIE_OPTIONS,\r\n ...config.cookies.options,\r\n };\r\n\r\n // Resolve endpoints\r\n const endpoints: Required<EndpointConfig> = {\r\n ...DEFAULT_ENDPOINTS,\r\n ...config.endpoints,\r\n };\r\n\r\n // Resolve CSRF config\r\n const csrf: ResolvedCsrfConfig = {\r\n enabled: config.csrf?.enabled ?? false,\r\n strategy: config.csrf?.strategy ?? DEFAULT_CSRF_CONFIG.strategy,\r\n secret: config.csrf?.secret ?? generateCsrfSecret(),\r\n cookieName: config.csrf?.cookieName ?? DEFAULT_CSRF_CONFIG.cookieName,\r\n headerName: config.csrf?.headerName ?? DEFAULT_CSRF_CONFIG.headerName,\r\n ignoreMethods: config.csrf?.ignoreMethods ?? DEFAULT_CSRF_CONFIG.ignoreMethods,\r\n trustSameSite: config.csrf?.trustSameSite ?? DEFAULT_CSRF_CONFIG.trustSameSite,\r\n };\r\n\r\n // Resolve rate limit config\r\n const rateLimit: ResolvedRateLimitConfig = {\r\n enabled: config.rateLimit?.enabled ?? false,\r\n windowMs: config.rateLimit?.windowMs ?? DEFAULT_RATE_LIMIT_CONFIG.windowMs,\r\n maxRequests: config.rateLimit?.maxRequests ?? DEFAULT_RATE_LIMIT_CONFIG.maxRequests,\r\n keyFn: config.rateLimit?.keyFn ?? defaultRateLimitKeyFn,\r\n skipRoutes: config.rateLimit?.skipRoutes ?? DEFAULT_RATE_LIMIT_CONFIG.skipRoutes,\r\n onRateLimited: config.rateLimit?.onRateLimited,\r\n };\r\n\r\n // Resolve audit config\r\n const audit: ResolvedAuditConfig = {\r\n enabled: config.audit?.enabled ?? false,\r\n events: config.audit?.events ?? [...DEFAULT_AUDIT_CONFIG.events] as AuditEventType[],\r\n logger: config.audit?.logger,\r\n };\r\n\r\n return {\r\n ...config,\r\n apiBaseUrl,\r\n _resolved: {\r\n cookieOptions,\r\n endpoints,\r\n csrf,\r\n rateLimit,\r\n audit,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Resolves API client configuration with defaults\r\n */\r\nexport function resolveApiClientConfig(config: ApiClientConfig = {}) {\r\n return {\r\n sanitization: {\r\n enabled: config.sanitization?.enabled ?? true,\r\n allowedTags: config.sanitization?.allowedTags,\r\n skipFields: config.sanitization?.skipFields ?? [],\r\n skipEndpoints: config.sanitization?.skipEndpoints ?? [],\r\n },\r\n i18n: {\r\n enabled: config.i18n?.enabled ?? false,\r\n paramName: config.i18n?.paramName ?? 'lang',\r\n locales: config.i18n?.locales ?? [],\r\n defaultLocale: config.i18n?.defaultLocale ?? 'en',\r\n },\r\n auth: {\r\n skipByDefault: config.auth?.skipByDefault ?? false,\r\n publicEndpoints: config.auth?.publicEndpoints ?? [],\r\n },\r\n methodSpoofing: config.methodSpoofing ?? false,\r\n errorMessages: {\r\n noToken: config.errorMessages?.noToken ?? 'Token bulunamadı.',\r\n connectionError: config.errorMessages?.connectionError ?? 'Bağlantı hatası oluştu.',\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Type guard to check if a value is defined\r\n */\r\nexport function isDefined<T>(value: T | undefined | null): value is T {\r\n return value !== undefined && value !== null;\r\n}\r\n","/**\r\n * Token Validation\r\n * Handles token validation and refresh with backend API\r\n */\r\n\r\nimport type { \r\n TokenInfo, \r\n RefreshResult, \r\n AuthMeResponse, \r\n GuestTokenResponse,\r\n InternalProxyConfig,\r\n ResponseMappers,\r\n} from '../shared/types';\r\n\r\n/**\r\n * Default response parsers (standard format)\r\n */\r\nconst defaultMappers: Required<ResponseMappers> = {\r\n // Default: { success: true, data: { type, exp, ...user } }\r\n parseAuthMe: (response: unknown): TokenInfo | null => {\r\n const res = response as AuthMeResponse | null;\r\n if (!res?.success || !res?.data) {\r\n return null;\r\n }\r\n return {\r\n isValid: true,\r\n tokenType: res.data.type || 'user',\r\n exp: res.data.exp || null,\r\n userData: res.data,\r\n };\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseRefreshToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.success && res?.data?.accessToken ? res.data.accessToken : null;\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseGuestToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.data?.accessToken || null;\r\n },\r\n};\r\n\r\n/**\r\n * Creates token validation functions\r\n */\r\nexport function createTokenValidation(\r\n config: InternalProxyConfig\r\n) {\r\n const { apiBaseUrl, _resolved, responseMappers } = config;\r\n const { endpoints } = _resolved;\r\n \r\n // Merge custom mappers with defaults\r\n const mappers: Required<ResponseMappers> = {\r\n parseAuthMe: responseMappers?.parseAuthMe || defaultMappers.parseAuthMe,\r\n parseRefreshToken: responseMappers?.parseRefreshToken || defaultMappers.parseRefreshToken,\r\n parseGuestToken: responseMappers?.parseGuestToken || defaultMappers.parseGuestToken,\r\n };\r\n\r\n /**\r\n * Validates a token against the backend\r\n */\r\n async function validateToken(token: string): Promise<TokenInfo> {\r\n const invalidResult: TokenInfo = { isValid: false, tokenType: null, exp: null, userData: null };\r\n \r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.validate}`, {\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return invalidResult;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const parsed = mappers.parseAuthMe(rawResponse);\r\n \r\n if (!parsed || !parsed.isValid) {\r\n return invalidResult;\r\n }\r\n\r\n return parsed;\r\n } catch {\r\n return invalidResult;\r\n }\r\n }\r\n\r\n /**\r\n * Gets token info (validates with backend)\r\n */\r\n async function getTokenInfo(token: string): Promise<TokenInfo> {\r\n return validateToken(token);\r\n }\r\n\r\n /**\r\n * Refreshes a token\r\n */\r\n async function refreshToken(oldToken: string): Promise<RefreshResult> {\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.refresh}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Authorization': `Bearer ${oldToken}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const newToken = mappers.parseRefreshToken(rawResponse);\r\n\r\n if (!newToken) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n return { success: true, newToken };\r\n } catch {\r\n return { success: false, newToken: null };\r\n }\r\n }\r\n\r\n /**\r\n * Creates a guest token\r\n */\r\n async function createGuestToken(): Promise<string | null> {\r\n const guestConfig = config.guestToken;\r\n \r\n if (!guestConfig?.enabled || !guestConfig.credentials) {\r\n return null;\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.guest}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n username: guestConfig.credentials.username,\r\n password: guestConfig.credentials.password,\r\n }),\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return null;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n return mappers.parseGuestToken(rawResponse);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n validateToken,\r\n getTokenInfo,\r\n refreshToken,\r\n createGuestToken,\r\n };\r\n}\r\n\r\nexport type TokenValidation = ReturnType<typeof createTokenValidation>;\r\n","/**\r\n * Proxy Handlers\r\n * Request handling logic for different scenarios\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { InternalProxyConfig, TokenInfo } from '../shared/types';\r\nimport { HEADERS, TOKEN_TYPES } from '../shared/constants';\r\nimport type { TokenValidation } from './tokenValidation';\r\n\r\n/**\r\n * Extracts locale from pathname based on i18n config\r\n * e.g., /en/dashboard → 'en', /dashboard → defaultLocale or null\r\n */\r\nexport function extractLocale(pathname: string, i18n?: InternalProxyConfig['i18n']): string | null {\r\n if (!i18n?.enabled) return null;\r\n \r\n const locales = i18n.locales ?? [];\r\n const defaultLocale = i18n.defaultLocale;\r\n \r\n // Extract first path segment\r\n const segments = pathname.split('/').filter(Boolean);\r\n const firstSegment = segments[0];\r\n \r\n // Check if it's a valid locale\r\n if (firstSegment && locales.includes(firstSegment)) {\r\n return firstSegment;\r\n }\r\n \r\n // Return default locale if provided\r\n return defaultLocale ?? null;\r\n}\r\n\r\n/**\r\n * Strips locale prefix from pathname for route matching\r\n * e.g., /tr/giris → /giris, /en/dashboard → /dashboard\r\n */\r\nexport function stripLocale(pathname: string, i18n?: InternalProxyConfig['i18n']): string {\r\n if (!i18n?.enabled) return pathname;\r\n \r\n const locales = i18n.locales ?? [];\r\n const segments = pathname.split('/').filter(Boolean);\r\n const firstSegment = segments[0];\r\n \r\n // If first segment is a valid locale, strip it\r\n if (firstSegment && locales.includes(firstSegment)) {\r\n const strippedPath = '/' + segments.slice(1).join('/');\r\n return strippedPath || '/';\r\n }\r\n \r\n return pathname;\r\n}\r\n\r\n/**\r\n * Creates proxy handlers\r\n */\r\nexport function createHandlers(\r\n config: InternalProxyConfig,\r\n validation: TokenValidation\r\n) {\r\n const { cookies, guestToken, access, i18n, _resolved } = config;\r\n const { cookieOptions } = _resolved;\r\n\r\n /**\r\n * Safely deletes a cookie only if it exists in the request.\r\n * Prevents empty-value cookies from being created when deleting non-existent cookies.\r\n */\r\n function safeDeleteCookie(req: NextRequest, response: NextResponse, cookieName: string): void {\r\n if (req.cookies.get(cookieName)?.value) {\r\n response.cookies.delete(cookieName);\r\n }\r\n }\r\n\r\n /**\r\n * Deletes all auth cookies from response (only if they exist)\r\n */\r\n function deleteAllAuthCookies(req: NextRequest, response: NextResponse): NextResponse {\r\n safeDeleteCookie(req, response, cookies.guest);\r\n safeDeleteCookie(req, response, cookies.user);\r\n return response;\r\n }\r\n\r\n /**\r\n * Creates a JSON error response\r\n */\r\n function jsonError(message: string, status = 500): NextResponse {\r\n return new NextResponse(\r\n JSON.stringify({ success: false, message }),\r\n { status, headers: { 'Content-Type': 'application/json' } }\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is an auth page\r\n */\r\n function isAuthPage(pathname: string): boolean {\r\n const cleanPath = stripLocale(pathname, i18n);\r\n const authRoutes = access?.authRoutes ?? [];\r\n return authRoutes.some(route => \r\n cleanPath === route || cleanPath.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is a protected route\r\n */\r\n function isProtectedRoute(pathname: string): boolean {\r\n const cleanPath = stripLocale(pathname, i18n);\r\n \r\n // If protectedByDefault is true, everything is protected except public/auth routes\r\n if (access?.protectedByDefault) {\r\n return !isPublicRoute(pathname) && !isAuthPage(pathname);\r\n }\r\n \r\n const protectedRoutes = access?.protectedRoutes ?? [];\r\n return protectedRoutes.some(route => \r\n cleanPath === route || cleanPath.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is explicitly public\r\n */\r\n function isPublicRoute(pathname: string): boolean {\r\n const cleanPath = stripLocale(pathname, i18n);\r\n const publicRoutes = access?.publicRoutes ?? [];\r\n return publicRoutes.some(route => \r\n cleanPath === route || cleanPath.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if token type is allowed\r\n */\r\n function isTokenTypeAllowed(tokenType: string | null): boolean {\r\n const allowedTypes = access?.allowedTokenTypes;\r\n \r\n // If no restriction, all types allowed\r\n if (!allowedTypes || allowedTypes.length === 0) {\r\n return true;\r\n }\r\n \r\n return tokenType ? allowedTypes.includes(tokenType) : false;\r\n }\r\n\r\n /**\r\n * Handles request when no token is present\r\n */\r\n async function handleNoToken(\r\n req: NextRequest,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { origin } = req.nextUrl;\r\n \r\n // Try to create guest token\r\n if (guestToken?.enabled) {\r\n const guestAccessToken = await validation.createGuestToken();\r\n \r\n if (guestAccessToken) {\r\n let response: NextResponse;\r\n \r\n if (isApiRoute) {\r\n response = NextResponse.next();\r\n } else if (isProtectedRoute(req.nextUrl.pathname)) {\r\n // Redirect to login if protected route\r\n response = NextResponse.redirect(new URL('/login', origin));\r\n } else {\r\n response = NextResponse.next();\r\n }\r\n \r\n response.cookies.set(cookies.guest, guestAccessToken, {\r\n ...cookieOptions,\r\n maxAge: 3600, // 1 hour default for guest tokens\r\n });\r\n \r\n return response;\r\n }\r\n }\r\n \r\n // No guest token - just continue or redirect\r\n if (isApiRoute) {\r\n return jsonError('Token bulunamadı', 401);\r\n }\r\n \r\n if (isProtectedRoute(req.nextUrl.pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n \r\n return NextResponse.next();\r\n }\r\n\r\n /**\r\n * Handles token validation result\r\n */\r\n async function handleValidationResult(\r\n req: NextRequest,\r\n tokenInfo: TokenInfo,\r\n isUserToken: boolean,\r\n currentToken: string,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const { isValid, tokenType, userData } = tokenInfo;\r\n const isGuest = tokenType === TOKEN_TYPES.GUEST;\r\n\r\n // ===== TOKEN INVALID =====\r\n if (!isValid) {\r\n // Try refresh if user token\r\n if (isUserToken && currentToken) {\r\n const refreshResult = await validation.refreshToken(currentToken);\r\n \r\n if (refreshResult.success && refreshResult.newToken) {\r\n const newTokenInfo = await validation.getTokenInfo(refreshResult.newToken);\r\n \r\n if (newTokenInfo.isValid) {\r\n // Successful refresh\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (newTokenInfo.userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(newTokenInfo.userData));\r\n }\r\n requestHeaders.set(HEADERS.REFRESHED_TOKEN, refreshResult.newToken);\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n let response: NextResponse;\r\n \r\n if (isAuthPage(pathname)) {\r\n response = NextResponse.redirect(new URL('/', origin));\r\n } else {\r\n response = NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n response.cookies.set(cookies.user, refreshResult.newToken, {\r\n ...cookieOptions,\r\n maxAge: cookieOptions.maxAge,\r\n });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n\r\n return response;\r\n }\r\n }\r\n }\r\n\r\n // Refresh failed or no user token - handle as no token\r\n const response = await handleNoToken(req, isApiRoute);\r\n \r\n // Check if handleNoToken created a new guest token\r\n const hasNewGuestToken = response.cookies.get(cookies.guest)?.value;\r\n \r\n if (!hasNewGuestToken) {\r\n // No new guest token created - delete all auth cookies\r\n deleteAllAuthCookies(req, response);\r\n } else {\r\n // New guest token created - only delete the invalid user cookie\r\n safeDeleteCookie(req, response, cookies.user);\r\n }\r\n \r\n return response;\r\n }\r\n\r\n // ===== TOKEN VALID =====\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(userData));\r\n }\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n // Check if token type is allowed\r\n if (!isGuest && !isTokenTypeAllowed(tokenType)) {\r\n if (isApiRoute) {\r\n const response = jsonError('Bu işlem için yetkiniz yok', 403);\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n \r\n const response = NextResponse.redirect(new URL('/login', origin));\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n\r\n // Guest token handling\r\n if (isGuest) {\r\n if (isApiRoute) {\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // Protected routes require login\r\n if (isProtectedRoute(pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // User token - block auth pages\r\n if (isAuthPage(pathname)) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n\r\n // Normal access\r\n const response = NextResponse.next({ request: { headers: requestHeaders } });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n \r\n return response;\r\n }\r\n\r\n return {\r\n deleteAllAuthCookies,\r\n jsonError,\r\n isAuthPage,\r\n isProtectedRoute,\r\n isPublicRoute,\r\n isTokenTypeAllowed,\r\n handleNoToken,\r\n handleValidationResult,\r\n };\r\n}\r\n\r\nexport type Handlers = ReturnType<typeof createHandlers>;\r\n","/**\r\n * CSRF Protection\r\n * \r\n * Implements OWASP recommended CSRF protection:\r\n * - Fetch Metadata (Sec-Fetch-Site header) for modern browsers (98%+ coverage)\r\n * - Signed HMAC Double-Submit Cookie as fallback\r\n * \r\n * @see https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedCsrfConfig } from '../shared/types';\r\n\r\nexport interface CsrfValidationResult {\r\n valid: boolean;\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Creates CSRF validator functions\r\n */\r\nexport function createCsrfValidator(config: ResolvedCsrfConfig) {\r\n /**\r\n * Generate HMAC-signed CSRF token\r\n * Format: hmac.randomValue\r\n */\r\n async function generateToken(sessionId: string): Promise<string> {\r\n const randomValue = generateRandomValue();\r\n const hmac = await computeHmac(config.secret, sessionId, randomValue);\r\n return `${hmac}.${randomValue}`;\r\n }\r\n\r\n /**\r\n * Validate CSRF request using configured strategy\r\n */\r\n function validateRequest(req: NextRequest): CsrfValidationResult {\r\n const method = req.method.toUpperCase();\r\n \r\n // Skip safe methods\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n\r\n const strategy = config.strategy;\r\n \r\n // Fetch Metadata validation (primary, modern browsers)\r\n if (strategy === 'fetch-metadata' || strategy === 'both') {\r\n const fetchResult = validateFetchMetadata(req);\r\n \r\n if (strategy === 'fetch-metadata') {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata passes, we're good\r\n if (fetchResult.valid) {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata fails or not available, try double-submit\r\n if (fetchResult.reason === 'missing-headers') {\r\n // Fall through to double-submit\r\n } else {\r\n // Explicit cross-site rejection from Fetch Metadata\r\n return fetchResult;\r\n }\r\n }\r\n\r\n // Double-Submit Cookie validation (fallback)\r\n if (strategy === 'double-submit' || strategy === 'both') {\r\n return validateDoubleSubmit(req);\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Validate using Fetch Metadata headers (Sec-Fetch-Site)\r\n * @see https://web.dev/fetch-metadata/\r\n */\r\n function validateFetchMetadata(req: NextRequest): CsrfValidationResult {\r\n const secFetchSite = req.headers.get('sec-fetch-site');\r\n \r\n // No Fetch Metadata headers (older browser or stripped by proxy)\r\n if (!secFetchSite) {\r\n return { valid: false, reason: 'missing-headers' };\r\n }\r\n\r\n // Same-origin requests are always trusted\r\n if (secFetchSite === 'same-origin') {\r\n return { valid: true };\r\n }\r\n\r\n // None = direct navigation (bookmark, typed URL) - allow for GET\r\n if (secFetchSite === 'none') {\r\n const method = req.method.toUpperCase();\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n // Non-safe method from direct navigation is suspicious\r\n return { valid: false, reason: 'direct-navigation-unsafe-method' };\r\n }\r\n\r\n // Same-site: trust based on config\r\n if (secFetchSite === 'same-site') {\r\n if (config.trustSameSite) {\r\n return { valid: true };\r\n }\r\n // Conservative: don't trust same-site by default (subdomain takeover risk)\r\n return { valid: false, reason: 'same-site-not-trusted' };\r\n }\r\n\r\n // Cross-site: reject state-changing requests\r\n if (secFetchSite === 'cross-site') {\r\n return { valid: false, reason: 'cross-site-request' };\r\n }\r\n\r\n // Unknown value - be conservative\r\n return { valid: false, reason: 'unknown-sec-fetch-site' };\r\n }\r\n\r\n /**\r\n * Validate using Double-Submit Cookie pattern with HMAC\r\n */\r\n function validateDoubleSubmit(req: NextRequest): CsrfValidationResult {\r\n // Get token from cookie\r\n const cookieToken = req.cookies.get(config.cookieName)?.value;\r\n \r\n if (!cookieToken) {\r\n return { valid: false, reason: 'missing-cookie-token' };\r\n }\r\n\r\n // Get token from header (or form field)\r\n const headerToken = req.headers.get(config.headerName);\r\n \r\n if (!headerToken) {\r\n return { valid: false, reason: 'missing-header-token' };\r\n }\r\n\r\n // Tokens must match (constant-time comparison to prevent timing attacks)\r\n if (!constantTimeEqual(cookieToken, headerToken)) {\r\n return { valid: false, reason: 'token-mismatch' };\r\n }\r\n\r\n // Validate HMAC structure\r\n const parts = cookieToken.split('.');\r\n if (parts.length !== 2) {\r\n return { valid: false, reason: 'invalid-token-format' };\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Create response with CSRF cookie set\r\n */\r\n async function attachCsrfCookie(\r\n response: NextResponse, \r\n sessionId: string\r\n ): Promise<NextResponse> {\r\n const token = await generateToken(sessionId);\r\n \r\n response.cookies.set(config.cookieName, token, {\r\n httpOnly: false, // Must be readable by JS\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/',\r\n });\r\n\r\n return response;\r\n }\r\n\r\n return {\r\n validateRequest,\r\n generateToken,\r\n attachCsrfCookie,\r\n };\r\n}\r\n\r\n// ==================== Helper Functions ====================\r\n\r\n/**\r\n * Generate cryptographically random value\r\n */\r\nfunction generateRandomValue(): string {\r\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n return Array.from(array, b => b.toString(16).padStart(2, '0')).join('');\r\n }\r\n // Fallback (less secure, for edge cases)\r\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\r\n}\r\n\r\n/**\r\n * Compute HMAC-SHA256\r\n */\r\nasync function computeHmac(\r\n secret: string, \r\n sessionId: string, \r\n randomValue: string\r\n): Promise<string> {\r\n const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`;\r\n \r\n if (typeof crypto !== 'undefined' && crypto.subtle) {\r\n const encoder = new TextEncoder();\r\n const keyData = encoder.encode(secret);\r\n const messageData = encoder.encode(message);\r\n \r\n const key = await crypto.subtle.importKey(\r\n 'raw',\r\n keyData,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign']\r\n );\r\n \r\n const signature = await crypto.subtle.sign('HMAC', key, messageData);\r\n return Array.from(new Uint8Array(signature), b => \r\n b.toString(16).padStart(2, '0')\r\n ).join('');\r\n }\r\n \r\n // Fallback (less secure)\r\n let hash = 0;\r\n const str = secret + message;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = ((hash << 5) - hash) + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(16);\r\n}\r\n\r\n/**\r\n * Constant-time string comparison (prevents timing attacks)\r\n */\r\nfunction constantTimeEqual(a: string, b: string): boolean {\r\n if (a.length !== b.length) {\r\n return false;\r\n }\r\n \r\n let result = 0;\r\n for (let i = 0; i < a.length; i++) {\r\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\r\n }\r\n \r\n return result === 0;\r\n}\r\n\r\nexport type CsrfValidator = ReturnType<typeof createCsrfValidator>;\r\n","/**\r\n * Rate Limiting\r\n * \r\n * Implements token bucket algorithm for rate limiting.\r\n * In-memory store by default, designed for single-instance deployments.\r\n * \r\n * For horizontal scaling (multiple instances), use a custom store\r\n * with Redis or similar distributed cache.\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedRateLimitConfig } from '../shared/types';\r\n\r\ninterface RateLimitEntry {\r\n count: number;\r\n resetAt: number;\r\n}\r\n\r\nexport interface RateLimitResult {\r\n allowed: boolean;\r\n remaining: number;\r\n resetAt: number;\r\n limit: number;\r\n}\r\n\r\n/**\r\n * Creates a rate limiter with in-memory store\r\n */\r\nexport function createRateLimiter(config: ResolvedRateLimitConfig) {\r\n // In-memory store\r\n const store = new Map<string, RateLimitEntry>();\r\n \r\n // Cleanup expired entries periodically\r\n const cleanupInterval = setInterval(() => {\r\n const now = Date.now();\r\n for (const [key, entry] of store) {\r\n if (entry.resetAt <= now) {\r\n store.delete(key);\r\n }\r\n }\r\n }, config.windowMs);\r\n\r\n // Prevent memory leak in long-running processes\r\n if (typeof process !== 'undefined' && process.on) {\r\n process.on('beforeExit', () => clearInterval(cleanupInterval));\r\n }\r\n\r\n /**\r\n * Check if route should skip rate limiting\r\n */\r\n function shouldSkip(pathname: string): boolean {\r\n return config.skipRoutes.some(pattern => {\r\n // Simple glob matching\r\n if (pattern.endsWith('*')) {\r\n return pathname.startsWith(pattern.slice(0, -1));\r\n }\r\n if (pattern.endsWith('**')) {\r\n return pathname.startsWith(pattern.slice(0, -2));\r\n }\r\n return pathname === pattern;\r\n });\r\n }\r\n\r\n /**\r\n * Check rate limit for request\r\n */\r\n function check(req: NextRequest): RateLimitResult {\r\n const pathname = req.nextUrl.pathname;\r\n \r\n // Skip if route matches skip patterns\r\n if (shouldSkip(pathname)) {\r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests,\r\n resetAt: 0,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n const key = config.keyFn(req);\r\n const now = Date.now();\r\n \r\n let entry = store.get(key);\r\n \r\n // New window or expired\r\n if (!entry || entry.resetAt <= now) {\r\n entry = {\r\n count: 1,\r\n resetAt: now + config.windowMs,\r\n };\r\n store.set(key, entry);\r\n \r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests - 1,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n // Existing window\r\n entry.count++;\r\n \r\n const remaining = Math.max(0, config.maxRequests - entry.count);\r\n const allowed = entry.count <= config.maxRequests;\r\n \r\n return {\r\n allowed,\r\n remaining,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n /**\r\n * Apply rate limit headers to response\r\n */\r\n function applyHeaders(response: NextResponse, result: RateLimitResult): NextResponse {\r\n response.headers.set('X-RateLimit-Limit', result.limit.toString());\r\n response.headers.set('X-RateLimit-Remaining', result.remaining.toString());\r\n response.headers.set('X-RateLimit-Reset', Math.ceil(result.resetAt / 1000).toString());\r\n return response;\r\n }\r\n\r\n /**\r\n * Create rate limited response (429 Too Many Requests)\r\n */\r\n function createLimitedResponse(req: NextRequest, result: RateLimitResult): NextResponse {\r\n // User-provided handler\r\n if (config.onRateLimited) {\r\n const response = config.onRateLimited(req);\r\n return applyHeaders(response, result);\r\n }\r\n\r\n // Default response\r\n const response = NextResponse.json(\r\n {\r\n success: false,\r\n message: 'Too many requests. Please try again later.',\r\n retryAfter: Math.ceil((result.resetAt - Date.now()) / 1000),\r\n },\r\n { status: 429 }\r\n );\r\n\r\n response.headers.set('Retry-After', Math.ceil((result.resetAt - Date.now()) / 1000).toString());\r\n return applyHeaders(response, result);\r\n }\r\n\r\n /**\r\n * Reset rate limit for a key (useful for testing)\r\n */\r\n function reset(key: string): void {\r\n store.delete(key);\r\n }\r\n\r\n /**\r\n * Clear all rate limit entries\r\n */\r\n function clear(): void {\r\n store.clear();\r\n }\r\n\r\n /**\r\n * Get current store size (for monitoring)\r\n */\r\n function size(): number {\r\n return store.size;\r\n }\r\n\r\n return {\r\n check,\r\n applyHeaders,\r\n createLimitedResponse,\r\n shouldSkip,\r\n reset,\r\n clear,\r\n size,\r\n };\r\n}\r\n\r\nexport type RateLimiter = ReturnType<typeof createRateLimiter>;\r\n","/**\r\n * Audit Logging\r\n * \r\n * Event-based security audit logging system.\r\n * Emits events for authentication, access control, and security violations.\r\n */\r\n\r\nimport { NextRequest } from 'next/server';\r\nimport type { ResolvedAuditConfig, AuditEvent, AuditEventType } from '../shared/types';\r\n\r\n/**\r\n * Creates an audit logger instance\r\n */\r\nexport function createAuditLogger(config: ResolvedAuditConfig) {\r\n /**\r\n * Check if event type is enabled\r\n */\r\n function isEnabled(type: AuditEventType): boolean {\r\n return config.enabled && config.events.includes(type);\r\n }\r\n\r\n /**\r\n * Extract IP address from request\r\n */\r\n function getIp(req: NextRequest): string | null {\r\n return (\r\n req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||\r\n req.headers.get('x-real-ip') ||\r\n null\r\n );\r\n }\r\n\r\n /**\r\n * Emit an audit event\r\n */\r\n async function emit(\r\n type: AuditEventType,\r\n req: NextRequest,\r\n options: {\r\n success: boolean;\r\n userId?: string;\r\n metadata?: Record<string, unknown>;\r\n }\r\n ): Promise<void> {\r\n if (!isEnabled(type)) {\r\n return;\r\n }\r\n\r\n const event: AuditEvent = {\r\n type,\r\n timestamp: new Date(),\r\n ip: getIp(req),\r\n userId: options.userId,\r\n path: req.nextUrl.pathname,\r\n method: req.method,\r\n success: options.success,\r\n metadata: options.metadata,\r\n };\r\n\r\n // Call user's logger\r\n if (config.logger) {\r\n try {\r\n await config.logger(event);\r\n } catch (error) {\r\n // Silently fail - don't break the request flow for logging errors\r\n console.error('[next-api-layer] Audit logger error:', error);\r\n }\r\n }\r\n }\r\n\r\n // ==================== Convenience Methods ====================\r\n\r\n /**\r\n * Log successful authentication\r\n */\r\n function authSuccess(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:success', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log failed authentication\r\n */\r\n function authFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log token refresh\r\n */\r\n function authRefresh(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:refresh', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log guest token creation\r\n */\r\n function authGuest(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:guest', req, { success: true, metadata });\r\n }\r\n\r\n /**\r\n * Log access denied\r\n */\r\n function accessDenied(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('access:denied', req, { success: false, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log CSRF validation failure\r\n */\r\n function csrfFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('csrf:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log rate limit exceeded\r\n */\r\n function rateLimitExceeded(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('rateLimit:exceeded', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log general error\r\n */\r\n function error(req: NextRequest, err: Error, metadata?: Record<string, unknown>) {\r\n return emit('error', req, { \r\n success: false, \r\n metadata: { \r\n ...metadata, \r\n error: err.message,\r\n stack: err.stack,\r\n } \r\n });\r\n }\r\n\r\n return {\r\n emit,\r\n isEnabled,\r\n // Convenience methods\r\n authSuccess,\r\n authFail,\r\n authRefresh,\r\n authGuest,\r\n accessDenied,\r\n csrfFail,\r\n rateLimitExceeded,\r\n error,\r\n };\r\n}\r\n\r\nexport type AuditLogger = ReturnType<typeof createAuditLogger>;\r\n","/**\r\n * createAuthProxy\r\n * Factory function to create Next.js middleware for external JWT authentication\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { AuthProxyConfig, AuthResult } from '../shared/types';\r\nimport { resolveProxyConfig } from '../shared/config';\r\nimport { createTokenValidation } from './tokenValidation';\r\nimport { createHandlers, extractLocale } from './handlers';\r\nimport { createCsrfValidator } from './csrf';\r\nimport { createRateLimiter } from './rateLimit';\r\nimport { createAuditLogger } from './audit';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\n/**\r\n * Merge two NextResponse objects, preserving headers and cookies from both\r\n * Target response takes priority for conflicts\r\n */\r\nfunction mergeResponses(source: NextResponse, target: NextResponse): NextResponse {\r\n // Copy critical headers from source to target (if not already set)\r\n const criticalHeaders = [HEADERS.LOCALE, HEADERS.AUTH_USER, HEADERS.REFRESHED_TOKEN];\r\n \r\n for (const header of criticalHeaders) {\r\n const value = source.headers.get(header);\r\n if (value && !target.headers.has(header)) {\r\n target.headers.set(header, value);\r\n }\r\n }\r\n \r\n // Copy cookies from source to target (if not already set)\r\n source.cookies.getAll().forEach(cookie => {\r\n if (!target.cookies.get(cookie.name)) {\r\n target.cookies.set(cookie.name, cookie.value);\r\n }\r\n });\r\n \r\n return target;\r\n}\r\n\r\n/**\r\n * Creates an authentication proxy middleware for Next.js\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts\r\n * import { createAuthProxy } from 'next-api-layer';\r\n * \r\n * const authProxy = createAuthProxy({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * cookies: {\r\n * user: 'userAuthToken',\r\n * guest: 'guestAuthToken',\r\n * },\r\n * guestToken: {\r\n * enabled: true,\r\n * credentials: {\r\n * username: process.env.GUEST_USERNAME!,\r\n * password: process.env.GUEST_PASSWORD!,\r\n * },\r\n * },\r\n * access: {\r\n * protectedRoutes: ['/dashboard', '/profile'],\r\n * authRoutes: ['/login', '/register'],\r\n * },\r\n * // Security features\r\n * csrf: { enabled: true },\r\n * rateLimit: { enabled: true, maxRequests: 100 },\r\n * audit: { \r\n * enabled: true, \r\n * logger: (event) => console.log('[AUDIT]', event) \r\n * },\r\n * });\r\n * \r\n * export default authProxy;\r\n * \r\n * export const config = {\r\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\r\n * };\r\n * ```\r\n */\r\nexport function createAuthProxy(userConfig: AuthProxyConfig) {\r\n // Resolve config with defaults\r\n const config = resolveProxyConfig(userConfig);\r\n \r\n // Create validation functions\r\n const validation = createTokenValidation(config);\r\n \r\n // Create handlers\r\n const handlers = createHandlers(config, validation);\r\n\r\n // Create security modules\r\n const csrf = createCsrfValidator(config._resolved.csrf);\r\n const rateLimiter = createRateLimiter(config._resolved.rateLimit);\r\n const audit = createAuditLogger(config._resolved.audit);\r\n\r\n /**\r\n * The middleware function\r\n */\r\n async function authProxy(req: NextRequest): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const isApiRoute = pathname.startsWith('/api');\r\n\r\n // ============ Rate Limiting ============\r\n // Check early to protect against DoS\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n \r\n if (!rateLimitResult.allowed) {\r\n await audit.rateLimitExceeded(req, { \r\n limit: rateLimitResult.limit,\r\n resetAt: rateLimitResult.resetAt,\r\n });\r\n return rateLimiter.createLimitedResponse(req, rateLimitResult);\r\n }\r\n }\r\n\r\n // ============ CSRF Protection ============\r\n // Check before any state-changing operations\r\n if (config._resolved.csrf.enabled) {\r\n const csrfResult = csrf.validateRequest(req);\r\n \r\n if (!csrfResult.valid) {\r\n await audit.csrfFail(req, { reason: csrfResult.reason });\r\n return NextResponse.json(\r\n { success: false, message: 'CSRF validation failed' },\r\n { status: 403 }\r\n );\r\n }\r\n }\r\n\r\n // ============ Block Browser API Access ============\r\n // Prevents direct browser access to API routes (when Accept: text/html)\r\n if (config.blockBrowserApiAccess && isApiRoute) {\r\n const acceptHeader = req.headers.get('accept') || '';\r\n if (acceptHeader.includes('text/html')) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n }\r\n\r\n // ============ beforeAuth Hook ============\r\n // Allows user to handle request before auth validation\r\n if (config.beforeAuth) {\r\n const beforeResult = await config.beforeAuth(req);\r\n if (beforeResult) {\r\n return beforeResult; // User handled the request\r\n }\r\n }\r\n\r\n // Skip excluded paths\r\n const excludedPaths = config.excludedPaths ?? [];\r\n if (excludedPaths.some(path => pathname.startsWith(path))) {\r\n return applyMiddlewaresAndHooks(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Skip auth API endpoints (they handle their own auth)\r\n const authApiPaths = [\r\n '/api/auth/login',\r\n '/api/auth/logout',\r\n '/api/auth/me',\r\n '/api/auth/refresh',\r\n '/api/auth/register',\r\n ];\r\n \r\n if (authApiPaths.includes(pathname)) {\r\n return applyMiddlewaresAndHooks(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Get tokens from cookies\r\n const userToken = req.cookies?.get(config.cookies.user)?.value;\r\n const guestToken = req.cookies?.get(config.cookies.guest)?.value;\r\n const currentToken = userToken || guestToken;\r\n const isUserToken = !!userToken;\r\n\r\n // No token - handle appropriately\r\n if (!currentToken) {\r\n await audit.authFail(req, { reason: 'no-token' });\r\n const response = await handlers.handleNoToken(req, isApiRoute);\r\n return applyMiddlewaresAndHooks(req, response, { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Validate token\r\n const tokenInfo = await validation.getTokenInfo(currentToken);\r\n \r\n // Build auth result for afterAuth hook\r\n const authResult: AuthResult = {\r\n isAuthenticated: tokenInfo.isValid && tokenInfo.tokenType !== 'guest',\r\n isGuest: tokenInfo.isValid && tokenInfo.tokenType === 'guest',\r\n tokenType: tokenInfo.tokenType,\r\n user: tokenInfo.userData,\r\n };\r\n\r\n // Audit logging based on validation result \r\n if (tokenInfo.isValid) {\r\n if (tokenInfo.tokenType === 'guest') {\r\n await audit.authGuest(req);\r\n } else {\r\n const userId = tokenInfo.userData?.id?.toString();\r\n await audit.authSuccess(req, userId, { tokenType: tokenInfo.tokenType });\r\n }\r\n } else {\r\n await audit.authFail(req, { reason: 'invalid-token' });\r\n }\r\n\r\n // Handle validation result\r\n const response = await handlers.handleValidationResult(\r\n req,\r\n tokenInfo,\r\n isUserToken,\r\n currentToken,\r\n isApiRoute\r\n );\r\n \r\n // Apply CSRF cookie if enabled (for authenticated requests)\r\n let finalResponse = await applyMiddlewaresAndHooks(req, response, authResult);\r\n \r\n if (config._resolved.csrf.enabled && authResult.isAuthenticated) {\r\n const sessionId = tokenInfo.userData?.id?.toString() || currentToken.slice(0, 32);\r\n finalResponse = await csrf.attachCsrfCookie(finalResponse, sessionId);\r\n }\r\n\r\n // Apply rate limit headers\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n finalResponse = rateLimiter.applyHeaders(finalResponse, rateLimitResult);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n \r\n /**\r\n * Helper to apply i18n middleware and afterAuth hook\r\n * Merges responses to preserve critical headers (x-locale, x-auth-user, etc.)\r\n */\r\n async function applyMiddlewaresAndHooks(req: NextRequest, response: NextResponse, authResult: AuthResult): Promise<NextResponse> {\r\n let finalResponse = response;\r\n \r\n // Apply i18n middleware if configured\r\n if (config.i18n?.middleware) {\r\n const intlResponse = await Promise.resolve(config.i18n.middleware(req));\r\n // Merge library's response headers into i18n response\r\n finalResponse = mergeResponses(response, intlResponse);\r\n }\r\n \r\n // Ensure x-locale is set as response header (not just request header)\r\n // This is needed because intl middleware creates a new response that loses request headers\r\n if (config.i18n?.enabled) {\r\n const locale = extractLocale(req.nextUrl.pathname, config.i18n);\r\n if (locale) {\r\n finalResponse.headers.set(HEADERS.LOCALE, locale);\r\n }\r\n }\r\n \r\n // Apply afterAuth hook if configured\r\n if (config.afterAuth) {\r\n const hookResponse = await config.afterAuth(req, finalResponse, authResult);\r\n // Merge previous response headers into hook's response\r\n finalResponse = mergeResponses(finalResponse, hookResponse);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n\r\n // Attach instances for debugging/testing\r\n authProxy.config = config;\r\n authProxy.csrf = csrf;\r\n authProxy.rateLimiter = rateLimiter;\r\n authProxy.audit = audit;\r\n\r\n return authProxy;\r\n}\r\n\r\nexport type AuthProxy = ReturnType<typeof createAuthProxy>;\r\n\r\n","/**\r\n * createProxyHandler\r\n * Factory function to create Next.js API route handlers that proxy requests to backend\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\nexport interface ProxyHandlerConfig {\r\n /** Base URL of the backend API */\r\n apiBaseUrl: string;\r\n /** Cookie name for user auth token */\r\n userCookieName?: string;\r\n /** Cookie name for guest auth token */\r\n guestCookieName?: string;\r\n /** \r\n * Default behavior for auth - if true, all requests skip auth by default\r\n * Individual requests can override with X-Skip-Auth header\r\n */\r\n skipAuthByDefault?: boolean;\r\n /**\r\n * Public endpoints that should never include auth token (glob patterns)\r\n * e.g., ['news/*', 'public/**', 'categories']\r\n */\r\n publicEndpoints?: string[];\r\n /** Headers to forward from client request */\r\n forwardHeaders?: string[];\r\n /** Headers to exclude from forwarding */\r\n excludeHeaders?: string[];\r\n /** Custom request transformer */\r\n transformRequest?: (req: NextRequest, headers: Headers) => Headers | Promise<Headers>;\r\n /** Custom response transformer */\r\n transformResponse?: (response: Response) => Response | Promise<Response>;\r\n}\r\n\r\n/**\r\n * Check if endpoint matches any patterns (glob support)\r\n */\r\nfunction matchesPattern(endpoint: string, patterns: string[]): boolean {\r\n if (!patterns || patterns.length === 0) return false;\r\n \r\n // Normalize endpoint\r\n const normalizedEndpoint = endpoint\r\n .replace(/^\\/api\\//, '')\r\n .replace(/^\\//, '');\r\n \r\n return patterns.some(pattern => {\r\n if (pattern === normalizedEndpoint) return true;\r\n \r\n if (pattern.includes('*')) {\r\n const regexPattern = pattern\r\n .replace(/\\*\\*/g, '<<<DOUBLE>>>')\r\n .replace(/\\*/g, '[^/]+')\r\n .replace(/<<<DOUBLE>>>/g, '.+');\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(normalizedEndpoint);\r\n }\r\n \r\n return false;\r\n });\r\n}\r\n\r\n/**\r\n * Creates a proxy handler for Next.js API routes\r\n * \r\n * @example\r\n * ```ts\r\n * // app/api/[...path]/route.ts\r\n * import { createProxyHandler } from 'next-api-layer';\r\n * \r\n * const handler = createProxyHandler({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * userCookieName: 'auth_token',\r\n * guestCookieName: 'guest_token',\r\n * publicEndpoints: ['news/*', 'categories', 'public/**'],\r\n * });\r\n * \r\n * export const GET = handler;\r\n * export const POST = handler;\r\n * export const PUT = handler;\r\n * export const PATCH = handler;\r\n * export const DELETE = handler;\r\n * ```\r\n */\r\nexport function createProxyHandler(config: ProxyHandlerConfig) {\r\n const {\r\n apiBaseUrl,\r\n userCookieName = 'auth_token',\r\n guestCookieName = 'guest_token',\r\n skipAuthByDefault = false,\r\n publicEndpoints = [],\r\n forwardHeaders = ['content-type', 'accept', 'accept-language', 'x-requested-with'],\r\n excludeHeaders = ['host', 'connection', 'cookie'],\r\n transformRequest,\r\n transformResponse,\r\n } = config;\r\n\r\n // Normalize base URL (remove trailing slash)\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n /**\r\n * Determine if auth should be skipped for this request\r\n */\r\n function shouldSkipAuth(req: NextRequest, endpoint: string): boolean {\r\n // Check X-Skip-Auth header from client\r\n const skipAuthHeader = req.headers.get(HEADERS.SKIP_AUTH);\r\n if (skipAuthHeader === 'true') {\r\n return true;\r\n }\r\n \r\n // Check if endpoint matches public patterns\r\n if (matchesPattern(endpoint, publicEndpoints)) {\r\n return true;\r\n }\r\n \r\n // Default behavior\r\n return skipAuthByDefault;\r\n }\r\n\r\n /**\r\n * The proxy handler function\r\n */\r\n async function handler(req: NextRequest): Promise<NextResponse> {\r\n try {\r\n // Extract endpoint from URL path (remove /api/ prefix)\r\n const url = new URL(req.url);\r\n const endpoint = url.pathname.replace(/^\\/api\\/?/, '');\r\n \r\n // Build backend URL\r\n const backendUrl = new URL(`${baseUrl}/${endpoint}`);\r\n backendUrl.search = url.search; // Forward query params\r\n\r\n // Build headers for backend request\r\n const headers = new Headers();\r\n \r\n // Forward allowed headers from original request\r\n forwardHeaders.forEach(headerName => {\r\n const value = req.headers.get(headerName);\r\n if (value) {\r\n headers.set(headerName, value);\r\n }\r\n });\r\n\r\n // Forward all headers except excluded ones\r\n req.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n if (!excludeHeaders.includes(lowerKey) && !headers.has(key)) {\r\n headers.set(key, value);\r\n }\r\n });\r\n\r\n // Add Authorization header if auth is not skipped\r\n if (!shouldSkipAuth(req, endpoint)) {\r\n const userToken = req.cookies.get(userCookieName)?.value;\r\n const guestToken = req.cookies.get(guestCookieName)?.value;\r\n const token = userToken || guestToken;\r\n \r\n if (token) {\r\n headers.set(HEADERS.AUTHORIZATION, `Bearer ${token}`);\r\n }\r\n }\r\n\r\n // Remove skip-auth header (internal use only)\r\n headers.delete(HEADERS.SKIP_AUTH);\r\n\r\n // Allow custom request transformation\r\n const finalHeaders = transformRequest \r\n ? await transformRequest(req, headers) \r\n : headers;\r\n\r\n // Get request body if present\r\n let body: BodyInit | null = null;\r\n if (req.method !== 'GET' && req.method !== 'HEAD') {\r\n const contentType = req.headers.get('content-type') || '';\r\n \r\n if (contentType.includes('application/json')) {\r\n body = await req.text();\r\n } else if (contentType.includes('multipart/form-data')) {\r\n body = await req.formData();\r\n } else {\r\n body = await req.text();\r\n }\r\n }\r\n\r\n // Make request to backend\r\n const backendResponse = await fetch(backendUrl.toString(), {\r\n method: req.method,\r\n headers: finalHeaders,\r\n body,\r\n });\r\n\r\n // Get response body\r\n const contentType = backendResponse.headers.get('content-type') || '';\r\n let responseBody: ArrayBuffer | string;\r\n \r\n if (contentType.includes('application/json')) {\r\n responseBody = await backendResponse.text();\r\n } else {\r\n responseBody = await backendResponse.arrayBuffer();\r\n }\r\n\r\n // Build response headers (forward relevant ones)\r\n const responseHeaders = new Headers();\r\n backendResponse.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n // Skip hop-by-hop headers\r\n if (!['transfer-encoding', 'connection', 'keep-alive'].includes(lowerKey)) {\r\n responseHeaders.set(key, value);\r\n }\r\n });\r\n\r\n // Create response\r\n let response = new NextResponse(responseBody, {\r\n status: backendResponse.status,\r\n statusText: backendResponse.statusText,\r\n headers: responseHeaders,\r\n });\r\n\r\n // Allow custom response transformation\r\n if (transformResponse) {\r\n response = await transformResponse(response) as NextResponse;\r\n }\r\n\r\n return response;\r\n } catch (error) {\r\n console.error('[Proxy Error]', error);\r\n \r\n return NextResponse.json(\r\n { \r\n success: false, \r\n message: 'Proxy error: Unable to connect to backend',\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n },\r\n { status: 502 }\r\n );\r\n }\r\n }\r\n\r\n // Attach config for debugging\r\n handler.config = config;\r\n\r\n return handler;\r\n}\r\n\r\nexport type ProxyHandler = ReturnType<typeof createProxyHandler>;\r\n"]}
|