Next.js: Integrate @logto/next
This tutorial assumes you have created an Application of type "Traditional Web" in Admin Console. If you are not ready, read this before continuing.
Add Logto SDK as a dependencyโ
- npm
- Yarn
- pnpm
npm i @logto/next
yarn add @logto/next
pnpm add @logto/next
Init LogtoClientโ
In the following code snippets, we assume your app is running on http://localhost:3000
.
You can find and copy "App Secret" from application details page in Admin Console:

Import and initialize LogtoClient:
// libraries/logto.ts
import LogtoClient from '@logto/next';
export const logtoClient = new LogtoClient({
appId: '<your-application-id>',
appSecret: '<your-app-secret-copied-from-console>',
endpoint: '<your-logto-endpoint>', // E.g. http://localhost:3001
baseUrl: '<your-nextjs-app-base-url>', // E.g. http://localhost:3000
cookieSecret: 'complex_password_at_least_32_characters_long',
cookieSecure: process.env.NODE_ENV === 'production',
});
Sign inโ
The sign-in flow can be simplified as:
Configure sign-in redirect URIโ
Let's switch to the Application details page of Admin Console in this section. Add a Redirect URI http://localhost:3000/api/logto/sign-in-callback
and click "Save Changes".

Redirect URI is an OAuth 2.0 concept which implies the location should redirect after authentication.
Prepare API routesโ
Prepare API routes to connect with Logto.
Go back to your IDE/editor, use the following code to implement the API routes first:
// pages/api/logto/[action].ts
import { logtoClient } from '../../../libraries/logto';
export default logtoClient.handleAuthRoutes();
This will create 4 routes automatically:
/api/logto/sign-in
: Sign in with Logto./api/logto/sign-in-callback
: Handle sign-in callback./api/logto/sign-out
: Sign out with Logto./api/logto/user
: Check if user is authenticated with Logto. If yes, return user info.
Implement sign-in buttonโ
We're almost there! In the last step, we will create a sign-in button:
import { useRouter } from 'next/router';
<button onClick={() => window.location.assign('/api/logto/sign-in')}>Sign In</button>;
Now you will be navigated to Logto sign-in page when you click the button.
Get user profileโ
There are two ways to get user profile.
Through API request in front-endโ
You can fetch user info by calling /api/logto/user
import { LogtoContext } from '@logto/next';
import useSWR from 'swr';
const Home = () => {
const { data } = useSWR<LogtoContext>('/api/logto/user');
return <div>userId: {data?.claims?.sub}</div>;
};
export default Profile;
Check this guide to learn more about useSWR
.
Through getServerSideProps
โ
import { LogtoContext } from '@logto/next';
import { logtoClient } from '../libraries/logto';
type Props = {
user: LogtoContext;
};
const Profile = ({ user }: Props) => {
return <div>User ID: {user.claims?.sub}</div>;
};
export default Profile;
export const getServerSideProps = logtoClient.withLogtoSsr(async function ({ request }) {
const { user } = request;
return {
props: { user },
};
});
Check Next.js documentation for more details on getServerSideProps
.
Fetch user infoโ
For most cases, it is recommended to use claims
, this can be fast because claims
is cached when tokens are granted. If you need more accurate user info, set config.fetchUserInfo
to true
, to tell the SDK to fetch the user information from the OIDC UserInfo Endpoint.
// pages/api/logto/user-info.ts
import { logtoClient } from '../../../libraries/logto';
export default logtoClient.handleAuthRoutes({ fetchUserInfo: true });
The user information response will vary based on the scopes used in the LogtoConfig
while initializing the LogtoClient
; and the following table lists the relations between user information and scopes:
Field Name | Type | Required Scope | Notes |
---|---|---|---|
sub | string | openid | The openid scope is added by default. |
name | string | profile | The profile scope is added by default. |
username | string | profile | The profile scope is added by default. |
picture | string | profile | The profile scope is added by default. |
string | email | ||
email_verified | boolean | email | |
phone_number | string | phone | |
phone_number_verified | boolean | phone | |
custom_data | object | custom_data | |
identities | object | identities |
Protect API and pagesโ
Protect API routesโ
Wrap your handler with logtoClient.withLogtoApiRoute
.
// pages/api/protected-resource.ts
import { logtoClient } from '../../libraries/logto';
export default logtoClient.withLogtoApiRoute((request, response) => {
if (!request.user.isAuthenticated) {
response.status(401).json({ message: 'Unauthorized' });
return;
}
response.json({
data: 'this_is_protected_resource',
});
});
Protect pagesโ
If you don't want anonymous users to access a page, use logtoClient.withLogtoSsr
to get auth state, and redirect to sign-in route if not authenticated.
export const getServerSideProps = logtoClient.withLogtoSsr(async function ({ req, res }) {
const { user } = req;
if (!user.isAuthenticated) {
res.setHeader('location', '/api/logto/sign-in');
res.statusCode = 302;
res.end();
}
return {
props: { user },
};
});
Sign outโ
Calling /api/logto/sign-out
will clear all the Logto data in memory and cookies if they exist.
After signing out, it'll be great to redirect your user back to your website. Let's add http://localhost:3000
as one of the Post Sign-out URIs in Admin Console (shows under Redirect URIs).
Implement a sign-out buttonโ
<button onClick={() => window.location.assign('/api/logto/sign-out')}>Sign Out</button>
Fetch user informationโ
Logto SDK helps you fetch the user information from the OIDC UserInfo Endpoint.
You can get the user information by calling logtoClient.handleUser({ fetchUserInfo: true })
after signing in.
The user information response will vary based on the scopes used in the LogtoConfig
while initializing the LogtoClient
; and the following table lists the relations between user information and scopes:
Field Name | Type | Required Scope | Notes |
---|---|---|---|
sub | string | openid | The openid scope is added by default. |
name | string | profile | The profile scope is added by default. |
username | string | profile | The profile scope is added by default. |
picture | string | profile | The profile scope is added by default. |
string | email | ||
email_verified | boolean | email | |
phone_number | string | phone | |
phone_number_verified | boolean | phone | |
custom_data | object | custom_data | |
identities | object | identities |
Backend API authorizationโ
Logto also helps you apply authorization to your backend APIs . Please check our Protect your API see how to integrate Logto with your backend applications.
You can claim an authorization token for a protected API Resource request easily through Logto SDK.
In order to grant an audience restricted authorization token for your request, make sure the requested API Resource is registered in the Logto Admin Console.
Add your API resource indicators to the Logto SDK configs:
// libraries/logto.ts
import LogtoClient from '@logto/next';
export const logtoClient = new LogtoClient({
appId: '<your-application-id>',
appSecret: '<your-app-secret-copied-from-console>',
endpoint: '<your-logto-endpoint>', // E.g. http://localhost:3001
baseUrl: '<your-nextjs-app-base-url>', // E.g. http://localhost:3000
cookieSecret: 'complex_password_at_least_32_characters_long',
cookieSecure: process.env.NODE_ENV === 'production',
resources: ['<your-api-resource>'],
});
Specify the resource when creating the API routes:
// pages/api/logto/[action].ts
import { logtoClient } from '../../../libraries/logto';
export default logtoClient.handleAuthRoutes({ resource: '<your-target-api-resource>' });
Claim an authorization token from inside wrapper withLogtoApiRoute:
// pages/api/protected-resource.ts
import { logtoClient } from '../../libraries/logto';
export default logtoClient.withLogtoApiRoute(
(request, response) => {
if (!request.user.isAuthenticated) {
response.status(401).json({ message: 'Unauthorized' });
return;
}
// Access token can be obtained from request.user.accessToken
response.json({
data: 'this_is_protected_resource',
});
},
{ getAccessToken: true, resource: '<your-target-api-resource>' }
);
You can also get the access token from inside getServerSideProps:
import { LogtoContext } from '@logto/next';
import { logtoClient } from '../libraries/logto';
type Props = {
user: LogtoContext;
};
const Profile = ({ user }: Props) => {
return <div>User ID: {user.claims?.sub}</div>;
};
export default Profile;
export const getServerSideProps = logtoClient.withLogtoSsr(
async function ({ req }) {
const { user } = req;
// Access token can be obtained from user.accessToken
return {
props: { user },
};
},
{ getAccessToken: true, resource: '<your-target-api-resource>' }
);
With the user's authorization status, a JWT format access_token
will be granted and issued by Logto, specifically for the requested API resource. Encrypted and audience-restricted with an expiration time. The token carries all the necessary info to represent the authority of this request.
Put the token in the Authorization
field of HTTP headers with the Bearer format (Bearer YOUR_TOKEN
), and you are good to go.
The Bearer Token's integration flow may vary based on the framework or requester you are using. Choose your own way to apply the request Authorization
header.
Edge runtimeโ
Added in @logto/[email protected]
If you want to use the edge runtime API routes, you need to use the sub package @logto/next/edge
.
// libraries/logto-edge.ts
import LogtoClient from '@logto/next/edge';
export const logtoClient = new LogtoClient({
appId: '<your-application-id>',
appSecret: '<your-app-secret-copied-from-console>',
endpoint: '<your-logto-endpoint>', // E.g. http://localhost:3001
baseUrl: '<your-nextjs-app-base-url>', // E.g. http://localhost:3000
cookieSecret: 'complex_password_at_least_32_characters_long',
cookieSecure: process.env.NODE_ENV === 'production',
resources: ['<your-api-resource>'],
});
Then set the runtime to experimental-edge
or edge
in the API route.
import { logtoClient } from '../../../libraries/logto-edge';
export default logtoClient.handleSignIn();
export const config = {
runtime: 'experimental-edge',
};
Check the next-sample to see full example.