Easy to use authentication APIs designed to provide a flexible, secure, and fast integration.
Integrating User Management features into your app is quick and easy. In this guide, we’ll take you through adding a hosted authentication flow to your application using AuthKit.
To get the most out of this guide, you’ll need:
Let’s add the necessary dependencies and configuration in your WorkOS Dashboard.
If you’re using Next.js, you can use the authkit-nextjs library. The Next.js library is the fastest way to get AuthKit and Impersonation working in your Next.js application with full session management.
Alternatively you can use one of the several native SDKs that WorkOS provides. This guide will use the popular Node.js SDK.
npm install @workos-inc/node
npm install @workos-inc/authkit-nextjs
A redirect URI is a callback endpoint that WorkOS will redirect to after a user has authenticated. This endpoint will exchange the authorization code returned by WorkOS for an authenticated User object. We’ll be creating this endpoint in the next step.
You can set a redirect URI in the Redirects section of the WorkOS Dashboard – be sure not to include wildcard subdomains or query parameters.
To make calls to WorkOS, provide the API key and the client ID. Store these values as managed secrets and pass them to the SDKs either as environment variables or directly in your app’s configuration depending on your preferences.
WORKOS_API_KEY='sk_example_123456789' WORKOS_CLIENT_ID='client_123456789'
WORKOS_API_KEY='sk_example_123456789' WORKOS_CLIENT_ID='client_123456789' WORKOS_REDIRECT_URI="http://localhost:3000/callback" # configured in the WorkOS dashboard WORKOS_COOKIE_PASSWORD="<your password>" # generate a secure password here
The Next.js library requires you set a strong password to encrypt cookies. This password must be at least 32 characters long. You can generate a secure password by using the 1Password generator or the openssl
library via the command line:
openssl rand -base64 24
The code examples use your staging API keys when signed in
First, we’ll need to direct users to sign in (or sign up) using AuthKit before redirecting them back to your application. We’ll do this by generating an AuthKit authorization URL server side and redirecting the user to it.
You can use the optional state parameter to encode arbitrary information to help restore application state
between redirects.
const express = require('express'); const { WorkOS } = require('@workos-inc/node'); const app = express(); const workos = new WorkOS(process.env.WORKOS_API_KEY); const clientId = process.env.WORKOS_CLIENT_ID; app.get('/auth', (req, res) => { const authorizationUrl = workos.userManagement.getAuthorizationUrl({ // Specify that we'd like AuthKit to handle the authentication flow provider: 'authkit', // The callback endpoint that WorkOS will redirect to after a user authenticates redirectUri: 'http://localhost:3000/callback', clientId, }); // Redirect the user to the AuthKit sign-in page res.redirect(authorizationUrl); });
import Link from 'next/link'; import { getSignInUrl, getSignUpUrl, getUser, signOut, } from '@workos-inc/authkit-nextjs'; export default async function HomePage() { // Retrieves the user from the session or returns `null` if no user is signed in const { user } = await getUser(); // Get the URL to redirect the user to AuthKit to sign in const signInUrl = await getSignInUrl(); // Get the URL to redirect the user to AuthKit to sign up const signUpUrl = await getSignUpUrl(); /** * If a signed-in user is mandatory, you can use the `ensureSignedIn` * configuration option. If logged out, the below will immediately redirect * the user to AuthKit. After signing in, the user will automatically * be redirected back to this page. * */ // const { user } = await getUser({ ensureSignedIn: true }); if (!user) { return ( <> <Link href={signInUrl}>Sign in</Link>; <Link href={signUpUrl}>Sign up</Link>; </> ); } return ( <form action={async () => { 'use server'; await signOut(); }} > <p>Welcome back{user.firstName && `, ${user.firstName}`}</p> <button type="submit">Sign out</button> </form> ); }
WorkOS will redirect to your Redirect URI if there is an issue generating an authorization URL. Read our API Reference for more details.
Next, let’s add the callback endpoint (referenced in Configure a redirect URI) which will exchange the authorization code (valid for 10 minutes) for an authenticated User object.
const express = require('express'); const { WorkOS } = require('@workos-inc/node'); const app = express(); const workos = new WorkOS(process.env.WORKOS_API_KEY); const clientId = process.env.WORKOS_CLIENT_ID; app.get('/callback', async (req, res) => { // The authorization code returned by AuthKit const code = req.query.code; if (!code) { return res.status(400).send('No code provided'); } const { user } = await workos.userManagement.authenticateWithCode({ code, clientId, }); // Use the information in `user` for further business logic. // Redirect the user to the homepage return res.redirect('/'); });
Next.js middleware is required to determine which routes require authentication.
import { authkitMiddleware } from '@workos-inc/authkit-nextjs'; export default authkitMiddleware(); // Match against pages that require authentication // Leave this out if you want authentication on every page in your application export const config = { matcher: ['/'] };
Make sure this route matches the WORKOS_REDIRECT_URI
environment variable and the configured redirect URI in your WorkOS dashboard.
import { handleAuth } from '@workos-inc/authkit-nextjs'; // Redirect the user to `/` after successful sign in // The redirect can be customized: `handleAuth({ returnPathname: '/foo' })` export const GET = handleAuth();
Navigate to the authentication endpoint we created and sign up for an account. You can then sign in with the newly created credentials and see the user listed in the Users section of the WorkOS Dashboard.
If using the authkit-nextjs
library, session management is handled for you. No further integration is required.
In order to persist the authenticated state of the user in the application, we need to store and access a session.
First, generate a unique password to seal the session with.
openssl rand -base64 64
node -e "console.log(require('crypto').randomBytes(64).toString('base64'));"
Then add it to the environment variables file.
WORKOS_API_KEY='sk_example_123456789' WORKOS_CLIENT_ID='client_123456789' WORKOS_COOKIE_PASSWORD='<your password>'
Next, use the SDK to authenticate the user and return a password protected session. The refresh token is considered sensitive as it can be used to re-authenticate, hence why the session is encrypted before storing it in a session cookie.
import cookieParser from 'cookie-parser'; app.use(cookieParser()); app.get('/callback', async (req, res) => { // The authorization code returned by AuthKit const code = req.query.code; if (!code) { return res.status(400).send('No code provided'); } try { const authenticateResponse = await workos.userManagement.authenticateWithCode({ clientId: process.env.WORKOS_CLIENT_ID, code, session: { sealSession: true, cookiePassword: process.env.WORKOS_COOKIE_PASSWORD, }, }); const { user, sealedSession } = authenticateResponse; // Store the session in a cookie res.cookie('wos-session', sealedSession, { path: '/', httpOnly: true, secure: true, sameSite: 'lax', }); // Use the information in `user` for further business logic. // Redirect the user to the homepage return res.redirect('/'); } catch (error) { return res.redirect('/login'); } });
Then, use middleware to specify which routes should be protected. If the session has expired, use the SDK to attempt to generate a new one.
import { WorkOS, AuthenticateWithSessionCookieFailureReason, } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY, { clientId: process.env.WORKOS_CLIENT_ID, }); // Auth middleware function async function withAuth(req, res, next) { const authenticationResponse = await workos.userManagement.authenticateWithSessionCookie({ sessionData: req.cookies['wos-session'], cookiePassword: process.env.WORKOS_COOKIE_PASSWORD, }); const { authenticated, reason } = authenticationResponse; if (authenticated) { return next(); } // If no session, redirect the user to the login page if ( !authenticated && reason === AuthenticateWithSessionCookieFailureReason.NO_SESSION_COOKIE_PROVIDED ) { return res.redirect('/login'); } try { // If the session is invalid (i.e. the access token has expired) // attempt to re-authenticate with the refresh token const refreshResponse = await workos.userManagement.refreshAndSealSessionData({ sessionData: req.cookies['wos-session'], cookiePassword: process.env.WORKOS_COOKIE_PASSWORD, }); if (!refreshResponse.authenticated) { return res.redirect('/login'); } // Update the cookie res.cookie('wos-session', refreshResponse.sealedSession, { path: '/', httpOnly: true, secure: true, sameSite: 'lax', }); return next(); } catch (e) { // Failed to refresh access token, redirect user to login page // after deleting the cookie res.clearCookie('wos-session'); return res.redirect('/login'); } }
Finally, add the middleware to the route that should only be accessible to logged in users.
// Specify the `withAuth` middleware function we defined earlier to protect this route app.get('/dashboard', withAuth, async (req, res) => { const session = await workos.userManagement.getSessionFromCookie({ sessionData: req.cookies['wos-session'], cookiePassword: process.env.WORKOS_COOKIE_PASSWORD, }); console.log(`User ${session.user.firstName} is logged in`); // ... render dashboard page });