@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/CHANGELOG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @atlasauth/nextjs changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Next.js 16 `proxy.ts` support.** `atlasProxy(options)` returns `(request) => Promise<Response | null>`. It resolves to a `Response` when Atlas must act (the sign-in redirect, the cross-property handshake, or a JSON 401 for `/api/*`) and to `null` to let the rest of the proxy run. It takes the same options and makes the same decisions as `atlasMiddleware`. It uses only Web-standard APIs, so it runs on both the Node.js runtime (the Next 16 default) and the Edge runtime.
|
|
7
|
+
- `withAtlasHeaders(request, response, auth?)` forwards the verified session to the app as request headers: `x-atlas-auth-status`, `x-atlas-user-id`, `x-atlas-session-id`, `x-atlas-org-id` and `x-atlas-org-role`. It works on a `NextResponse.next()` or a rewrite produced by any later step, such as next-intl, and merges with request-header overrides already set. It always drops `x-atlas-*` headers sent by the client.
|
|
8
|
+
- `readAtlasHeaders(headers)` returns the forwarded session as a `ForwardedAuth` (`{ signedIn, userId, sessionId, orgId, orgRole }`). `proxyAuth(request)` returns the `AuthState` the proxy resolved for a request. `ATLAS_AUTH_HEADERS` names the header constants.
|
|
9
|
+
- New types: `AtlasProxy`, `ProxyRequest`, `HeaderCarryingResponse`, `ForwardedAuth`.
|
|
10
|
+
- **Locale-prefixed routes.** Route patterns accept whole-segment `:name` parameters (`/:locale/dashboard(.*)`). The new `MiddlewareConfig` fields `locales` and `defaultLocale` restrict a `:locale` segment to the listed locales and fill in a `signInUrl` such as `/:locale/sign-in` from the request. New exports: `localizeSignInUrl` and the `PatternOptions` type. `matchesPattern`, `matchesAny` and `createRouteMatcher` take an optional `PatternOptions` argument.
|
|
11
|
+
- `createAtlasDecider(options)` exposes the runtime-neutral decision engine behind both `atlasMiddleware` and `atlasProxy`. It resolves to an `AtlasDecision` (`continue`, `redirect` with cookies, or `unauthorized`). New types: `AtlasDecision`, `AtlasCookie`. New constant: `UNAUTHENTICATED_BODY`.
|
|
12
|
+
- A package README.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- `atlasMiddleware` / `authMiddleware` now render the shared decision engine. Their behaviour and signature are unchanged.
|
|
16
|
+
- Patterns without a `:name` segment keep their exact previous meaning.
|
|
17
|
+
|
|
18
|
+
## 0.1.4
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- New `@atlasauth/nextjs/server` entry with pre-bound, zero-argument `auth()` and `currentUser()`. They read the ambient request through `next/headers` and their config from `ATLAS_JWKS_URL` / `ATLAS_ISSUER` (or the `NEXT_PUBLIC_` forms). The base entry still doesn't need `next` installed.
|
|
22
|
+
- `atlasClient()`: a configured `@atlasauth/backend` client built from `ATLAS_SECRET_KEY` (and `ATLAS_API_URL`, if set).
|
|
23
|
+
- `configFromEnv()`, `backendConfigFromEnv()`, `buildEdgeRequest()`, and the `ServerAuth`, `CookieStoreLike` and `HeaderStoreLike` types.
|
|
24
|
+
|
|
25
|
+
## 0.1.3
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- `createRouteMatcher(patterns)`. It returns a predicate over a `NextRequest`, a `{ nextUrl }` / `{ url }` object, or a path string, using the same `(.*)` pattern engine as the middleware.
|
|
29
|
+
|
|
30
|
+
## 0.1.2
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- The middleware honours the durable signed-in hint (`__atlas_uat`, or the same-origin `__atlas_rt` cookie). A signed-in user whose short-lived `__session` has lapsed between refreshes is let through instead of being redirected to sign-in. Exported as `hasSignedInHint(request)`.
|
|
34
|
+
- `authMiddleware`, an alias of `atlasMiddleware`.
|
|
35
|
+
- `createCurrentUserHelper(options)` returns `currentUser(request)`. It gives a `CurrentUser` (`id`, `orgId`, `orgRole`, `email`, `name`, `firstName`, `lastName`), read from the verified session claims without a network call.
|
|
36
|
+
|
|
37
|
+
## 0.1.1
|
|
38
|
+
|
|
39
|
+
First release published under the `@atlasauth` scope.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- `atlasMiddleware(options)`: verifies the session JWT locally (JWKS cached in-process), protects routes by default (`publicRoutes` / `protectedRoutes`, where protected wins), returns a 401 JSON response for `/api/*`, skips static assets, and validates `redirect_url` (`safeRedirect`).
|
|
43
|
+
- `createAuthHelper(options)` returns `auth(request)`, with `has(condition)` and `protect(condition)` over the full permission/role condition set shared with the other Atlas SDKs.
|
|
44
|
+
- Cross-property SSO: `handshake: true` (with `publishableKey`, and optionally `fapiOrigin`) bounces a signed-out visitor through the Atlas handshake once, PKCE-bound and loop-guarded. A cross-domain return redeems the nonce and sets first-party `__session` / `__atlas_rt` cookies.
|
|
45
|
+
- Exports: `resolveAuth`, `SIGNED_OUT`, `decideRoute`, `matchesPattern`, `matchesAny`, `isStaticAsset`, `DEFAULT_SIGN_IN_URL`, and the `AuthState`, `EdgeRequest`, `EdgeResponse`, `EdgeRedirect`, `EdgeCookies`, `MiddlewareConfig` and `RouteDecision` types.
|
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @atlasauth/nextjs
|
|
2
|
+
|
|
3
|
+
Atlas authentication for Next.js (App Router). It covers route protection in `middleware.ts` (Next 14/15) or `proxy.ts` (Next 16), plus `auth()` and `currentUser()` in server components and route handlers. Session tokens are verified locally against your instance's JWKS, so a warm instance makes no network call.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @atlasauth/nextjs
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
`next` is an optional peer dependency (`>=13.4`, including 16).
|
|
10
|
+
|
|
11
|
+
## Next.js 16: `proxy.ts`
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// proxy.ts
|
|
15
|
+
import { NextResponse, type NextRequest } from 'next/server';
|
|
16
|
+
import { atlasProxy, withAtlasHeaders } from '@atlasauth/nextjs';
|
|
17
|
+
|
|
18
|
+
const atlas = atlasProxy({
|
|
19
|
+
jwksUrl: 'https://auth.example.com/.well-known/jwks.json',
|
|
20
|
+
issuer: 'https://auth.example.com',
|
|
21
|
+
publicRoutes: ['/', '/sign-in(.*)', '/sign-up(.*)'],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export async function proxy(request: NextRequest) {
|
|
25
|
+
return (await atlas(request)) ?? withAtlasHeaders(request, NextResponse.next());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- `atlas(request)` resolves to a `Response` when Atlas has to act (a sign-in redirect, the cross-property handshake, or a JSON 401 for `/api/*`). Otherwise it resolves to `null` and your proxy carries on.
|
|
32
|
+
- `withAtlasHeaders(request, response)` forwards the verified session to your app on whatever response the rest of your proxy returns, as `x-atlas-auth-status`, `x-atlas-user-id`, `x-atlas-session-id`, `x-atlas-org-id` and `x-atlas-org-role` request headers. It merges with request-header overrides that other steps have already set, and it always drops `x-atlas-*` headers sent by the client. Read them in a server component with `readAtlasHeaders(await headers())`.
|
|
33
|
+
- `proxyAuth(request)` returns the auth state the proxy already resolved, for extra checks inside `proxy.ts`.
|
|
34
|
+
|
|
35
|
+
**Why `Response | null`?** In Next 16 the proxy is usually a chain (auth, then i18n, then your own logic), so Atlas returns a response only when it must and otherwise steps aside. The session is attached to the final response with `withAtlasHeaders`, and the token is verified only once.
|
|
36
|
+
|
|
37
|
+
It uses only Web-standard APIs, so it runs on both the Node.js runtime (the Next 16 default) and the Edge runtime.
|
|
38
|
+
|
|
39
|
+
### With next-intl
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import createMiddleware from 'next-intl/middleware';
|
|
43
|
+
import type { NextRequest } from 'next/server';
|
|
44
|
+
import { atlasProxy, withAtlasHeaders } from '@atlasauth/nextjs';
|
|
45
|
+
import { routing } from './i18n/routing';
|
|
46
|
+
|
|
47
|
+
const intl = createMiddleware(routing);
|
|
48
|
+
const atlas = atlasProxy({
|
|
49
|
+
jwksUrl: 'https://auth.example.com/.well-known/jwks.json',
|
|
50
|
+
issuer: 'https://auth.example.com',
|
|
51
|
+
locales: routing.locales,
|
|
52
|
+
defaultLocale: routing.defaultLocale,
|
|
53
|
+
publicRoutes: ['/', '/:locale', '/:locale/sign-in(.*)'],
|
|
54
|
+
protectedRoutes: ['/:locale/dashboard(.*)'],
|
|
55
|
+
signInUrl: '/:locale/sign-in', // /de/dashboard → /de/sign-in?redirect_url=…
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export async function proxy(request: NextRequest) {
|
|
59
|
+
return (await atlas(request)) ?? withAtlasHeaders(request, intl(request));
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
A `:locale` segment matches only the configured `locales`. Any other `:name` segment matches any single path segment.
|
|
64
|
+
|
|
65
|
+
## Next.js 14/15: `middleware.ts`
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { authMiddleware } from '@atlasauth/nextjs';
|
|
69
|
+
import { NextResponse, type NextRequest } from 'next/server';
|
|
70
|
+
|
|
71
|
+
const middleware = authMiddleware({
|
|
72
|
+
jwksUrl: 'https://auth.example.com/.well-known/jwks.json',
|
|
73
|
+
issuer: 'https://auth.example.com',
|
|
74
|
+
publicRoutes: ['/', '/sign-in(.*)'],
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export default function (request: NextRequest) {
|
|
78
|
+
return middleware(request, NextResponse);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`atlasMiddleware` is the same function as `authMiddleware`.
|
|
83
|
+
|
|
84
|
+
## Server components and route handlers
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { auth, currentUser } from '@atlasauth/nextjs/server';
|
|
88
|
+
|
|
89
|
+
const { userId, protect } = await auth();
|
|
90
|
+
const user = await currentUser(); // from the verified session claims, no network call
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
These read `ATLAS_JWKS_URL` and `ATLAS_ISSUER` (or their `NEXT_PUBLIC_` forms) from the environment.
|
|
94
|
+
|
|
95
|
+
## Routing rules
|
|
96
|
+
|
|
97
|
+
- Routes are protected unless they match `publicRoutes`. `protectedRoutes` wins over `publicRoutes`.
|
|
98
|
+
- Patterns support a trailing `(.*)` and whole-segment `:name` parameters. They aren't regular expressions.
|
|
99
|
+
- A signed-in user whose short-lived `__session` cookie has expired is let through on the durable signed-in hint, so the client can refresh it. It doesn't bounce to sign-in.
|
|
100
|
+
|
|
101
|
+
## Changelog
|
|
102
|
+
|
|
103
|
+
See [CHANGELOG.md](./CHANGELOG.md).
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -16,4 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./matcher"), exports);
|
|
18
18
|
__exportStar(require("./middleware"), exports);
|
|
19
|
+
__exportStar(require("./proxy"), exports);
|
|
19
20
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,+CAA6B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,+CAA6B;AAC7B,0CAAwB"}
|
package/dist/matcher.d.ts
CHANGED
|
@@ -12,15 +12,26 @@
|
|
|
12
12
|
* which is a loud failure rather than a silent breach.
|
|
13
13
|
*/
|
|
14
14
|
/**
|
|
15
|
-
* Patterns support
|
|
16
|
-
*
|
|
15
|
+
* Patterns support two forms, both from Next.js's own matcher dialect so a
|
|
16
|
+
* developer writes one dialect rather than two:
|
|
17
|
+
*
|
|
18
|
+
* - `(.*)` at the end — matches the rest of the path (`/admin(.*)`).
|
|
19
|
+
* - `:name` as a whole segment — matches exactly one non-empty segment
|
|
20
|
+
* (`/:locale/dashboard`). This is what makes locale-prefixed routing
|
|
21
|
+
* (next-intl and friends) expressible. When {@link PatternOptions.locales} is
|
|
22
|
+
* given, a `:locale` segment matches ONLY those values, so
|
|
23
|
+
* `/:locale/sign-in` can't make `/admin/sign-in` public by accident.
|
|
17
24
|
*
|
|
18
25
|
* Deliberately NOT a general regex. A customer-authored regex in an
|
|
19
26
|
* authorization path is a catastrophic-backtracking outage waiting to happen,
|
|
20
27
|
* and `/admin(.*)` covers what anyone actually needs.
|
|
21
28
|
*/
|
|
22
|
-
export
|
|
23
|
-
|
|
29
|
+
export interface PatternOptions {
|
|
30
|
+
/** The app's locales. Restricts what a `:locale` pattern segment matches. */
|
|
31
|
+
locales?: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
export declare function matchesPattern(pathname: string, pattern: string, options?: PatternOptions): boolean;
|
|
34
|
+
export declare function matchesAny(pathname: string, patterns: readonly string[], options?: PatternOptions): boolean;
|
|
24
35
|
/**
|
|
25
36
|
* §10.1 Clerk-parity `createRouteMatcher(patterns)` — the idiom integrators write
|
|
26
37
|
* at the top of `middleware.ts` (`const isProtected = createRouteMatcher(['/dashboard(.*)'])`).
|
|
@@ -30,7 +41,7 @@ export declare function matchesAny(pathname: string, patterns: readonly string[]
|
|
|
30
41
|
* matcher written here and the middleware's own routing agree exactly (no second
|
|
31
42
|
* dialect, no customer regex in the auth path).
|
|
32
43
|
*/
|
|
33
|
-
export declare function createRouteMatcher(patterns: readonly string[]): (input: string | {
|
|
44
|
+
export declare function createRouteMatcher(patterns: readonly string[], options?: PatternOptions): (input: string | {
|
|
34
45
|
nextUrl?: {
|
|
35
46
|
pathname?: string;
|
|
36
47
|
};
|
|
@@ -45,7 +56,23 @@ export interface MiddlewareConfig {
|
|
|
45
56
|
signInUrl?: string;
|
|
46
57
|
/** Exact origins a `redirect_url` may point at (§13.1). */
|
|
47
58
|
allowedOrigins?: readonly string[];
|
|
59
|
+
/**
|
|
60
|
+
* The app's locales, for locale-prefixed routing (next-intl and similar). A
|
|
61
|
+
* `:locale` segment in `publicRoutes`/`protectedRoutes` then matches only
|
|
62
|
+
* these values, and a `signInUrl` containing `/:locale` is filled in from the
|
|
63
|
+
* request's own locale prefix.
|
|
64
|
+
*/
|
|
65
|
+
locales?: readonly string[];
|
|
66
|
+
/** Locale used for a `/:locale` sign-in URL when the request carries none. When
|
|
67
|
+
* absent, the `/:locale` segment is dropped and the i18n layer picks one. */
|
|
68
|
+
defaultLocale?: string;
|
|
48
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Fill a `signInUrl` like `/:locale/sign-in` from the request path's locale
|
|
72
|
+
* prefix, so a signed-out visitor on `/de/dashboard` lands on `/de/sign-in`
|
|
73
|
+
* rather than bouncing through the i18n layer's own redirect.
|
|
74
|
+
*/
|
|
75
|
+
export declare function localizeSignInUrl(signInUrl: string, pathname: string, config?: Pick<MiddlewareConfig, 'locales' | 'defaultLocale'>): string;
|
|
49
76
|
export declare const DEFAULT_SIGN_IN_URL = "/sign-in";
|
|
50
77
|
export type RouteDecision = {
|
|
51
78
|
action: 'skip';
|
package/dist/matcher.js
CHANGED
|
@@ -18,28 +18,51 @@ exports.matchesPattern = matchesPattern;
|
|
|
18
18
|
exports.matchesAny = matchesAny;
|
|
19
19
|
exports.createRouteMatcher = createRouteMatcher;
|
|
20
20
|
exports.isStaticAsset = isStaticAsset;
|
|
21
|
+
exports.localizeSignInUrl = localizeSignInUrl;
|
|
21
22
|
exports.decideRoute = decideRoute;
|
|
22
23
|
exports.safeRedirect = safeRedirect;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
function segmentMatches(actual, pattern, options) {
|
|
25
|
+
if (!pattern.startsWith(':'))
|
|
26
|
+
return actual === pattern;
|
|
27
|
+
if (actual === '')
|
|
28
|
+
return false;
|
|
29
|
+
if (pattern === ':locale' && options.locales)
|
|
30
|
+
return options.locales.includes(actual);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
function matchesPattern(pathname, pattern, options = {}) {
|
|
32
34
|
if (pattern === pathname)
|
|
33
35
|
return true;
|
|
34
36
|
const wildcard = pattern.indexOf('(.*)');
|
|
35
|
-
if (wildcard === -1)
|
|
36
|
-
return false;
|
|
37
37
|
// Anything after the wildcard is meaningless — `(.*)` already matched it.
|
|
38
|
-
const prefix = pattern.slice(0, wildcard);
|
|
39
|
-
|
|
38
|
+
const prefix = wildcard === -1 ? pattern : pattern.slice(0, wildcard);
|
|
39
|
+
// No `:param` segment: the original string semantics, unchanged.
|
|
40
|
+
if (!prefix.includes('/:')) {
|
|
41
|
+
return wildcard !== -1 && pathname.startsWith(prefix);
|
|
42
|
+
}
|
|
43
|
+
const want = prefix.split('/');
|
|
44
|
+
const have = pathname.split('/');
|
|
45
|
+
if (wildcard === -1) {
|
|
46
|
+
return (want.length === have.length &&
|
|
47
|
+
want.every((segment, i) => segmentMatches(have[i], segment, options)));
|
|
48
|
+
}
|
|
49
|
+
// Wildcard: every segment but the last matches whole; the last one keeps the
|
|
50
|
+
// string-prefix semantics (`/:locale/dash(.*)` matches `/en/dashboard`), and a
|
|
51
|
+
// trailing `:param` must still be present.
|
|
52
|
+
if (have.length < want.length)
|
|
53
|
+
return false;
|
|
54
|
+
const last = want.length - 1;
|
|
55
|
+
for (let i = 0; i < last; i++) {
|
|
56
|
+
if (!segmentMatches(have[i], want[i], options))
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const tail = want[last];
|
|
60
|
+
return tail.startsWith(':')
|
|
61
|
+
? segmentMatches(have[last], tail, options)
|
|
62
|
+
: have[last].startsWith(tail);
|
|
40
63
|
}
|
|
41
|
-
function matchesAny(pathname, patterns) {
|
|
42
|
-
return patterns.some((pattern) => matchesPattern(pathname, pattern));
|
|
64
|
+
function matchesAny(pathname, patterns, options = {}) {
|
|
65
|
+
return patterns.some((pattern) => matchesPattern(pathname, pattern, options));
|
|
43
66
|
}
|
|
44
67
|
/**
|
|
45
68
|
* §10.1 Clerk-parity `createRouteMatcher(patterns)` — the idiom integrators write
|
|
@@ -50,7 +73,7 @@ function matchesAny(pathname, patterns) {
|
|
|
50
73
|
* matcher written here and the middleware's own routing agree exactly (no second
|
|
51
74
|
* dialect, no customer regex in the auth path).
|
|
52
75
|
*/
|
|
53
|
-
function createRouteMatcher(patterns) {
|
|
76
|
+
function createRouteMatcher(patterns, options = {}) {
|
|
54
77
|
return (input) => {
|
|
55
78
|
let pathname;
|
|
56
79
|
if (typeof input === 'string') {
|
|
@@ -61,7 +84,7 @@ function createRouteMatcher(patterns) {
|
|
|
61
84
|
pathname =
|
|
62
85
|
input.nextUrl?.pathname ?? (input.url ? new URL(input.url, 'http://x').pathname : '');
|
|
63
86
|
}
|
|
64
|
-
return matchesAny(pathname, patterns);
|
|
87
|
+
return matchesAny(pathname, patterns, options);
|
|
65
88
|
};
|
|
66
89
|
}
|
|
67
90
|
/**
|
|
@@ -79,6 +102,20 @@ function isStaticAsset(pathname) {
|
|
|
79
102
|
const last = pathname.split('/').pop() ?? '';
|
|
80
103
|
return last.includes('.');
|
|
81
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Fill a `signInUrl` like `/:locale/sign-in` from the request path's locale
|
|
107
|
+
* prefix, so a signed-out visitor on `/de/dashboard` lands on `/de/sign-in`
|
|
108
|
+
* rather than bouncing through the i18n layer's own redirect.
|
|
109
|
+
*/
|
|
110
|
+
function localizeSignInUrl(signInUrl, pathname, config = {}) {
|
|
111
|
+
if (!signInUrl.includes('/:locale'))
|
|
112
|
+
return signInUrl;
|
|
113
|
+
const first = pathname.split('/')[1] ?? '';
|
|
114
|
+
const locale = config.locales?.includes(first) ? first : config.defaultLocale;
|
|
115
|
+
return locale
|
|
116
|
+
? signInUrl.replace('/:locale', `/${locale}`)
|
|
117
|
+
: signInUrl.replace('/:locale', '') || '/';
|
|
118
|
+
}
|
|
82
119
|
exports.DEFAULT_SIGN_IN_URL = '/sign-in';
|
|
83
120
|
/**
|
|
84
121
|
* Decide what to do with a request.
|
|
@@ -92,8 +129,9 @@ function decideRoute(input) {
|
|
|
92
129
|
const config = input.config ?? {};
|
|
93
130
|
if (isStaticAsset(input.pathname))
|
|
94
131
|
return { action: 'skip' };
|
|
95
|
-
const
|
|
96
|
-
const
|
|
132
|
+
const patternOptions = { locales: config.locales };
|
|
133
|
+
const explicitlyProtected = matchesAny(input.pathname, config.protectedRoutes ?? [], patternOptions);
|
|
134
|
+
const isPublic = !explicitlyProtected && matchesAny(input.pathname, config.publicRoutes ?? [], patternOptions);
|
|
97
135
|
if (isPublic || input.signedIn)
|
|
98
136
|
return { action: 'allow' };
|
|
99
137
|
/**
|
|
@@ -103,7 +141,7 @@ function decideRoute(input) {
|
|
|
103
141
|
*/
|
|
104
142
|
if (input.isApiRoute)
|
|
105
143
|
return { action: 'unauthorized' };
|
|
106
|
-
const signInUrl = config.signInUrl ?? exports.DEFAULT_SIGN_IN_URL;
|
|
144
|
+
const signInUrl = localizeSignInUrl(config.signInUrl ?? exports.DEFAULT_SIGN_IN_URL, input.pathname, config);
|
|
107
145
|
return {
|
|
108
146
|
action: 'redirect',
|
|
109
147
|
to: `${signInUrl}?redirect_url=${encodeURIComponent(input.pathname)}`,
|
package/dist/matcher.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matcher.js","sourceRoot":"","sources":["../src/matcher.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;
|
|
1
|
+
{"version":3,"file":"matcher.js","sourceRoot":"","sources":["../src/matcher.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AA6BH,wCAqCC;AAED,gCAMC;AAWD,gDAeC;AAWD,sCAKC;AA2BD,8CAWC;AAkBD,kCAkCC;AASD,oCAiBC;AAlND,SAAS,cAAc,CAAC,MAAc,EAAE,OAAe,EAAE,OAAuB;IAC9E,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,KAAK,OAAO,CAAC;IACxD,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAChC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,OAAO;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,cAAc,CAC5B,QAAgB,EAChB,OAAe,EACf,UAA0B,EAAE;IAE5B,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,0EAA0E;IAC1E,MAAM,MAAM,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAEtE,iEAAiE;IACjE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,OAAO,QAAQ,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;QACpB,OAAO,CACL,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAC3B,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CACvE,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,+EAA+E;IAC/E,2CAA2C;IAC3C,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;IACjE,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAE,CAAC;IACzB,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACzB,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAE,EAAE,IAAI,EAAE,OAAO,CAAC;QAC5C,CAAC,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,SAAgB,UAAU,CACxB,QAAgB,EAChB,QAA2B,EAC3B,UAA0B,EAAE;IAE5B,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,kBAAkB,CAChC,QAA2B,EAC3B,UAA0B,EAAE;IAE5B,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,IAAI,QAAgB,CAAC;QACrB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,oCAAoC;YACpC,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;QACxE,CAAC;aAAM,CAAC;YACN,QAAQ;gBACN,KAAK,CAAC,OAAO,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC;AAEnG,SAAgB,aAAa,CAAC,QAAgB;IAC5C,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9E,gDAAgD;IAChD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IAC7C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAsBD;;;;GAIG;AACH,SAAgB,iBAAiB,CAC/B,SAAiB,EACjB,QAAgB,EAChB,SAA8D,EAAE;IAEhE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,SAAS,CAAC;IACtD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;IAC9E,OAAO,MAAM;QACX,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,MAAM,EAAE,CAAC;QAC7C,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;AAC/C,CAAC;AAEY,QAAA,mBAAmB,GAAG,UAAU,CAAC;AAQ9C;;;;;;;GAOG;AACH,SAAgB,WAAW,CAAC,KAM3B;IACC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;IAElC,IAAI,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAE7D,MAAM,cAAc,GAAmB,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IACnE,MAAM,mBAAmB,GAAG,UAAU,CACpC,KAAK,CAAC,QAAQ,EACd,MAAM,CAAC,eAAe,IAAI,EAAE,EAC5B,cAAc,CACf,CAAC;IACF,MAAM,QAAQ,GACZ,CAAC,mBAAmB,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;IAEhG,IAAI,QAAQ,IAAI,KAAK,CAAC,QAAQ;QAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAE3D;;;;OAIG;IACH,IAAI,KAAK,CAAC,UAAU;QAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAExD,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,SAAS,IAAI,2BAAmB,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrG,OAAO;QACL,MAAM,EAAE,UAAU;QAClB,EAAE,EAAE,GAAG,SAAS,iBAAiB,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;KACtE,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,YAAY,CAC1B,WAAsC,EACtC,iBAAoC,EAAE,EACtC,QAAQ,GAAG,GAAG;IAEd,IAAI,CAAC,WAAW;QAAE,OAAO,QAAQ,CAAC;IAElC,yEAAyE;IACzE,IAAI,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IAClD,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,WAAW,CAAC;IAEpD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;QACjC,OAAO,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC"}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -95,6 +95,48 @@ export declare function hasSignedInHint(request: EdgeRequest): boolean;
|
|
|
95
95
|
* user needs anyway.
|
|
96
96
|
*/
|
|
97
97
|
export declare function resolveAuth(backend: AtlasBackend, request: EdgeRequest): Promise<AuthState>;
|
|
98
|
+
/** A first-party cookie Atlas wants set on the response it returns. */
|
|
99
|
+
export interface AtlasCookie {
|
|
100
|
+
name: string;
|
|
101
|
+
value: string;
|
|
102
|
+
options: {
|
|
103
|
+
path?: string;
|
|
104
|
+
httpOnly?: boolean;
|
|
105
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
106
|
+
secure?: boolean;
|
|
107
|
+
maxAge?: number;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* What Atlas decided for one request, before it is rendered into any particular
|
|
112
|
+
* response type. `atlasMiddleware` renders it through Next's `NextResponse`
|
|
113
|
+
* helpers; `atlasProxy` renders it as a standard Web `Response`. Keeping the
|
|
114
|
+
* decision in one place means the two can never disagree about who is let in.
|
|
115
|
+
*/
|
|
116
|
+
export type AtlasDecision = {
|
|
117
|
+
kind: 'continue';
|
|
118
|
+
auth: AuthState;
|
|
119
|
+
} | {
|
|
120
|
+
kind: 'redirect';
|
|
121
|
+
location: string;
|
|
122
|
+
cookies: AtlasCookie[];
|
|
123
|
+
auth: AuthState;
|
|
124
|
+
} | {
|
|
125
|
+
kind: 'unauthorized';
|
|
126
|
+
auth: AuthState;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* The runtime-neutral decision engine behind {@link atlasMiddleware} and
|
|
130
|
+
* `atlasProxy`. Uses only Web-standard APIs (`URL`, `fetch`, Web Crypto), so it
|
|
131
|
+
* runs unchanged on the Edge runtime and on Node.
|
|
132
|
+
*/
|
|
133
|
+
export declare function createAtlasDecider(options: AtlasMiddlewareOptions): (request: EdgeRequest) => Promise<AtlasDecision>;
|
|
134
|
+
/** The JSON body Atlas answers an unauthenticated API request with. */
|
|
135
|
+
export declare const UNAUTHENTICATED_BODY: {
|
|
136
|
+
readonly errors: readonly [{
|
|
137
|
+
readonly code: "UNAUTHENTICATED";
|
|
138
|
+
}];
|
|
139
|
+
};
|
|
98
140
|
export declare function atlasMiddleware(options: AtlasMiddlewareOptions): (request: EdgeRequest, response: EdgeResponse) => Promise<unknown>;
|
|
99
141
|
/**
|
|
100
142
|
* The `auth()` helper for server components and route handlers.
|
package/dist/middleware.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.safeRedirect = exports.authMiddleware = exports.SIGNED_OUT = void 0;
|
|
3
|
+
exports.safeRedirect = exports.authMiddleware = exports.UNAUTHENTICATED_BODY = exports.SIGNED_OUT = void 0;
|
|
4
4
|
exports.hasSignedInHint = hasSignedInHint;
|
|
5
5
|
exports.resolveAuth = resolveAuth;
|
|
6
|
+
exports.createAtlasDecider = createAtlasDecider;
|
|
6
7
|
exports.atlasMiddleware = atlasMiddleware;
|
|
7
8
|
exports.createAuthHelper = createAuthHelper;
|
|
8
9
|
exports.createCurrentUserHelper = createCurrentUserHelper;
|
|
@@ -79,14 +80,19 @@ async function resolveAuth(backend, request) {
|
|
|
79
80
|
claims: result.claims,
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
|
-
|
|
83
|
+
/**
|
|
84
|
+
* The runtime-neutral decision engine behind {@link atlasMiddleware} and
|
|
85
|
+
* `atlasProxy`. Uses only Web-standard APIs (`URL`, `fetch`, Web Crypto), so it
|
|
86
|
+
* runs unchanged on the Edge runtime and on Node.
|
|
87
|
+
*/
|
|
88
|
+
function createAtlasDecider(options) {
|
|
83
89
|
const backend = new backend_1.AtlasBackend({
|
|
84
90
|
jwksUrl: options.jwksUrl,
|
|
85
91
|
issuer: options.issuer,
|
|
86
92
|
authorizedParties: options.authorizedParties,
|
|
87
93
|
fetchImpl: options.fetchImpl,
|
|
88
94
|
});
|
|
89
|
-
return async function
|
|
95
|
+
return async function decide(request) {
|
|
90
96
|
/**
|
|
91
97
|
* Cross-domain satellite RETURN: the handshake endpoint bounced the browser
|
|
92
98
|
* back with `?__atlas_hs=ok&__atlas_hu=<userId>&__atlas_hn=<nonce>`. Exchange
|
|
@@ -123,17 +129,21 @@ function atlasMiddleware(options) {
|
|
|
123
129
|
for (const k of ['__atlas_hs', '__atlas_hu', '__atlas_hn'])
|
|
124
130
|
clean.searchParams.delete(k);
|
|
125
131
|
if (session) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
return {
|
|
133
|
+
kind: 'redirect',
|
|
134
|
+
location: clean.toString(),
|
|
135
|
+
auth: exports.SIGNED_OUT,
|
|
136
|
+
cookies: [
|
|
137
|
+
{ name: '__session', value: session.jwt, options: { path: '/', sameSite: 'lax', secure: true } },
|
|
138
|
+
{
|
|
139
|
+
name: '__atlas_rt',
|
|
140
|
+
value: session.refreshToken,
|
|
141
|
+
options: { path: '/', httpOnly: true, sameSite: 'lax', secure: true },
|
|
142
|
+
},
|
|
143
|
+
// The verifier is spent — clear it so it can't be reused or leak.
|
|
144
|
+
{ name: '__atlas_hv', value: '', options: { path: '/', maxAge: 0 } },
|
|
145
|
+
],
|
|
146
|
+
};
|
|
137
147
|
}
|
|
138
148
|
// A bad/expired/replayed nonce, a rejected PKCE proof, or no verifier →
|
|
139
149
|
// no session. Carry a SPENT marker (__atlas_hs=failed) instead of a clean
|
|
@@ -141,9 +151,12 @@ function atlasMiddleware(options) {
|
|
|
141
151
|
// URL here would bounce straight back to the handshake forever. With the
|
|
142
152
|
// marker, normal routing takes over and sends the user to sign-in.
|
|
143
153
|
clean.searchParams.set('__atlas_hs', 'failed');
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
154
|
+
return {
|
|
155
|
+
kind: 'redirect',
|
|
156
|
+
location: clean.toString(),
|
|
157
|
+
auth: exports.SIGNED_OUT,
|
|
158
|
+
cookies: [{ name: '__atlas_hv', value: '', options: { path: '/', maxAge: 0 } }],
|
|
159
|
+
};
|
|
147
160
|
}
|
|
148
161
|
}
|
|
149
162
|
const auth = await resolveAuth(backend, request);
|
|
@@ -156,9 +169,9 @@ function atlasMiddleware(options) {
|
|
|
156
169
|
switch (decision.action) {
|
|
157
170
|
case 'skip':
|
|
158
171
|
case 'allow':
|
|
159
|
-
return
|
|
172
|
+
return { kind: 'continue', auth };
|
|
160
173
|
case 'unauthorized':
|
|
161
|
-
return
|
|
174
|
+
return { kind: 'unauthorized', auth };
|
|
162
175
|
case 'redirect': {
|
|
163
176
|
/**
|
|
164
177
|
* §R3.1 FIRST, don't bounce a user who is actually signed in here but
|
|
@@ -171,7 +184,7 @@ function atlasMiddleware(options) {
|
|
|
171
184
|
* common "same-property, token just expired" case never leaves the app.
|
|
172
185
|
*/
|
|
173
186
|
if (hasSignedInHint(request))
|
|
174
|
-
return
|
|
187
|
+
return { kind: 'continue', auth };
|
|
175
188
|
/**
|
|
176
189
|
* Cross-property handshake: before sending a signed-out user to the
|
|
177
190
|
* sign-in page, try the Atlas handshake ONCE — they may already have a
|
|
@@ -193,15 +206,24 @@ function atlasMiddleware(options) {
|
|
|
193
206
|
hs.searchParams.set('publishable_key', options.publishableKey);
|
|
194
207
|
hs.searchParams.set('redirect_url', current.toString());
|
|
195
208
|
hs.searchParams.set('code_challenge', challenge);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
209
|
+
return {
|
|
210
|
+
kind: 'redirect',
|
|
211
|
+
location: hs.toString(),
|
|
212
|
+
auth,
|
|
213
|
+
cookies: [
|
|
214
|
+
{
|
|
215
|
+
name: '__atlas_hv',
|
|
216
|
+
value: verifier,
|
|
217
|
+
options: {
|
|
218
|
+
path: '/',
|
|
219
|
+
httpOnly: true,
|
|
220
|
+
sameSite: 'lax',
|
|
221
|
+
secure: true,
|
|
222
|
+
maxAge: 300, // the handshake round trip is seconds; don't linger
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
};
|
|
205
227
|
}
|
|
206
228
|
}
|
|
207
229
|
/**
|
|
@@ -211,7 +233,28 @@ function atlasMiddleware(options) {
|
|
|
211
233
|
* would otherwise be carried forward unchecked (§13.1).
|
|
212
234
|
*/
|
|
213
235
|
const base = request.url ? new URL(request.url).origin : '';
|
|
214
|
-
return
|
|
236
|
+
return { kind: 'redirect', location: `${base}${decision.to}`, auth, cookies: [] };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/** The JSON body Atlas answers an unauthenticated API request with. */
|
|
242
|
+
exports.UNAUTHENTICATED_BODY = { errors: [{ code: 'UNAUTHENTICATED' }] };
|
|
243
|
+
function atlasMiddleware(options) {
|
|
244
|
+
const decide = createAtlasDecider(options);
|
|
245
|
+
return async function middleware(request, response) {
|
|
246
|
+
const decision = await decide(request);
|
|
247
|
+
switch (decision.kind) {
|
|
248
|
+
case 'continue':
|
|
249
|
+
return response.next();
|
|
250
|
+
case 'unauthorized':
|
|
251
|
+
return response.json(exports.UNAUTHENTICATED_BODY, { status: 401 });
|
|
252
|
+
case 'redirect': {
|
|
253
|
+
const res = response.redirect(decision.location);
|
|
254
|
+
for (const cookie of decision.cookies) {
|
|
255
|
+
res.cookies?.set(cookie.name, cookie.value, cookie.options);
|
|
256
|
+
}
|
|
257
|
+
return res;
|
|
215
258
|
}
|
|
216
259
|
}
|
|
217
260
|
};
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":";;;AAgHA,0CAMC;AAUD,kCAkBC;
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|