#Client Side Authentication
1 messages · Page 1 of 1 (latest)
Hi @past vigil. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
To implement a client-side authentication flow using an httpOnly cookie in Next.js, you need to set the authentication cookie on the server (not on the client), since httpOnly cookies cannot be accessed via JavaScript for security reasons. Here’s how the flow typically works:
- User submits login credentials from the client.
- Client sends credentials to a server endpoint (API Route or Route Handler).
- Server validates credentials and sets an httpOnly cookie in the response.
- Subsequent client requests automatically include the cookie.
Example: Setting an httpOnly Cookie on the Server
You can use a Route Handler or API Route to set the cookie:
import { cookies } from 'next/headers'
export async function POST(request: Request) {
// Validate user credentials here...
const sessionToken = 'your-session-token'
const cookieStore = await cookies()
cookieStore.set('session', sessionToken, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 7, // 1 week
})
return new Response('Logged in', { status: 200 })
}
Client-Side Flow
- The client (browser) never directly accesses the httpOnly cookie.
- To check authentication status, the client can call a protected API route or fetch user info; the server checks the cookie and responds accordingly.
- For logout, the client calls a server endpoint that clears the cookie.
Key Points
- httpOnly cookies must be set and cleared on the server.
- The client interacts with authentication state by making requests to the server, not by reading the cookie directly.
- This approach protects the cookie from XSS attacks, as JavaScript cannot access httpOnly cookies.
For more details and recommended options for setting cookies, refer to the Next.js documentation.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@past vigil Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@valid flower I am using an external backend api endpoint. This Approach looks like using proxy server.
Let me check the knowledge base for you again. Give me one moment! :books:
Yes, when you use a Next.js API Route or Route Handler to handle authentication and set httpOnly cookies, and your actual authentication logic (such as verifying credentials or issuing tokens) is performed by an external backend API, your Next.js server is effectively acting as a proxy between the client and your backend API.
This pattern is common and recommended for several reasons:
- Security: The httpOnly cookie is set by your Next.js server, so sensitive tokens are never exposed to the client-side JavaScript.
- Separation of concerns: Your backend API handles authentication logic, while Next.js manages session state and frontend rendering.
- Flexibility: You can add validation, logging, or additional logic in your Next.js Route Handler before forwarding requests to the backend.
The official Next.js documentation describes this approach as "proxying to a backend." You can use a Route Handler to forward requests to your backend API, optionally adding validation or other logic before proxying the request. Here’s an example from the docs:
export async function POST(request: Request, { params }) {
// ...validation logic...
const proxyRequest = new Request(proxyURL, request)
return fetch(proxyRequest)
}
So, your understanding is correct: this approach uses Next.js as a backend-for-frontend (BFF) proxy, which is a well-established pattern for handling authentication with external APIs.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@past vigil Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@valid flower Can you show me an authentication flow example code? Using it in server components and client components.
Let me check the knowledge base for you again. Give me one moment! :books:
Certainly! Here’s an example authentication flow using httpOnly cookies, showing how you can use it in both Server Components and Client Components in Next.js. This approach assumes you are proxying authentication requests through a Next.js Route Handler to your external backend API.
1. Route Handler: Authenticate and Set Cookie
This Route Handler receives login credentials from the client, forwards them to your backend API, and sets an httpOnly cookie if authentication succeeds.
// app/api/auth/login/route.ts
import { cookies } from 'next/headers'
export async function POST(request: Request) {
const { email, password } = await request.json()
// Forward credentials to your backend API
const backendRes = await fetch('<https://your-backend.com/api/login',> {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
if (!backendRes.ok) {
return new Response('Unauthorized', { status: 401 })
}
const { token } = await backendRes.json()
// Set httpOnly cookie
const cookieStore = cookies()
cookieStore.set('session', token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 7, // 1 week
})
return new Response('Logged in', { status: 200 })
}
See: Setting cookies (recommended options)
2. Client Component: Login Form
The client component submits credentials to the Route Handler.
// app/login/page.tsx
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
export default function LoginPage() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const router = useRouter()
async function handleSubmit(e) {
e.preventDefault()
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
if (res.ok) {
router.push('/profile')
} else {
alert('Login failed')
}
}
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={e => setEmail(e.target.value)} placeholder="Email" />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder="Password" />
<button type="submit">Login</button>
</form>
)
}
[See: Pages Router login example (same pattern applies)](<https://nextjs.org/docs/pages/guides/authentication#authentication>)
---
## 3. Server Component: Fetch Authenticated Data
In a Server Component, you can read the cookie and use it to fetch data from your backend API.
```typescript
// app/profile/page.tsx
import { cookies } from 'next/headers'
export default async function ProfilePage() {
const cookieStore = cookies()
const session = cookieStore.get('session')
// Use the session token to fetch user data from your backend API
const res = await fetch('<https://your-backend.com/api/profile',> {
headers: {
Authorization: `Bearer ${session?.value}`,
},
// Optionally, you can forward cookies as well if your backend expects them
})
if (!res.ok) {
return <div>Not authenticated</div>
}
const user = await res.json()
return <div>Welcome, {user.name}!</div>
}
See: Reading cookies in Server Components
Key Points:
- Set and clear httpOnly cookies only in Route Handlers or Server Actions.
- Client Components interact with authentication by making requests to these endpoints.
- Server Components can read cookies to fetch authenticated data, but cannot set or modify cookies directly.
If you need more advanced flows (refresh tokens, logout, etc.), the same pattern applies: always handle cookie modifications in Route Handlers or Server Actions, and use fetch from Client or Server Components to interact with those endpoints.
If you need a more specific example (e.g., with refresh tokens), let me know!
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@past vigil Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@valid flower This approach should create API routes that require authentication. I want to handle only setting and getting tokens in the api route.
Let me check the knowledge base for you again. Give me one moment! :books:
Yes, your understanding is correct: you can use API routes (or Route Handlers in the App Router) only for setting and getting tokens (i.e., handling authentication/session cookies), while your actual application logic and data fetching can remain elsewhere.
How to Structure This
- API Route/Route Handler: Handles login/logout, sets or clears the httpOnly cookie with the token.
- Other API Routes or Server Components: For protected data, simply read the cookie and forward the token to your backend API for authentication.
Example: Only Setting and Getting Tokens in API Route
Login Route Handler (sets token):
// app/api/auth/login/route.ts
import { cookies } from 'next/headers'
export async function POST(request: Request) {
const { token } = await request.json() // Assume token comes from backend API
const cookieStore = cookies()
cookieStore.set('session', token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 7,
})
return new Response('OK', { status: 200 })
}
See: Setting cookies (recommended options)
Logout Route Handler (clears token):
// app/api/auth/logout/route.ts
import { cookies } from 'next/headers'
export async function POST() {
const cookieStore = cookies()
cookieStore.delete('session')
return new Response('Logged out', { status: 200 })
}
See: Example for deleting cookies
Server Component: Reads Cookie and Forwards to Backend
import { cookies } from 'next/headers'
export default async function ProtectedPage() {
const cookieStore = cookies()
const session = cookieStore.get('session')
// Forward token to backend API
const res = await fetch('https://your-backend.com/protected', {
headers: { Authorization: Bearer ${session?.value} },
})
// ...handle response
}
[See: Reading cookies in Server Components](<https://nextjs.org/docs/app/guides/data-security>)
---
**Summary:**
You do not need to implement all authentication logic in your API routes. You can restrict your API routes to just setting and getting tokens (via cookies), and use those tokens in your Server Components or other API routes to authenticate requests to your backend API. This is a recommended and secure pattern in Next.js.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
@past vigil Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@valid flower Show me how to do this in a client componet.
Let me check the knowledge base for you again. Give me one moment! :books:
To handle authentication in a Client Component using httpOnly cookies in Next.js, you should:
- Send login credentials from the client to an API Route or Route Handler.
- Let the server set the httpOnly cookie.
- On the client, check authentication status by calling a protected API route or fetching user info.
You cannot access httpOnly cookies directly in the client component (for security reasons), but you can trigger authentication and check status by making requests.
Example: Login Flow in a Client Component
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
export default function LoginForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const router = useRouter()
async function handleSubmit(e) {
e.preventDefault()
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
if (res.ok) {
// The httpOnly cookie is set by the server
router.push('/profile')
} else {
alert('Login failed')
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
)
}
This pattern is shown in the Next.js documentation for authentication.
Key Points:
- The client component never accesses the httpOnly cookie directly.
- Authentication state is checked by making requests to protected endpoints; the server checks the cookie and responds accordingly.
- For logout, you can call a logout API route that clears the cookie.