Handling user authentication in a React Server-Side Rendering (SSR) application involves several steps:
Step 1: Configure State Management
Create a `provider.js` file to manage state across the application. This file should wrap the `SessionProvider` from `next-auth/react` to make session data available to all components.javascript
// provider.js
import { SessionProvider } from "next-auth/react";
export default function Providers({ children }) {
return <SessionProvider>{children}</SessionProvider>;
}
Step 2: Implement Authentication Logic
Create a login function in your `apiServices` that handles authentication and returns user information if successful.javascript
// apiServices.js
import axios from "axios";
const login = async (username, password) => {
const response = await axios.post("/api/auth/login", { username, password });
if (response.data) {
return response.data;
} else {
return null;
}
};
export { login };
Step 3: Access Session Data in Components
Use the `useSession` hook from `next-auth/react` to access the session data within components.javascript
// components/Login.js
import { useSession } from "next-auth/react";
export default function Login() {
const { data: session, status } = useSession();
if (status === "authenticated") {
return <h1>Welcome {session.user.name}!</h1>;
} else {
return <p>You are not logged in.</p>;
}
}
Step 4: Handle Authentication on the Client Side
Use the `auth` function from your `apiServices` to handle authentication on the client side.javascript
// components/Login.js
import { useState } from "react";
import { auth } from "@/services/apiServices";
export default function Login() {
const [input, setInput] = useState({ username: "", password: "" });
const handleSubmit = (e) => {
e.preventDefault();
if (input.username !== "" && input.password !== "") {
auth.loginAuth(input);
return;
}
alert("Please provide a valid input");
};
return (
<form onSubmit={handleSubmit}>
<label>Username: </label>
<br />
<input type="text" onChange={(e) => setInput({ ...input, username: e.target.value })} />
<br />
<label>Password: </label>
<br />
<input type="password" onChange={(e) => setInput({ ...input, password: e.target.value })} />
<br />
<button type="submit">Login</button>
</form>
);
}
Step 5: Access Session Data in Server-Side Components
Use the `getServerSession` function from `next-auth/next` to fetch the user session on the server side.javascript
// pages/api/public/page.js
import { getServerSession } from "next-auth/next";
import { GetServerSidePropsContext } from "next";
import { login } from "@/services/apiServices";
export default function Protected({ user }) {
return (
<div>
<div>
{user ? (
<h1>Hi {user.name}!</h1>
) : (
<a href="/api/auth/signin">Sign in</a>
)}
</div>
</div>
);
}
export async function getServerSideProps(context) {
const session = await getServerSession(context);
const user = session ? session.user : null;
return {
props: {
user,
},
};
}
Step 6: Configure API Routes
Protect API routes using `getServerSession` to ensure that only authenticated users can access them.javascript
// pages/api/secure-api.js
import { authOptions } from './auth/[...nextauth]';
import { getServerSession } from "next-auth/next";
export default async (req, res) => {
const session = await getServerSession(req, res, authOptions);
if (session) {
res.status(200).json({
name: "John Doe",
message: "Welcome authenticated user",
});
} else {
res.status(403).json({
error: "You must sign-in to view the content on this page.",
});
};
};
Step 7: Integrate NextAuth
Integrate NextAuth into your application by creating an `auth` directory and adding the necessary files.javascript
// auth/[...nextauth].js
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
export default NextAuth({
// Configure one or more authentication providers
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
});
Step 8: Set Up API Routes
Set up API routes to handle authentication and authorization.javascript
// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
export default NextAuth({
// Configure one or more authentication providers
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
});
Step 9: Use NextAuth in Components
Use the `useSession` hook to access the session data within components.javascript
// components/Login.js
import { useSession } from "next-auth/react";
export default function Login() {
const { data: session, status } = useSession();
if (status === "authenticated") {
return <h1>Welcome {session.user.name}!</h1>;
} else {
return <p>You are not logged in.</p>;
}
}
Step 10: Handle Authentication on the Server Side
Handle authentication on the server side by using `getServerSession` to fetch the user session.javascript
// pages/api/public/page.js
import { getServerSession } from "next-auth/next";
import { GetServerSidePropsContext } from "next";
import { login } from "@/services/apiServices";
export default function Protected({ user }) {
return (
<div>
<div>
{user ? (
<h1>Hi {user.name}!</h1>
) : (
<a href="/api/auth/signin">Sign in</a>
)}
</div>
</div>
);
}
export async function getServerSideProps(context) {
const session = await getServerSession(context);
const user = session ? session.user : null;
return {
props: {
user,
},
};
}
By following these steps, you can effectively handle user authentication in a React SSR application using Next.js and `next-auth`. This setup ensures that authentication is handled both on the server and client sides, providing robust security and user management for your application[1][2][3][5].
Citations:[1] https://stackoverflow.com/questions/60799741/next-js-authentication-strategies
[2] https://blog.openreplay.com/authentication-and-authorization-with-ssr-in-nextjs/
[3] https://buttercms.com/blog/nextjs-authentication-tutorial/
[4] https://www.reddit.com/r/nextjs/comments/v01uy1/why_should_i_use_next_other_than_ssr/
[5] https://github.com/nextauthjs/next-auth/discussions/3394