Getting Started with Hosted Authentication
Learn how to integrate OAuth42's hosted authentication pages into your Next.js application in under 10 minutes.
What You'll Build
In this tutorial, you'll create a Next.js application that uses OAuth42's hosted authentication pages. Users will be redirected to OAuth42's secure login page, authenticate, and then return to your app with an authorization code that you'll exchange for access tokens.
Benefits of Hosted Auth:
- No need to build login/signup UI
- Enterprise-grade security out of the box
- Customizable branding and features
- Automatic MFA support
- Faster time to market
Prerequisites
- Node.js 18+ installed
- An OAuth42 account (sign up at oauth42.com)
- Basic knowledge of Next.js and React
Step 1: Create an OAuth42 Application
- Log in to the OAuth42 Portal
- Click "Applications" in the sidebar
- Click "Create New Application"
- Fill in the application details:
- Name: "My Next.js App"
- Redirect URIs:
https://yourdomain.com/api/oauth/callback
- Copy your Client ID and Client Secret (you'll need these in Step 3)
⚠️ Important: Replace yourdomain.com with your actual domain, or use http://localhost:3000 for local testing.
💡 Testing with Your Account: You can test the login flow using the same email/password you used to sign up for OAuth42. Any user in your organization can authenticate with your applications.
Step 2: Create a Next.js Application
npx create-next-app@latest my-oauth42-app
cd my-oauth42-app
npm install @oauth42/nextStep 3: Configure Environment Variables
Create a .env.local file in your project root:
# OAuth42 Configuration
OAUTH42_CLIENT_ID=your_client_id_here
OAUTH42_CLIENT_SECRET=your_client_secret_here
OAUTH42_ISSUER=https://api.oauth42.com
# NextAuth Configuration
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=generate_a_random_secret_here💡 Note: The OAuth42 SDK automatically discovers the hosted auth URL from the OIDC discovery endpoint, so you don't need to configure it separately.
💡 Generate a random secret:
openssl rand -base64 32Step 4: Configure Next.js for Local Development (Optional)
If you're using https://localhost:8443 for your OAUTH42_ISSUER, you need to configure Next.js to accept self-signed SSL certificates.
Update the dev script in your package.json:
{
"scripts": {
"dev": "NODE_TLS_REJECT_UNAUTHORIZED='0' next dev"
}
}💡 Production Note: This only affects the dev script. Production builds will use proper SSL validation. Skip this step if you're using the production API at https://api.oauth42.com.
Step 5: Create Authentication API Route
First, create the directory structure for the NextAuth catch-all route:
mkdir -p app/api/auth/[...nextauth]
touch app/api/auth/[...nextauth]/route.ts📁 Note: The folder name is literally [...nextauth] (including brackets and dots). This is a Next.js catch-all route that handles all /api/auth/* paths.
Now add the following code to app/api/auth/[...nextauth]/route.ts:
import { createAuth } from '@oauth42/next/server';
const { auth: authOptions, handlers } = createAuth({
clientId: process.env.OAUTH42_CLIENT_ID,
clientSecret: process.env.OAUTH42_CLIENT_SECRET,
issuer: process.env.OAUTH42_ISSUER,
// Optional: Use a unique prefix if running multiple apps on the same domain
// cookiePrefix: 'myapp',
});
export { authOptions };
export const { GET, POST } = handlers;💡 What's happening here:
- The
createAuthfunction sets up NextAuth with OAuth42 configuration - It automatically configures OIDC discovery, PKCE, token handling, and session callbacks
- Returns both the auth options (for use in pages) and handlers (for the API route)
- All environment variables are read automatically from your
.env.localfile
✨ Massive complexity reduction:
- NextAuth config: 16 lines (vs 60+ lines of manual setup)
- OAuth callback: 5 lines (vs 100+ lines of token exchange)
- TypeScript types: 0 lines (automatically included in SDK)
- Total: 21 lines of code for complete OAuth integration!
Step 6: Create OAuth Callback Handler
Create the hosted auth callback handler that integrates with NextAuth:
mkdir -p app/api/oauth/callback
touch app/api/oauth/callback/route.tsAdd the following code to app/api/oauth/callback/route.ts:
import { createHostedAuthCallback } from '@oauth42/next/server';
export const GET = createHostedAuthCallback({
redirectUrl: '/dashboard',
});💡 What's happening here:
- The
createHostedAuthCallbackfunction handles the OAuth callback from hosted auth - It exchanges the authorization code for tokens
- Creates a NextAuth-compatible session cookie
- Redirects to your dashboard (or any URL you specify)
- All environment variables are automatically read from your
.env.localfile
✨ That's it! Just 5 lines of code to handle the entire OAuth callback flow and integrate with NextAuth's session management.
💡 TypeScript Definitions Included: The @oauth42/next SDK automatically includes TypeScript type definitions for NextAuth's Session and JWT interfaces with OAuth42-specific fields (accessToken, idToken, etc.). No manual type setup needed!
Step 7: Protect Routes with Middleware
Create middleware.ts in your project root:
import { withOAuth42Auth } from '@oauth42/next/middleware';
export default withOAuth42Auth({
// If you used cookiePrefix in createAuth(), use the same value here
// cookiePrefix: 'myapp',
// IMPORTANT: Only mark '/api/auth' as public, NOT '/api'!
// This ensures token refresh works for all your API routes.
publicPaths: ['/api/auth', '/auth'],
});
export const config = {
matcher: [
// Match all paths except static files
'/((?!_next/static|_next/image|favicon.ico).*)',
]
}💡 What's happening here:
- The
withOAuth42Authmiddleware protects routes by checking for a valid session - Unauthenticated users are redirected to OAuth42's hosted login page automatically
- The
publicPathsoption excludes certain paths from authentication and token refresh - If you use
cookiePrefixincreateAuth(), you must use the same value here
✨ Automatic Token Refresh:
- The middleware automatically refreshes expired access tokens
- Uses secure refresh token rotation - each refresh issues a new refresh token and invalidates the old one
- Users stay logged in seamlessly without re-authenticating (as long as the refresh token is valid - 6 months by default)
- Works transparently on every navigation - no client-side polling needed
⚠️ Important: The middleware runs on the Edge runtime. Always import from @oauth42/next/middleware (not /server) to avoid Node.js module errors.
🚨 Critical: Never put '/api' in publicPaths!
If you add '/api' to publicPaths, token refresh will be skipped for ALL your API routes, causing 401 errors when tokens expire. Only '/api/auth' should be public (for NextAuth internal routes).
Multi-App Cookie Isolation (Optional)
If you're running multiple Next.js apps on the same domain (e.g., multiple apps on localhost during development), you'll need to use unique cookie prefixes to prevent session conflicts.
Why this matters:
- By default, NextAuth uses the same cookie names for all apps
- When apps share a domain, logging into one app can log you out of another
- The
cookiePrefixoption gives each app unique cookie names
Step 1: Add cookiePrefix to your auth configuration:
// app/api/auth/[...nextauth]/route.ts
import { createAuth } from '@oauth42/next/server';
const { auth: authOptions, handlers } = createAuth({
clientId: process.env.OAUTH42_CLIENT_ID,
clientSecret: process.env.OAUTH42_CLIENT_SECRET,
issuer: process.env.OAUTH42_ISSUER,
cookiePrefix: 'myapp', // Unique prefix for this app
});
export { authOptions };
export const { GET, POST } = handlers;Step 2: Use the same prefix in your middleware:
// middleware.ts
import { withOAuth42Auth } from '@oauth42/next/middleware';
export default withOAuth42Auth({
cookiePrefix: 'myapp', // Must match the prefix in createAuth()
// IMPORTANT: Only '/api/auth' should be public, NOT '/api'!
publicPaths: ['/api/auth', '/auth'],
});
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}Example: Running 3 apps on localhost
| App | Port | Cookie Prefix |
|---|---|---|
| Portal | 3000 | portal |
| Admin | 3001 | admin |
| Customer App | 3002 | customer |
With unique prefixes, you can be logged in as different users in each app simultaneously!
Client-Side Session Access (Optional)
If you need client-side session access (e.g., using the useSession hook), wrap your app with SessionProvider:
// app/providers.tsx
"use client"
import { SessionProvider } from "@oauth42/next/client"
export default function Providers({
children,
}: {
children: React.ReactNode
}) {
return (
<SessionProvider
refetchInterval={0}
refetchOnWindowFocus={false}
>
{children}
</SessionProvider>
)
}Then use it in your app/layout.tsx:
import Providers from "./providers"
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}⚠️ Important: Disable background polling
- Set
refetchInterval={0}to disable background session polling - Token refresh is handled by middleware (on navigation), not client-side polling
- Background polling can cause issues with OAuth42's secure refresh token rotation
Step 8: Create a Protected Dashboard Page
Create app/dashboard/page.tsx:
import { getServerSession } from "next-auth/next"
import { authOptions } from "@/app/api/auth/[...nextauth]/route"
import { redirect } from "next/navigation"
export default async function Dashboard() {
const session = await getServerSession(authOptions)
if (!session) {
redirect("/api/auth/signin")
}
return (
<div className="min-h-screen bg-gray-100 py-12 px-4">
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold text-gray-900 mb-8">
Welcome, {session.user?.name || session.user?.email}!
</h1>
<div className="bg-white shadow rounded-lg p-6">
<h2 className="text-xl font-semibold mb-4">Profile Information</h2>
<dl className="space-y-2">
<div>
<dt className="text-sm font-medium text-gray-500">Email</dt>
<dd className="text-sm text-gray-900">{session.user?.email}</dd>
</div>
<div>
<dt className="text-sm font-medium text-gray-500">Name</dt>
<dd className="text-sm text-gray-900">{session.user?.name}</dd>
</div>
</dl>
</div>
</div>
</div>
)
}Step 9: Add Sign In Button to Home Page
Update app/page.tsx:
💡 Pro Tip: You can use the redirectToHostedAuth() helper from the SDK for a more direct integration.
Option 1: Using NextAuth (Recommended for most cases)
"use client"
import { signIn } from "@oauth42/next/client"
export default function Home() {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow">
<div className="text-center">
<h1 className="text-3xl font-bold text-gray-900">Welcome</h1>
<p className="mt-2 text-gray-600">
Sign in with OAuth42 to continue
</p>
</div>
<button
onClick={() => signIn("oauth42", { callbackUrl: "/dashboard" })}
className="w-full py-3 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors"
>
Sign in with OAuth42
</button>
</div>
</div>
)
}Option 2: Using OAuth42 SDK Helper (Direct approach)
"use client"
import { redirectToHostedAuth } from "@oauth42/next/client"
export default function Home() {
const handleSignIn = () => {
redirectToHostedAuth({
clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,
redirectUri: `${window.location.origin}/api/oauth/callback`,
scope: "openid profile email",
issuer: process.env.NEXT_PUBLIC_OAUTH42_ISSUER || "https://api.oauth42.com"
})
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow">
<div className="text-center">
<h1 className="text-3xl font-bold text-gray-900">Welcome</h1>
<p className="mt-2 text-gray-600">
Sign in with OAuth42 to continue
</p>
</div>
<button
onClick={handleSignIn}
className="w-full py-3 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors"
>
Sign in with OAuth42
</button>
</div>
</div>
)
}Step 10: Add Logout Button
Create a logout button component that properly clears the user's session. Create app/components/logout-button.tsx:
'use client';
import { logout } from '@oauth42/next/client';
export default function LogoutButton() {
const handleLogout = async () => {
await logout({
callbackUrl: '/login',
clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID,
issuer: process.env.NEXT_PUBLIC_OAUTH_ISSUER,
});
};
return (
<button
onClick={handleLogout}
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg transition-colors"
>
Sign Out
</button>
);
}⚠️ Important: Make sure to add these environment variables to your .env.local file for client-side access:
NEXT_PUBLIC_OAUTH42_CLIENT_ID=your_client_id_here
NEXT_PUBLIC_OAUTH_ISSUER=https://api.oauth42.com💡 Why use the SDK's logout instead of NextAuth's signOut?
- The SDK's
logoutfunction clears the user's app selection from OAuth42's session registry - This enables the account picker to show on next login (Google-style multi-account support)
- Using NextAuth's
signOutdirectly only clears the local session, so the next login auto-selects the previous account - The SDK handles both OAuth42 logout and NextAuth session cleanup automatically
Now add the logout button to your dashboard page:
import { getServerSession } from "next-auth/next"
import { authOptions } from "@/app/api/auth/[...nextauth]/route"
import { redirect } from "next/navigation"
import LogoutButton from "@/app/components/logout-button"
export default async function Dashboard() {
const session = await getServerSession(authOptions)
if (!session) {
redirect("/login")
}
return (
<div className="min-h-screen bg-gray-100 py-12 px-4">
<div className="max-w-4xl mx-auto">
<div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold text-gray-900">
Welcome, {session.user?.name || session.user?.email}!
</h1>
<LogoutButton />
</div>
{/* ... rest of dashboard content */}
</div>
</div>
)
}Step 11: Test Your Application
- Start your development server:
npm run dev - Open http://localhost:3000 in your browser
- Click "Sign in with OAuth42"
- You'll be redirected to OAuth42's hosted login page
- Sign in using your OAuth42 account credentials
- After successful authentication, you'll be redirected back to your dashboard at
localhost:3000/dashboard
✅ Success! If you see your dashboard with the user's email and name displayed, OAuth42 hosted authentication is working correctly!
Customizing Your Hosted Auth Pages
You can customize the look and feel of your hosted auth pages from the OAuth42 Portal:
- Go to Applications → Your App → Hosted Auth Settings
- Upload your logo
- Set your primary and accent colors
- Configure features (signup, password reset, MFA, etc.)
- Set password requirements
Next Steps
Need Help?
Join our Discord community or check out the documentation for more detailed guides.