Skip to main content
This example demonstrates how to integrate logto-authkit into a Next.js application with both client and server-side authentication.

Complete Example

1

Install Dependencies

Install logto-authkit and required dependencies:
npm install @ouim/logto-authkit @logto/react
2

Create Root Layout

Set up the auth provider in your root layout:
app/layout.tsx
import { AuthProvider } from '@ouim/logto-authkit'
import './globals.css'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <AuthProvider
          config={{
            endpoint: process.env.NEXT_PUBLIC_LOGTO_ENDPOINT!,
            appId: process.env.NEXT_PUBLIC_LOGTO_APP_ID!,
            resources: [process.env.NEXT_PUBLIC_API_RESOURCE!],
            scopes: ['openid', 'profile', 'email'],
          }}
          callbackUrl={`${process.env.NEXT_PUBLIC_APP_URL}/callback`}
          enablePopupSignIn={true}
        >
          {children}
        </AuthProvider>
      </body>
    </html>
  )
}
3

Create Home Page

Build your landing page with authentication:
app/page.tsx
'use client'

import { useAuth, UserCenter } from '@ouim/logto-authkit'
import Link from 'next/link'

export default function Home() {
  const { user, isLoadingUser, signIn } = useAuth()

  return (
    <div className="min-h-screen bg-gradient-to-b from-gray-50 to-gray-100">
      <nav className="bg-white shadow-sm">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
          <div className="flex justify-between h-16">
            <div className="flex items-center">
              <Link href="/" className="text-xl font-bold text-gray-900">
                logto-authkit App
              </Link>
            </div>
            <div className="flex items-center gap-4">
              {user && (
                <Link
                  href="/dashboard"
                  className="text-gray-700 hover:text-gray-900"
                >
                  Dashboard
                </Link>
              )}
              <UserCenter />
            </div>
          </div>
        </div>
      </nav>

      <main className="max-w-7xl mx-auto py-16 px-4 sm:px-6 lg:px-8">
        <div className="text-center">
          <h1 className="text-5xl font-bold text-gray-900 mb-6">
            Welcome to logto-authkit
          </h1>
          <p className="text-xl text-gray-600 mb-8 max-w-2xl mx-auto">
            A simplified authentication solution for Next.js applications.
            Get started with secure, production-ready auth in minutes.
          </p>

          {isLoadingUser ? (
            <div className="flex justify-center">
              <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
            </div>
          ) : user ? (
            <div className="space-y-4">
              <div className="bg-white rounded-lg shadow-md p-6 max-w-md mx-auto">
                <p className="text-lg font-semibold text-gray-900 mb-2">
                  Welcome back, {user.name || user.email}!
                </p>
                <p className="text-gray-600">
                  You are successfully authenticated.
                </p>
              </div>
              <Link
                href="/dashboard"
                className="inline-block bg-blue-600 text-white px-8 py-3 rounded-lg hover:bg-blue-700 font-semibold"
              >
                Go to Dashboard
              </Link>
            </div>
          ) : (
            <div className="space-y-4">
              <button
                onClick={() => signIn()}
                className="bg-blue-600 text-white px-8 py-3 rounded-lg hover:bg-blue-700 font-semibold"
              >
                Sign In
              </button>
              <p className="text-sm text-gray-500">
                Sign in to access your personalized dashboard
              </p>
            </div>
          )}
        </div>
      </main>
    </div>
  )
}
4

Create Protected Dashboard

Build a dashboard with automatic authentication protection:
app/dashboard/page.tsx
'use client'

import { useAuth, UserCenter } from '@ouim/logto-authkit'
import Link from 'next/link'

export default function Dashboard() {
  const { user, isLoadingUser } = useAuth({
    middleware: 'auth',
    redirectTo: '/signin',
  })

  if (isLoadingUser) {
    return (
      <div className="flex items-center justify-center min-h-screen">
        <div className="animate-spin rounded-full h-16 w-16 border-b-2 border-blue-600"></div>
      </div>
    )
  }

  return (
    <div className="min-h-screen bg-gray-50">
      <nav className="bg-white shadow-sm">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
          <div className="flex justify-between h-16">
            <div className="flex items-center">
              <Link href="/" className="text-xl font-bold text-gray-900">
                logto-authkit App
              </Link>
            </div>
            <div className="flex items-center">
              <UserCenter />
            </div>
          </div>
        </div>
      </nav>

      <main className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
        <div className="mb-8">
          <h1 className="text-3xl font-bold text-gray-900">Dashboard</h1>
          <p className="text-gray-600 mt-2">Welcome to your protected dashboard</p>
        </div>

        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          <div className="bg-white rounded-lg shadow p-6">
            <h2 className="text-xl font-semibold mb-4 text-gray-900">
              User Profile
            </h2>
            <dl className="space-y-3">
              <div>
                <dt className="text-sm font-medium text-gray-500">User ID</dt>
                <dd className="mt-1 text-sm text-gray-900 font-mono">{user?.id}</dd>
              </div>
              <div>
                <dt className="text-sm font-medium text-gray-500">Name</dt>
                <dd className="mt-1 text-sm text-gray-900">
                  {user?.name || 'Not provided'}
                </dd>
              </div>
              <div>
                <dt className="text-sm font-medium text-gray-500">Email</dt>
                <dd className="mt-1 text-sm text-gray-900">
                  {user?.email || 'Not provided'}
                </dd>
              </div>
            </dl>
          </div>

          <div className="bg-white rounded-lg shadow p-6">
            <h2 className="text-xl font-semibold mb-4 text-gray-900">
              Authentication Status
            </h2>
            <div className="space-y-3">
              <div className="flex items-center">
                <div className="w-3 h-3 bg-green-500 rounded-full mr-3"></div>
                <span className="text-sm text-gray-900">Authenticated</span>
              </div>
              <p className="text-sm text-gray-600">
                You have successfully authenticated and can access protected
                resources.
              </p>
              <Link
                href="/api/user"
                target="_blank"
                className="inline-block text-sm text-blue-600 hover:text-blue-800"
              >
                View API Response →
              </Link>
            </div>
          </div>
        </div>
      </main>
    </div>
  )
}
5

Create API Route with Server-Side Auth

Add a protected API route using server-side authentication:
app/api/user/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { verifyNextAuth } from '@ouim/logto-authkit/server'

export async function GET(request: NextRequest) {
  const result = await verifyNextAuth(request, {
    logtoUrl: process.env.LOGTO_ENDPOINT!,
    audience: process.env.LOGTO_API_RESOURCE!,
  })

  if (!result.success) {
    return NextResponse.json(
      { error: result.error },
      { status: 401 }
    )
  }

  // Access authenticated user info
  const { auth } = result

  return NextResponse.json({
    userId: auth.userId,
    isAuthenticated: auth.isAuthenticated,
    payload: auth.payload,
  })
}
6

Add Authentication Pages

Create the required callback and sign-in pages:
'use client'

import { CallbackPage } from '@ouim/logto-authkit'

export default function Callback() {
  return (
    <CallbackPage
      onSuccess={() => {
        console.log('Authentication successful')
      }}
      onError={(error) => {
        console.error('Authentication failed:', error)
      }}
    />
  )
}
7

Configure Environment Variables

Create a .env.local file with your Logto configuration:
.env.local
NEXT_PUBLIC_LOGTO_ENDPOINT=https://your-tenant.logto.app
NEXT_PUBLIC_LOGTO_APP_ID=your-app-id
NEXT_PUBLIC_API_RESOURCE=https://api.yourapp.com
NEXT_PUBLIC_APP_URL=http://localhost:3000

# Server-side variables
LOGTO_ENDPOINT=https://your-tenant.logto.app
LOGTO_API_RESOURCE=https://api.yourapp.com

Server-Side Authentication

logto-authkit supports server-side authentication for API routes and middleware:
app/api/protected/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { verifyNextAuth } from '@ouim/logto-authkit/server'

export async function GET(request: NextRequest) {
  const result = await verifyNextAuth(request, {
    logtoUrl: process.env.LOGTO_ENDPOINT!,
    audience: process.env.LOGTO_API_RESOURCE!,
  })

  if (!result.success) {
    return NextResponse.json(
      { error: 'Unauthorized' },
      { status: 401 }
    )
  }

  return NextResponse.json({
    message: 'Protected data',
    userId: result.auth.userId,
  })
}

Features Demonstrated

  • Next.js App Router: Full integration with App Router
  • Client-Side Auth: Protected pages with automatic redirects
  • Server-Side Auth: Secure API routes with JWT verification
  • Environment Variables: Proper configuration management
  • Popup Sign-In: Optional popup-based authentication
  • User Center: Pre-built UI component for user management

Running the Example

npm run dev
Visit http://localhost:3000 to see your Next.js app with authentication.

Next Steps

Protected Routes

Advanced route protection patterns

Server Integration

Learn more about server-side auth