Skip to main content
Version: Next

Next.js: Integrate @logto/next

note

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 i @logto/next

Init LogtoClientโ€‹

note

In the following code snippets, we assume your app is running on http://localhost:3000.

tip

You can find and copy "App Secret" from application details page in Admin Console:

App Secret

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:

Web sign-in flow

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 in Admin Console

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:

  1. /api/logto/sign-in: Sign in with Logto.
  2. /api/logto/sign-in-callback: Handle sign-in callback.
  3. /api/logto/sign-out: Sign out with Logto.
  4. /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 NameTypeRequired ScopeNotes
substringopenidThe openid scope is added by default.
namestringprofileThe profile scope is added by default.
usernamestringprofileThe profile scope is added by default.
picturestringprofileThe profile scope is added by default.
emailstringemail
email_verifiedbooleanemail
phone_numberstringphone
phone_number_verifiedbooleanphone
custom_dataobjectcustom_data
identitiesobjectidentities

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 NameTypeRequired ScopeNotes
substringopenidThe openid scope is added by default.
namestringprofileThe profile scope is added by default.
usernamestringprofileThe profile scope is added by default.
picturestringprofileThe profile scope is added by default.
emailstringemail
email_verifiedbooleanemail
phone_numberstringphone
phone_number_verifiedbooleanphone
custom_dataobjectcustom_data
identitiesobjectidentities

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.

info

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.

note

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',
};
note

Check the next-sample to see full example.

Further readingsโ€‹