Skip to main content

Add authentication to your Passport.js application

This guide will show you how to integrate Logto into your application with Passport.js and OIDC strategy.

tip:
  • In this guide, we assume you have set up Express with session in you project. If you haven't, check out the Express.js website to get started.

Prerequisites

Installation

Install Logto SDK via your favorite package manager:

npm i passport passport-openidconnect

Integration

Initialize Passport.js with OIDC strategy

passport.ts
import passport from 'passport';
import OpenIDConnectStrategy, { type Profile, type VerifyCallback } from 'passport-openidconnect';

const endpoint = '<your-logto-endpoint>';
const appId = '<your-application-id>';
const appSecret = '<your-application-secret>';

export default function initPassport() {
passport.use(
new OpenIDConnectStrategy(
{
issuer: `${endpoint}/oidc`,
authorizationURL: `${endpoint}/oidc/auth`,
tokenURL: `${endpoint}/oidc/token`,
userInfoURL: `${endpoint}/oidc/me`,
clientID: appId,
clientSecret: appSecret,
callbackURL: '/callback',
scope: ['profile', 'offline_access'],
},
(issuer: string, profile: Profile, callback: VerifyCallback) => {
callback(null, profile);
}
)
);

passport.serializeUser((user, callback) => {
callback(null, user);
});

passport.deserializeUser(function (user, callback) {
callback(null, user as Express.User);
});
}

This code initializes Passport with the OpenIDConnectStrategy. The serialize and deserialize methods are set for demonstration purposes.

Ensure to initialize and attach Passport middleware in your application:

your-app-entry.ts
import initPassport from './passport';

// ... other code
initPassport();
// ... other code
app.use(passport.authenticate('session'));
// ... other code

Configure redirect URIs

Before we dive into the details, here's a quick overview of the end-user experience. The sign-in process can be simplified as follows:

  1. Your app invokes the sign-in method.
  2. The user is redirected to the Logto sign-in page. For native apps, the system browser is opened.
  3. The user signs in and is redirected back to your app (configured as the redirect URI).

Regarding redirect-based sign-in

  1. This authentication process follows the OpenID Connect (OIDC) protocol, and Logto enforces strict security measures to protect user sign-in.
  2. If you have multiple apps, you can use the same identity provider (Logto). Once the user signs in to one app, Logto will automatically complete the sign-in process when the user accesses another app.

To learn more about the rationale and benefits of redirect-based sign-in, see Logto sign-in experience explained.


note:

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

Configure redirect URIs

Switch to the application details page of Logto Console. Add a redirect URI http://localhost:3000/callback.

Redirect URI in Logto Console

Just like signing in, users should be redirected to Logto for signing out of the shared session. Once finished, it would be great to redirect the user back to your website. For example, add http://localhost:3000/ as the post sign-out redirect URI section.

Then click "Save" to save the changes.

Implement sign-in and sign-out

We'll now create specific routes for authentication processes:

your-app-entry.ts
app.get('/sign-in', passport.authenticate('openidconnect'));
app.get(
'/callback',
passport.authenticate('openidconnect', {
successReturnToOrRedirect: '/',
})
);
app.get('/sign-out', (request, response, next) => {
request.logout((error) => {
if (error) {
next(error);
return;
}
response.redirect(`${endpoint}/oidc/session/end?client_id=${appId}`);
});
});

Then add to the homepage

your-app-entry.ts
app.get('/', (request: Request, response) => {
const { user } = request;
response.setHeader('content-type', 'text/html');

if (user) {
response.end(
`<h1>Hello Logto</h1><p>Signed in as ${JSON.stringify(
user
)}, <a href="/sign-out">Sign Out</a></p>`
);
} else {
response.end(`<h1>Hello Logto</h1><p><a href="/sign-in">Sign In</a></p>`);
}
});

Checkpoint: Test your application

Now, you can test your application:

  1. Run your application, you will see the sign-in button.
  2. Click the sign-in button, the SDK will init the sign-in process and redirect you to the Logto sign-in page.
  3. After you signed in, you will be redirected back to your application and see the sign-out button.
  4. Click the sign-out button to clear token storage and sign out.

Scopes and claims

Logto uses OIDC scopes and claims conventions to define the scopes and claims for retrieving user information from the ID token and OIDC userinfo endpoint. Both of the "scope" and the "claim" are terms from the OAuth 2.0 and OpenID Connect (OIDC) specifications.

For standard OIDC claims, the inclusion in the ID token is strictly determined by the requested scopes. Extended claims (such as custom_data and organizations) can be additionally configured to appear in the ID token through the Custom ID token settings.

In short, when you request a scope, you will get the corresponding claims in the user information. For example, if you request the `email` scope, you will get the `email` and `email_verified` data of the user.

By default, Logto SDK will always request three scopes: `openid`, `profile`. And `offline_access`, and there is no way to remove these default scopes. But you can add more scopes when configuring Logto:

export default function initPassport() {
passport.use(
new OpenIDConnectStrategy(
{
// ... other options
clientID: appId,
clientSecret: appSecret,
callbackURL: '/callback',
scope: ['openid', 'offline_access', 'profile', 'email'],
}
// ... other options
)
);
// ... other options
}

Here's the list of supported scopes and the corresponding claims:

Standard OIDC scopes

openid (default)

Claim nameTypeDescription
substringThe unique identifier of the user

profile (default)

Claim nameTypeDescription
namestringThe full name of the user
usernamestringThe username of the user
picturestringURL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User.
created_atnumberTime the End-User was created. The time is represented as the number of milliseconds since the Unix epoch (1970-01-01T00:00:00Z).
updated_atnumberTime the End-User's information was last updated. The time is represented as the number of milliseconds since the Unix epoch (1970-01-01T00:00:00Z).

Other standard claims include family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo, and locale will be also included in the profile scope without the need for requesting the userinfo endpoint. A difference compared to the claims above is that these claims will only be returned when their values are not empty, while the claims above will return null if the values are empty.

note:

Unlike the standard claims, the created_at and updated_at claims are using milliseconds instead of seconds.

email

Claim nameTypeDescription
emailstringThe email address of the user
email_verifiedbooleanWhether the email address has been verified

phone

Claim nameTypeDescription
phone_numberstringThe phone number of the user
phone_number_verifiedbooleanWhether the phone number has been verified

address

Please refer to the OpenID Connect Core 1.0 for the details of the address claim.

info:

Scopes marked with (default) are always requested by the Logto SDK. Claims under standard OIDC scopes are always included in the ID token when the corresponding scope is requested — they cannot be turned off.

Extended scopes

The following scopes are extended by Logto and will return claims through the userinfo endpoint. These claims can also be configured to be included directly in the ID token through Console > Custom JWT. See Custom ID token for more details.

custom_data

Claim nameTypeDescriptionIncluded in ID token by default
custom_dataobjectThe custom data of the user

identities

Claim nameTypeDescriptionIncluded in ID token by default
identitiesobjectThe linked identities of the user
sso_identitiesarrayThe linked SSO identities of the user

roles

Claim nameTypeDescriptionIncluded in ID token by default
rolesstring[]The roles of the user

urn:logto:scope:organizations

Claim nameTypeDescriptionIncluded in ID token by default
organizationsstring[]The organization IDs the user belongs to
organization_dataobject[]The organization data the user belongs to
note:

These organization claims can also be retrieved via the userinfo endpoint when using an opaque token. However, opaque tokens cannot be used as organization tokens for accessing organization-specific resources. See Opaque token and organizations for more details.

urn:logto:scope:organization_roles

Claim nameTypeDescriptionIncluded in ID token by default
organization_rolesstring[]The organization roles the user belongs to with the format of <organization_id>:<role_name>

Further readings

End-user flows: authentication flows, account flows, and organization flows Configure connectors Authorization