@strivacity/sdk-next 4.0.0-beta.1 → 4.0.0-beta.2
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 +125 -59
- package/dist/server/handlers/api.cjs +1 -1
- package/dist/server/handlers/api.cjs.map +1 -1
- package/dist/server/handlers/api.mjs +1 -1
- package/dist/server/handlers/api.mjs.map +1 -1
- package/dist/server/handlers/route.cjs +1 -1
- package/dist/server/handlers/route.cjs.map +1 -1
- package/dist/server/handlers/route.mjs +1 -1
- package/dist/server/handlers/route.mjs.map +1 -1
- package/dist/server/sdk.cjs +1 -1
- package/dist/server/sdk.cjs.map +1 -1
- package/dist/server/sdk.mjs +1 -1
- package/dist/server/sdk.mjs.map +1 -1
- package/dist/server/storages.cjs +1 -1
- package/dist/server/storages.cjs.map +1 -1
- package/dist/server/storages.mjs +1 -1
- package/dist/server/storages.mjs.map +1 -1
- package/dist/server/types.d.ts +11 -3
- package/package.json +5 -11
- package/testing/tests/client/components.spec.ts +0 -10
- package/testing/tests/client/hooks.spec.ts +0 -10
- package/testing/tests/client/index.spec.ts +0 -106
- package/testing/tests/client/storages.spec.ts +0 -10
- package/testing/tests/client/utils.spec.ts +0 -10
- package/testing/tests/server/handlers/api.spec.ts +0 -110
- package/testing/tests/server/handlers/route.spec.ts +0 -173
- package/testing/tests/server/index.spec.ts +0 -103
- package/testing/tests/server/sdk.spec.ts +0 -250
- package/testing/tests/server/storages.spec.ts +0 -353
- package/testing/tests/server/utils.spec.ts +0 -110
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ export const sdkOptions: SDKInitConfig = {
|
|
|
106
106
|
redirectUri: 'https://your-app.example.com/callback', // callback URL after authentication
|
|
107
107
|
scopes: ['openid', 'profile', 'email'], // requested user permissions/data
|
|
108
108
|
|
|
109
|
-
// Omit
|
|
109
|
+
// Omit these lines entirely for client-managed sessions
|
|
110
110
|
serverSessionUri: '/auth/login',
|
|
111
111
|
};
|
|
112
112
|
```
|
|
@@ -148,10 +148,10 @@ export const DELETE = sdk.handler;
|
|
|
148
148
|
| `GET` | `/auth/register` | Starts the registration flow and redirects to the IDP | `302` redirect to IDP |
|
|
149
149
|
| `GET` | `/auth/callback` | Completes authentication (handles the IDP callback) | `302` redirect or popup close script |
|
|
150
150
|
| `GET` | `/auth/refresh` | Refreshes the access token | `204 No Content` or `302` redirect |
|
|
151
|
-
| `GET` | `/auth/revoke` | Revokes tokens and clears the session | `
|
|
152
|
-
| `GET` | `/auth/logout` | Ends the session and redirects to the IDP logout page
|
|
153
|
-
| `GET` | `/auth/entry` | Handles external flow entry (e.g., password reset link) - embedded/native modes only |
|
|
154
|
-
| `POST` | `/auth/backchannel-logout` | Processes back-channel logout requests from the IDP | `
|
|
151
|
+
| `GET` | `/auth/revoke` | Revokes tokens and clears the session | `302` redirect to `postLogoutRedirectUri` |
|
|
152
|
+
| `GET` | `/auth/logout` | Ends the session and redirects to the IDP logout page; accepts a same-origin `?returnTo=` (default: `postLogoutRedirectUri`) | `302` to IDP logout (straight to `returnTo` when the session has no ID token) |
|
|
153
|
+
| `GET` | `/auth/entry` | Handles external flow entry (e.g., password reset link) - embedded/native modes only | `302` redirect to `loginUri` |
|
|
154
|
+
| `POST` | `/auth/backchannel-logout` | Processes back-channel logout requests from the IDP | `200 OK` (`400` invalid token, `501` storage without `deleteByLogoutToken`, `500` delete failed) |
|
|
155
155
|
|
|
156
156
|
> The `/auth` prefix and route names come from `authUrlPrefix` - see [Server configuration reference](#server-configuration-reference).
|
|
157
157
|
|
|
@@ -198,13 +198,14 @@ For server-managed sessions, fetch the session once in your root layout (a Serve
|
|
|
198
198
|
```tsx
|
|
199
199
|
// app/layout.tsx
|
|
200
200
|
import type { ReactNode } from 'react';
|
|
201
|
+
import { toClientSession } from '@strivacity/sdk-next/server';
|
|
201
202
|
import { AuthProvider } from '../lib/auth/provider';
|
|
202
203
|
import { sdk } from '../lib/auth/server';
|
|
203
204
|
|
|
204
205
|
export const dynamic = 'force-dynamic';
|
|
205
206
|
|
|
206
207
|
export default async function RootLayout({ children }: { children: ReactNode }) {
|
|
207
|
-
const session = await sdk.getSession();
|
|
208
|
+
const session = toClientSession(await sdk.getSession());
|
|
208
209
|
|
|
209
210
|
return (
|
|
210
211
|
<html lang="en-US">
|
|
@@ -216,6 +217,8 @@ export default async function RootLayout({ children }: { children: ReactNode })
|
|
|
216
217
|
}
|
|
217
218
|
```
|
|
218
219
|
|
|
220
|
+
> **Note:** The session passed to the provider is serialized into the rendered HTML, so `toClientSession()` (exported from both `@strivacity/sdk-next/server` and `@strivacity/sdk-next/client`) removes the long-lived credentials before handing it over: the refresh token (refresh goes through `/auth/refresh` on the server) and the ID token (only the server-side `/auth/logout` route needs it).
|
|
221
|
+
|
|
219
222
|
---
|
|
220
223
|
|
|
221
224
|
## Client SDK
|
|
@@ -415,7 +418,7 @@ export default function LogoutPage() {
|
|
|
415
418
|
|
|
416
419
|
**Server-managed sessions**:
|
|
417
420
|
|
|
418
|
-
With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
|
|
421
|
+
With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
|
|
419
422
|
|
|
420
423
|
```tsx
|
|
421
424
|
// app/logout/page.tsx
|
|
@@ -468,7 +471,7 @@ export function TokenPanel() {
|
|
|
468
471
|
|
|
469
472
|
**Server-managed sessions**:
|
|
470
473
|
|
|
471
|
-
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the catch-all route, then let it redirect back:
|
|
474
|
+
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the catch-all route, then let it redirect back. `sdk.refresh({ returnTo: '/profile' })` (`sdk` from `useStrivacity()`) performs the same refresh navigation for you:
|
|
472
475
|
|
|
473
476
|
```tsx
|
|
474
477
|
'use client';
|
|
@@ -568,11 +571,39 @@ Popup mode always needs client-side JavaScript to open the window, so there's no
|
|
|
568
571
|
|
|
569
572
|
**Client-managed sessions**:
|
|
570
573
|
|
|
571
|
-
The
|
|
574
|
+
The pop-up still navigates to your `redirectUri`, so you still need a callback page there - without it nothing posts the result back to the opener. Call `handleCallback()` on that page; in popup mode it never resolves (the opener closes the pop-up once it receives the result), so don't rely on code after it running:
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
// app/callback/page.tsx
|
|
578
|
+
'use client';
|
|
579
|
+
|
|
580
|
+
import { useEffect } from 'react';
|
|
581
|
+
import { useStrivacity } from '@strivacity/sdk-next/client';
|
|
582
|
+
|
|
583
|
+
export default function CallbackPage() {
|
|
584
|
+
const { loading, handleCallback } = useStrivacity();
|
|
585
|
+
|
|
586
|
+
useEffect(() => {
|
|
587
|
+
if (loading) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
void handleCallback();
|
|
592
|
+
}, [loading]);
|
|
593
|
+
|
|
594
|
+
return (
|
|
595
|
+
<section>
|
|
596
|
+
<h1>Logging in...</h1>
|
|
597
|
+
</section>
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
`login()` resolves in the opener once the result has been received and, for client-managed sessions, the tokens have been exchanged.
|
|
572
603
|
|
|
573
604
|
**Server-managed sessions**:
|
|
574
605
|
|
|
575
|
-
|
|
606
|
+
The callback page must not call `handleCallback()`: forward the query string to `/auth/callback` instead, as in the server-managed redirect callback. The Server SDK exchanges the code, sets the session cookie and responds with a page that posts the result to the opener and closes the pop-up.
|
|
576
607
|
|
|
577
608
|
##### Registration
|
|
578
609
|
|
|
@@ -665,7 +696,7 @@ export default function LogoutPage() {
|
|
|
665
696
|
|
|
666
697
|
**Server-managed sessions**:
|
|
667
698
|
|
|
668
|
-
With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
|
|
699
|
+
With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
|
|
669
700
|
|
|
670
701
|
```tsx
|
|
671
702
|
// app/logout/page.tsx
|
|
@@ -718,7 +749,7 @@ export function TokenPanel() {
|
|
|
718
749
|
|
|
719
750
|
**Server-managed sessions**:
|
|
720
751
|
|
|
721
|
-
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the catch-all route, then let it redirect back:
|
|
752
|
+
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the catch-all route, then let it redirect back. `sdk.refresh({ returnTo: '/profile' })` (`sdk` from `useStrivacity()`) performs the same refresh navigation for you:
|
|
722
753
|
|
|
723
754
|
```tsx
|
|
724
755
|
'use client';
|
|
@@ -1246,7 +1277,7 @@ export default function LogoutPage() {
|
|
|
1246
1277
|
|
|
1247
1278
|
**Server-managed sessions**:
|
|
1248
1279
|
|
|
1249
|
-
With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
|
|
1280
|
+
With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
|
|
1250
1281
|
|
|
1251
1282
|
```tsx
|
|
1252
1283
|
// app/logout/page.tsx
|
|
@@ -1299,7 +1330,7 @@ export function TokenPanel() {
|
|
|
1299
1330
|
|
|
1300
1331
|
**Server-managed sessions**:
|
|
1301
1332
|
|
|
1302
|
-
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the catch-all route, then let it redirect back:
|
|
1333
|
+
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the catch-all route, then let it redirect back. `sdk.refresh({ returnTo: '/profile' })` (`sdk` from `useStrivacity()`) performs the same refresh navigation for you:
|
|
1303
1334
|
|
|
1304
1335
|
```tsx
|
|
1305
1336
|
'use client';
|
|
@@ -1353,7 +1384,6 @@ export default function LoginPage() {
|
|
|
1353
1384
|
params: {
|
|
1354
1385
|
prompt: 'login', // use 'create' to open the registration flow instead
|
|
1355
1386
|
language: 'en-US', // set the UI language (BCP 47 language tag)
|
|
1356
|
-
sdk: 'web-minimal', // rendering mode: 'web-minimal' for simplified rendering, 'web' (default) for full rendering hints and branding
|
|
1357
1387
|
sessionId: searchParams.get('session_id'), // pass a session ID to resume an existing flow
|
|
1358
1388
|
},
|
|
1359
1389
|
onLogin: async () => {
|
|
@@ -1604,7 +1634,7 @@ export default function LogoutPage() {
|
|
|
1604
1634
|
|
|
1605
1635
|
**Server-managed sessions**:
|
|
1606
1636
|
|
|
1607
|
-
With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
|
|
1637
|
+
With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
|
|
1608
1638
|
|
|
1609
1639
|
```tsx
|
|
1610
1640
|
// app/logout/page.tsx
|
|
@@ -1657,7 +1687,7 @@ export function TokenPanel() {
|
|
|
1657
1687
|
|
|
1658
1688
|
**Server-managed sessions**:
|
|
1659
1689
|
|
|
1660
|
-
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the catch-all route, then let it redirect back:
|
|
1690
|
+
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the catch-all route, then let it redirect back. `sdk.refresh({ returnTo: '/profile' })` (`sdk` from `useStrivacity()`) performs the same refresh navigation for you:
|
|
1661
1691
|
|
|
1662
1692
|
```tsx
|
|
1663
1693
|
'use client';
|
|
@@ -1702,23 +1732,31 @@ const ctx = useStrivacity<RedirectFlow>();
|
|
|
1702
1732
|
```ts
|
|
1703
1733
|
{
|
|
1704
1734
|
// SDK instance (access any SDK method)
|
|
1705
|
-
sdk: RedirectFlow | PopupFlow | EmbeddedFlow | NativeFlow;
|
|
1735
|
+
readonly sdk: RedirectFlow | PopupFlow | EmbeddedFlow | NativeFlow;
|
|
1706
1736
|
|
|
1707
1737
|
// Reactive state (plain values - the component re-renders when they change)
|
|
1708
|
-
loading: boolean; // True during initialization
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
//
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1738
|
+
readonly loading: boolean; // True during initialization
|
|
1739
|
+
readonly language: string; // Current BCP 47 language code
|
|
1740
|
+
readonly isAuthenticated: boolean; // True if user has valid session
|
|
1741
|
+
readonly idTokenClaims: IdTokenClaims | null; // Decoded ID token claims
|
|
1742
|
+
readonly accessToken: string | null; // Current access token
|
|
1743
|
+
readonly refreshToken: string | null; // Current refresh token
|
|
1744
|
+
readonly accessTokenExpired: boolean; // True once the access token has expired
|
|
1745
|
+
readonly accessTokenExpirationDate: number | null; // Access token expiration timestamp
|
|
1746
|
+
|
|
1747
|
+
// Methods (same signatures as the underlying flow's methods)
|
|
1748
|
+
init(): Promise<void>; // Initialize the SDK (load metadata, restore a persisted session)
|
|
1749
|
+
checkAuthentication(params?: { autoRefresh: boolean }): Promise<boolean>; // Check the session, optionally refreshing the token (never when serverSessionUri is set)
|
|
1750
|
+
login(params?: RedirectParams | PopupParams): Promise<void>; // Start login flow (redirect: `returnTo` sets where handleCallback() sends the user) (embedded/native: returns an EmbeddedLoginFlow/NativeLoginFlow instead)
|
|
1751
|
+
register(params?: RedirectParams | PopupParams): Promise<void>; // Start registration flow (embedded/native: returns an EmbeddedLoginFlow/NativeLoginFlow instead)
|
|
1752
|
+
handleCallback(url?: string | URL): Promise<{ returnTo?: string }>; // Handle OAuth callback; resolves to the `returnTo` given to login()
|
|
1718
1753
|
logout(params?: LogoutParams): Promise<void>; // End session
|
|
1719
|
-
refresh(): Promise<void>; // Refresh access token
|
|
1754
|
+
refresh(params?: { returnTo?: string }): Promise<void>; // Refresh access token (server-managed: navigates to the server's refresh route, `${authUrlPrefix}/refresh`, instead; `returnTo` sets where it returns, default: the current URL)
|
|
1720
1755
|
revoke(): Promise<void>; // Revoke tokens
|
|
1721
|
-
|
|
1756
|
+
tokenExchange(params?: Record<string, string>): Promise<void>; // Perform a token exchange
|
|
1757
|
+
entry(url?: string | URL, params?: RedirectOptions): Promise<void | EntryResponse>; // Handle external entry (resolves to EntryResponse in embedded/native mode)
|
|
1758
|
+
subscribeToEvent<T extends keyof EventFunctions>(eventName: T, callbackFn: (...params: Parameters<EventFunctions[T]>) => Promise<void> | void): { dispose: () => void }; // Subscribe to a single SDK event
|
|
1759
|
+
subscribeToAllEvents(callbackFn: (...params: Array<unknown>) => Promise<void> | void): { dispose: () => void }; // Subscribe to all SDK events
|
|
1722
1760
|
}
|
|
1723
1761
|
```
|
|
1724
1762
|
|
|
@@ -1744,10 +1782,10 @@ const ctx = useNativeLogin({
|
|
|
1744
1782
|
{
|
|
1745
1783
|
params?: NativeParams; // Initial flow parameters
|
|
1746
1784
|
onLogin?: (session: SessionData) => void | Promise<void>; // Called on successful login
|
|
1747
|
-
onClose?: () => void
|
|
1748
|
-
onError?: (error:
|
|
1749
|
-
onFallback?: (error: FallbackError) => void
|
|
1750
|
-
onGlobalMessage?: (message: NativeFlowMessage) => void
|
|
1785
|
+
onClose?: () => void | Promise<void>; // Called when user closes the flow
|
|
1786
|
+
onError?: (error: Error) => void | Promise<void>; // Called on error
|
|
1787
|
+
onFallback?: (error: FallbackError) => void | Promise<void>; // Called when fallback needed
|
|
1788
|
+
onGlobalMessage?: (message: NativeFlowMessage) => void | Promise<void>; // Called for global messages
|
|
1751
1789
|
}
|
|
1752
1790
|
```
|
|
1753
1791
|
|
|
@@ -1770,9 +1808,19 @@ const ctx = useNativeLogin({
|
|
|
1770
1808
|
}
|
|
1771
1809
|
```
|
|
1772
1810
|
|
|
1811
|
+
#### useNativeLoginContext
|
|
1812
|
+
|
|
1813
|
+
Returns the context of the currently mounted `useNativeLogin()` from any component (e.g. your own widget components), without passing it down as props. Before `useNativeLogin()` has mounted it returns an empty default context (`loading: true`, no-op methods).
|
|
1814
|
+
|
|
1815
|
+
```ts
|
|
1816
|
+
import { useNativeLoginContext } from '@strivacity/sdk-next/client';
|
|
1817
|
+
|
|
1818
|
+
const { forms, messages, setFormValue, submitForm } = useNativeLoginContext();
|
|
1819
|
+
```
|
|
1820
|
+
|
|
1773
1821
|
#### withAuthGuard (client)
|
|
1774
1822
|
|
|
1775
|
-
Higher-order component that guards a component with authentication. Waits for the SDK to finish loading, checks the session, and redirects to the login page if the user is not authenticated. Not to be confused with the Server SDK's [`withAuthGuard`](#withauthguard), which guards a page/route server-side.
|
|
1823
|
+
Higher-order component that guards a component with authentication. Waits for the SDK to finish loading, checks the session, and redirects to the login page if the user is not authenticated (with the current path as `returnTo`, e.g. `/login?returnTo=%2Forders` - pass it on with `login({ returnTo })`). With server-managed sessions, an expired session is sent through the server's refresh route instead (`sdk.refresh()`, returning to the current URL). Not to be confused with the Server SDK's [`withAuthGuard`](#withauthguard), which guards a page/route server-side.
|
|
1776
1824
|
|
|
1777
1825
|
```tsx
|
|
1778
1826
|
'use client';
|
|
@@ -1846,7 +1894,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|
|
1846
1894
|
|
|
1847
1895
|
### Accessing the session server-side
|
|
1848
1896
|
|
|
1849
|
-
Call `getSession()` from any Server Component, Route Handler, or `getServerSideProps` to read the current session without going through the client SDK:
|
|
1897
|
+
Call `getSession()` from any Server Component, Route Handler, or `getServerSideProps` (pass the request there: `sdk.getSession(ctx.req)`) to read the current session without going through the client SDK. In Server Components (no request) and for Pages Router requests it reads the stored session without refreshing, so the returned session can be expired: a rotated session could not be saved there. Use [`withAuthGuard`](#withauthguard), which sends an expiring session through `/auth/refresh`, or check `expires_at` yourself:
|
|
1850
1898
|
|
|
1851
1899
|
```tsx
|
|
1852
1900
|
// app/profile/page.tsx (Server Component)
|
|
@@ -1864,10 +1912,25 @@ export default async function ProfilePage() {
|
|
|
1864
1912
|
}
|
|
1865
1913
|
```
|
|
1866
1914
|
|
|
1915
|
+
With an App Router request - in Route Handlers, where cookies can be written - `getSession(req)` also refreshes: with `autoRefresh` (the default) it refreshes an expired (or within `refreshSkew`) session first when a refresh token exists, at most once per request (concurrent and later calls with the same `req` share the result), and resolves to `null` if the refresh fails.
|
|
1916
|
+
|
|
1917
|
+
```ts
|
|
1918
|
+
// app/api/me/route.ts
|
|
1919
|
+
import type { NextRequest } from 'next/server';
|
|
1920
|
+
import { sdk } from '../../../lib/auth/server';
|
|
1921
|
+
|
|
1922
|
+
export async function GET(req: NextRequest) {
|
|
1923
|
+
const session = await sdk.getSession(req);
|
|
1924
|
+
return session ? Response.json({ claims: session.claims }) : Response.json({ error: 'not_authenticated' }, { status: 401 });
|
|
1925
|
+
}
|
|
1926
|
+
```
|
|
1927
|
+
|
|
1867
1928
|
### withAuthGuard
|
|
1868
1929
|
|
|
1869
1930
|
Guards a page and injects a `session` prop, redirecting to `options.loginUri` (default `/login`) when there's no active session.
|
|
1870
1931
|
|
|
1932
|
+
The guard also checks expiry. When the access token has expired (or is within `refreshSkew` seconds of expiry) and the session has a refresh token, it redirects to `${authUrlPrefix}/refresh?returnTo=...` - the refresh route writes the rotated session cookie and redirects back. The `returnTo` is the guard's `returnTo` option, falling back to `postLoginRedirectUri` (App Router) or the requested page, `ctx.resolvedUrl` (Pages Router). An expired session without a refresh token - or any expired session with `autoRefresh: false` - is treated as no session and redirects to the login page.
|
|
1933
|
+
|
|
1871
1934
|
**App Router:**
|
|
1872
1935
|
|
|
1873
1936
|
```tsx
|
|
@@ -1893,7 +1956,7 @@ export default function ProfilePage({ session }: { session: SessionData }) {
|
|
|
1893
1956
|
}
|
|
1894
1957
|
```
|
|
1895
1958
|
|
|
1896
|
-
Pass `returnTo` to redirect back to a specific page after login, or (Pages Router only) `getServerSideProps` to run your own data fetching alongside the guard:
|
|
1959
|
+
Pass `returnTo` to redirect back to a specific page after login or refresh, or (Pages Router only) `getServerSideProps` to run your own data fetching alongside the guard:
|
|
1897
1960
|
|
|
1898
1961
|
```ts
|
|
1899
1962
|
export const getServerSideProps = sdk.withAuthGuard({
|
|
@@ -1938,7 +2001,7 @@ By default the Server SDK stores tokens encrypted in http-only cookies and login
|
|
|
1938
2001
|
#### Built-in session storages
|
|
1939
2002
|
|
|
1940
2003
|
- **`createEncryptedCookieStorage(secret, options?)`** - default storage that keeps the session encrypted in an http-only cookie.
|
|
1941
|
-
- **`createSessionIdCookieStorage(storage, options?)`** - puts only a small, random session-id cookie on the client and keeps the actual session payload in the `storage` you provide.
|
|
2004
|
+
- **`createSessionIdCookieStorage(storage, options?)`** - puts only a small, random session-id cookie on the client and keeps the actual session payload in the `storage` you provide. It supports back-channel logout when `storage` implements `deleteByLogoutToken`.
|
|
1942
2005
|
|
|
1943
2006
|
```ts
|
|
1944
2007
|
// lib/auth/storage.ts
|
|
@@ -1967,37 +2030,40 @@ export const sdk = createServerSDK({
|
|
|
1967
2030
|
|
|
1968
2031
|
#### Custom storage
|
|
1969
2032
|
|
|
1970
|
-
For example you can use Redis via [unstorage](https://npmjs.com/package/unstorage)
|
|
2033
|
+
For example you can use Redis via [unstorage](https://npmjs.com/package/unstorage).
|
|
2034
|
+
|
|
2035
|
+
The SDK always reads and writes the session under the same key (`storageTokenName`), and passes the current request as the last argument. A storage that ignores the request would therefore share a single session across all users, so key the data per browser session instead. The easiest way is to wrap your key-value store with `createSessionIdCookieStorage`, which puts a random session id in a cookie and calls your store with that id as the key:
|
|
1971
2036
|
|
|
1972
2037
|
```ts
|
|
1973
2038
|
// lib/auth/storage.ts
|
|
1974
2039
|
import { createStorage } from 'unstorage';
|
|
1975
2040
|
import redisDriver from 'unstorage/drivers/redis';
|
|
1976
|
-
import type {
|
|
2041
|
+
import type { ServerStorage, SDKStorage } from '@strivacity/sdk-next/server';
|
|
1977
2042
|
|
|
1978
2043
|
const unstorageInstance = createStorage({ driver: redisDriver({ url: process.env.REDIS_URL }) });
|
|
1979
2044
|
|
|
1980
|
-
//
|
|
1981
|
-
|
|
2045
|
+
// Session payload store - `key` is the per-browser session id provided by `createSessionIdCookieStorage`
|
|
2046
|
+
// (raw get/set keep the serialized session string as-is)
|
|
2047
|
+
export const sessionStore: ServerStorage = {
|
|
1982
2048
|
async get(key) {
|
|
1983
|
-
return unstorageInstance.
|
|
2049
|
+
return unstorageInstance.getItemRaw<string>(`session:${key}`);
|
|
1984
2050
|
},
|
|
1985
2051
|
async set(key, value) {
|
|
1986
|
-
await unstorageInstance.
|
|
2052
|
+
await unstorageInstance.setItemRaw(`session:${key}`, value);
|
|
1987
2053
|
},
|
|
1988
2054
|
async delete(key) {
|
|
1989
|
-
await unstorageInstance.removeItem(key);
|
|
2055
|
+
await unstorageInstance.removeItem(`session:${key}`);
|
|
1990
2056
|
},
|
|
1991
2057
|
// Required for back-channel logout support - see below.
|
|
1992
|
-
// Scans all stored sessions and removes those
|
|
2058
|
+
// Scans all stored sessions and removes those whose ID token claims match the logout token's sid or sub claim.
|
|
1993
2059
|
async deleteByLogoutToken(logoutToken) {
|
|
1994
|
-
const keys = await unstorageInstance.getKeys();
|
|
2060
|
+
const keys = await unstorageInstance.getKeys('session');
|
|
1995
2061
|
await Promise.all(
|
|
1996
2062
|
keys.map(async (key) => {
|
|
1997
|
-
const raw = await unstorageInstance.
|
|
2063
|
+
const raw = await unstorageInstance.getItemRaw<string>(key);
|
|
1998
2064
|
if (!raw) return;
|
|
1999
2065
|
const session = JSON.parse(raw);
|
|
2000
|
-
if ((logoutToken.sid && session.sid === logoutToken.sid) || (logoutToken.sub && session.sub === logoutToken.sub)) {
|
|
2066
|
+
if ((logoutToken.sid && session.claims?.sid === logoutToken.sid) || (logoutToken.sub && session.claims?.sub === logoutToken.sub)) {
|
|
2001
2067
|
await unstorageInstance.removeItem(key);
|
|
2002
2068
|
}
|
|
2003
2069
|
}),
|
|
@@ -2021,13 +2087,13 @@ export const stateStorage: SDKStorage = {
|
|
|
2021
2087
|
|
|
2022
2088
|
```ts
|
|
2023
2089
|
// lib/auth/server.ts
|
|
2024
|
-
import { createServerSDK } from '@strivacity/sdk-next/server';
|
|
2090
|
+
import { createServerSDK, createSessionIdCookieStorage } from '@strivacity/sdk-next/server';
|
|
2025
2091
|
import { sdkOptions } from './options';
|
|
2026
|
-
import {
|
|
2092
|
+
import { sessionStore, stateStorage } from './storage';
|
|
2027
2093
|
|
|
2028
2094
|
export const sdk = createServerSDK({
|
|
2029
2095
|
...sdkOptions,
|
|
2030
|
-
storage:
|
|
2096
|
+
storage: createSessionIdCookieStorage(sessionStore), // Custom Redis-backed session storage, keyed by a session-id cookie
|
|
2031
2097
|
stateStorage, // Custom Redis-backed state storage
|
|
2032
2098
|
});
|
|
2033
2099
|
```
|
|
@@ -2038,9 +2104,9 @@ export const sdk = createServerSDK({
|
|
|
2038
2104
|
|
|
2039
2105
|
OIDC back-channel logout lets the authorization server terminate sessions server-to-server, without involving the browser. When the IDP sends a logout event (e.g. an admin terminates a session, or the user logs out from a different device), it POSTs a signed `logout_token` JWT to `/auth/backchannel-logout` - already wired up by the catch-all route from [Setup](#setup) - which routes it to `sdk.handleBackChannelLogout(req)`.
|
|
2040
2106
|
|
|
2041
|
-
The handler verifies the token's signature against the IDP's JWKS, validates the `iss`, `aud`, `iat` (freshness), and `jti` (replay protection) claims, requires the `http://schemas.openid.net/event/backchannel-logout` event and a `sid` or `sub` claim, then calls `storage.deleteByLogoutToken({ sid?, sub? })` to remove the matching session(s). It responds `200` on success, `400` for an invalid or malformed `logout_token`,
|
|
2107
|
+
The handler verifies the token's signature against the IDP's JWKS, validates the `iss`, `aud`, `iat` (freshness), and `jti` (replay protection) claims, requires the `http://schemas.openid.net/event/backchannel-logout` event and a `sid` or `sub` claim, then calls `storage.deleteByLogoutToken({ sid?, sub? })` to remove the matching session(s). It responds `200` on success, `400` for an invalid or malformed `logout_token`, `501` if the configured storage doesn't implement `deleteByLogoutToken`, and `500` if deleting the session fails.
|
|
2042
2108
|
|
|
2043
|
-
> **The default encrypted-cookie storage does not support back-channel logout** because each cookie is bound to a single browser session - there is no server-side index to look up by `sid` or `sub`. To support back-channel logout, use [`createSessionIdCookieStorage`](#server-storages) with a `storage` that implements `deleteByLogoutToken
|
|
2109
|
+
> **The default encrypted-cookie storage does not support back-channel logout** because each cookie is bound to a single browser session - there is no server-side index to look up by `sid` or `sub`. To support back-channel logout, use [`createSessionIdCookieStorage`](#server-storages) with a `storage` that implements `deleteByLogoutToken`, e.g. `createServerMemoryStorage()` for local testing or the Redis-backed store from the [Custom storage](#server-storages) example above.
|
|
2044
2110
|
|
|
2045
2111
|
Configure the **Back-channel logout URI** in your Strivacity application settings to:
|
|
2046
2112
|
|
|
@@ -2057,7 +2123,7 @@ For a complete explanation of the handshake and validation performed, see the co
|
|
|
2057
2123
|
options: ServerSDKOptions; // resolved server SDK configuration
|
|
2058
2124
|
|
|
2059
2125
|
// Session management
|
|
2060
|
-
getSession(req?): Promise<SessionData | null>; // read the current session
|
|
2126
|
+
getSession(req?): Promise<SessionData | null>; // read the current session; with an App Router request and `autoRefresh` (default) it refreshes an expired one first (once per request, null if the refresh fails). Without a request (Server Components) or with a Pages Router request it never refreshes
|
|
2061
2127
|
updateSession(session, req?): Promise<void>; // persist new session data
|
|
2062
2128
|
refreshSession(req?): Promise<SessionData>; // refresh tokens using the refresh token
|
|
2063
2129
|
revokeSession(req?): Promise<void>; // revoke tokens and clear the session
|
|
@@ -2093,8 +2159,8 @@ The Server SDK accepts the same configuration as the client SDK (see [Configurat
|
|
|
2093
2159
|
| `stateStorage` | `SDKStorage` | No | In-memory `Map` | Custom OAuth2 state storage |
|
|
2094
2160
|
| `authUrlPrefix` | `string` | No | `'/auth'` | URL prefix matched by `handler`/the catch-all route |
|
|
2095
2161
|
| `loginUri` | `string` | No | `'/login'` | Page `withAuthGuard` redirects to when there's no session |
|
|
2096
|
-
| `postLoginRedirectUri` | `string` | No |
|
|
2097
|
-
| `postLogoutRedirectUri` | `string` | No |
|
|
2162
|
+
| `postLoginRedirectUri` | `string` | No | origin of `redirectUri` | Default redirect after login when no `?returnTo=` is given |
|
|
2163
|
+
| `postLogoutRedirectUri` | `string` | No | origin of `redirectUri` | Default redirect after logout |
|
|
2098
2164
|
| `cookieMaxAge` | `number` | No | `2592000` (30 days) | Max age of the session cookie in seconds |
|
|
2099
2165
|
|
|
2100
2166
|
---
|
|
@@ -2130,7 +2196,7 @@ The client SDK accepts the same configuration as the core SDK - see the [core SD
|
|
|
2130
2196
|
|
|
2131
2197
|
### Migrating to v4.0
|
|
2132
2198
|
|
|
2133
|
-
v4 replaces the SDK's class-based flow architecture with function-based architecture, and adds a first-class Server SDK for server-managed (BFF) sessions. `StyAuthProvider`, `useStrivacity()`,
|
|
2199
|
+
v4 replaces the SDK's class-based flow architecture with function-based architecture, and adds a first-class Server SDK for server-managed (BFF) sessions. `StyAuthProvider`, `useStrivacity()`, and the built-in `redirect`/`popup`/`embedded`/`native` modes are unchanged (`useStrivacity()` no longer exposes `options` - use `sdk.options`). `useNativeLogin()` is new in v4: it replaces v3's `<StyLoginRenderer>` component (and `NativeFlowContext`), so `native` mode now renders your own widget components driven by [`useNativeLogin()`](#usenativelogin) and [`useNativeLoginContext()`](#usenativelogincontext). Apps that rendered `<StyLoginRenderer>`, used `mode: 'custom'`, or drove `native` mode through the old `NativeFlowHandler` need to update their code.
|
|
2134
2200
|
|
|
2135
2201
|
#### Class-based flows replaced by functions
|
|
2136
2202
|
|
|
@@ -2157,7 +2223,7 @@ import type { SDKInitConfig, SDKOptions } from '@strivacity/sdk-core/types';
|
|
|
2157
2223
|
|
|
2158
2224
|
export function createCustomFlow(initConfig: SDKInitConfig) {
|
|
2159
2225
|
const state = getDefaultFlowState();
|
|
2160
|
-
const options = getSDKOptions<SDKOptions>(
|
|
2226
|
+
const options = getSDKOptions<SDKOptions>(initConfig);
|
|
2161
2227
|
const base = createBaseFlow(state, options);
|
|
2162
2228
|
|
|
2163
2229
|
async function refresh(): Promise<void> {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../utils.cjs");let t=require("next/server");var n=n=>r=>async(i,a)=>{let o=i instanceof Request?e.toNextRequest(i):i;if(!await n.getSession(o))return t.NextResponse.json({error:`not_authenticated`,description:`The user does not have an active session or is not authenticated`},{status:401});let s=await r(o,a);return s instanceof t.NextResponse?s:new t.NextResponse(s.body,s)},r=e=>t=>async(n,r)=>{if(!await e.getSession(n))return r.status(401).json({error:`not_authenticated`,description:`The user does not have an active session or is not authenticated`});await t(n,r)};exports.apiAppRouteHandlerFactory=n,exports.apiPageRouteHandlerFactory=r;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../utils.cjs");let t=require("next/server");var n=n=>r=>async(i,a)=>{let o=i instanceof Request?e.toNextRequest(i):i;if(!await n.getSession(o))return t.NextResponse.json({error:`not_authenticated`,description:`The user does not have an active session or is not authenticated`},{status:401});let s=await r(o,a);return s instanceof t.NextResponse?s:new t.NextResponse(s.body,s)},r=e=>t=>async(n,r)=>{if(!await e.getSession(n,{autoRefresh:!1}))return r.status(401).json({error:`not_authenticated`,description:`The user does not have an active session or is not authenticated`});await t(n,r)};exports.apiAppRouteHandlerFactory=n,exports.apiPageRouteHandlerFactory=r;
|
|
2
2
|
//# sourceMappingURL=api.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.cjs","names":[],"sources":["../../../src/server/handlers/api.ts"],"sourcesContent":["import type { BaseServerSDK, PagesRouterRequest, WithApiAuthRequiredAppRoute, WithApiAuthRequiredPageRoute } from '../types';\nimport { NextRequest, NextResponse } from 'next/server';\nimport { toNextRequest } from '../utils';\n\nexport const apiAppRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithApiAuthRequiredAppRoute =>\n\t(apiRoute) =>\n\tasync (req: NextRequest | Request, params): Promise<NextResponse> => {\n\t\tconst nextReq = req instanceof Request ? toNextRequest(req) : req;\n\n\t\tconst session = await sdk.getSession(nextReq);\n\n\t\tif (!session) {\n\t\t\treturn NextResponse.json(\n\t\t\t\t{\n\t\t\t\t\terror: 'not_authenticated',\n\t\t\t\t\tdescription: 'The user does not have an active session or is not authenticated',\n\t\t\t\t},\n\t\t\t\t{ status: 401 },\n\t\t\t);\n\t\t}\n\n\t\tconst apiRes: NextResponse | Response = await apiRoute(nextReq, params);\n\t\tconst nextApiRes: NextResponse = apiRes instanceof NextResponse ? apiRes : new NextResponse(apiRes.body, apiRes);\n\n\t\treturn nextApiRes;\n\t};\n\nexport const apiPageRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithApiAuthRequiredPageRoute =>\n\t(apiRoute) =>\n\tasync (req, res) => {\n\t\tconst session = await sdk.getSession(req);\n\n\t\tif (!session) {\n\t\t\treturn res.status(401).json({\n\t\t\t\terror: 'not_authenticated',\n\t\t\t\tdescription: 'The user does not have an active session or is not authenticated',\n\t\t\t});\n\t\t}\n\n\t\tawait apiRoute(req, res);\n\t};\n"],"mappings":"gIAIA,IAAa,EACX,GACA,GACD,MAAO,EAA4B,IAAkC,CACpE,IAAM,EAAU,aAAe,QAAU,EAAA,cAAc,CAAG,EAAI,EAI9D,GAAI,CAAC,MAFiB,EAAI,WAAW,CAAO,EAG3C,OAAO,EAAA,aAAa,KACnB,CACC,MAAO,oBACP,YAAa,kEACd,EACA,CAAE,OAAQ,GAAI,CACf,EAGD,IAAM,EAAkC,MAAM,EAAS,EAAS,CAAM,EAGtE,OAFiC,aAAkB,EAAA,aAAe,EAAS,IAAI,EAAA,aAAa,EAAO,KAAM,CAAM,CAGhH,EAEY,EACX,GACA,GACD,MAAO,EAAK,IAAQ,
|
|
1
|
+
{"version":3,"file":"api.cjs","names":[],"sources":["../../../src/server/handlers/api.ts"],"sourcesContent":["import type { BaseServerSDK, PagesRouterRequest, WithApiAuthRequiredAppRoute, WithApiAuthRequiredPageRoute } from '../types';\nimport { NextRequest, NextResponse } from 'next/server';\nimport { toNextRequest } from '../utils';\n\nexport const apiAppRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithApiAuthRequiredAppRoute =>\n\t(apiRoute) =>\n\tasync (req: NextRequest | Request, params): Promise<NextResponse> => {\n\t\tconst nextReq = req instanceof Request ? toNextRequest(req) : req;\n\n\t\tconst session = await sdk.getSession(nextReq);\n\n\t\tif (!session) {\n\t\t\treturn NextResponse.json(\n\t\t\t\t{\n\t\t\t\t\terror: 'not_authenticated',\n\t\t\t\t\tdescription: 'The user does not have an active session or is not authenticated',\n\t\t\t\t},\n\t\t\t\t{ status: 401 },\n\t\t\t);\n\t\t}\n\n\t\tconst apiRes: NextResponse | Response = await apiRoute(nextReq, params);\n\t\tconst nextApiRes: NextResponse = apiRes instanceof NextResponse ? apiRes : new NextResponse(apiRes.body, apiRes);\n\n\t\treturn nextApiRes;\n\t};\n\nexport const apiPageRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithApiAuthRequiredPageRoute =>\n\t(apiRoute) =>\n\tasync (req, res) => {\n\t\t// NOTE: Pages Router: no cookies() store to save a refreshed session in, so read it as stored\n\t\tconst session = await sdk.getSession(req, { autoRefresh: false });\n\n\t\tif (!session) {\n\t\t\treturn res.status(401).json({\n\t\t\t\terror: 'not_authenticated',\n\t\t\t\tdescription: 'The user does not have an active session or is not authenticated',\n\t\t\t});\n\t\t}\n\n\t\tawait apiRoute(req, res);\n\t};\n"],"mappings":"gIAIA,IAAa,EACX,GACA,GACD,MAAO,EAA4B,IAAkC,CACpE,IAAM,EAAU,aAAe,QAAU,EAAA,cAAc,CAAG,EAAI,EAI9D,GAAI,CAAC,MAFiB,EAAI,WAAW,CAAO,EAG3C,OAAO,EAAA,aAAa,KACnB,CACC,MAAO,oBACP,YAAa,kEACd,EACA,CAAE,OAAQ,GAAI,CACf,EAGD,IAAM,EAAkC,MAAM,EAAS,EAAS,CAAM,EAGtE,OAFiC,aAAkB,EAAA,aAAe,EAAS,IAAI,EAAA,aAAa,EAAO,KAAM,CAAM,CAGhH,EAEY,EACX,GACA,GACD,MAAO,EAAK,IAAQ,CAInB,GAAI,CAAC,MAFiB,EAAI,WAAW,EAAK,CAAE,YAAa,EAAM,CAAC,EAG/D,OAAO,EAAI,OAAO,GAAG,CAAC,CAAC,KAAK,CAC3B,MAAO,oBACP,YAAa,kEACd,CAAC,EAGF,MAAM,EAAS,EAAK,CAAG,CACxB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{toNextRequest as e}from"../utils.mjs";import{NextResponse as t}from"next/server";var n=n=>r=>async(i,a)=>{let o=i instanceof Request?e(i):i;if(!await n.getSession(o))return t.json({error:`not_authenticated`,description:`The user does not have an active session or is not authenticated`},{status:401});let s=await r(o,a);return s instanceof t?s:new t(s.body,s)},r=e=>t=>async(n,r)=>{if(!await e.getSession(n))return r.status(401).json({error:`not_authenticated`,description:`The user does not have an active session or is not authenticated`});await t(n,r)};export{n as apiAppRouteHandlerFactory,r as apiPageRouteHandlerFactory};
|
|
1
|
+
import{toNextRequest as e}from"../utils.mjs";import{NextResponse as t}from"next/server";var n=n=>r=>async(i,a)=>{let o=i instanceof Request?e(i):i;if(!await n.getSession(o))return t.json({error:`not_authenticated`,description:`The user does not have an active session or is not authenticated`},{status:401});let s=await r(o,a);return s instanceof t?s:new t(s.body,s)},r=e=>t=>async(n,r)=>{if(!await e.getSession(n,{autoRefresh:!1}))return r.status(401).json({error:`not_authenticated`,description:`The user does not have an active session or is not authenticated`});await t(n,r)};export{n as apiAppRouteHandlerFactory,r as apiPageRouteHandlerFactory};
|
|
2
2
|
//# sourceMappingURL=api.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.mjs","names":[],"sources":["../../../src/server/handlers/api.ts"],"sourcesContent":["import type { BaseServerSDK, PagesRouterRequest, WithApiAuthRequiredAppRoute, WithApiAuthRequiredPageRoute } from '../types';\nimport { NextRequest, NextResponse } from 'next/server';\nimport { toNextRequest } from '../utils';\n\nexport const apiAppRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithApiAuthRequiredAppRoute =>\n\t(apiRoute) =>\n\tasync (req: NextRequest | Request, params): Promise<NextResponse> => {\n\t\tconst nextReq = req instanceof Request ? toNextRequest(req) : req;\n\n\t\tconst session = await sdk.getSession(nextReq);\n\n\t\tif (!session) {\n\t\t\treturn NextResponse.json(\n\t\t\t\t{\n\t\t\t\t\terror: 'not_authenticated',\n\t\t\t\t\tdescription: 'The user does not have an active session or is not authenticated',\n\t\t\t\t},\n\t\t\t\t{ status: 401 },\n\t\t\t);\n\t\t}\n\n\t\tconst apiRes: NextResponse | Response = await apiRoute(nextReq, params);\n\t\tconst nextApiRes: NextResponse = apiRes instanceof NextResponse ? apiRes : new NextResponse(apiRes.body, apiRes);\n\n\t\treturn nextApiRes;\n\t};\n\nexport const apiPageRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithApiAuthRequiredPageRoute =>\n\t(apiRoute) =>\n\tasync (req, res) => {\n\t\tconst session = await sdk.getSession(req);\n\n\t\tif (!session) {\n\t\t\treturn res.status(401).json({\n\t\t\t\terror: 'not_authenticated',\n\t\t\t\tdescription: 'The user does not have an active session or is not authenticated',\n\t\t\t});\n\t\t}\n\n\t\tawait apiRoute(req, res);\n\t};\n"],"mappings":"wFAIA,IAAa,EACX,GACA,GACD,MAAO,EAA4B,IAAkC,CACpE,IAAM,EAAU,aAAe,QAAU,EAAc,CAAG,EAAI,EAI9D,GAAI,CAAC,MAFiB,EAAI,WAAW,CAAO,EAG3C,OAAO,EAAa,KACnB,CACC,MAAO,oBACP,YAAa,kEACd,EACA,CAAE,OAAQ,GAAI,CACf,EAGD,IAAM,EAAkC,MAAM,EAAS,EAAS,CAAM,EAGtE,OAFiC,aAAkB,EAAe,EAAS,IAAI,EAAa,EAAO,KAAM,CAAM,CAGhH,EAEY,EACX,GACA,GACD,MAAO,EAAK,IAAQ,
|
|
1
|
+
{"version":3,"file":"api.mjs","names":[],"sources":["../../../src/server/handlers/api.ts"],"sourcesContent":["import type { BaseServerSDK, PagesRouterRequest, WithApiAuthRequiredAppRoute, WithApiAuthRequiredPageRoute } from '../types';\nimport { NextRequest, NextResponse } from 'next/server';\nimport { toNextRequest } from '../utils';\n\nexport const apiAppRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithApiAuthRequiredAppRoute =>\n\t(apiRoute) =>\n\tasync (req: NextRequest | Request, params): Promise<NextResponse> => {\n\t\tconst nextReq = req instanceof Request ? toNextRequest(req) : req;\n\n\t\tconst session = await sdk.getSession(nextReq);\n\n\t\tif (!session) {\n\t\t\treturn NextResponse.json(\n\t\t\t\t{\n\t\t\t\t\terror: 'not_authenticated',\n\t\t\t\t\tdescription: 'The user does not have an active session or is not authenticated',\n\t\t\t\t},\n\t\t\t\t{ status: 401 },\n\t\t\t);\n\t\t}\n\n\t\tconst apiRes: NextResponse | Response = await apiRoute(nextReq, params);\n\t\tconst nextApiRes: NextResponse = apiRes instanceof NextResponse ? apiRes : new NextResponse(apiRes.body, apiRes);\n\n\t\treturn nextApiRes;\n\t};\n\nexport const apiPageRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithApiAuthRequiredPageRoute =>\n\t(apiRoute) =>\n\tasync (req, res) => {\n\t\t// NOTE: Pages Router: no cookies() store to save a refreshed session in, so read it as stored\n\t\tconst session = await sdk.getSession(req, { autoRefresh: false });\n\n\t\tif (!session) {\n\t\t\treturn res.status(401).json({\n\t\t\t\terror: 'not_authenticated',\n\t\t\t\tdescription: 'The user does not have an active session or is not authenticated',\n\t\t\t});\n\t\t}\n\n\t\tawait apiRoute(req, res);\n\t};\n"],"mappings":"wFAIA,IAAa,EACX,GACA,GACD,MAAO,EAA4B,IAAkC,CACpE,IAAM,EAAU,aAAe,QAAU,EAAc,CAAG,EAAI,EAI9D,GAAI,CAAC,MAFiB,EAAI,WAAW,CAAO,EAG3C,OAAO,EAAa,KACnB,CACC,MAAO,oBACP,YAAa,kEACd,EACA,CAAE,OAAQ,GAAI,CACf,EAGD,IAAM,EAAkC,MAAM,EAAS,EAAS,CAAM,EAGtE,OAFiC,aAAkB,EAAe,EAAS,IAAI,EAAa,EAAO,KAAM,CAAM,CAGhH,EAEY,EACX,GACA,GACD,MAAO,EAAK,IAAQ,CAInB,GAAI,CAAC,MAFiB,EAAI,WAAW,EAAK,CAAE,YAAa,EAAM,CAAC,EAG/D,OAAO,EAAI,OAAO,GAAG,CAAC,CAAC,KAAK,CAC3B,MAAO,oBACP,YAAa,kEACd,CAAC,EAGF,MAAM,EAAS,EAAK,CAAG,CACxB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@strivacity/sdk-core/utils");var t=function(e,t,n){let r=Promise.resolve();function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};function n(e,t){return`${e.options.authUrlPrefix}/refresh?returnTo=${encodeURIComponent(t)}`}var r=r=>(i,a={})=>async o=>{let s=await r.getSession(void 0,{autoRefresh:!1});if(s&&(0,e.isSessionExpired)(s,r.options.refreshSkew)){if(s.refresh_token&&r.options.autoRefresh!==!1){let e=typeof a.returnTo==`function`?await a.returnTo(o):a.returnTo,{redirect:i}=await t(async()=>{let{redirect:e}=await import(`next/navigation`);return{redirect:e}},void 0);i(n(r,e||r.options.postLoginRedirectUri))}s=null}if(!s){let e=typeof a.returnTo==`function`?await a.returnTo(o):a.returnTo,{redirect:n}=await t(async()=>{let{redirect:e}=await import(`next/navigation`);return{redirect:e}},void 0);n(`${r.options.loginUri}${e?`?returnTo=${encodeURIComponent(e)}`:``}`)}return i({...o,session:s})},i=t=>({getServerSideProps:r,returnTo:i}={})=>async a=>{let o=await t.getSession(a.req,{autoRefresh:!1});if(o&&(0,e.isSessionExpired)(o,t.options.refreshSkew))return{redirect:{destination:o.refresh_token&&t.options.autoRefresh!==!1?n(t,i||a.resolvedUrl):`${t.options.loginUri}?returnTo=${encodeURIComponent(i||a.resolvedUrl)}`,permanent:!1}};if(!o)return{redirect:{destination:`${t.options.loginUri}?returnTo=${encodeURIComponent(i||a.resolvedUrl)}`,permanent:!1}};let s={props:{}};if(r&&(s=await r(a)),s.props instanceof Promise){let e=await s.props;return{...s,props:{session:o,...e}}}return{...s,props:{session:o,...s.props}}};exports.appRouteHandlerFactory=r,exports.pageRouteHandlerFactory=i;
|
|
2
2
|
//# sourceMappingURL=route.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.cjs","names":[],"sources":["../../../src/server/handlers/route.ts"],"sourcesContent":["import type { NextRequest } from 'next/server';\nimport type {\n\tAppRouterPageRoute,\n\tAppRouterPageRouteOpts,\n\tBaseServerSDK,\n\tPagesRouterRequest,\n\tWithAuthGuardAppRouter,\n\tWithAuthGuardAppRouterOptions,\n\tWithAuthGuardPageRouter,\n} from '../types';\n\nexport const appRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithAuthGuardAppRouter =>\n\t<P extends AppRouterPageRouteOpts = AppRouterPageRouteOpts>(handler: AppRouterPageRoute<P>, opts: WithAuthGuardAppRouterOptions<P> = {}) =>\n\tasync (params: P) => {\n\t\
|
|
1
|
+
{"version":3,"file":"route.cjs","names":[],"sources":["../../../src/server/handlers/route.ts"],"sourcesContent":["import type { NextRequest } from 'next/server';\nimport type {\n\tAppRouterPageRoute,\n\tAppRouterPageRouteOpts,\n\tBaseServerSDK,\n\tPagesRouterRequest,\n\tWithAuthGuardAppRouter,\n\tWithAuthGuardAppRouterOptions,\n\tWithAuthGuardPageRouter,\n} from '../types';\nimport { isSessionExpired } from '@strivacity/sdk-core/utils';\n\n/**\n * Builds the Server SDK refresh route URL for an expired session. Guards cannot refresh in place: server components cannot set\n * cookies, so the rotated session would be lost. The refresh route writes the cookie and then redirects back to `returnTo`.\n *\n * @param {BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>} sdk - The server SDK.\n * @param {string} returnTo - Where to return after the refresh.\n * @returns {string} The refresh route URL.\n */\nfunction refreshUrl(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>, returnTo: string): string {\n\treturn `${sdk.options.authUrlPrefix}/refresh?returnTo=${encodeURIComponent(returnTo)}`;\n}\n\nexport const appRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithAuthGuardAppRouter =>\n\t<P extends AppRouterPageRouteOpts = AppRouterPageRouteOpts>(handler: AppRouterPageRoute<P>, opts: WithAuthGuardAppRouterOptions<P> = {}) =>\n\tasync (params: P) => {\n\t\t// NOTE: Server Components cannot set cookies, so never refresh here: an expired session goes through the refresh route\n\t\tlet session = await sdk.getSession(undefined, { autoRefresh: false });\n\n\t\tif (session && isSessionExpired(session, sdk.options.refreshSkew)) {\n\t\t\tif (session.refresh_token && sdk.options.autoRefresh !== false) {\n\t\t\t\tconst returnTo = typeof opts.returnTo === 'function' ? await opts.returnTo(params) : opts.returnTo;\n\t\t\t\tconst { redirect } = await import('next/navigation');\n\n\t\t\t\tredirect(refreshUrl(sdk, returnTo || sdk.options.postLoginRedirectUri));\n\t\t\t}\n\n\t\t\tsession = null;\n\t\t}\n\n\t\tif (!session) {\n\t\t\tconst returnTo = typeof opts.returnTo === 'function' ? await opts.returnTo(params) : opts.returnTo;\n\t\t\tconst { redirect } = await import('next/navigation');\n\n\t\t\tredirect(`${sdk.options.loginUri}${returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : ''}`);\n\t\t}\n\n\t\treturn handler({ ...params, session });\n\t};\n\nexport const pageRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithAuthGuardPageRouter =>\n\t({ getServerSideProps, returnTo } = {}) =>\n\tasync (ctx) => {\n\t\t// NOTE: Server Components cannot set cookies, so never refresh here: an expired session goes through the refresh route\n\t\tconst session = await sdk.getSession(ctx.req, { autoRefresh: false });\n\n\t\tif (session && isSessionExpired(session, sdk.options.refreshSkew)) {\n\t\t\treturn {\n\t\t\t\tredirect: {\n\t\t\t\t\tdestination:\n\t\t\t\t\t\tsession.refresh_token && sdk.options.autoRefresh !== false\n\t\t\t\t\t\t\t? refreshUrl(sdk, returnTo || ctx.resolvedUrl)\n\t\t\t\t\t\t\t: `${sdk.options.loginUri}?returnTo=${encodeURIComponent(returnTo || ctx.resolvedUrl)}`,\n\t\t\t\t\tpermanent: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (!session) {\n\t\t\treturn {\n\t\t\t\tredirect: {\n\t\t\t\t\tdestination: `${sdk.options.loginUri}?returnTo=${encodeURIComponent(returnTo || ctx.resolvedUrl)}`,\n\t\t\t\t\tpermanent: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tlet ret: any = { props: {} };\n\n\t\tif (getServerSideProps) {\n\t\t\tret = await getServerSideProps(ctx);\n\t\t}\n\n\t\tif (ret.props instanceof Promise) {\n\t\t\tconst props = await ret.props;\n\n\t\t\treturn {\n\t\t\t\t...ret,\n\t\t\t\tprops: {\n\t\t\t\t\tsession,\n\t\t\t\t\t...props,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn { ...ret, props: { session, ...ret.props } };\n\t};\n"],"mappings":"kYAoBA,SAAS,EAAW,EAA4E,EAA0B,CACzH,MAAO,GAAG,EAAI,QAAQ,cAAc,oBAAoB,mBAAmB,CAAQ,GACpF,CAEA,IAAa,EACX,IAC2D,EAAgC,EAAyC,CAAC,IACtI,KAAO,IAAc,CAEpB,IAAI,EAAU,MAAM,EAAI,WAAW,IAAA,GAAW,CAAE,YAAa,EAAM,CAAC,EAEpE,GAAI,IAAA,EAAW,EAAA,iBAAA,CAAiB,EAAS,EAAI,QAAQ,WAAW,EAAG,CAClE,GAAI,EAAQ,eAAiB,EAAI,QAAQ,cAAgB,GAAO,CAC/D,IAAM,EAAW,OAAO,EAAK,UAAa,WAAa,MAAM,EAAK,SAAS,CAAM,EAAI,EAAK,SACpF,CAAE,YAAA,MAAA,EAAA,SAAA,CAAF,GAAA,CAAE,YAAa,MAAM,OAAO,+CAElC,EAAS,EAAW,EAAK,GAAY,EAAI,QAAQ,oBAAoB,CAAC,CACvE,CAEA,EAAU,IACX,CAEA,GAAI,CAAC,EAAS,CACb,IAAM,EAAW,OAAO,EAAK,UAAa,WAAa,MAAM,EAAK,SAAS,CAAM,EAAI,EAAK,SACpF,CAAE,YAAA,MAAA,EAAA,SAAA,CAAF,GAAA,CAAE,YAAa,MAAM,OAAO,+CAElC,EAAS,GAAG,EAAI,QAAQ,WAAW,EAAW,aAAa,mBAAmB,CAAQ,IAAM,IAAI,CACjG,CAEA,OAAO,EAAQ,CAAE,GAAG,EAAQ,SAAQ,CAAC,CACtC,EAEY,EACX,IACA,CAAE,qBAAoB,YAAa,CAAC,IACrC,KAAO,IAAQ,CAEd,IAAM,EAAU,MAAM,EAAI,WAAW,EAAI,IAAK,CAAE,YAAa,EAAM,CAAC,EAEpE,GAAI,IAAA,EAAW,EAAA,iBAAA,CAAiB,EAAS,EAAI,QAAQ,WAAW,EAC/D,MAAO,CACN,SAAU,CACT,YACC,EAAQ,eAAiB,EAAI,QAAQ,cAAgB,GAClD,EAAW,EAAK,GAAY,EAAI,WAAW,EAC3C,GAAG,EAAI,QAAQ,SAAS,YAAY,mBAAmB,GAAY,EAAI,WAAW,IACtF,UAAW,EACZ,CACD,EAGD,GAAI,CAAC,EACJ,MAAO,CACN,SAAU,CACT,YAAa,GAAG,EAAI,QAAQ,SAAS,YAAY,mBAAmB,GAAY,EAAI,WAAW,IAC/F,UAAW,EACZ,CACD,EAID,IAAI,EAAW,CAAE,MAAO,CAAC,CAAE,EAM3B,GAJI,IACH,EAAM,MAAM,EAAmB,CAAG,GAG/B,EAAI,iBAAiB,QAAS,CACjC,IAAM,EAAQ,MAAM,EAAI,MAExB,MAAO,CACN,GAAG,EACH,MAAO,CACN,UACA,GAAG,CACJ,CACD,CACD,CAEA,MAAO,CAAE,GAAG,EAAK,MAAO,CAAE,UAAS,GAAG,EAAI,KAAM,CAAE,CACnD"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
import{isSessionExpired as e}from"@strivacity/sdk-core/utils";var t=`modulepreload`,n=function(e){return`/`+e},r={},i=function(e,i,a){let o=Promise.resolve();if(i&&i.length>0){let e=document.getElementsByTagName(`link`),s=document.querySelector(`meta[property=csp-nonce]`),c=s?.nonce||s?.getAttribute(`nonce`);function l(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function u(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}o=l(i.map(i=>{if(i=n(i,a),i=u(i),i in r)return;r[i]=!0;let o=i.endsWith(`.css`);for(let t=e.length-1;t>=0;t--){let n=e[t];if(n.href===i&&(!o||n.rel===`stylesheet`))return}let s=document.createElement(`link`);if(s.rel=o?`stylesheet`:t,o||(s.as=`script`),s.crossOrigin=``,s.href=i,c&&s.setAttribute(`nonce`,c),document.head.appendChild(s),o)return new Promise((e,t)=>{s.addEventListener(`load`,e),s.addEventListener(`error`,()=>t(Error(`Unable to preload CSS for ${i}`)))})}))}function s(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return o.then(t=>{for(let e of t||[])e.status===`rejected`&&s(e.reason);return e().catch(s)})};function a(e,t){return`${e.options.authUrlPrefix}/refresh?returnTo=${encodeURIComponent(t)}`}var o=t=>(n,r={})=>async o=>{let s=await t.getSession(void 0,{autoRefresh:!1});if(s&&e(s,t.options.refreshSkew)){if(s.refresh_token&&t.options.autoRefresh!==!1){let e=typeof r.returnTo==`function`?await r.returnTo(o):r.returnTo,{redirect:n}=await i(async()=>{let{redirect:e}=await import(`next/navigation`);return{redirect:e}},[]);n(a(t,e||t.options.postLoginRedirectUri))}s=null}if(!s){let e=typeof r.returnTo==`function`?await r.returnTo(o):r.returnTo,{redirect:n}=await i(async()=>{let{redirect:e}=await import(`next/navigation`);return{redirect:e}},[]);n(`${t.options.loginUri}${e?`?returnTo=${encodeURIComponent(e)}`:``}`)}return n({...o,session:s})},s=t=>({getServerSideProps:n,returnTo:r}={})=>async i=>{let o=await t.getSession(i.req,{autoRefresh:!1});if(o&&e(o,t.options.refreshSkew))return{redirect:{destination:o.refresh_token&&t.options.autoRefresh!==!1?a(t,r||i.resolvedUrl):`${t.options.loginUri}?returnTo=${encodeURIComponent(r||i.resolvedUrl)}`,permanent:!1}};if(!o)return{redirect:{destination:`${t.options.loginUri}?returnTo=${encodeURIComponent(r||i.resolvedUrl)}`,permanent:!1}};let s={props:{}};if(n&&(s=await n(i)),s.props instanceof Promise){let e=await s.props;return{...s,props:{session:o,...e}}}return{...s,props:{session:o,...s.props}}};export{o as appRouteHandlerFactory,s as pageRouteHandlerFactory};
|
|
2
2
|
//# sourceMappingURL=route.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"mappings":"
|
|
1
|
+
{"version":3,"mappings":"+tCAoBA,SAAS,EAAW,EAA4E,EAA0B,CACzH,MAAO,GAAG,EAAI,QAAQ,cAAc,oBAAoB,mBAAmB,CAAQ,GACpF,CAEA,IAAa,EACX,IAC2D,EAAgC,EAAyC,CAAC,IACtI,KAAO,IAAc,CAEpB,IAAI,EAAU,MAAM,EAAI,WAAW,OAAW,CAAE,YAAa,EAAM,CAAC,EAEpE,GAAI,GAAW,EAAiB,EAAS,EAAI,QAAQ,WAAW,EAAG,CAClE,GAAI,EAAQ,eAAiB,EAAI,QAAQ,cAAgB,GAAO,CAC/D,IAAM,EAAW,OAAO,EAAK,UAAa,WAAa,MAAM,EAAK,SAAS,CAAM,EAAI,EAAK,SACpF,CAAE,8BAAF,IAAE,YAAa,MAAM,OAAO,2CAElC,EAAS,EAAW,EAAK,GAAY,EAAI,QAAQ,oBAAoB,CAAC,CACvE,CAEA,EAAU,IACX,CAEA,GAAI,CAAC,EAAS,CACb,IAAM,EAAW,OAAO,EAAK,UAAa,WAAa,MAAM,EAAK,SAAS,CAAM,EAAI,EAAK,SACpF,CAAE,8BAAF,IAAE,YAAa,MAAM,OAAO,2CAElC,EAAS,GAAG,EAAI,QAAQ,WAAW,EAAW,aAAa,mBAAmB,CAAQ,IAAM,IAAI,CACjG,CAEA,OAAO,EAAQ,CAAE,GAAG,EAAQ,SAAQ,CAAC,CACtC,EAEY,EACX,IACA,CAAE,qBAAoB,YAAa,CAAC,IACrC,KAAO,IAAQ,CAEd,IAAM,EAAU,MAAM,EAAI,WAAW,EAAI,IAAK,CAAE,YAAa,EAAM,CAAC,EAEpE,GAAI,GAAW,EAAiB,EAAS,EAAI,QAAQ,WAAW,EAC/D,MAAO,CACN,SAAU,CACT,YACC,EAAQ,eAAiB,EAAI,QAAQ,cAAgB,GAClD,EAAW,EAAK,GAAY,EAAI,WAAW,EAC3C,GAAG,EAAI,QAAQ,SAAS,YAAY,mBAAmB,GAAY,EAAI,WAAW,IACtF,UAAW,EACZ,CACD,EAGD,GAAI,CAAC,EACJ,MAAO,CACN,SAAU,CACT,YAAa,GAAG,EAAI,QAAQ,SAAS,YAAY,mBAAmB,GAAY,EAAI,WAAW,IAC/F,UAAW,EACZ,CACD,EAID,IAAI,EAAW,CAAE,MAAO,CAAC,CAAE,EAM3B,GAJI,IACH,EAAM,MAAM,EAAmB,CAAG,GAG/B,EAAI,iBAAiB,QAAS,CACjC,IAAM,EAAQ,MAAM,EAAI,MAExB,MAAO,CACN,GAAG,EACH,MAAO,CACN,UACA,GAAG,CACJ,CACD,CACD,CAEA,MAAO,CAAE,GAAG,EAAK,MAAO,CAAE,UAAS,GAAG,EAAI,KAAM,CAAE,CACnD","names":[],"ignoreList":[],"sources":["../../../src/server/handlers/route.ts"],"sourcesContent":["import type { NextRequest } from 'next/server';\nimport type {\n\tAppRouterPageRoute,\n\tAppRouterPageRouteOpts,\n\tBaseServerSDK,\n\tPagesRouterRequest,\n\tWithAuthGuardAppRouter,\n\tWithAuthGuardAppRouterOptions,\n\tWithAuthGuardPageRouter,\n} from '../types';\nimport { isSessionExpired } from '@strivacity/sdk-core/utils';\n\n/**\n * Builds the Server SDK refresh route URL for an expired session. Guards cannot refresh in place: server components cannot set\n * cookies, so the rotated session would be lost. The refresh route writes the cookie and then redirects back to `returnTo`.\n *\n * @param {BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>} sdk - The server SDK.\n * @param {string} returnTo - Where to return after the refresh.\n * @returns {string} The refresh route URL.\n */\nfunction refreshUrl(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>, returnTo: string): string {\n\treturn `${sdk.options.authUrlPrefix}/refresh?returnTo=${encodeURIComponent(returnTo)}`;\n}\n\nexport const appRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithAuthGuardAppRouter =>\n\t<P extends AppRouterPageRouteOpts = AppRouterPageRouteOpts>(handler: AppRouterPageRoute<P>, opts: WithAuthGuardAppRouterOptions<P> = {}) =>\n\tasync (params: P) => {\n\t\t// NOTE: Server Components cannot set cookies, so never refresh here: an expired session goes through the refresh route\n\t\tlet session = await sdk.getSession(undefined, { autoRefresh: false });\n\n\t\tif (session && isSessionExpired(session, sdk.options.refreshSkew)) {\n\t\t\tif (session.refresh_token && sdk.options.autoRefresh !== false) {\n\t\t\t\tconst returnTo = typeof opts.returnTo === 'function' ? await opts.returnTo(params) : opts.returnTo;\n\t\t\t\tconst { redirect } = await import('next/navigation');\n\n\t\t\t\tredirect(refreshUrl(sdk, returnTo || sdk.options.postLoginRedirectUri));\n\t\t\t}\n\n\t\t\tsession = null;\n\t\t}\n\n\t\tif (!session) {\n\t\t\tconst returnTo = typeof opts.returnTo === 'function' ? await opts.returnTo(params) : opts.returnTo;\n\t\t\tconst { redirect } = await import('next/navigation');\n\n\t\t\tredirect(`${sdk.options.loginUri}${returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : ''}`);\n\t\t}\n\n\t\treturn handler({ ...params, session });\n\t};\n\nexport const pageRouteHandlerFactory =\n\t(sdk: BaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>): WithAuthGuardPageRouter =>\n\t({ getServerSideProps, returnTo } = {}) =>\n\tasync (ctx) => {\n\t\t// NOTE: Server Components cannot set cookies, so never refresh here: an expired session goes through the refresh route\n\t\tconst session = await sdk.getSession(ctx.req, { autoRefresh: false });\n\n\t\tif (session && isSessionExpired(session, sdk.options.refreshSkew)) {\n\t\t\treturn {\n\t\t\t\tredirect: {\n\t\t\t\t\tdestination:\n\t\t\t\t\t\tsession.refresh_token && sdk.options.autoRefresh !== false\n\t\t\t\t\t\t\t? refreshUrl(sdk, returnTo || ctx.resolvedUrl)\n\t\t\t\t\t\t\t: `${sdk.options.loginUri}?returnTo=${encodeURIComponent(returnTo || ctx.resolvedUrl)}`,\n\t\t\t\t\tpermanent: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (!session) {\n\t\t\treturn {\n\t\t\t\tredirect: {\n\t\t\t\t\tdestination: `${sdk.options.loginUri}?returnTo=${encodeURIComponent(returnTo || ctx.resolvedUrl)}`,\n\t\t\t\t\tpermanent: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tlet ret: any = { props: {} };\n\n\t\tif (getServerSideProps) {\n\t\t\tret = await getServerSideProps(ctx);\n\t\t}\n\n\t\tif (ret.props instanceof Promise) {\n\t\t\tconst props = await ret.props;\n\n\t\t\treturn {\n\t\t\t\t...ret,\n\t\t\t\tprops: {\n\t\t\t\t\tsession,\n\t\t\t\t\t...props,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn { ...ret, props: { session, ...ret.props } };\n\t};\n"],"file":"route.mjs"}
|
package/dist/server/sdk.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./storages.cjs"),t=require("./handlers/api.cjs"),n=require("./handlers/route.cjs");let r=require("@strivacity/sdk-core/utils/errors"),i=require("next/server"),a=require("@strivacity/sdk-core/server");function o(o){if(!o.storage){if(!o.secret)throw new r.ConfigurationError(`Secret is required for encrypted cookie storage`);o.cookieMaxAge??=2592e3,o.storage=e.createEncryptedCookieStorage(o.secret,{defaultCookieOptions:{maxAge:o.cookieMaxAge}})}let s=(0,a.createBaseServerSDK)({toRequest:e=>e,redirect:(e,t)=>i.NextResponse.redirect(e,t)},o);function c(e){return(n,r)=>n instanceof Request?t.apiAppRouteHandlerFactory(s)(e)(n,r??{}):t.apiPageRouteHandlerFactory(s)(e)(n,r)}function l(e,t){return typeof e==`function`?n.appRouteHandlerFactory(s)(e,t):n.pageRouteHandlerFactory(s)(e)}return{get options(){return s.options},withAuthGuard:l,withApiAuthRequired:c,getSession:e=>s.getSession(e),updateSession:(e,t)=>s.updateSession(e,t),refreshSession:e=>s.refreshSession(e),revokeSession:e=>s.revokeSession(e),getEntrySession:e=>s.getEntrySession(e),completeLogin:(e,t)=>s.completeLogin(e,t),logout:(e,t)=>s.logout(e,t),handleLogin:e=>s.handleLogin(e),handleRegister:e=>s.handleRegister(e),handleCallback:e=>s.handleCallback(e),handleRefresh:e=>s.handleRefresh(e),handleRevoke:e=>s.handleRevoke(e),handleEntry:e=>s.handleEntry(e),handleLogout:e=>s.handleLogout(e),handleBackChannelLogout:e=>s.handleBackChannelLogout(e),handler:async e=>await s.handler(e)??new i.NextResponse(`Not Found`,{status:404})}}exports.createServerSDK=o;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./storages.cjs"),t=require("./handlers/api.cjs"),n=require("./handlers/route.cjs");let r=require("@strivacity/sdk-core/utils/errors"),i=require("next/server"),a=require("@strivacity/sdk-core/server");function o(o){if(!o.storage){if(!o.secret)throw new r.ConfigurationError(`Secret is required for encrypted cookie storage`);o.cookieMaxAge??=2592e3,o.storage=e.createEncryptedCookieStorage(o.secret,{defaultCookieOptions:{maxAge:o.cookieMaxAge}})}let s=(0,a.createBaseServerSDK)({toRequest:e=>e,redirect:(e,t)=>i.NextResponse.redirect(e,t)},o);function c(e){return(n,r)=>n instanceof Request?t.apiAppRouteHandlerFactory(s)(e)(n,r??{}):t.apiPageRouteHandlerFactory(s)(e)(n,r)}function l(e,t){return typeof e==`function`?n.appRouteHandlerFactory(s)(e,t):n.pageRouteHandlerFactory(s)(e)}return{get options(){return s.options},withAuthGuard:l,withApiAuthRequired:c,getSession:(e,t)=>s.getSession(e,e instanceof Request?t:{autoRefresh:!1}),updateSession:(e,t)=>s.updateSession(e,t),refreshSession:e=>s.refreshSession(e),revokeSession:e=>s.revokeSession(e),getEntrySession:e=>s.getEntrySession(e),completeLogin:(e,t)=>s.completeLogin(e,t),logout:(e,t)=>s.logout(e,t),handleLogin:e=>s.handleLogin(e),handleRegister:e=>s.handleRegister(e),handleCallback:e=>s.handleCallback(e),handleRefresh:e=>s.handleRefresh(e),handleRevoke:e=>s.handleRevoke(e),handleEntry:e=>s.handleEntry(e),handleLogout:e=>s.handleLogout(e),handleBackChannelLogout:e=>s.handleBackChannelLogout(e),handler:async e=>await s.handler(e)??new i.NextResponse(`Not Found`,{status:404})}}exports.createServerSDK=o;
|
|
2
2
|
//# sourceMappingURL=sdk.cjs.map
|
package/dist/server/sdk.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.cjs","names":[],"sources":["../../src/server/sdk.ts"],"sourcesContent":["import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';\nimport type { ParsedUrlQuery } from 'node:querystring';\nimport type {\n\tNextServerSDK,\n\tNextServerSDKInitConfig,\n\tWithAuthGuardAppRouterOptions,\n\tWithAuthGuardPageRouterOptions,\n\tAppRouterPageRoute,\n\tAppRouterPageRouteOpts,\n\tPageRoute,\n\tAppRouteHandlerFn,\n\tAppRouteHandlerFnContext,\n\tPagesRouterRequest,\n} from './types';\nimport { NextResponse, type NextRequest } from 'next/server';\nimport { createBaseServerSDK } from '@strivacity/sdk-core/server';\nimport { ConfigurationError } from '@strivacity/sdk-core/utils/errors';\nimport { createEncryptedCookieStorage } from './storages';\nimport { apiAppRouteHandlerFactory, apiPageRouteHandlerFactory } from './handlers/api';\nimport { appRouteHandlerFactory, pageRouteHandlerFactory } from './handlers/route';\n\n/**\n * Creates a Next.js server SDK instance with the provided configuration.\n *\n * @param {NextServerSDKInitConfig} initConfig - The initialization configuration for the SDK.\n * @returns {NextServerSDK} The initialized Next.js server SDK instance.\n */\nexport function createServerSDK(initConfig: NextServerSDKInitConfig): NextServerSDK {\n\tif (!initConfig.storage) {\n\t\tif (!initConfig.secret) {\n\t\t\tthrow new ConfigurationError('Secret is required for encrypted cookie storage');\n\t\t}\n\n\t\tinitConfig.cookieMaxAge ??= 30 * 24 * 60 * 60;\n\t\tinitConfig.storage = createEncryptedCookieStorage(initConfig.secret, { defaultCookieOptions: { maxAge: initConfig.cookieMaxAge } });\n\t}\n\n\tconst base = createBaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>(\n\t\t{\n\t\t\ttoRequest: (req) => req as Request,\n\t\t\tredirect: (url, status) => NextResponse.redirect(url, status),\n\t\t},\n\t\tinitConfig,\n\t);\n\n\tfunction withApiAuthRequired(apiRoute: AppRouteHandlerFn): AppRouteHandlerFn;\n\tfunction withApiAuthRequired(apiRoute: NextApiHandler): NextApiHandler;\n\tfunction withApiAuthRequired(apiRoute: AppRouteHandlerFn | NextApiHandler): AppRouteHandlerFn | NextApiHandler {\n\t\treturn (req: NextRequest | Request | NextApiRequest, resOrCtx?: AppRouteHandlerFnContext | NextApiResponse) => {\n\t\t\tif (req instanceof Request) {\n\t\t\t\tconst appRouteHandler = apiAppRouteHandlerFactory(base);\n\t\t\t\tconst wrappedAppRoute = appRouteHandler(apiRoute as AppRouteHandlerFn);\n\t\t\t\treturn wrappedAppRoute(req, (resOrCtx ?? {}) as AppRouteHandlerFnContext);\n\t\t\t}\n\n\t\t\tconst pageRouteHandler = apiPageRouteHandlerFactory(base);\n\t\t\tconst wrappedPageRoute = pageRouteHandler(apiRoute as NextApiHandler);\n\t\t\treturn wrappedPageRoute(req, resOrCtx as NextApiResponse);\n\t\t};\n\t}\n\n\tfunction withAuthGuard(): PageRoute<Record<string, unknown>, ParsedUrlQuery>;\n\tfunction withAuthGuard<P extends Record<string, unknown> = Record<string, unknown>, Q extends ParsedUrlQuery = ParsedUrlQuery>(\n\t\topts: WithAuthGuardPageRouterOptions<P, Q>,\n\t): PageRoute<P, Q>;\n\tfunction withAuthGuard<P extends AppRouterPageRouteOpts = AppRouterPageRouteOpts>(\n\t\tfn: AppRouterPageRoute<P>,\n\t\topts?: WithAuthGuardAppRouterOptions<P>,\n\t): AppRouterPageRoute<P>;\n\tfunction withAuthGuard(\n\t\tfnOrOpts?: WithAuthGuardPageRouterOptions | AppRouterPageRoute,\n\t\topts?: WithAuthGuardAppRouterOptions,\n\t): PageRoute<Record<string, unknown>> | AppRouterPageRoute {\n\t\tif (typeof fnOrOpts === 'function') {\n\t\t\tconst appRouteHandler = appRouteHandlerFactory(base);\n\t\t\treturn appRouteHandler(fnOrOpts, opts);\n\t\t}\n\n\t\tconst pageRouteHandler = pageRouteHandlerFactory(base);\n\t\treturn pageRouteHandler(fnOrOpts);\n\t}\n\n\treturn {\n\t\tget options() {\n\t\t\treturn base.options;\n\t\t},\n\t\twithAuthGuard,\n\t\twithApiAuthRequired,\n\t\tgetSession: (req) => base.getSession(req),\n\t\tupdateSession: (
|
|
1
|
+
{"version":3,"file":"sdk.cjs","names":[],"sources":["../../src/server/sdk.ts"],"sourcesContent":["import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';\nimport type { ParsedUrlQuery } from 'node:querystring';\nimport type {\n\tNextServerSDK,\n\tNextServerSDKInitConfig,\n\tWithAuthGuardAppRouterOptions,\n\tWithAuthGuardPageRouterOptions,\n\tAppRouterPageRoute,\n\tAppRouterPageRouteOpts,\n\tPageRoute,\n\tAppRouteHandlerFn,\n\tAppRouteHandlerFnContext,\n\tPagesRouterRequest,\n} from './types';\nimport { NextResponse, type NextRequest } from 'next/server';\nimport { createBaseServerSDK } from '@strivacity/sdk-core/server';\nimport { ConfigurationError } from '@strivacity/sdk-core/utils/errors';\nimport { createEncryptedCookieStorage } from './storages';\nimport { apiAppRouteHandlerFactory, apiPageRouteHandlerFactory } from './handlers/api';\nimport { appRouteHandlerFactory, pageRouteHandlerFactory } from './handlers/route';\n\n/**\n * Creates a Next.js server SDK instance with the provided configuration.\n *\n * @param {NextServerSDKInitConfig} initConfig - The initialization configuration for the SDK.\n * @returns {NextServerSDK} The initialized Next.js server SDK instance.\n */\nexport function createServerSDK(initConfig: NextServerSDKInitConfig): NextServerSDK {\n\tif (!initConfig.storage) {\n\t\tif (!initConfig.secret) {\n\t\t\tthrow new ConfigurationError('Secret is required for encrypted cookie storage');\n\t\t}\n\n\t\tinitConfig.cookieMaxAge ??= 30 * 24 * 60 * 60;\n\t\tinitConfig.storage = createEncryptedCookieStorage(initConfig.secret, { defaultCookieOptions: { maxAge: initConfig.cookieMaxAge } });\n\t}\n\n\tconst base = createBaseServerSDK<NextRequest | Request | PagesRouterRequest | undefined>(\n\t\t{\n\t\t\ttoRequest: (req) => req as Request,\n\t\t\tredirect: (url, status) => NextResponse.redirect(url, status),\n\t\t},\n\t\tinitConfig,\n\t);\n\n\tfunction withApiAuthRequired(apiRoute: AppRouteHandlerFn): AppRouteHandlerFn;\n\tfunction withApiAuthRequired(apiRoute: NextApiHandler): NextApiHandler;\n\tfunction withApiAuthRequired(apiRoute: AppRouteHandlerFn | NextApiHandler): AppRouteHandlerFn | NextApiHandler {\n\t\treturn (req: NextRequest | Request | NextApiRequest, resOrCtx?: AppRouteHandlerFnContext | NextApiResponse) => {\n\t\t\tif (req instanceof Request) {\n\t\t\t\tconst appRouteHandler = apiAppRouteHandlerFactory(base);\n\t\t\t\tconst wrappedAppRoute = appRouteHandler(apiRoute as AppRouteHandlerFn);\n\t\t\t\treturn wrappedAppRoute(req, (resOrCtx ?? {}) as AppRouteHandlerFnContext);\n\t\t\t}\n\n\t\t\tconst pageRouteHandler = apiPageRouteHandlerFactory(base);\n\t\t\tconst wrappedPageRoute = pageRouteHandler(apiRoute as NextApiHandler);\n\t\t\treturn wrappedPageRoute(req, resOrCtx as NextApiResponse);\n\t\t};\n\t}\n\n\tfunction withAuthGuard(): PageRoute<Record<string, unknown>, ParsedUrlQuery>;\n\tfunction withAuthGuard<P extends Record<string, unknown> = Record<string, unknown>, Q extends ParsedUrlQuery = ParsedUrlQuery>(\n\t\topts: WithAuthGuardPageRouterOptions<P, Q>,\n\t): PageRoute<P, Q>;\n\tfunction withAuthGuard<P extends AppRouterPageRouteOpts = AppRouterPageRouteOpts>(\n\t\tfn: AppRouterPageRoute<P>,\n\t\topts?: WithAuthGuardAppRouterOptions<P>,\n\t): AppRouterPageRoute<P>;\n\tfunction withAuthGuard(\n\t\tfnOrOpts?: WithAuthGuardPageRouterOptions | AppRouterPageRoute,\n\t\topts?: WithAuthGuardAppRouterOptions,\n\t): PageRoute<Record<string, unknown>> | AppRouterPageRoute {\n\t\tif (typeof fnOrOpts === 'function') {\n\t\t\tconst appRouteHandler = appRouteHandlerFactory(base);\n\t\t\treturn appRouteHandler(fnOrOpts, opts);\n\t\t}\n\n\t\tconst pageRouteHandler = pageRouteHandlerFactory(base);\n\t\treturn pageRouteHandler(fnOrOpts);\n\t}\n\n\treturn {\n\t\tget options() {\n\t\t\treturn base.options;\n\t\t},\n\t\twithAuthGuard,\n\t\twithApiAuthRequired,\n\t\t// NOTE: refreshes (with autoRefresh) only for App Router requests, whose route handlers can write the rotated cookie.\n\t\t// Server Components (no request) and Pages Router requests read the stored session: a refresh there could not be saved.\n\t\tgetSession: (req, params) => base.getSession(req, req instanceof Request ? params : { autoRefresh: false }),\n\t\tupdateSession: (session, req) => base.updateSession(session, req),\n\t\trefreshSession: (req) => base.refreshSession(req),\n\t\trevokeSession: (req) => base.revokeSession(req),\n\t\tgetEntrySession: (entryUrl) => base.getEntrySession(entryUrl),\n\t\tcompleteLogin: (params, req) => base.completeLogin(params, req),\n\t\tlogout: (postLogoutRedirectUri, req) => base.logout(postLogoutRedirectUri, req),\n\t\thandleLogin: (req) => base.handleLogin(req),\n\t\thandleRegister: (req) => base.handleRegister(req),\n\t\thandleCallback: (req) => base.handleCallback(req),\n\t\thandleRefresh: (req) => base.handleRefresh(req),\n\t\thandleRevoke: (req) => base.handleRevoke(req),\n\t\thandleEntry: (req) => base.handleEntry(req),\n\t\thandleLogout: (req) => base.handleLogout(req),\n\t\thandleBackChannelLogout: (req) => base.handleBackChannelLogout(req),\n\t\thandler: async (req) => (await base.handler(req)) ?? new NextResponse('Not Found', { status: 404 }),\n\t};\n}\n"],"mappings":"4RA2BA,SAAgB,EAAgB,EAAoD,CACnF,GAAI,CAAC,EAAW,QAAS,CACxB,GAAI,CAAC,EAAW,OACf,MAAM,IAAI,EAAA,mBAAmB,iDAAiD,EAG/E,EAAW,eAAiB,OAC5B,EAAW,QAAU,EAAA,6BAA6B,EAAW,OAAQ,CAAE,qBAAsB,CAAE,OAAQ,EAAW,YAAa,CAAE,CAAC,CACnI,CAEA,IAAM,GAAA,EAAO,EAAA,oBAAA,CACZ,CACC,UAAY,GAAQ,EACpB,UAAW,EAAK,IAAW,EAAA,aAAa,SAAS,EAAK,CAAM,CAC7D,EACA,CACD,EAIA,SAAS,EAAoB,EAAkF,CAC9G,OAAQ,EAA6C,IAChD,aAAe,QACM,EAAA,0BAA0B,CAC1B,CAAA,CAAgB,CACjC,CAAA,CAAgB,EAAM,GAAY,CAAC,CAA8B,EAGhD,EAAA,2BAA2B,CAC3B,CAAA,CAAiB,CACnC,CAAA,CAAiB,EAAK,CAA2B,CAE1D,CAUA,SAAS,EACR,EACA,EAC0D,CAO1D,OANI,OAAO,GAAa,WACC,EAAA,uBAAuB,CACxC,CAAA,CAAgB,EAAU,CAAI,EAGb,EAAA,wBAAwB,CAC1C,CAAA,CAAiB,CAAQ,CACjC,CAEA,MAAO,CACN,IAAI,SAAU,CACb,OAAO,EAAK,OACb,EACA,gBACA,sBAGA,YAAa,EAAK,IAAW,EAAK,WAAW,EAAK,aAAe,QAAU,EAAS,CAAE,YAAa,EAAM,CAAC,EAC1G,eAAgB,EAAS,IAAQ,EAAK,cAAc,EAAS,CAAG,EAChE,eAAiB,GAAQ,EAAK,eAAe,CAAG,EAChD,cAAgB,GAAQ,EAAK,cAAc,CAAG,EAC9C,gBAAkB,GAAa,EAAK,gBAAgB,CAAQ,EAC5D,eAAgB,EAAQ,IAAQ,EAAK,cAAc,EAAQ,CAAG,EAC9D,QAAS,EAAuB,IAAQ,EAAK,OAAO,EAAuB,CAAG,EAC9E,YAAc,GAAQ,EAAK,YAAY,CAAG,EAC1C,eAAiB,GAAQ,EAAK,eAAe,CAAG,EAChD,eAAiB,GAAQ,EAAK,eAAe,CAAG,EAChD,cAAgB,GAAQ,EAAK,cAAc,CAAG,EAC9C,aAAe,GAAQ,EAAK,aAAa,CAAG,EAC5C,YAAc,GAAQ,EAAK,YAAY,CAAG,EAC1C,aAAe,GAAQ,EAAK,aAAa,CAAG,EAC5C,wBAA0B,GAAQ,EAAK,wBAAwB,CAAG,EAClE,QAAS,KAAO,IAAS,MAAM,EAAK,QAAQ,CAAG,GAAM,IAAI,EAAA,aAAa,YAAa,CAAE,OAAQ,GAAI,CAAC,CACnG,CACD"}
|