Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ShaikhZayan/ffb16b87baef36519b12c9856768fca1 to your computer and use it in GitHub Desktop.

Select an option

Save ShaikhZayan/ffb16b87baef36519b12c9856768fca1 to your computer and use it in GitHub Desktop.
How You can use FastAPI with OAuth2 for authentication and integrate it with a Next.js frontend to create a login system.

Here's a formatted guide for adding a cookie-based authentication system to a Next.js application while integrating with FastAPI. You can include this in your GitHub README:


Cookie-Based Authentication System with Next.js and FastAPI

Adding a cookie-based authentication system to Next.js while integrating with FastAPI involves setting up both the backend and frontend to handle user signup, login, and authentication using cookies.

1. FastAPI Setup

FastAPI OAuth2 Setup with Signup

First, add signup functionality to your FastAPI app and configure OAuth2 to handle cookies.

Install dependencies:

pip install fastapi uvicorn python-jose passlib

FastAPI app with signup and OAuth2:

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
from pydantic import BaseModel

app = FastAPI()

SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# In-memory user storage for demonstration purposes
fake_users_db = {}

class User(BaseModel):
    username: str
    email: str
    full_name: Optional[str] = None
    disabled: Optional[bool] = None

class UserInDB(User):
    hashed_password: str

def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_user(db, email: str):
    if email in db:
        user_dict = db[email]
        return UserInDB(**user_dict)

def authenticate_user(fake_db, email: str, password: str):
    user = get_user(fake_db, email)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    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"}

@app.post("/signup")
async def signup(email: str, password: str, full_name: Optional[str] = None):
    if email in fake_users_db:
        raise HTTPException(status_code=400, detail="Email already registered")
    hashed_password = pwd_context.hash(password)
    fake_users_db[email] = {
        "username": email,
        "email": email,
        "full_name": full_name,
        "hashed_password": hashed_password,
        "disabled": False,
    }
    return {"message": "User created"}

async def get_current_user(token: str = Depends(oauth2_scheme)):
    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: User = Depends(get_current_user)):
    return current_user

2. Next.js Setup

Dependencies

Install the necessary packages:

npm install next-auth axios cookie

Create a next-auth configuration file:

// pages/api/auth/[...nextauth].js
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 { accessToken: res.data.access_token };
          } else {
            return null;
          }
        } catch (error) {
          console.error(error);
          return null;
        }
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.accessToken = user.accessToken;
      }
      return token;
    },
    async session({ session, token }) {
      session.accessToken = token.accessToken;
      return session;
    },
  },
  pages: {
    signIn: '/auth/signin',
    signUp: '/auth/signup',
  },
});

Create a signup page:

// pages/auth/signup.js
import { useState } from 'react';
import axios from 'axios';

export default function Signup() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [fullName, setFullName] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      await axios.post('http://localhost:8000/signup', { email, password, full_name: fullName });
      alert('Signup successful');
    } catch (error) {
      console.error(error);
      alert('Signup failed');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Full Name"
        value={fullName}
        onChange={(e) => setFullName(e.target.value)}
      />
      <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 Up</button>
    </form>
  );
}

Create a sign-in page:

// pages/auth/signin.js
import { useState } from 'react';
import { signIn } from 'next-auth/react';

export default function SignIn() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    const result = await signIn('credentials', {
      redirect: false,
      email,
      password,
    });

    if (result.error) {
      alert(result.error);
    } else {
      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>
  );
}

Handle authentication with cookies in API calls:

// utils/axios.js
import axios from 'axios';
import { getSession } from 'next-auth/react';

export const fetchWithAuth = async (url, options = {}) => {
  const session = await getSession();
  const headers = session ? { Authorization: `Bearer ${session.accessToken}` } : {};

  return axios(url, {
    ...options,
    headers: { ...headers, ...options.headers },
  });
};

Use the fetchWithAuth utility to make authenticated API calls:

// pages/protected.js
import { useEffect, useState } from 'react';
import { fetchWithAuth } from '../utils/axios';

export default function ProtectedPage() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetchWithAuth('http://localhost:8000/users/me');
        setData(response.data);
      } catch (error) {
        console.error(error);
      }
    };

    fetchData();
  }, []);

  return (
    <div>
      <h1>Protected Content</h1>
      {data ? <pre>{JSON.stringify(data, null, 2)}</

pre> : <p>Loading...</p>}
    </div>
  );
}

Summary

  • FastAPI: Manages signup and login, issuing JWT tokens.
  • Next.js: Utilizes next-auth for authentication and cookies, with dedicated signup and login pages.
  • Cookies: Stored and sent with each request, enabling authenticated API calls to FastAPI endpoints.

Feel free to customize further according to your needs!


You can copy this directly into your README file, and it should provide clear instructions for setting up cookie-based authentication between Next.js and FastAPI.

@hqman
Copy link

hqman commented May 26, 2025

Thanks for sharing, password login works.
Have you tried logging in with Google OAuth via Gmail?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment