|
32 | 32 | - [Custom routes](#custom-routes) |
33 | 33 | - [Testing helpers](#testing-helpers) |
34 | 34 | - [`generateSessionCookie`](#generatesessioncookie) |
| 35 | +- [Programmatically starting interactive login](#programmatically-starting-interactive-login) |
| 36 | + - [Passing authorization parameters](#passing-authorization-parameters-1) |
| 37 | + - [The `returnTo` parameter](#the-returnto-parameter-1) |
| 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) |
35 | 43 |
|
36 | 44 | ## Passing authorization parameters |
37 | 45 |
|
@@ -60,14 +68,18 @@ The `returnTo` parameter can be appended to the login to specify where you would |
60 | 68 |
|
61 | 69 | For example: `/auth/login?returnTo=/dashboard` would redirect the user to the `/dashboard` route after they have authenticated. |
62 | 70 |
|
| 71 | +> [!NOTE] |
| 72 | +> The URL specified as `returnTo` parameters must be registered in your client's **Allowed Callback URLs**. |
| 73 | +
|
| 74 | + |
63 | 75 | ### Redirecting the user after logging out |
64 | 76 |
|
65 | 77 | The `returnTo` parameter can be appended to the logout to specify where you would like to redirect the user after they have logged out. |
66 | 78 |
|
67 | 79 | For example: `/auth/login?returnTo=https://example.com/some-page` would redirect the user to the `https://example.com/some-page` URL after they have logged out. |
68 | 80 |
|
69 | 81 | > [!NOTE] |
70 | | -> The URLs specified as `returnTo` parameters must be registered in your client's **Allowed Logout URLs**. |
| 82 | +> The URL specified as `returnTo` parameters must be registered in your client's **Allowed Logout URLs**. |
71 | 83 |
|
72 | 84 | ## Accessing the authenticated user |
73 | 85 |
|
@@ -185,6 +197,15 @@ export async function middleware(request: NextRequest) { |
185 | 197 | > [!IMPORTANT] |
186 | 198 | > The `request` object must be passed as a parameter to the `getSession(request)` method when called from a middleware to ensure that any updates to the session can be read within the same request. |
187 | 199 |
|
| 200 | +## Accessing the idToken |
| 201 | +`idToken` can be accessed from the session in the following way: |
| 202 | + |
| 203 | +```js |
| 204 | +const session = await auth0.getSession(); |
| 205 | +const idToken = session.tokenSet.idToken; |
| 206 | +``` |
| 207 | + |
| 208 | + |
188 | 209 | ## Updating the session |
189 | 210 |
|
190 | 211 | The `updateSession` method could be used to update the session of the currently authenticated user in the App Router, Pages Router, and middleware. If the user does not have a session, an error will be thrown. |
@@ -753,32 +774,200 @@ const sessionCookieValue = await generateSessionCookie( |
753 | 774 | ) |
754 | 775 | ``` |
755 | 776 |
|
| 777 | +## Programmatically starting interactive login |
756 | 778 |
|
757 | | -## Programmatic Pushed Authentication Requests (PAR) |
758 | | - |
759 | | -The method `startInteractiveLogin` can be called with authorizationParams to initiate an interactive login flow. |
760 | | -The code collects authorization parameters on the server side rather than constructing them directly in the browser. |
| 779 | +Additionally to the ability to initialize the interactive login process by redirecting the user to the built-in `auth/login` endpoint, |
| 780 | +the `startInteractiveLogin` method can also be called programmatically. |
761 | 781 |
|
762 | 782 | ```typescript |
763 | | -// app/api/auth/login/route.ts |
764 | 783 | import { auth0 } from "./lib/auth0"; |
765 | 784 | import { NextRequest } from "next/server"; |
766 | 785 |
|
767 | 786 | export const GET = async (req: NextRequest) => { |
768 | | - // Extract custom parameters from request URL if needed |
769 | | - const searchParams = Object.fromEntries(req.nextUrl.searchParams.entries()); |
| 787 | + return auth0.startInteractiveLogin(); |
| 788 | +}; |
| 789 | +``` |
| 790 | + |
| 791 | +### Passing authorization parameters |
| 792 | + |
| 793 | +There are 2 ways to customize the authorization parameters that will be passed to the `/authorize` endpoint when calling `startInteractiveLogin` programmatically. The first option is through static configuration when instantiating the client, like so: |
| 794 | + |
| 795 | +```ts |
| 796 | +export const auth0 = new Auth0Client({ |
| 797 | + authorizationParameters: { |
| 798 | + scope: "openid profile email", |
| 799 | + audience: "urn:custom:api", |
| 800 | + }, |
| 801 | +}); |
| 802 | +``` |
| 803 | + |
| 804 | +The second option is by configuring `authorizationParams` when calling `startInteractiveLogin`: |
770 | 805 |
|
| 806 | +```ts |
| 807 | +import { auth0 } from "./lib/auth0"; |
| 808 | +import { NextRequest } from "next/server"; |
| 809 | + |
| 810 | +export const GET = async (req: NextRequest) => { |
771 | 811 | // Call startInteractiveLogin with optional parameters |
772 | 812 | return auth0.startInteractiveLogin({ |
773 | | - // a custom returnTo URL can be specified |
774 | | - returnTo: "/dashboard", |
775 | 813 | authorizationParameters: { |
776 | | - prompt: searchParams.prompt, |
777 | | - login_hint: searchParams.login_hint, |
778 | | - // Add any custom auth parameters if required |
779 | | - audience: "custom-audience" |
| 814 | + scope: "openid profile email", |
| 815 | + audience: "urn:custom:api", |
780 | 816 | } |
781 | 817 | }); |
782 | 818 | }; |
| 819 | +``` |
| 820 | + |
| 821 | +## The `returnTo` parameter |
| 822 | + |
| 823 | +### Redirecting the user after authentication |
| 824 | + |
| 825 | +When calling `startInteractiveLogin`, the `returnTo` parameter can be configured to specify where you would like to redirect the user to after they have completed their authentication and have returned to your application. |
| 826 | + |
| 827 | +```ts |
| 828 | +import { auth0 } from "./lib/auth0"; |
| 829 | +import { NextRequest } from "next/server"; |
| 830 | + |
| 831 | +export const GET = async (req: NextRequest) => { |
| 832 | + return auth0.startInteractiveLogin({ |
| 833 | + returnTo: '/dashboard', |
| 834 | + }); |
| 835 | +}; |
| 836 | +``` |
| 837 | + |
| 838 | +> [!NOTE] |
| 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" |
783 | 887 |
|
784 | | -``` |
| 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