Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ShaikhZayan/ffb16b87baef36519b12c9856768fca1 to your computer and use it in GitHub Desktop.
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.

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:

1. Set Up FastAPI with OAuth2

First, you'll need to set up FastAPI with OAuth2 for authentication. Here's a basic example:

FastAPI OAuth2 Setup

  1. Install the necessary dependencies:

    pip install fastapi uvicorn python-jose passlib
  2. 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

2. Integrate with Next.js

Next.js Authentication

  1. Install the necessary dependencies:

    npm install next-auth axios
  2. 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',
      },
    });
  3. 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>
      );
    }
  4. 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();

3. Secure Next.js Routes

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 },
  };
};

Summary

  • 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!

@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