├── .eslintignore
├── .eslintrc.cjs
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── ci.yml
│ ├── coana-analysis.yml
│ ├── coana-guardrail.yml
│ └── release.yml
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── __tests__
├── actions.spec.ts
├── auth.spec.ts
├── authkit-callback-route.spec.ts
├── authkit-provider.spec.tsx
├── button.spec.tsx
├── cookie.spec.ts
├── get-authorization-url.spec.ts
├── impersonation.spec.tsx
├── min-max-button.spec.tsx
├── session.spec.ts
├── test-helpers.ts
├── useAccessToken.spec.tsx
├── useTokenClaims.spec.tsx
├── utils.spec.ts
└── workos.spec.ts
├── jest.config.ts
├── jest.setup.ts
├── package-lock.json
├── package.json
├── src
├── actions.ts
├── auth.ts
├── authkit-callback-route.ts
├── components
│ ├── authkit-provider.tsx
│ ├── button.tsx
│ ├── impersonation.tsx
│ ├── index.ts
│ ├── min-max-button.tsx
│ ├── useAccessToken.ts
│ └── useTokenClaims.ts
├── cookie.ts
├── env-variables.ts
├── get-authorization-url.ts
├── index.ts
├── interfaces.ts
├── middleware.ts
├── session.ts
├── utils.ts
└── workos.ts
├── tsconfig.json
└── types
└── react.d.ts
/.eslintignore:
--------------------------------------------------------------------------------
1 | .eslintrc.cjs
2 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: [
4 | 'eslint:recommended',
5 | 'prettier',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:import/recommended',
8 | 'plugin:import/typescript',
9 | ],
10 | settings: {
11 | 'import/resolver': {
12 | typescript: {
13 | extensions: ['.js'],
14 | },
15 | node: {
16 | extensions: ['.js'],
17 | },
18 | },
19 | },
20 | rules: {
21 | 'import/extensions': [
22 | 'error',
23 | 'always',
24 | {
25 | ts: 'never',
26 | tsx: 'never',
27 | },
28 | ],
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # See GitHub's docs for more details:
2 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
3 |
4 | # TypeScript Team
5 | * @workos/typescript
6 |
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - authkit-nextjs version [e.g. 0.12.0]
30 | - Next.js version [e.g. 14.2.5]
31 |
32 | **Additional context**
33 | Add any other context about the problem here.
34 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | pull_request: {}
8 |
9 | defaults:
10 | run:
11 | shell: bash
12 |
13 | jobs:
14 | test:
15 | name: Test Node ${{ matrix.node }}
16 | runs-on: ubuntu-latest
17 | strategy:
18 | matrix:
19 | node: [18, 20, 22]
20 | steps:
21 | - uses: actions/checkout@v4
22 | - uses: actions/setup-node@v4
23 | with:
24 | node-version: ${{ matrix.node }}
25 |
26 | - name: Install Dependencies
27 | run: |
28 | npm install
29 |
30 | - name: Prettier
31 | run: |
32 | npm run prettier
33 |
34 | - name: Lint
35 | run: |
36 | npm run lint
37 |
38 | - name: Build
39 | run: |
40 | npm run build
41 |
42 | - name: Test
43 | run: |
44 | npm run test -- --coverage
45 |
--------------------------------------------------------------------------------
/.github/workflows/coana-analysis.yml:
--------------------------------------------------------------------------------
1 | name: Coana Vulnerability Analysis
2 |
3 | on:
4 | schedule:
5 | - cron: '0 3 * * *' # every day at 3 AM
6 | workflow_dispatch:
7 | inputs:
8 | tags:
9 | description: 'Manually run vulnerability analysis'
10 | # Required by the return-dispatch action
11 | distinct_id:
12 |
13 | jobs:
14 | coana-vulnerability-analysis:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout code
19 | uses: actions/checkout@v4
20 |
21 | - name: Run Coana CLI
22 | id: coana-cli
23 | uses: docker://coana/coana:latest
24 | with:
25 | args: |
26 | coana run . \
27 | --api-key ${{ secrets.COANA_API_KEY }} \
28 | --repo-url https://github.com/${{github.repository}}
29 |
--------------------------------------------------------------------------------
/.github/workflows/coana-guardrail.yml:
--------------------------------------------------------------------------------
1 | name: Coana Guardrail
2 |
3 | on: pull_request
4 |
5 | jobs:
6 | guardrail:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - name: Checkout the ${{github.base_ref}} branch
11 | uses: actions/checkout@v4
12 | with:
13 | ref: ${{github.base_ref}} # checkout the base branch (usually master/main).
14 |
15 | - name: Fetch the PR branch
16 | run: |
17 | git fetch ${{ github.event.pull_request.head.repo.clone_url }} ${{ github.head_ref }}:${{ github.head_ref }} --depth=1
18 |
19 | - name: Get list of changed files relative to the main/master branch
20 | id: changed-files
21 | run: |
22 | echo "all_changed_files=$(git diff --name-only ${{ github.base_ref }} ${{ github.head_ref }} | tr '\n' ' ')" >> $GITHUB_OUTPUT
23 |
24 | - name: Use Node.js 20.x
25 | uses: actions/setup-node@v4
26 | with:
27 | node-version: 20.x
28 |
29 | - name: Run Coana on the ${{github.base_ref}} branch
30 | run: |
31 | npx @coana-tech/cli run . \
32 | --guardrail-mode \
33 | --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \
34 | -o /tmp/main-branch \
35 | --changed-files ${{ steps.changed-files.outputs.all_changed_files }} \
36 | --lightweight-reachability \
37 |
38 | # Reset file permissions.
39 | # This is necessary because the Coana CLI may add
40 | # new files with root ownership since it's using docker.
41 | # These files will not be deleted by the clean step in checkout
42 | # if the permissions are not reset.
43 | - name: Reset file permissions
44 | run: sudo chown -R $USER:$USER .
45 |
46 | - name: Checkout the current branch
47 | uses: actions/checkout@v4
48 | with:
49 | clean: true
50 |
51 | - name: Run Coana on the current branch
52 | run: |
53 | npx @coana-tech/cli run . \
54 | --guardrail-mode \
55 | --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \
56 | -o /tmp/current-branch \
57 | --changed-files ${{ steps.changed-files.outputs.all_changed_files }} \
58 | --lightweight-reachability \
59 |
60 | - name: Run Report Comparison
61 | run: |
62 | npx @coana-tech/cli compare-reports \
63 | --api-key ${{ secrets.COANA_API_KEY || 'api-key-unavailable' }} \
64 | /tmp/main-branch/coana-report.json \
65 | /tmp/current-branch/coana-report.json
66 | env:
67 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | # Support manually pushing a new release
5 | workflow_dispatch: {}
6 | # Trigger when a release is published
7 | release:
8 | types: [published]
9 |
10 | defaults:
11 | run:
12 | shell: bash
13 |
14 | jobs:
15 | test:
16 | name: Publish to NPM
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@v4
20 | - uses: actions/setup-node@v4
21 | with:
22 | node-version: 18
23 | registry-url: 'https://registry.npmjs.org'
24 |
25 | - name: Install Dependencies
26 | run: |
27 | npm install
28 |
29 | - name: Build project
30 | run: |
31 | npm run build
32 |
33 | - name: Push Release
34 | if: ${{ !github.event.release.prerelease }}
35 | env:
36 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
37 | run: |
38 | npm publish --tag latest --access=public
39 |
40 | - name: Push Pre-Release
41 | if: ${{ github.event.release.prerelease }}
42 | env:
43 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
44 | run: |
45 | npm publish --tag next --access=public
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | dist
4 | coverage
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "quoteProps": "consistent",
3 | "singleQuote": true,
4 | "trailingComma": "all",
5 | "printWidth": 120
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 WorkOS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AuthKit Next.js Library
2 |
3 | The AuthKit library for Next.js provides convenient helpers for authentication and session management using WorkOS & AuthKit with Next.js.
4 |
5 | > Note: This library is intended for use with the Next.js App Router.
6 |
7 | ## Installation
8 |
9 | Install the package with:
10 |
11 | ```
12 | npm i @workos-inc/authkit-nextjs
13 | ```
14 |
15 | or
16 |
17 | ```
18 | yarn add @workos-inc/authkit-nextjs
19 | ```
20 |
21 | ## Video tutorial
22 |
23 |
24 |
25 |
26 |
27 | ## Pre-flight
28 |
29 | Make sure the following values are present in your `.env.local` environment variables file. The client ID and API key can be found in the [WorkOS dashboard](https://dashboard.workos.com), and the redirect URI can also be configured there.
30 |
31 | ```sh
32 | WORKOS_CLIENT_ID="client_..." # retrieved from the WorkOS dashboard
33 | WORKOS_API_KEY="sk_test_..." # retrieved from the WorkOS dashboard
34 | WORKOS_COOKIE_PASSWORD="" # generate a secure password here
35 | NEXT_PUBLIC_WORKOS_REDIRECT_URI="http://localhost:3000/callback" # configured in the WorkOS dashboard
36 | ```
37 |
38 | `WORKOS_COOKIE_PASSWORD` is the private key used to encrypt the session cookie. It has to be at least 32 characters long. You can use the [1Password generator](https://1password.com/password-generator/) or the `openssl` library to generate a strong password via the command line:
39 |
40 | ```
41 | openssl rand -base64 24
42 | ```
43 |
44 | To use the `signOut` method, you'll need to set a default Logout URI in your WorkOS dashboard settings under "Redirects".
45 |
46 | ### Optional configuration
47 |
48 | Certain environment variables are optional and can be used to debug or configure cookie settings.
49 |
50 | ```sh
51 | WORKOS_COOKIE_MAX_AGE='600' # maximum age of the cookie in seconds. Defaults to 400 days, the maximum allowed in Chrome
52 | WORKOS_COOKIE_DOMAIN='example.com'
53 | WORKOS_COOKIE_NAME='authkit-cookie'
54 | WORKOS_API_HOSTNAME='api.workos.com' # base WorkOS API URL
55 | WORKOS_API_HTTPS=true # whether to use HTTPS in API calls
56 | WORKOS_API_PORT=3000 # port to use for API calls
57 |
58 | # Only change this if you specifically need cross-origin cookie support.
59 | WORKOS_COOKIE_SAMESITE='lax' # SameSite attribute for cookies: 'lax' (default), 'strict', or 'none'.
60 | ```
61 |
62 | > [!WARNING]
63 | > Setting `WORKOS_COOKIE_SAMESITE='none'` allows cookies to be sent in cross-origin contexts (like iframes), but reduces protection against CSRF attacks. This setting forces cookies to be secure (HTTPS only) and should only be used when absolutely necessary for your application architecture.
64 |
65 | > [!TIP] >`WORKOS_COOKIE_DOMAIN` can be used to share WorkOS sessions between apps/domains. Note: The `WORKOS_COOKIE_PASSWORD` would need to be the same across apps/domains. Not needed for most use cases.
66 |
67 | ## Setup
68 |
69 | ### Callback route
70 |
71 | WorkOS requires that you have a callback URL to redirect users back to after they've authenticated. In your Next.js app, [expose an API route](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) and add the following.
72 |
73 | ```ts
74 | import { handleAuth } from '@workos-inc/authkit-nextjs';
75 |
76 | export const GET = handleAuth();
77 | ```
78 |
79 | Make sure this route matches the `WORKOS_REDIRECT_URI` variable and the configured redirect URI in your WorkOS dashboard. For instance if your redirect URI is `http://localhost:3000/auth/callback` then you'd put the above code in `/app/auth/callback/route.ts`.
80 |
81 | You can also control the pathname the user will be sent to after signing-in by passing a `returnPathname` option to `handleAuth` like so:
82 |
83 | ```ts
84 | export const GET = handleAuth({ returnPathname: '/dashboard' });
85 | ```
86 |
87 | If your application needs to persist data upon a successful authentication, like the `oauthTokens` from an upstream provider, you can pass in a `onSuccess` function that will get called after the user has successfully authenticated:
88 |
89 | ```ts
90 | export const GET = handleAuth({
91 | onSuccess: async ({ user, oauthTokens, authenticationMethod, organizationId }) => {
92 | await saveTokens(oauthTokens);
93 | if (authenticationMethod) {
94 | await saveAuthMethod(user.id, authenticationMethod);
95 | }
96 | },
97 | });
98 | ```
99 |
100 | `handleAuth` can be used with the following options.
101 |
102 | | Option | Default | Description |
103 | | ---------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
104 | | `returnPathname` | `/` | The pathname to redirect the user to after signing in |
105 | | `baseURL` | `undefined` | The base URL to use for the redirect URI instead of the one in the request. Useful if the app is being run in a container like docker where the hostname can be different from the one in the request |
106 | | `onSuccess` | `undefined` | A function that receives successful authentication data and can be used for side-effects like persisting tokens |
107 | | `onError` | `undefined` | A function that can receive the error and the request and handle the error in its own way. |
108 |
109 | #### onSuccess callback data
110 |
111 | The `onSuccess` callback receives the following data:
112 |
113 | | Property | Type | Description |
114 | | ---------------------- | --------------------------- | -------------------------------------------------------------------------------------------------- |
115 | | `user` | `User` | The authenticated user object |
116 | | `accessToken` | `string` | JWT access token |
117 | | `refreshToken` | `string` | Refresh token for session renewal |
118 | | `impersonator` | `Impersonator \| undefined` | Present if user is being impersonated |
119 | | `oauthTokens` | `OauthTokens \| undefined` | OAuth tokens from upstream provider |
120 | | `authenticationMethod` | `string \| undefined` | How the user authenticated (e.g., 'password', 'google-oauth'). Only available during initial login |
121 | | `organizationId` | `string \| undefined` | Organization context of authentication |
122 |
123 | **Note**: `authenticationMethod` is only provided during the initial authentication callback. It will not be available in subsequent requests or session refreshes.
124 |
125 | ### Middleware
126 |
127 | This library relies on [Next.js middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) to provide session management for routes. Put the following in your `middleware.ts` file in the root of your project:
128 |
129 | ```ts
130 | import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
131 |
132 | export default authkitMiddleware();
133 |
134 | // Match against pages that require auth
135 | // Leave this out if you want auth on every resource (including images, css etc.)
136 | export const config = { matcher: ['/', '/admin'] };
137 | ```
138 |
139 | The middleware can be configured with several options.
140 |
141 | | Option | Default | Description |
142 | | ---------------- | ----------- | ------------------------------------------------------------------------------------------------------ |
143 | | `redirectUri` | `undefined` | Used in cases where you need your redirect URI to be set dynamically (e.g. Vercel preview deployments) |
144 | | `middlewareAuth` | `undefined` | Used to configure middleware auth options. See [middleware auth](#middleware-auth) for more details. |
145 | | `debug` | `false` | Enables debug logs. |
146 | | `signUpPaths` | `[]` | Used to specify paths that should use the 'sign-up' screen hint when redirecting to AuthKit. |
147 |
148 | #### Custom redirect URI
149 |
150 | In cases where you need your redirect URI to be set dynamically (e.g. Vercel preview deployments), use the `redirectUri` option in `authkitMiddleware`:
151 |
152 | ```ts
153 | import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
154 |
155 | export default authkitMiddleware({
156 | redirectUri: 'https://foo.example.com/callback',
157 | });
158 |
159 | // Match against pages that require auth
160 | // Leave this out if you want auth on every resource (including images, css etc.)
161 | export const config = { matcher: ['/', '/admin'] };
162 | ```
163 |
164 | Custom redirect URIs will be used over a redirect URI configured in the environment variables.
165 |
166 | ## Usage
167 |
168 | ### Wrap your app in `AuthKitProvider`
169 |
170 | Use `AuthKitProvider` to wrap your app layout, which provides client side auth methods adds protections for auth edge cases.
171 |
172 | ```jsx
173 | import { AuthKitProvider } from '@workos-inc/authkit-nextjs/components';
174 |
175 | export default function RootLayout({ children }: { children: React.ReactNode }) {
176 | return (
177 |
178 |
179 | {children}
180 |
181 |
182 | );
183 | }
184 | ```
185 |
186 | ### Get the current user in a server component
187 |
188 | For pages where you want to display a signed-in and signed-out view, use `withAuth` to retrieve the user session from WorkOS.
189 |
190 | ```jsx
191 | import Link from 'next/link.js';
192 | import { getSignInUrl, getSignUpUrl, withAuth, signOut } from '@workos-inc/authkit-nextjs';
193 |
194 | export default async function HomePage() {
195 | // Retrieves the user from the session or returns `null` if no user is signed in
196 | const { user } = await withAuth();
197 |
198 | if (!user) {
199 | // Get the URL to redirect the user to AuthKit to sign in
200 | const signInUrl = await getSignInUrl();
201 |
202 | // Get the URL to redirect the user to AuthKit to sign up
203 | const signUpUrl = await getSignUpUrl();
204 |
205 | return (
206 | <>
207 | Log in
208 | Sign Up
209 | >
210 | );
211 | }
212 |
213 | return (
214 |
223 | );
224 | }
225 | ```
226 |
227 | ### Get the current user in a client component
228 |
229 | For client components, use the `useAuth` hook to get the current user session.
230 |
231 | ```jsx
232 | // Note the updated import path
233 | import { useAuth } from '@workos-inc/authkit-nextjs/components';
234 |
235 | export default function MyComponent() {
236 | // Retrieves the user from the session or returns `null` if no user is signed in
237 | const { user, loading } = useAuth();
238 |
239 | if (loading) {
240 | return
Loading...
;
241 | }
242 |
243 | return
{user?.firstName}
;
244 | }
245 | ```
246 |
247 | ### Requiring auth
248 |
249 | For pages where a signed-in user is mandatory, you can use the `ensureSignedIn` option:
250 |
251 | ```jsx
252 | // Server component
253 | const { user } = await withAuth({ ensureSignedIn: true });
254 |
255 | // Client component
256 | const { user, loading } = useAuth({ ensureSignedIn: true });
257 | ```
258 |
259 | Enabling `ensureSignedIn` will redirect users to AuthKit if they attempt to access the page without being authenticated.
260 |
261 | ### Refreshing the session
262 |
263 | Use the `refreshSession` method in a server action or route handler to fetch the latest session details, including any changes to the user's roles or permissions.
264 |
265 | The `organizationId` parameter can be passed to `refreshSession` in order to switch the session to a different organization. If the current session is not authorized for the next organization, an appropriate [authentication error](https://workos.com/docs/reference/user-management/authentication-errors) will be returned.
266 |
267 | In client components, you can refresh the session with the `refreshAuth` hook.
268 |
269 | ```tsx
270 | 'use client';
271 |
272 | import { useAuth } from '@workos-inc/authkit-nextjs/components';
273 | import React, { useEffect } from 'react';
274 |
275 | export function SwitchOrganizationButton() {
276 | const { user, organizationId, loading, refreshAuth } = useAuth();
277 |
278 | useEffect(() => {
279 | // This will log out the new organizationId after refreshing the session
280 | console.log('organizationId', organizationId);
281 | }, [organizationId]);
282 |
283 | if (loading) {
284 | return
342 | );
343 | }
344 | ```
345 |
346 | ##### API Reference
347 |
348 | | Property | Type | Description |
349 | | ------------- | ------------------------------------ | --------------------------------------------- |
350 | | `accessToken` | `string \| undefined` | The current access token |
351 | | `loading` | `boolean` | True when token is being fetched or refreshed |
352 | | `error` | `Error \| null` | Error during token fetch/refresh, or null |
353 | | `refresh` | `() => Promise` | Manually refresh the token |
354 |
355 | ##### Integration with useAuth
356 |
357 | The `useAccessToken` hook automatically synchronizes with the main authentication session. When you call `refreshAuth()` from `useAuth`, the access token will update accordingly. Similarly, using the `refresh()` method from `useAccessToken` will update the entire authentication session.
358 |
359 | ##### Security Considerations
360 |
361 | JWT tokens are sensitive credentials and should be handled carefully:
362 |
363 | - Only use the token where necessary
364 | - Don't store tokens in localStorage or sessionStorage
365 | - Be cautious about exposing tokens in your application state
366 |
367 | ### Session Refresh Callbacks
368 |
369 | When using the `authkit` function directly, you can provide callbacks to be notified when a session is refreshed:
370 |
371 | ```typescript
372 | const { session, headers } = await authkit(request, {
373 | onSessionRefreshSuccess: async ({ accessToken, user, impersonator }) => {
374 | // Log successful refresh
375 | console.log(`Session refreshed for ${user.email}.`);
376 | },
377 | onSessionRefreshError: async ({ error, request }) => {
378 | // Log refresh failure
379 | console.error('Session refresh failed:', error);
380 | // Notify monitoring system
381 | await notifyMonitoring('session_refresh_failed', {
382 | url: request.url,
383 | error: error.message,
384 | });
385 | },
386 | });
387 | ```
388 |
389 | These callbacks provide a way to perform side effects when sessions are refreshed in the middleware. Common use cases include:
390 |
391 | - Logging authentication events
392 | - Updating last activity timestamps
393 | - Triggering organization-specific data prefetching
394 | - Recording failed refresh attempts
395 |
396 | ### Middleware auth
397 |
398 | The default behavior of this library is to request authentication via the `withAuth` method on a per-page basis. There are some use cases where you don't want to call `withAuth` (e.g. you don't need user data for your page) or if you'd prefer a "secure by default" approach where every route defined in your middleware matcher is protected unless specified otherwise. In those cases you can opt-in to use middleware auth instead:
399 |
400 | ```ts
401 | import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
402 |
403 | export default authkitMiddleware({
404 | middlewareAuth: {
405 | enabled: true,
406 | unauthenticatedPaths: ['/', '/about'],
407 | },
408 | });
409 |
410 | // Match against pages that require auth
411 | // Leave this out if you want auth on every resource (including images, css etc.)
412 | export const config = { matcher: ['/', '/admin/:path*', '/about'] };
413 | ```
414 |
415 | In the above example the `/admin` page will require a user to be signed in, whereas `/` and `/about` can be accessed without signing in.
416 |
417 | `unauthenticatedPaths` uses the same glob logic as the [Next.js matcher](https://nextjs.org/docs/pages/building-your-application/routing/middleware#matcher).
418 |
419 | ### Composing middleware
420 |
421 | If you don't want to use `authkitMiddleware` and instead want to compose your own middleware, you can use the `authkit` method. In this mode you are responsible to handling what to do when there's no session on a protected route.
422 |
423 | ```ts
424 | export default async function middleware(request: NextRequest) {
425 | // Perform logic before or after AuthKit
426 |
427 | // Auth object contains the session, response headers and an auhorization URL in the case that the session isn't valid
428 | // This method will automatically handle setting the cookie and refreshing the session
429 | const { session, headers, authorizationUrl } = await authkit(request, {
430 | debug: true,
431 | });
432 |
433 | // Control of what to do when there's no session on a protected route is left to the developer
434 | if (request.url.includes('/account') && !session.user) {
435 | console.log('No session on protected path');
436 | return NextResponse.redirect(authorizationUrl);
437 |
438 | // Alternatively you could redirect to your own login page, for example if you want to use your own UI instead of hosted AuthKit
439 | return NextResponse.redirect('/login');
440 | }
441 |
442 | // Headers from the authkit response need to be included in every non-redirect response to ensure that `withAuth` works as expected
443 | return NextResponse.next({
444 | headers: headers,
445 | });
446 | }
447 |
448 | // Match against the pages
449 | export const config = { matcher: ['/', '/account/:path*'] };
450 | ```
451 |
452 | ### Signing out
453 |
454 | Use the `signOut` method to sign out the current logged in user and redirect to your app's default Logout URI. The Logout URI is set in your WorkOS dashboard settings under "Redirect".
455 |
456 | To use a non-default Logout URI, you can use the `returnTo` parameter.
457 |
458 | ```tsx
459 | await signOut({ returnTo: 'https://your-app.com/signed-out' });
460 | ```
461 |
462 | ### Visualizing an impersonation
463 |
464 | Render the `Impersonation` component in your app so that it is clear when someone is [impersonating a user](https://workos.com/docs/user-management/impersonation).
465 | The component will display a frame with some information about the impersonated user, as well as a button to stop impersonating.
466 |
467 | ```jsx
468 | import { Impersonation, AuthKitProvider } from '@workos-inc/authkit-nextjs/components';
469 |
470 | export default function App() {
471 | return (
472 |