@atlasauth/nextjs 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +103 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/matcher.d.ts +32 -5
- package/dist/matcher.js +58 -20
- package/dist/matcher.js.map +1 -1
- package/dist/middleware.d.ts +42 -0
- package/dist/middleware.js +73 -30
- package/dist/middleware.js.map +1 -1
- package/dist/proxy.d.ts +96 -0
- package/dist/proxy.js +189 -0
- package/dist/proxy.js.map +1 -0
- package/dist/server.d.ts +80 -0
- package/dist/server.js +216 -0
- package/dist/server.js.map +1 -0
- package/package.json +25 -4
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type AtlasMiddlewareOptions, type AuthState, type EdgeRequest } from './middleware';
|
|
2
|
+
/**
|
|
3
|
+
* Next.js 16 renamed `middleware.ts` to `proxy.ts` (exporting `proxy` or a
|
|
4
|
+
* default function) and made it run on the Node.js runtime by default. A proxy
|
|
5
|
+
* is usually a CHAIN — auth, then i18n (next-intl), then whatever else — so the
|
|
6
|
+
* shape that composes is not "Atlas returns the final response" but "Atlas
|
|
7
|
+
* returns a response only when it must act, otherwise steps aside":
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const atlas = atlasProxy({ ... });
|
|
11
|
+
* export async function proxy(req: NextRequest) {
|
|
12
|
+
* return (await atlas(req)) ?? withAtlasHeaders(req, intl(req));
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* `atlas(req)` resolves to a `Response` (sign-in redirect, handshake bounce,
|
|
17
|
+
* 401 for an API route) or `null` (let the app continue). The verified auth
|
|
18
|
+
* state is remembered for that request, so {@link withAtlasHeaders} can forward
|
|
19
|
+
* it to the app on WHATEVER response the rest of the chain produced — without
|
|
20
|
+
* verifying the token a second time and without Atlas having to own the final
|
|
21
|
+
* response.
|
|
22
|
+
*
|
|
23
|
+
* Everything here is Web-standard (`Response`, `Headers`, `URL`, Web Crypto),
|
|
24
|
+
* so the same proxy runs on the Node.js runtime and on the Edge runtime.
|
|
25
|
+
*/
|
|
26
|
+
/** Request headers the proxy forwards to the app (server components, route
|
|
27
|
+
* handlers) describing the verified session. */
|
|
28
|
+
export declare const ATLAS_AUTH_HEADERS: {
|
|
29
|
+
readonly status: "x-atlas-auth-status";
|
|
30
|
+
readonly userId: "x-atlas-user-id";
|
|
31
|
+
readonly sessionId: "x-atlas-session-id";
|
|
32
|
+
readonly orgId: "x-atlas-org-id";
|
|
33
|
+
readonly orgRole: "x-atlas-org-role";
|
|
34
|
+
};
|
|
35
|
+
/** What `atlasProxy` needs from a request: a `NextRequest` satisfies it. */
|
|
36
|
+
export interface ProxyRequest extends EdgeRequest {
|
|
37
|
+
headers: {
|
|
38
|
+
get(name: string): string | null;
|
|
39
|
+
forEach(callback: (value: string, key: string) => void): void;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** The request-headers slice of a response — `NextResponse` and `Response`
|
|
43
|
+
* both satisfy it. */
|
|
44
|
+
export interface HeaderCarryingResponse {
|
|
45
|
+
status: number;
|
|
46
|
+
headers: Headers;
|
|
47
|
+
}
|
|
48
|
+
export type AtlasProxy = (request: ProxyRequest) => Promise<Response | null>;
|
|
49
|
+
/**
|
|
50
|
+
* The Next.js 16 `proxy.ts` entry for Atlas. Same options, same decisions as
|
|
51
|
+
* `atlasMiddleware` (they share one engine) — only the calling convention
|
|
52
|
+
* differs: it takes just the request and resolves to `Response | null`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function atlasProxy(options: AtlasMiddlewareOptions): AtlasProxy;
|
|
55
|
+
/**
|
|
56
|
+
* The auth state the proxy resolved for this request (signed-out if the proxy
|
|
57
|
+
* hasn't run on it). For extra checks inside `proxy.ts` itself — a role gate on
|
|
58
|
+
* `/admin`, say — without verifying the token again.
|
|
59
|
+
*/
|
|
60
|
+
export declare function proxyAuth(request: object): AuthState;
|
|
61
|
+
/**
|
|
62
|
+
* Forward the verified session to the app as `x-atlas-*` REQUEST headers on a
|
|
63
|
+
* response produced by any later step of the chain (`NextResponse.next()`, a
|
|
64
|
+
* next-intl rewrite, …). Returns the same response, for `return withAtlasHeaders(...)`.
|
|
65
|
+
*
|
|
66
|
+
* It speaks Next's request-header override protocol (what
|
|
67
|
+
* `NextResponse.next({ request: { headers } })` emits), MERGING with any
|
|
68
|
+
* override a previous step already set rather than clobbering it, so it composes
|
|
69
|
+
* with next-intl or your own header tweaks in either order.
|
|
70
|
+
*
|
|
71
|
+
* Any `x-atlas-*` header the CLIENT sent is always dropped — a browser can't
|
|
72
|
+
* forge `x-atlas-user-id` past the proxy. Responses that don't continue to the
|
|
73
|
+
* app (redirects, direct bodies) are returned untouched: there's no downstream
|
|
74
|
+
* request to carry headers.
|
|
75
|
+
*
|
|
76
|
+
* `auth` defaults to what {@link atlasProxy} resolved for `request`, or
|
|
77
|
+
* signed-out if it didn't run.
|
|
78
|
+
*/
|
|
79
|
+
export declare function withAtlasHeaders<R extends HeaderCarryingResponse>(request: ProxyRequest, response: R, auth?: AuthState): R;
|
|
80
|
+
/** The session as forwarded by {@link withAtlasHeaders}. */
|
|
81
|
+
export interface ForwardedAuth {
|
|
82
|
+
signedIn: boolean;
|
|
83
|
+
userId: string | null;
|
|
84
|
+
sessionId: string | null;
|
|
85
|
+
orgId: string | null;
|
|
86
|
+
orgRole: string | null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Read the `x-atlas-*` headers the proxy forwarded (e.g. from `await headers()`
|
|
90
|
+
* in a server component). Cheap — no verification — so use it for rendering
|
|
91
|
+
* decisions; keep authorization on `auth()`/`protect()`, which verify the token
|
|
92
|
+
* themselves and don't depend on the proxy's matcher covering the route.
|
|
93
|
+
*/
|
|
94
|
+
export declare function readAtlasHeaders(headers: {
|
|
95
|
+
get(name: string): string | null;
|
|
96
|
+
}): ForwardedAuth;
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ATLAS_AUTH_HEADERS = void 0;
|
|
4
|
+
exports.atlasProxy = atlasProxy;
|
|
5
|
+
exports.proxyAuth = proxyAuth;
|
|
6
|
+
exports.withAtlasHeaders = withAtlasHeaders;
|
|
7
|
+
exports.readAtlasHeaders = readAtlasHeaders;
|
|
8
|
+
const middleware_1 = require("./middleware");
|
|
9
|
+
/**
|
|
10
|
+
* Next.js 16 renamed `middleware.ts` to `proxy.ts` (exporting `proxy` or a
|
|
11
|
+
* default function) and made it run on the Node.js runtime by default. A proxy
|
|
12
|
+
* is usually a CHAIN — auth, then i18n (next-intl), then whatever else — so the
|
|
13
|
+
* shape that composes is not "Atlas returns the final response" but "Atlas
|
|
14
|
+
* returns a response only when it must act, otherwise steps aside":
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* const atlas = atlasProxy({ ... });
|
|
18
|
+
* export async function proxy(req: NextRequest) {
|
|
19
|
+
* return (await atlas(req)) ?? withAtlasHeaders(req, intl(req));
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* `atlas(req)` resolves to a `Response` (sign-in redirect, handshake bounce,
|
|
24
|
+
* 401 for an API route) or `null` (let the app continue). The verified auth
|
|
25
|
+
* state is remembered for that request, so {@link withAtlasHeaders} can forward
|
|
26
|
+
* it to the app on WHATEVER response the rest of the chain produced — without
|
|
27
|
+
* verifying the token a second time and without Atlas having to own the final
|
|
28
|
+
* response.
|
|
29
|
+
*
|
|
30
|
+
* Everything here is Web-standard (`Response`, `Headers`, `URL`, Web Crypto),
|
|
31
|
+
* so the same proxy runs on the Node.js runtime and on the Edge runtime.
|
|
32
|
+
*/
|
|
33
|
+
/** Request headers the proxy forwards to the app (server components, route
|
|
34
|
+
* handlers) describing the verified session. */
|
|
35
|
+
exports.ATLAS_AUTH_HEADERS = {
|
|
36
|
+
status: 'x-atlas-auth-status',
|
|
37
|
+
userId: 'x-atlas-user-id',
|
|
38
|
+
sessionId: 'x-atlas-session-id',
|
|
39
|
+
orgId: 'x-atlas-org-id',
|
|
40
|
+
orgRole: 'x-atlas-org-role',
|
|
41
|
+
};
|
|
42
|
+
const ATLAS_HEADER_PREFIX = 'x-atlas-';
|
|
43
|
+
const OVERRIDE_LIST = 'x-middleware-override-headers';
|
|
44
|
+
const OVERRIDE_PREFIX = 'x-middleware-request-';
|
|
45
|
+
/** Auth state per request, recorded by the proxy for `withAtlasHeaders` /
|
|
46
|
+
* `proxyAuth`. Weak, so a finished request's state is collected with it. */
|
|
47
|
+
const resolved = new WeakMap();
|
|
48
|
+
function serializeCookie({ name, value, options }) {
|
|
49
|
+
// Same encoding Next's `response.cookies.set` applies, so readers agree.
|
|
50
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
51
|
+
if (options.path)
|
|
52
|
+
parts.push(`Path=${options.path}`);
|
|
53
|
+
if (options.maxAge !== undefined)
|
|
54
|
+
parts.push(`Max-Age=${options.maxAge}`);
|
|
55
|
+
if (options.httpOnly)
|
|
56
|
+
parts.push('HttpOnly');
|
|
57
|
+
if (options.secure)
|
|
58
|
+
parts.push('Secure');
|
|
59
|
+
if (options.sameSite) {
|
|
60
|
+
parts.push(`SameSite=${options.sameSite[0].toUpperCase()}${options.sameSite.slice(1)}`);
|
|
61
|
+
}
|
|
62
|
+
return parts.join('; ');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The Next.js 16 `proxy.ts` entry for Atlas. Same options, same decisions as
|
|
66
|
+
* `atlasMiddleware` (they share one engine) — only the calling convention
|
|
67
|
+
* differs: it takes just the request and resolves to `Response | null`.
|
|
68
|
+
*/
|
|
69
|
+
function atlasProxy(options) {
|
|
70
|
+
const decide = (0, middleware_1.createAtlasDecider)(options);
|
|
71
|
+
return async function proxy(request) {
|
|
72
|
+
const decision = await decide(request);
|
|
73
|
+
resolved.set(request, decision.auth);
|
|
74
|
+
switch (decision.kind) {
|
|
75
|
+
case 'continue':
|
|
76
|
+
return null;
|
|
77
|
+
case 'unauthorized':
|
|
78
|
+
return new Response(JSON.stringify(middleware_1.UNAUTHENTICATED_BODY), {
|
|
79
|
+
status: 401,
|
|
80
|
+
headers: { 'content-type': 'application/json' },
|
|
81
|
+
});
|
|
82
|
+
case 'redirect': {
|
|
83
|
+
// Built by hand rather than `Response.redirect`, whose headers are
|
|
84
|
+
// immutable — the handshake legs must also set first-party cookies.
|
|
85
|
+
const headers = new Headers({ location: decision.location });
|
|
86
|
+
for (const cookie of decision.cookies)
|
|
87
|
+
headers.append('set-cookie', serializeCookie(cookie));
|
|
88
|
+
return new Response(null, { status: 307, headers });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The auth state the proxy resolved for this request (signed-out if the proxy
|
|
95
|
+
* hasn't run on it). For extra checks inside `proxy.ts` itself — a role gate on
|
|
96
|
+
* `/admin`, say — without verifying the token again.
|
|
97
|
+
*/
|
|
98
|
+
function proxyAuth(request) {
|
|
99
|
+
return resolved.get(request) ?? middleware_1.SIGNED_OUT;
|
|
100
|
+
}
|
|
101
|
+
function authHeaderValues(auth) {
|
|
102
|
+
const values = [
|
|
103
|
+
[exports.ATLAS_AUTH_HEADERS.status, auth.userId ? 'signed-in' : 'signed-out'],
|
|
104
|
+
];
|
|
105
|
+
if (auth.userId)
|
|
106
|
+
values.push([exports.ATLAS_AUTH_HEADERS.userId, auth.userId]);
|
|
107
|
+
if (auth.sessionId)
|
|
108
|
+
values.push([exports.ATLAS_AUTH_HEADERS.sessionId, auth.sessionId]);
|
|
109
|
+
if (auth.orgId)
|
|
110
|
+
values.push([exports.ATLAS_AUTH_HEADERS.orgId, auth.orgId]);
|
|
111
|
+
if (auth.orgRole)
|
|
112
|
+
values.push([exports.ATLAS_AUTH_HEADERS.orgRole, auth.orgRole]);
|
|
113
|
+
return values;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Forward the verified session to the app as `x-atlas-*` REQUEST headers on a
|
|
117
|
+
* response produced by any later step of the chain (`NextResponse.next()`, a
|
|
118
|
+
* next-intl rewrite, …). Returns the same response, for `return withAtlasHeaders(...)`.
|
|
119
|
+
*
|
|
120
|
+
* It speaks Next's request-header override protocol (what
|
|
121
|
+
* `NextResponse.next({ request: { headers } })` emits), MERGING with any
|
|
122
|
+
* override a previous step already set rather than clobbering it, so it composes
|
|
123
|
+
* with next-intl or your own header tweaks in either order.
|
|
124
|
+
*
|
|
125
|
+
* Any `x-atlas-*` header the CLIENT sent is always dropped — a browser can't
|
|
126
|
+
* forge `x-atlas-user-id` past the proxy. Responses that don't continue to the
|
|
127
|
+
* app (redirects, direct bodies) are returned untouched: there's no downstream
|
|
128
|
+
* request to carry headers.
|
|
129
|
+
*
|
|
130
|
+
* `auth` defaults to what {@link atlasProxy} resolved for `request`, or
|
|
131
|
+
* signed-out if it didn't run.
|
|
132
|
+
*/
|
|
133
|
+
function withAtlasHeaders(request, response, auth = proxyAuth(request)) {
|
|
134
|
+
const continues = response.headers.has('x-middleware-next') || response.headers.has('x-middleware-rewrite');
|
|
135
|
+
if (!continues)
|
|
136
|
+
return response;
|
|
137
|
+
const existing = response.headers.get(OVERRIDE_LIST);
|
|
138
|
+
const names = new Set();
|
|
139
|
+
if (existing !== null) {
|
|
140
|
+
// An earlier step already chose the full downstream header set; keep its
|
|
141
|
+
// choices and only take over the `x-atlas-*` namespace.
|
|
142
|
+
for (const raw of existing.split(',')) {
|
|
143
|
+
const name = raw.trim().toLowerCase();
|
|
144
|
+
if (!name)
|
|
145
|
+
continue;
|
|
146
|
+
if (name.startsWith(ATLAS_HEADER_PREFIX)) {
|
|
147
|
+
response.headers.delete(`${OVERRIDE_PREFIX}${name}`);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
names.add(name);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// First override: start from the incoming request headers, as
|
|
156
|
+
// `NextResponse.next({ request: { headers: new Headers(req.headers) } })` does.
|
|
157
|
+
request.headers.forEach((value, key) => {
|
|
158
|
+
const name = key.toLowerCase();
|
|
159
|
+
if (name.startsWith(ATLAS_HEADER_PREFIX))
|
|
160
|
+
return;
|
|
161
|
+
names.add(name);
|
|
162
|
+
response.headers.set(`${OVERRIDE_PREFIX}${name}`, value);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
for (const [name, value] of authHeaderValues(auth)) {
|
|
166
|
+
names.add(name);
|
|
167
|
+
response.headers.set(`${OVERRIDE_PREFIX}${name}`, value);
|
|
168
|
+
}
|
|
169
|
+
response.headers.set(OVERRIDE_LIST, [...names].join(','));
|
|
170
|
+
return response;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Read the `x-atlas-*` headers the proxy forwarded (e.g. from `await headers()`
|
|
174
|
+
* in a server component). Cheap — no verification — so use it for rendering
|
|
175
|
+
* decisions; keep authorization on `auth()`/`protect()`, which verify the token
|
|
176
|
+
* themselves and don't depend on the proxy's matcher covering the route.
|
|
177
|
+
*/
|
|
178
|
+
function readAtlasHeaders(headers) {
|
|
179
|
+
const userId = headers.get(exports.ATLAS_AUTH_HEADERS.userId);
|
|
180
|
+
const signedIn = headers.get(exports.ATLAS_AUTH_HEADERS.status) === 'signed-in' && Boolean(userId);
|
|
181
|
+
return {
|
|
182
|
+
signedIn,
|
|
183
|
+
userId: signedIn ? userId : null,
|
|
184
|
+
sessionId: signedIn ? headers.get(exports.ATLAS_AUTH_HEADERS.sessionId) : null,
|
|
185
|
+
orgId: signedIn ? headers.get(exports.ATLAS_AUTH_HEADERS.orgId) : null,
|
|
186
|
+
orgRole: signedIn ? headers.get(exports.ATLAS_AUTH_HEADERS.orgRole) : null,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@atlasauth/nextjs/server` — the pre-bound, zero-argument App-Router helpers.
|
|
3
|
+
*
|
|
4
|
+
* §Q7 gap: the base entry ships FACTORIES (`createAuthHelper`,
|
|
5
|
+
* `createCurrentUserHelper`) that the integrator wires once and then feeds an
|
|
6
|
+
* `EdgeRequest` on every call. Clerk instead ships `auth()` / `currentUser()`
|
|
7
|
+
* that read the ambient request from `next/headers` with no argument and read
|
|
8
|
+
* their config from the environment. This subpath adds exactly that ergonomic
|
|
9
|
+
* ON TOP of the existing core — it reuses `resolveAuth` + `AtlasBackend` through
|
|
10
|
+
* the very same factories, so JWT verification is defined in exactly one place.
|
|
11
|
+
*
|
|
12
|
+
* It lives in its own entry precisely because it imports `next/headers`: keeping
|
|
13
|
+
* it out of `index.ts` means `@atlasauth/nextjs` (middleware + matcher) still
|
|
14
|
+
* builds and runs with no `next` installed. `next` is only an OPTIONAL peer.
|
|
15
|
+
*/
|
|
16
|
+
import { type AtlasClient, type AtlasClientConfig } from '@atlasauth/backend';
|
|
17
|
+
import { createAuthHelper, type AtlasMiddlewareOptions, type CurrentUser, type EdgeRequest } from './middleware';
|
|
18
|
+
type Env = Record<string, string | undefined>;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the edge-verification config from the environment, mirroring how Clerk
|
|
21
|
+
* reads `CLERK_*` / `NEXT_PUBLIC_CLERK_*`. Server-only names win over their
|
|
22
|
+
* `NEXT_PUBLIC_` fallbacks. Throws with an actionable message when a required
|
|
23
|
+
* value is absent, because a silently-misconfigured verifier fails every request
|
|
24
|
+
* anyway — better a clear error at the first call than a mystery sign-out loop.
|
|
25
|
+
*/
|
|
26
|
+
export declare function configFromEnv(env?: Env): AtlasMiddlewareOptions;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the Backend-API (secret-key) config from the environment, for
|
|
29
|
+
* {@link atlasClient}. The secret key is server-only by definition — there is no
|
|
30
|
+
* `NEXT_PUBLIC_` fallback for it, and there must never be one.
|
|
31
|
+
*/
|
|
32
|
+
export declare function backendConfigFromEnv(env?: Env): AtlasClientConfig;
|
|
33
|
+
/**
|
|
34
|
+
* `clerkClient()`-equivalent: a configured `@atlasauth/backend` management client
|
|
35
|
+
* built from `ATLAS_SECRET_KEY` (+ optional `ATLAS_API_URL`). Overrides win over
|
|
36
|
+
* the environment, for tests and multi-instance setups.
|
|
37
|
+
*/
|
|
38
|
+
export declare function atlasClient(overrides?: Partial<AtlasClientConfig>): AtlasClient;
|
|
39
|
+
/** The subset of Next's cookie store {@link buildEdgeRequest} reads. */
|
|
40
|
+
export interface CookieStoreLike {
|
|
41
|
+
getAll(): ReadonlyArray<{
|
|
42
|
+
name: string;
|
|
43
|
+
value: string;
|
|
44
|
+
}>;
|
|
45
|
+
get?(name: string): {
|
|
46
|
+
value: string;
|
|
47
|
+
} | undefined;
|
|
48
|
+
}
|
|
49
|
+
/** The subset of Next's header store {@link buildEdgeRequest} reads. */
|
|
50
|
+
export interface HeaderStoreLike {
|
|
51
|
+
get(name: string): string | null | undefined;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build an `EdgeRequest`-shaped object from a cookie store + a header store, the
|
|
55
|
+
* bridge between `next/headers` and the existing `resolveAuth` core. Pure and
|
|
56
|
+
* injectable: the ambient `auth()` feeds it the real Next stores, and tests feed
|
|
57
|
+
* it fakes — no request context required. `resolveAuth` reads only the
|
|
58
|
+
* `authorization` and `cookie` headers, so those are what we synthesize; the
|
|
59
|
+
* cookie header is reconstructed from the cookie store (which reflects any
|
|
60
|
+
* in-request mutations), falling back to the raw `cookie` header.
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildEdgeRequest(cookieStore: CookieStoreLike, headerStore: HeaderStoreLike): EdgeRequest;
|
|
63
|
+
/** What the pre-bound `auth()` resolves to — the same state + `has`/`protect` the
|
|
64
|
+
* factory helper returns. */
|
|
65
|
+
export type ServerAuth = Awaited<ReturnType<ReturnType<typeof createAuthHelper>>>;
|
|
66
|
+
/**
|
|
67
|
+
* Clerk-parity `auth()` — no arguments. Reads cookies/headers from the ambient
|
|
68
|
+
* `next/headers` request, verifies the session at the edge via the shared core,
|
|
69
|
+
* and returns the same `AuthState & { has, protect }`. Config comes from the
|
|
70
|
+
* environment (see {@link configFromEnv}). Signed-out is a plain state, not a
|
|
71
|
+
* throw — call `.protect()` where anonymous access is a bug.
|
|
72
|
+
*/
|
|
73
|
+
export declare function auth(): Promise<ServerAuth>;
|
|
74
|
+
/**
|
|
75
|
+
* Clerk-parity `currentUser()` — no arguments. The signed-in user's profile from
|
|
76
|
+
* the verified session claims (no round-trip), or `null` when signed out.
|
|
77
|
+
*/
|
|
78
|
+
export declare function currentUser(): Promise<CurrentUser | null>;
|
|
79
|
+
export type { AtlasClient, AtlasClientConfig } from '@atlasauth/backend';
|
|
80
|
+
export type { AuthState, CurrentUser, EdgeRequest } from './middleware';
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.configFromEnv = configFromEnv;
|
|
37
|
+
exports.backendConfigFromEnv = backendConfigFromEnv;
|
|
38
|
+
exports.atlasClient = atlasClient;
|
|
39
|
+
exports.buildEdgeRequest = buildEdgeRequest;
|
|
40
|
+
exports.auth = auth;
|
|
41
|
+
exports.currentUser = currentUser;
|
|
42
|
+
/**
|
|
43
|
+
* `@atlasauth/nextjs/server` — the pre-bound, zero-argument App-Router helpers.
|
|
44
|
+
*
|
|
45
|
+
* §Q7 gap: the base entry ships FACTORIES (`createAuthHelper`,
|
|
46
|
+
* `createCurrentUserHelper`) that the integrator wires once and then feeds an
|
|
47
|
+
* `EdgeRequest` on every call. Clerk instead ships `auth()` / `currentUser()`
|
|
48
|
+
* that read the ambient request from `next/headers` with no argument and read
|
|
49
|
+
* their config from the environment. This subpath adds exactly that ergonomic
|
|
50
|
+
* ON TOP of the existing core — it reuses `resolveAuth` + `AtlasBackend` through
|
|
51
|
+
* the very same factories, so JWT verification is defined in exactly one place.
|
|
52
|
+
*
|
|
53
|
+
* It lives in its own entry precisely because it imports `next/headers`: keeping
|
|
54
|
+
* it out of `index.ts` means `@atlasauth/nextjs` (middleware + matcher) still
|
|
55
|
+
* builds and runs with no `next` installed. `next` is only an OPTIONAL peer.
|
|
56
|
+
*/
|
|
57
|
+
const backend_1 = require("@atlasauth/backend");
|
|
58
|
+
const middleware_1 = require("./middleware");
|
|
59
|
+
/** Read `process.env` without requiring `process` to be typed (no @types/node). */
|
|
60
|
+
function readEnv() {
|
|
61
|
+
return globalThis.process?.env ?? {};
|
|
62
|
+
}
|
|
63
|
+
/** First non-empty env var among `names`. */
|
|
64
|
+
function pick(env, ...names) {
|
|
65
|
+
for (const name of names) {
|
|
66
|
+
const value = env[name];
|
|
67
|
+
if (value !== undefined && value !== '')
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve the edge-verification config from the environment, mirroring how Clerk
|
|
74
|
+
* reads `CLERK_*` / `NEXT_PUBLIC_CLERK_*`. Server-only names win over their
|
|
75
|
+
* `NEXT_PUBLIC_` fallbacks. Throws with an actionable message when a required
|
|
76
|
+
* value is absent, because a silently-misconfigured verifier fails every request
|
|
77
|
+
* anyway — better a clear error at the first call than a mystery sign-out loop.
|
|
78
|
+
*/
|
|
79
|
+
function configFromEnv(env = readEnv()) {
|
|
80
|
+
const jwksUrl = pick(env, 'ATLAS_JWKS_URL', 'NEXT_PUBLIC_ATLAS_JWKS_URL');
|
|
81
|
+
const issuer = pick(env, 'ATLAS_ISSUER', 'NEXT_PUBLIC_ATLAS_ISSUER');
|
|
82
|
+
if (!jwksUrl) {
|
|
83
|
+
throw new Error('@atlasauth/nextjs/server: missing ATLAS_JWKS_URL (or NEXT_PUBLIC_ATLAS_JWKS_URL).');
|
|
84
|
+
}
|
|
85
|
+
if (!issuer) {
|
|
86
|
+
throw new Error('@atlasauth/nextjs/server: missing ATLAS_ISSUER (or NEXT_PUBLIC_ATLAS_ISSUER).');
|
|
87
|
+
}
|
|
88
|
+
const authorizedPartiesRaw = pick(env, 'ATLAS_AUTHORIZED_PARTIES', 'NEXT_PUBLIC_ATLAS_AUTHORIZED_PARTIES');
|
|
89
|
+
const authorizedParties = authorizedPartiesRaw
|
|
90
|
+
? authorizedPartiesRaw
|
|
91
|
+
.split(',')
|
|
92
|
+
.map((part) => part.trim())
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
: undefined;
|
|
95
|
+
const publishableKey = pick(env, 'ATLAS_PUBLISHABLE_KEY', 'NEXT_PUBLIC_ATLAS_PUBLISHABLE_KEY');
|
|
96
|
+
return { jwksUrl, issuer, authorizedParties, publishableKey };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolve the Backend-API (secret-key) config from the environment, for
|
|
100
|
+
* {@link atlasClient}. The secret key is server-only by definition — there is no
|
|
101
|
+
* `NEXT_PUBLIC_` fallback for it, and there must never be one.
|
|
102
|
+
*/
|
|
103
|
+
function backendConfigFromEnv(env = readEnv()) {
|
|
104
|
+
const secretKey = pick(env, 'ATLAS_SECRET_KEY');
|
|
105
|
+
if (!secretKey) {
|
|
106
|
+
throw new Error('@atlasauth/nextjs/server: missing ATLAS_SECRET_KEY.');
|
|
107
|
+
}
|
|
108
|
+
const apiUrl = pick(env, 'ATLAS_API_URL', 'NEXT_PUBLIC_ATLAS_API_URL');
|
|
109
|
+
return { secretKey, apiUrl };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `clerkClient()`-equivalent: a configured `@atlasauth/backend` management client
|
|
113
|
+
* built from `ATLAS_SECRET_KEY` (+ optional `ATLAS_API_URL`). Overrides win over
|
|
114
|
+
* the environment, for tests and multi-instance setups.
|
|
115
|
+
*/
|
|
116
|
+
function atlasClient(overrides = {}) {
|
|
117
|
+
const env = readEnv();
|
|
118
|
+
// Resolve from the environment leniently so an explicit override can supply
|
|
119
|
+
// the secret key on its own; `createAtlasClient` still throws if none is found
|
|
120
|
+
// from either source.
|
|
121
|
+
return (0, backend_1.createAtlasClient)({
|
|
122
|
+
secretKey: overrides.secretKey ?? pick(env, 'ATLAS_SECRET_KEY') ?? '',
|
|
123
|
+
apiUrl: overrides.apiUrl ?? pick(env, 'ATLAS_API_URL', 'NEXT_PUBLIC_ATLAS_API_URL'),
|
|
124
|
+
fetch: overrides.fetch,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build an `EdgeRequest`-shaped object from a cookie store + a header store, the
|
|
129
|
+
* bridge between `next/headers` and the existing `resolveAuth` core. Pure and
|
|
130
|
+
* injectable: the ambient `auth()` feeds it the real Next stores, and tests feed
|
|
131
|
+
* it fakes — no request context required. `resolveAuth` reads only the
|
|
132
|
+
* `authorization` and `cookie` headers, so those are what we synthesize; the
|
|
133
|
+
* cookie header is reconstructed from the cookie store (which reflects any
|
|
134
|
+
* in-request mutations), falling back to the raw `cookie` header.
|
|
135
|
+
*/
|
|
136
|
+
function buildEdgeRequest(cookieStore, headerStore) {
|
|
137
|
+
const reconstructed = cookieStore
|
|
138
|
+
.getAll()
|
|
139
|
+
.map(({ name, value }) => `${name}=${value}`)
|
|
140
|
+
.join('; ');
|
|
141
|
+
const cookieHeader = reconstructed.length > 0 ? reconstructed : (headerStore.get('cookie') ?? null);
|
|
142
|
+
const authorization = headerStore.get('authorization') ?? null;
|
|
143
|
+
return {
|
|
144
|
+
nextUrl: { pathname: '' },
|
|
145
|
+
headers: {
|
|
146
|
+
get(name) {
|
|
147
|
+
const key = name.toLowerCase();
|
|
148
|
+
if (key === 'cookie')
|
|
149
|
+
return cookieHeader;
|
|
150
|
+
if (key === 'authorization')
|
|
151
|
+
return authorization;
|
|
152
|
+
return headerStore.get(name) ?? null;
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Read the ambient request from `next/headers` and shape it for the core.
|
|
159
|
+
* Imported dynamically so merely importing this module never touches
|
|
160
|
+
* `next/headers` (which throws outside a request scope) — the module loads only
|
|
161
|
+
* when `auth()`/`currentUser()` actually run inside a server component, route
|
|
162
|
+
* handler, or Server Action.
|
|
163
|
+
*/
|
|
164
|
+
async function edgeRequestFromContext() {
|
|
165
|
+
const { cookies, headers } = await Promise.resolve().then(() => __importStar(require('next/headers')));
|
|
166
|
+
const cookieStore = await cookies();
|
|
167
|
+
const headerStore = await headers();
|
|
168
|
+
return buildEdgeRequest(cookieStore, headerStore);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Memoize the factory-built helpers by their resolved config so the in-process
|
|
172
|
+
* JWKS cache inside `AtlasBackend` stays warm across calls — rebuilding a fresh
|
|
173
|
+
* backend per `auth()` would throw that cache away and re-fetch the JWKS on
|
|
174
|
+
* every render, defeating the whole point of edge-local verification.
|
|
175
|
+
*/
|
|
176
|
+
function cacheKey(options) {
|
|
177
|
+
return [options.jwksUrl, options.issuer, (options.authorizedParties ?? []).join(',')].join('\n');
|
|
178
|
+
}
|
|
179
|
+
let authHelperCache;
|
|
180
|
+
function getAuthHelper() {
|
|
181
|
+
const options = configFromEnv();
|
|
182
|
+
const key = cacheKey(options);
|
|
183
|
+
if (!authHelperCache || authHelperCache.key !== key) {
|
|
184
|
+
authHelperCache = { key, fn: (0, middleware_1.createAuthHelper)(options) };
|
|
185
|
+
}
|
|
186
|
+
return authHelperCache.fn;
|
|
187
|
+
}
|
|
188
|
+
let currentUserHelperCache;
|
|
189
|
+
function getCurrentUserHelper() {
|
|
190
|
+
const options = configFromEnv();
|
|
191
|
+
const key = cacheKey(options);
|
|
192
|
+
if (!currentUserHelperCache || currentUserHelperCache.key !== key) {
|
|
193
|
+
currentUserHelperCache = { key, fn: (0, middleware_1.createCurrentUserHelper)(options) };
|
|
194
|
+
}
|
|
195
|
+
return currentUserHelperCache.fn;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Clerk-parity `auth()` — no arguments. Reads cookies/headers from the ambient
|
|
199
|
+
* `next/headers` request, verifies the session at the edge via the shared core,
|
|
200
|
+
* and returns the same `AuthState & { has, protect }`. Config comes from the
|
|
201
|
+
* environment (see {@link configFromEnv}). Signed-out is a plain state, not a
|
|
202
|
+
* throw — call `.protect()` where anonymous access is a bug.
|
|
203
|
+
*/
|
|
204
|
+
async function auth() {
|
|
205
|
+
const helper = getAuthHelper();
|
|
206
|
+
return helper(await edgeRequestFromContext());
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Clerk-parity `currentUser()` — no arguments. The signed-in user's profile from
|
|
210
|
+
* the verified session claims (no round-trip), or `null` when signed out.
|
|
211
|
+
*/
|
|
212
|
+
async function currentUser() {
|
|
213
|
+
const helper = getCurrentUserHelper();
|
|
214
|
+
return helper(await edgeRequestFromContext());
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/nextjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./server": {
|
|
12
|
+
"types": "./dist/server.d.ts",
|
|
13
|
+
"default": "./dist/server.js"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
6
17
|
"dependencies": {
|
|
7
|
-
"@atlasauth/backend": "0.
|
|
18
|
+
"@atlasauth/backend": "0.7.0"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"next": ">=13.4"
|
|
22
|
+
},
|
|
23
|
+
"peerDependenciesMeta": {
|
|
24
|
+
"next": {
|
|
25
|
+
"optional": true
|
|
26
|
+
}
|
|
8
27
|
},
|
|
9
28
|
"devDependencies": {
|
|
10
29
|
"typescript": "^5.6.3",
|
|
11
30
|
"vitest": "^2.1.4",
|
|
12
|
-
"jose": "^5.9.6"
|
|
31
|
+
"jose": "^5.9.6",
|
|
32
|
+
"next": "^15.0.0"
|
|
13
33
|
},
|
|
14
34
|
"publishConfig": {
|
|
15
35
|
"access": "public"
|
|
16
36
|
},
|
|
17
37
|
"files": [
|
|
18
38
|
"dist",
|
|
19
|
-
"README.md"
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md"
|
|
20
41
|
],
|
|
21
42
|
"scripts": {
|
|
22
43
|
"build": "tsc -p tsconfig.build.json",
|