Skip to main content

A secure authentication and session management system for Django

Project description

SecureBite

SecureBite is a Django package that provides secure, cookie-based authentication and session management using JWT tokens. It integrates seamlessly with Django REST Framework (DRF) and SimpleJWT, offering a simple and secure authentication experience for SPAs and mobile apps.

It supports traditional username/password login, OAuth2 providers (Google, GitHub, etc.), automatic token refreshing, and logout handling — all with HTTP-only cookies for maximum security.


Features

  • Cookie-Based JWT Authentication – JWT tokens are stored in HTTP-only cookies to prevent XSS token theft.
  • Automatic Token Refreshing – Middleware refreshes access tokens before expiry.
  • Logout Support – Securely clears authentication cookies.
  • OAuth2 Login Support – Optional integration with external providers like Google, GitHub, etc.
  • User Serialization – Easily customize what user data is returned in /auth/me/.
  • Simple Integration – Works out of the box with minimal setup.

Installation

  1. Install required dependencies
pip install django djangorestframework djangorestframework-simplejwt djangorestframework-simplejwt[token_blacklist] secure_bite
  1. Add to settings.py
from datetime import timedelta

INSTALLED_APPS = [
    'secure_bite',
    'rest_framework',
    'rest_framework_simplejwt',
    'rest_framework_simplejwt.token_blacklist',
    'corsheaders',
    ...
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    ...
    'secure_bite.middleware.RefreshTokenMiddleware',  # SecureBite middleware
]

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "secure_bite.authentication.JWTAuthCookieAuthentication",
    ],
}

SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=15),
    "REFRESH_TOKEN_LIFETIME": timedelta(days=7),
    "ROTATE_REFRESH_TOKENS": True,
    "BLACKLIST_AFTER_ROTATION": True,
}

JWT_AUTH_COOKIE_SETTINGS = {
    "AUTH_COOKIE": "authToken",                 # Access token cookie name
    "REFRESH_COOKIE": "refreshToken",           # Refresh token cookie name
    "AUTH_COOKIE_HTTP_ONLY": True,              # Prevents JavaScript access
    "AUTH_COOKIE_SECURE": False,                # True in production (HTTPS)
    "AUTH_COOKIE_SAMESITE": "Lax",
    "USER_SERIALIZER": "secure_bite.serializers.UserSerializer",
}

URL Configuration

In your root urls.py:

from django.urls import path, include

urlpatterns = [
    path("auth/", include("secure_bite.urls", namespace="secure_bite")),
]

Usage

1. Login

POST /auth/login/

Authenticates a user and sets authToken and refreshToken cookies.

Request

{
  "email": "user@example.com",
  "password": "your_password"
}

Response

{
  "message": "Login successful"
}

2. OAuth2 Login

GET /auth/oauth_start//example.com/

Redirects the user to the OAuth provider (e.g., Google). After authentication, the user is redirected back with valid cookies set.

Supported providers (example):

  • /auth/oauth_start/google/example.com/
  • /auth/oauth_start/github/example.com/

3. Logout

POST /auth/logout/

Clears authentication cookies.

Response

{
  "message": "Logged out"
}

4. Get Current User

GET /auth/me/

Returns user details based on the active access token.

Response

{
  "id": 1,
  "email": "user@example.com"
}

5. Automatic Token Refresh

The middleware automatically refreshes tokens near expiry. No frontend logic is required — tokens are renewed silently via cookies.


Example React Integration

Below is a minimal React setup using Context API and Axios to work with SecureBite’s endpoints.


1. UserContext.js

import React, { createContext, useContext, useEffect, useState } from "react";
import axios from "axios";

const UserContext = createContext();

export const UserProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  const fetchUser = async () => {
    try {
      const res = await axios.get("/auth/me/", { withCredentials: true });
      setUser(res.data);
    } catch {
      setUser(null);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchUser();
  }, []);

  const login = async (email, password) => {
    await axios.post("/auth/login/", { email, password }, { withCredentials: true });
    await fetchUser();
  };

  const logout = async () => {
    await axios.post("/auth/logout/", {}, { withCredentials: true });
    setUser(null);
  };

  return (
    <UserContext.Provider value={{ user, loading, login, logout }}>
      {loading ? <div>Loading...</div> : children}
    </UserContext.Provider>
  );
};

export const useUser = () => useContext(UserContext);

2. Login.js

import React, { useState } from "react";
import { useUser } from "./UserContext";

const Login = () => {
  const { login } = useUser();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = async (e) => {
    e.preventDefault();
    await login(email, password);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" required />
      <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" required />
      <button type="submit">Login</button>
    </form>
  );
};

export default Login;

3. Profile.js

import React from "react";
import { useUser } from "./UserContext";

const Profile = () => {
  const { user, logout } = useUser();

  if (!user) return <div>Not logged in</div>;

  return (
    <div>
      <h2>Welcome, {user.email}</h2>
      <button onClick={logout}>Logout</button>
    </div>
  );
};

export default Profile;

4. App Setup

import React from "react";
import { UserProvider } from "./UserContext";
import Login from "./Login";
import Profile from "./Profile";

function App() {
  return (
    <UserProvider>
      <Profile />
      <Login />
    </UserProvider>
  );
}

export default App;

OAuth2 Integration (React)

SecureBite supports OAuth2 logins (Google, GitHub, etc.). The flow is simple: redirect the user to the provider, receive a callback, and set JWT cookies.


1. Redirect to Provider

Create a React button to redirect users:

import React from "react";

const OAuthLoginButton = ({ provider }) => {
  const handleLogin = () => {
    // Redirect user to backend OAuth endpoint
    window.location.href = `/auth/oauth_start/${provider}/example.com/`;
  };

  return (
    <button onClick={handleLogin}>
      Login with {provider.charAt(0).toUpperCase() + provider.slice(1)}
    </button>
  );
};

export default OAuthLoginButton;
  • provider can be "google", "github", etc.
  • Clicking the button sends the user to the backend OAuth login page.

2. OAuth Callback Handling

After the user authenticates with the provider, they are redirected back to your frontend (configured in the OAuth provider settings). SecureBite sets the JWT cookies automatically.

You can handle the frontend state like this:

import React, { useEffect } from "react";
import { useUser } from "./UserContext";
import { useNavigate } from "react-router-dom";

const OAuthCallback = () => {
  const { fetchUser } = useUser(); // from UserContext
  const navigate = useNavigate();

  useEffect(() => {
    // Fetch the authenticated user after OAuth callback
    const fetchAndRedirect = async () => {
      await fetchUser();
      navigate("/dashboard"); // Redirect to a protected route
    };
    fetchAndRedirect();
  }, [fetchUser, navigate]);

  return <div>Logging you in...</div>;
};

export default OAuthCallback;

Flow:

  1. User clicks Login with Google → Redirected to /auth/oauth_start/google/example.com/
  2. Authenticated by Google → Redirected back to your frontend callback URL
  3. SecureBite sets JWT cookies (authToken and refreshToken)
  4. React UserContext fetches user data → App renders protected pages

3. Example Routes Setup (React Router v6)

import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { UserProvider } from "./UserContext";
import Login from "./Login";
import Profile from "./Profile";
import OAuthCallback from "./OAuthCallback";

function App() {
  return (
    <UserProvider>
      <Router>
        <Routes>
          <Route path="/" element={<Login />} />
          <Route path="/profile" element={<Profile />} />
          <Route path="/oauth/callback" element={<OAuthCallback />} />
        </Routes>
      </Router>
    </UserProvider>
  );
}

export default App;

4. Key Considerations

  • CORS: Ensure your Django backend allows your frontend domain. Include withCredentials: true in Axios requests.
  • HTTPS: Required in production for secure cookies.
  • React State: After OAuth callback, always fetch the user via /auth/me/ to update the frontend state.
  • Logout: Works the same as standard login; JWT cookies are cleared by /auth/logout/.

This allows React apps to handle OAuth logins without manually dealing with tokens — SecureBite manages cookies automatically.



Security Tips

  • Use HTTPS in production to protect cookies.
  • Set AUTH_COOKIE_SECURE=True for production.
  • Enable CSRF protection when using POST requests.
  • Use token_blacklist to invalidate refresh tokens when users log out.

Contributing

We welcome contributions!

  1. Fork the repository
  2. Create a new branch (git checkout -b feature-branch)
  3. Commit your changes
  4. Push and open a pull request

License

This project is licensed under the Apache License 2.0. See the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

secure_bite-0.1.4.tar.gz (19.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

secure_bite-0.1.4-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

Details for the file secure_bite-0.1.4.tar.gz.

File metadata

  • Download URL: secure_bite-0.1.4.tar.gz
  • Upload date:
  • Size: 19.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.18

File hashes

Hashes for secure_bite-0.1.4.tar.gz
Algorithm Hash digest
SHA256 f7dd020ce218643364ef290b8e065bdfb4e81a1f1b1cb49daf22fbc70f7d6a1e
MD5 e122856e02accba49e9f2a8ae8ed10cf
BLAKE2b-256 ce2833b0625ac7bed2c913d716250f9840785613330af42df082560099aa29a0

See more details on using hashes here.

File details

Details for the file secure_bite-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: secure_bite-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.18

File hashes

Hashes for secure_bite-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 aa7f5c868c519a688012a4ec02c3aad54f4c1ef9243c44cc772f28af0b34fc09
MD5 3aef9d503e40b7d96a232319a7806f5c
BLAKE2b-256 119f6149d2797c6c7aeff193eb44b23496234f422dcfdf16b0d563a4f5519156

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page