|
36 | 36 | - [Passing authorization parameters](#passing-authorization-parameters-1) |
37 | 37 | - [The `returnTo` parameter](#the-returnto-parameter-1) |
38 | 38 | - [Redirecting the user after authentication](#redirecting-the-user-after-authentication-1) |
| 39 | +- [Getting access tokens for connections](#getting-access-tokens-for-connections) |
| 40 | + - [On the server (App Router)](#on-the-server-app-router-3) |
| 41 | + - [On the server (Pages Router)](#on-the-server-pages-router-3) |
| 42 | + - [Middleware](#middleware-3) |
39 | 43 |
|
40 | 44 | ## Passing authorization parameters |
41 | 45 |
|
@@ -770,7 +774,6 @@ const sessionCookieValue = await generateSessionCookie( |
770 | 774 | ) |
771 | 775 | ``` |
772 | 776 |
|
773 | | - |
774 | 777 | ## Programmatically starting interactive login |
775 | 778 |
|
776 | 779 | Additionally to the ability to initialize the interactive login process by redirecting the user to the built-in `auth/login` endpoint, |
@@ -834,3 +837,137 @@ export const GET = async (req: NextRequest) => { |
834 | 837 |
|
835 | 838 | > [!NOTE] |
836 | 839 | > The URLs specified as `returnTo` parameters must be registered in your client's **Allowed Callback URLs**. |
| 840 | +
|
| 841 | + |
| 842 | +## Getting access tokens for connections |
| 843 | +You can retrieve an access token for a connection using the `getAccessTokenForConnection()` method, which accepts an object with the following properties: |
| 844 | +- `connection`: The federated connection for which an access token should be retrieved. |
| 845 | +- `login_hint`: The optional login_hint parameter to pass to the `/authorize` endpoint. |
| 846 | + |
| 847 | +### On the server (App Router) |
| 848 | + |
| 849 | +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. |
| 850 | + |
| 851 | +> [!IMPORTANT] |
| 852 | +> 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. |
| 853 | +> |
| 854 | +> 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. |
| 855 | +
|
| 856 | +For example: |
| 857 | + |
| 858 | +```ts |
| 859 | +import { NextResponse } from "next/server" |
| 860 | + |
| 861 | +import { auth0 } from "@/lib/auth0" |
| 862 | + |
| 863 | +export async function GET() { |
| 864 | + try { |
| 865 | + const token = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2' }) |
| 866 | + // call external API with token... |
| 867 | + } catch (err) { |
| 868 | + // err will be an instance of AccessTokenError if an access token could not be obtained |
| 869 | + } |
| 870 | + |
| 871 | + return NextResponse.json({ |
| 872 | + message: "Success!", |
| 873 | + }) |
| 874 | +} |
| 875 | +``` |
| 876 | + |
| 877 | +Upon further calls for the same provider, the cached value will be used until it expires. |
| 878 | + |
| 879 | +### On the server (Pages Router) |
| 880 | + |
| 881 | +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: |
| 882 | + |
| 883 | +```ts |
| 884 | +import type { NextApiRequest, NextApiResponse } from "next" |
| 885 | + |
| 886 | +import { auth0 } from "@/lib/auth0" |
| 887 | + |
| 888 | +export default async function handler( |
| 889 | + req: NextApiRequest, |
| 890 | + res: NextApiResponse<{ message: string }> |
| 891 | +) { |
| 892 | + try { |
| 893 | + const token = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2' }, req, res) |
| 894 | + } catch (err) { |
| 895 | + // err will be an instance of AccessTokenError if an access token could not be obtained |
| 896 | + } |
| 897 | + |
| 898 | + res.status(200).json({ message: "Success!" }) |
| 899 | +} |
| 900 | +``` |
| 901 | + |
| 902 | +### Middleware |
| 903 | + |
| 904 | +In middleware, the `getAccessTokenForConnection({}, req, res)` helper can be used to get an access token for a connection, like so: |
| 905 | + |
| 906 | +```tsx |
| 907 | +import { NextRequest, NextResponse } from "next/server" |
| 908 | + |
| 909 | +import { auth0 } from "@/lib/auth0" |
| 910 | + |
| 911 | +export async function middleware(request: NextRequest) { |
| 912 | + const authRes = await auth0.middleware(request) |
| 913 | + |
| 914 | + if (request.nextUrl.pathname.startsWith("/auth")) { |
| 915 | + return authRes |
| 916 | + } |
| 917 | + |
| 918 | + const session = await auth0.getSession(request) |
| 919 | + |
| 920 | + if (!session) { |
| 921 | + // user is not authenticated, redirect to login page |
| 922 | + return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin)) |
| 923 | + } |
| 924 | + |
| 925 | + const accessToken = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2' }, request, authRes) |
| 926 | + |
| 927 | + // the headers from the auth middleware should always be returned |
| 928 | + return authRes |
| 929 | +} |
| 930 | +``` |
| 931 | + |
| 932 | +> [!IMPORTANT] |
| 933 | +> 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. |
| 934 | +
|
| 935 | +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. |
| 936 | + |
| 937 | +```ts |
| 938 | +import { NextRequest, NextResponse } from "next/server" |
| 939 | + |
| 940 | +import { auth0 } from "@/lib/auth0" |
| 941 | + |
| 942 | +export async function middleware(request: NextRequest) { |
| 943 | + const authRes = await auth0.middleware(request) |
| 944 | + |
| 945 | + if (request.nextUrl.pathname.startsWith("/auth")) { |
| 946 | + return authRes |
| 947 | + } |
| 948 | + |
| 949 | + const session = await auth0.getSession(request) |
| 950 | + |
| 951 | + if (!session) { |
| 952 | + // user is not authenticated, redirect to login page |
| 953 | + return NextResponse.redirect(new URL("/auth/login", request.nextUrl.origin)) |
| 954 | + } |
| 955 | + |
| 956 | + const accessToken = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2' }, request, authRes) |
| 957 | + |
| 958 | + // create a new response with the updated request headers |
| 959 | + const resWithCombinedHeaders = NextResponse.next({ |
| 960 | + request: { |
| 961 | + headers: request.headers, |
| 962 | + }, |
| 963 | + }) |
| 964 | + |
| 965 | + // set the response headers (set-cookie) from the auth response |
| 966 | + authRes.headers.forEach((value, key) => { |
| 967 | + resWithCombinedHeaders.headers.set(key, value) |
| 968 | + }) |
| 969 | + |
| 970 | + // the headers from the auth middleware should always be returned |
| 971 | + return resWithCombinedHeaders |
| 972 | +} |
| 973 | +``` |
0 commit comments