Authentication Overview
This application uses HTTP Basic Authentication to secure communication between the Next.js frontend and the Bun/Hono backend. All API requests include Base64-encoded credentials in the Authorization header.
Basic Authentication is suitable for this use case since the application runs over HTTPS and credentials are stored securely in environment variables.
How Basic Auth Works
Basic Authentication follows this pattern:
Credentials Storage
Username and password are stored in .env file as environment variables
Base64 Encoding
Credentials are combined as username:password and Base64-encoded
Authorization Header
Encoded credentials are sent in Authorization: Basic <encoded> header
Backend Verification
Backend decodes and verifies credentials before processing the request
Environment Variables
Credentials are stored in the .env file:
BACKEND_URL = http://localhost:3000
USERNAME = your_username
PASSWORD = your_password
Never commit the .env file to version control. Always add it to .gitignore.
Authentication in Server Components
Server Components can directly access environment variables and add authentication headers:
From src/components/Messages.tsx:29-35:
export default async function Messages () {
const data = await fetch ( ` ${ process . env . BACKEND_URL } /messages/all` , {
headers: {
Authorization: `Basic ${ Buffer . from (
` ${ process . env . USERNAME } : ${ process . env . PASSWORD } `
). toString ( "base64" ) } ` ,
},
});
if ( ! data . ok ) {
notFound ();
}
const messages = await data . json ();
}
From src/components/People.tsx:17-23:
export default async function People () {
const data = await fetch ( ` ${ process . env . BACKEND_URL } /people/all` , {
headers: {
Authorization: `Basic ${ Buffer . from (
` ${ process . env . USERNAME } : ${ process . env . PASSWORD } `
). toString ( "base64" ) } ` ,
},
});
}
Base64 Encoding Pattern
The encoding pattern is consistent across the application:
Authorization : `Basic ${ Buffer . from (
` ${ process . env . USERNAME } : ${ process . env . PASSWORD } `
). toString ( "base64" ) } `
This creates a header like:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Authentication in Server Actions
All Server Actions include authentication when making backend requests:
From src/lib/actions/message/createMessage.ts:39-51:
"use server" ;
export default async function createMessage (
prevState : MessagePrevState ,
formData : FormData
) : Promise < MessagePrevState > {
try {
const response = await fetch (
` ${ process . env . BACKEND_URL } /messages/create-one` ,
{
method: "POST" ,
headers: {
Authorization: `Basic ${ Buffer . from (
` ${ process . env . USERNAME } : ${ process . env . PASSWORD } `
). toString ( "base64" ) } ` ,
"Content-Type" : "application/json" ,
},
body: JSON . stringify ( parsedData . data ),
}
);
if ( ! response . ok ) {
return {
error: `Backend Error: ${ response . status } ${ response . statusText } ` ,
};
}
} catch ( error ) {
// Error handling
}
}
From src/lib/actions/people/createPerson.ts:35-47:
const response = await fetch (
` ${ process . env . BACKEND_URL } /people/create-one` ,
{
method: "POST" ,
headers: {
Authorization: `Basic ${ Buffer . from (
` ${ process . env . USERNAME } : ${ process . env . PASSWORD } `
). toString ( "base64" ) } ` ,
"Content-Type" : "application/json" ,
},
body: JSON . stringify ( parsedData . data ),
}
);
From src/lib/actions/reschedule/reschedule.ts:13-19:
const response = await fetch ( ` ${ process . env . BACKEND_URL } /messages/reschedule` , {
method: "GET" ,
headers: {
Authorization: `Basic ${ Buffer . from (
` ${ process . env . USERNAME } : ${ process . env . PASSWORD } `
). toString ( "base64" ) } ` ,
},
})
Security Considerations
Environment Variables are Server-Only
Environment variables prefixed without NEXT_PUBLIC_ are only available on the server . They are never exposed to the client.
This means:
✅ Safe: Access in Server Components
export default async function ServerComponent () {
const response = await fetch ( process . env . BACKEND_URL );
}
✅ Safe: Access in Server Actions
"use server" ;
export default async function serverAction () {
const url = process . env . BACKEND_URL ;
}
❌ Not Possible: Access in Client Components
"use client" ;
export default function ClientComponent () {
// process.env.BACKEND_URL is undefined here!
const response = await fetch ( process . env . BACKEND_URL );
}
HTTPS in Production
Basic Authentication sends credentials in every request. Always use HTTPS in production to encrypt these headers.
Development
HTTP is acceptable for local development (localhost)
Production
Always use HTTPS to encrypt authentication headers in transit
Deployment
Configure your hosting platform (Vercel, Railway, etc.) to enforce HTTPS
Error Handling
All authentication failures should be handled gracefully:
From src/components/Messages.tsx:36-38:
if ( ! data . ok ) {
notFound ();
}
From src/lib/actions/message/createMessage.ts:53-57:
if ( ! response . ok ) {
return {
error: `Backend Error: ${ response . status } ${ response . statusText } ` ,
};
}
Backend Validation
The Bun/Hono backend must validate the authentication header:
// Example backend validation (not from this repo)
app . use ( '*' , async ( c , next ) => {
const authHeader = c . req . header ( 'Authorization' );
if ( ! authHeader ?. startsWith ( 'Basic ' )) {
return c . json ({ error: 'Unauthorized' }, 401 );
}
const credentials = Buffer . from (
authHeader . slice ( 6 ),
'base64'
). toString ();
const [ username , password ] = credentials . split ( ':' );
if ( username !== expectedUsername || password !== expectedPassword ) {
return c . json ({ error: 'Invalid credentials' }, 401 );
}
await next ();
});
Complete Authentication Flow
Authentication Patterns by Component Type
Server Components directly access environment variables: export default async function ServerComponent () {
const response = await fetch ( ` ${ process . env . BACKEND_URL } /endpoint` , {
headers: {
Authorization: `Basic ${ Buffer . from (
` ${ process . env . USERNAME } : ${ process . env . PASSWORD } `
). toString ( "base64" ) } ` ,
},
});
const data = await response . json ();
return < div > { /* Render data */ } </ div > ;
}
Server Actions (with FormData)
Server Actions (without FormData)
Creating an Authentication Utility
To reduce code duplication, create a utility function:
// src/lib/auth.ts
export function getAuthHeader () {
return {
Authorization: `Basic ${ Buffer . from (
` ${ process . env . USERNAME } : ${ process . env . PASSWORD } `
). toString ( "base64" ) } ` ,
};
}
Then use it in your components and actions:
import { getAuthHeader } from "@/lib/auth" ;
const response = await fetch ( ` ${ process . env . BACKEND_URL } /endpoint` , {
headers: {
... getAuthHeader (),
"Content-Type" : "application/json" ,
},
});
This utility function must be imported only in server-side code (Server Components and Server Actions).
Troubleshooting
Possible causes:
Incorrect username or password in .env
Backend not properly decoding the Authorization header
Credentials not matching backend expectations
Solution:
Verify .env file has correct credentials
Check backend authentication middleware
Test credentials with curl:
curl -H "Authorization: Basic $( echo -n 'username:password' | base64 )" \
http://localhost:3000/messages/all
Environment Variables Undefined
Possible causes:
Accessing environment variables in Client Components
.env file not in project root
Server not restarted after changing .env
Solution:
Only access process.env in Server Components and Server Actions
Ensure .env is in project root (same level as package.json)
Restart the development server: npm run dev
Possible causes:
Backend not configured to accept requests from frontend origin
Missing CORS headers in backend responses
Solution:
Configure CORS in your Hono backend:import { cors } from 'hono/cors' ;
app . use ( '*' , cors ({
origin: [ 'http://localhost:3001' ],
credentials: true ,
}));
Security Best Practices
Environment Variables Store credentials in .env and never commit to version control
HTTPS Only Always use HTTPS in production to encrypt authentication headers
Server-Side Only Only access credentials in Server Components and Server Actions
Error Messages Don’t expose sensitive details in error messages returned to client
Next Steps
Server Actions Learn how to implement server-side mutations
Configuration Configure environment variables for production