You can use FastAPI with OAuth2 for authentication and integrate it with a Next.js frontend to create a login system. You can manage your backend logic and authentication in FastAPI and handle the frontend logic, including authorization, in Next.js. Here's a high-level overview of how you can achieve this:
First, you'll need to set up FastAPI with OAuth2 for authentication. Here's a basic example:
-
Install the necessary dependencies:
pip install fastapi uvicorn python-jose passlib
-
Create the FastAPI app:
from fastapi import FastAPI, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import JWTError, jwt from passlib.context import CryptContext from datetime import datetime, timedelta from typing import Optional, Dict app = FastAPI() # Secret key to encode and decode JWT tokens SECRET_KEY = "your_secret_key" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 # Password hashing context pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # OAuth2 scheme oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # In-memory user storage for demonstration purposes fake_users_db: Dict[str, Dict[str, str]] = { "[email protected]": { "username": "user", "full_name": "User Example", "email": "[email protected]", "hashed_password": pwd_context.hash("password"), "disabled": False, } } def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) def get_user(db: Dict[str, Dict[str, str]], email: str) -> Optional[Dict[str, str]]: if email in db: return db[email] def authenticate_user(fake_db: Dict[str, Dict[str, str]], email: str, password: str) -> Optional[Dict[str, str]]: user = get_user(fake_db, email) if not user: return None if not verify_password(password, user['hashed_password']): return None return user def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str: to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=15) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt @app.post("/token") async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()): user = authenticate_user(fake_users_db, form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": user['email']}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"} async def get_current_user(token: str = Depends(oauth2_scheme)) -> Optional[Dict[str, str]]: credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) email: str = payload.get("sub") if email is None: raise credentials_exception except JWTError: raise credentials_exception user = get_user(fake_users_db, email=email) if user is None: raise credentials_exception return user @app.get("/users/me") async def read_users_me(current_user: Dict[str, str] = Depends(get_current_user)): return current_user
-
Install the necessary dependencies:
npm install next-auth axios
-
Create an authentication provider in Next.js using
next-auth
:// pages/api/auth/[...nextauth].ts import NextAuth from 'next-auth'; import CredentialsProvider from 'next-auth/providers/credentials'; import axios from 'axios'; export default NextAuth({ providers: [ CredentialsProvider({ name: 'Credentials', credentials: { email: { label: 'Email', type: 'text' }, password: { label: 'Password', type: 'password' }, }, authorize: async (credentials) => { try { const res = await axios.post('http://localhost:8000/token', { username: credentials?.email, password: credentials?.password, }); if (res.status === 200) { return { token: res.data.access_token }; } else { return null; } } catch (error) { console.error(error); return null; } }, }), ], callbacks: { async jwt({ token, user }) { if (user) { token.accessToken = user.token; } return token; }, async session({ session, token }) { session.accessToken = token.accessToken; return session; }, }, pages: { signIn: '/auth/signin', }, });
-
Create a sign-in page:
// pages/auth/signin.tsx import { useState } from 'react'; import { signIn } from 'next-auth/react'; export default function SignIn() { const [email, setEmail] = useState<string>(''); const [password, setPassword] = useState<string>(''); const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); const result = await signIn('credentials', { redirect: false, email, password, }); if (result?.error) { // handle error console.error(result.error); } else { // redirect to home page window.location.href = '/'; } }; return ( <form onSubmit={handleSubmit}> <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} /> <input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} /> <button type="submit">Sign In</button> </form> ); }
-
Use the authentication token in your API calls:
import axios from 'axios'; import { useSession } from 'next-auth/react'; const fetchData = async () => { const { data: session } = useSession(); if (session) { const res = await axios.get('http://localhost:8000/users/me', { headers: { Authorization: `Bearer ${session.accessToken}`, }, }); console.log(res.data); } }; fetchData();
You can secure your Next.js routes by checking if the user is authenticated using the getSession
method from next-auth/react
.
// pages/protected.tsx
import { GetServerSideProps } from 'next';
import { getSession } from 'next-auth/react';
export default function ProtectedPage() {
return <div>Protected content</div>;
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getSession(context);
if (!session) {
return {
redirect: {
destination: '/auth/signin',
permanent: false,
},
};
}
return {
props: { session },
};
};
- FastAPI handles authentication, issuing JWT tokens for authenticated users.
- Next.js uses
next-auth
to manage authentication on the client side, obtaining tokens from FastAPI. - API calls from the frontend include the JWT token in the authorization header to access protected endpoints in FastAPI.
By following this setup, you can have a secure and functional login system with FastAPI as the backend and Next.js as the frontend.
Make Sure To Star The Repo If You Think It's Useful TY!
Thanks for sharing, password login works.
Have you tried logging in with Google OAuth via Gmail?