|
32 | 32 | - [Custom routes](#custom-routes)
|
33 | 33 | - [Testing helpers](#testing-helpers)
|
34 | 34 | - [`generateSessionCookie`](#generatesessioncookie)
|
| 35 | +- [Getting access tokens for connections](#getting-access-tokens-for-connections) |
| 36 | + - [On the server (App Router)](#on-the-server-app-router-3) |
| 37 | + - [On the server (Pages Router)](#on-the-server-pages-router-3) |
| 38 | + - [Middleware](#middleware-3) |
35 | 39 |
|
36 | 40 | ## Passing authorization parameters
|
37 | 41 |
|
@@ -754,22 +758,132 @@ const sessionCookieValue = await generateSessionCookie(
|
754 | 758 | ```
|
755 | 759 |
|
756 | 760 | ## Getting access tokens for connections
|
757 |
| -You can retrieve access tokens for connections using the `getAccessTokenForConnection()` method of `AuthClient`. |
758 |
| -This is an async method and can be run either from middleware, a client page or a server rendered page. |
759 |
| -Note that to cache these tokens retrieved from auth0, this needs to be called in the middleware: |
| 761 | +You can retrieve an access token for a connection using the `getAccessTokenForConnection()` method, which accepts an object with the following properties: |
| 762 | +- `connection`: The federated connection for which an access token should be retrieved. |
| 763 | +- `login_hint`: The optional login_hint parameter to pass to the `/authorize` endpoint. |
| 764 | + |
| 765 | +### On the server (App Router) |
| 766 | + |
| 767 | +On the server, the `getAccessTokenForConnection()` helper can be used in Server Routes, Server Actions and Server Components to get an access token for a connection. |
| 768 | + |
| 769 | +> [!IMPORTANT] |
| 770 | +> Server Components cannot set cookies. Calling `getAccessTokenForConnection()` in a Server Component will cause the access token to be refreshed, if it is expired, and the updated token set will not to be persisted. |
| 771 | +> |
| 772 | +> It is recommended to call `getAccessTokenForConnection(req, res)` in the middleware if you need to refresh the token in a Server Component as this will ensure the token is refreshed and correctly persisted. |
| 773 | +
|
| 774 | +For example: |
| 775 | + |
| 776 | +```ts |
| 777 | +import { NextResponse } from "next/server" |
| 778 | + |
| 779 | +import { auth0 } from "@/lib/auth0" |
| 780 | + |
| 781 | +export async function GET() { |
| 782 | + try { |
| 783 | + const token = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2' }) |
| 784 | + // call external API with token... |
| 785 | + } catch (err) { |
| 786 | + // err will be an instance of AccessTokenError if an access token could not be obtained |
| 787 | + } |
| 788 | + |
| 789 | + return NextResponse.json({ |
| 790 | + message: "Success!", |
| 791 | + }) |
| 792 | +} |
| 793 | +``` |
| 794 | + |
| 795 | +### On the server (Pages Router) |
| 796 | + |
| 797 | +On the server, the `getAccessTokenForConnection({}, req, res)` helper can be used in `getServerSideProps` and API routes to get an access token for a connection, like so: |
| 798 | + |
| 799 | +```ts |
| 800 | +import type { NextApiRequest, NextApiResponse } from "next" |
| 801 | + |
| 802 | +import { auth0 } from "@/lib/auth0" |
| 803 | + |
| 804 | +export default async function handler( |
| 805 | + req: NextApiRequest, |
| 806 | + res: NextApiResponse<{ message: string }> |
| 807 | +) { |
| 808 | + try { |
| 809 | + const token = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2' }, req, res) |
| 810 | + } catch (err) { |
| 811 | + // err will be an instance of AccessTokenError if an access token could not be obtained |
| 812 | + } |
| 813 | + |
| 814 | + res.status(200).json({ message: "Success!" }) |
| 815 | +} |
| 816 | +``` |
| 817 | + |
| 818 | +### Middleware |
| 819 | + |
| 820 | +In middleware, the `getAccessTokenForConnection({}, req, res)` helper can be used to get an access token for a connection, like so: |
| 821 | + |
| 822 | +```tsx |
| 823 | +import { NextRequest, NextResponse } from "next/server" |
| 824 | + |
| 825 | +import { auth0 } from "@/lib/auth0" |
760 | 826 |
|
761 |
| -```typescript |
762 | 827 | export async function middleware(request: NextRequest) {
|
763 |
| - const authResponse = await auth0.middleware(request); |
764 |
| - const session = await auth0.getSession(); |
765 |
| - if (session) { |
766 |
| - // cache token for google connection to session store |
767 |
| - console.log(await auth0.getAccessTokenForConnection({connection: 'google-oauth2'})); |
| 828 | + const authRes = await auth0.middleware(request) |
| 829 | + |
| 830 | + if (request.nextUrl.pathname.startsWith("/auth")) { |
| 831 | + return authRes |
| 832 | + } |
| 833 | + |
| 834 | + const session = await auth0.getSession(request) |
| 835 | + |
| 836 | + if (!session) { |
| 837 | + // user is not authenticated, redirect to login page |
| 838 | + return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin)) |
768 | 839 | }
|
769 |
| - return authResponse |
| 840 | + |
| 841 | + const accessToken = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2' }, request, authRes) |
| 842 | + |
| 843 | + // the headers from the auth middleware should always be returned |
| 844 | + return authRes |
770 | 845 | }
|
771 | 846 | ```
|
772 |
| -`connection` parameter is the federated connection string for the provider. |
773 |
| -Here, we are accessing the token for `google-oauth2` connection. |
774 | 847 |
|
775 |
| -Upon further calls for the same provider, the cached value will be used until it expires. |
| 848 | +> [!IMPORTANT] |
| 849 | +> The `request` and `response` objects must be passed as a parameters to the `getAccessTokenForConnection({}, request, response)` method when called from a middleware to ensure that the refreshed access token can be accessed within the same request. |
| 850 | +
|
| 851 | +If you are using the Pages Router and are calling the `getAccessTokenForConnection` method in both the middleware and an API Route or `getServerSideProps`, it's recommended to propagate the headers from the middleware, as shown below. This will ensure that calling `getAccessTokenForConnection` in the API Route or `getServerSideProps` will not result in the access token being refreshed again. |
| 852 | + |
| 853 | +```ts |
| 854 | +import { NextRequest, NextResponse } from "next/server" |
| 855 | + |
| 856 | +import { auth0 } from "@/lib/auth0" |
| 857 | + |
| 858 | +export async function middleware(request: NextRequest) { |
| 859 | + const authRes = await auth0.middleware(request) |
| 860 | + |
| 861 | + if (request.nextUrl.pathname.startsWith("/auth")) { |
| 862 | + return authRes |
| 863 | + } |
| 864 | + |
| 865 | + const session = await auth0.getSession(request) |
| 866 | + |
| 867 | + if (!session) { |
| 868 | + // user is not authenticated, redirect to login page |
| 869 | + return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin)) |
| 870 | + } |
| 871 | + |
| 872 | + const accessToken = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2' }, request, authRes) |
| 873 | + |
| 874 | + // create a new response with the updated request headers |
| 875 | + const resWithCombinedHeaders = NextResponse.next({ |
| 876 | + request: { |
| 877 | + headers: request.headers, |
| 878 | + }, |
| 879 | + }) |
| 880 | + |
| 881 | + // set the response headers (set-cookie) from the auth response |
| 882 | + authRes.headers.forEach((value, key) => { |
| 883 | + resWithCombinedHeaders.headers.set(key, value) |
| 884 | + }) |
| 885 | + |
| 886 | + // the headers from the auth middleware should always be returned |
| 887 | + return resWithCombinedHeaders |
| 888 | +} |
| 889 | +``` |
0 commit comments