django_session

package module
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 29, 2026 License: MIT Imports: 16 Imported by: 0

README

go-gin-django-session

A Go library for integrating Django session authentication with Gin web applications. This library allows your Go/Gin backend to authenticate users against Django sessions stored in PostgreSQL.

Note: This code was developed with the assistance of Claude AI.

Features

  • Tested with Django 4.2 - 6.0 - Should work with all modern Django versions
  • Django Session Validation - Validate Django sessions in Go applications
  • Session Signature Verification - Cryptographic verification using Django's signing algorithm
  • Fast Session Lookup - Optimized for high-performance with lazy decoding
  • Gin Middleware - Ready-to-use authentication middleware for Gin framework
  • User ID Extraction - Extract authenticated user ID from session data
  • Configurable - Support for custom session cookie names, max age, and logging

Requirements

  • Go 1.21 or higher
  • Django backend with database sessions
  • PostgreSQL database
  • pgx/v5 driver (compatibility with database/sql is NOT supported)
  • Gin web framework v1.9.1+

Installation

go get github.com/knrd/go-gin-django-session@v1.1.1

Quick Start

1. Basic Setup
package main

import (
    "context"
    "log"
    "os"
    
    "github.com/gin-gonic/gin"
    "github.com/jackc/pgx/v5/pgxpool"
    djsession "github.com/knrd/go-gin-django-session"
)

func main() {
    // Connect to Django's PostgreSQL database
    db, err := pgxpool.New(context.Background(), "postgres://django:secret@localhost:5432/djangodb")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // Create Django session client
    client, err := djsession.NewClient(djsession.ClientConfig{
        DB:                db,
        SecretKey:         "your-django-secret-key",
        SessionCookieName: "sessionid", // Django default
    })
    if err != nil {
        log.Fatal(err)
    }

    // Setup Gin router
    r := gin.Default()

    // Protected routes with Django session authentication
    protected := r.Group("/api")
    protected.Use(djsession.AuthMiddleware(djsession.MiddlewareConfig{
        Client:           client,
        LoginRedirectURL: "/account/login",
    }))
    
    protected.GET("/dashboard", func(c *gin.Context) {
        // Get raw session from context
        rawSession := c.MustGet("django_session").(*djsession.RawSession)
        
        // Decode user ID only when needed
        userID, err := client.DecodeSessionUserID(rawSession.SessionData)
        if err != nil {
            c.JSON(500, gin.H{"error": "Failed to decode session"})
            return
        }
        
        c.JSON(200, gin.H{
            "message": "Welcome!",
            "user_id": userID,
        })
    })

    r.Run(":8080")
}
2. With Custom Configuration
client, err := djsession.NewClient(djsession.ClientConfig{
    DB:                db,
    SecretKey:         os.Getenv("DJANGO_SECRET_KEY"),
    SessionCookieName: "sessionid",
    MaxAge:            24 * time.Hour, // Optional: validate session age
})
3. Custom Error Handling
authMiddleware := djsession.AuthMiddleware(djsession.MiddlewareConfig{
    Client: client,
    OnError: func(c *gin.Context, err error) {
        c.JSON(401, gin.H{
            "error": "Authentication failed",
            "detail": err.Error(),
        })
        c.Abort()
    },
})
4. Optional Authentication (for mixed public/private views)

Use OptionalAuthMiddleware for routes that should work for both authenticated and anonymous users:

// Routes accessible to everyone (logged in or not)
r.GET("/home", djsession.OptionalAuthMiddleware(djsession.MiddlewareConfig{
    Client: client,
}), func(c *gin.Context) {
    // Check if user is authenticated
    rawSession, exists := c.Get("django_session")
    
    if exists {
        // User is logged in
        session := rawSession.(*djsession.RawSession)
        userID, _ := client.DecodeSessionUserID(session.SessionData)
        c.JSON(200, gin.H{
            "message": "Welcome back!",
            "user_id": userID,
            "authenticated": true,
        })
    } else {
        // Anonymous user
        c.JSON(200, gin.H{
            "message": "Welcome, guest!",
            "authenticated": false,
        })
    }
})

API Reference

Client
NewClient(config ClientConfig) (*Client, error)

Creates a new Django session client.

Parameters:

  • DB (DBTX) - Database connection (required) - Compatible with *pgxpool.Pool
  • SecretKey (string) - Django SECRET_KEY (required)
  • SessionCookieName (string) - Session cookie name (default: "sessionid")
  • MaxAge (time.Duration) - Maximum session age for validation (optional)
GetRawSession(ctx context.Context, sessionKey string) (*RawSession, error)

Retrieves and validates a session without decoding the payload. Fast operation for middleware.

Returns:

  • RawSession with SessionKey, SessionData, and ExpireDate
  • Errors: ErrSessionNotFound, ErrSessionExpired
DecodeSessionUserID(sessionData string) (string, error)

Decodes session payload and extracts the authenticated user ID.

Returns:

  • User ID as string
  • Errors: ErrInvalidSignature, or parsing errors
Middleware
AuthMiddleware(config MiddlewareConfig) gin.HandlerFunc

Gin middleware for Django session authentication. Requires valid session.

Parameters:

  • Client (*Client) - Django session client (required)
  • LoginRedirectURL (string) - Redirect URL on auth failure (default: "/account/login")
  • SessionKey (string) - Context key for storing session (default: "django_session")
  • OnError (func) - Custom error handler (optional)

Behavior:

  • Validates session exists and is not expired
  • Stores RawSession in Gin context (payload not decoded)
  • Redirects or calls OnError on authentication failure
  • Aborts request if session is invalid
OptionalAuthMiddleware(config MiddlewareConfig) gin.HandlerFunc

Gin middleware for optional Django session authentication. Works for both authenticated and anonymous users.

Parameters:

  • Client (*Client) - Django session client (required)
  • SessionKey (string) - Context key for storing session (default: "django_session")
  • LoginRedirectURL - Not used (no redirects)
  • OnError - Not used (no error handling)

Behavior:

  • Validates session if present
  • Stores RawSession in Gin context only if session is valid
  • Does NOT redirect or abort on missing/invalid session
  • Request continues regardless of authentication status
  • Use c.Get(SessionKey) to check if user is authenticated

Error Types

var (
    ErrSessionNotFound  = errors.New("session not found")
    ErrSessionExpired   = errors.New("session expired")
    ErrInvalidSignature = errors.New("invalid session signature")
    ErrUserNotFound     = errors.New("user not found")
)

Performance Optimization

The library uses a two-phase approach for optimal performance:

  1. Fast Path (Middleware): Only validates session existence and expiration
  2. Lazy Decoding: Decode session payload only when user ID is needed

This avoids expensive cryptographic operations on every request.

Django Configuration

Ensure your Django project uses database sessions:

# settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
SESSION_COOKIE_NAME = 'sessionid'
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True  # For HTTPS
SESSION_COOKIE_SAMESITE = 'Lax'

Testing

Run tests with:

go test -v ./...

Examples

See the examples/ directory for more usage examples:

Security Considerations

  • ⚠️ Never expose SECRET_KEY - Use environment variables
  • ⚠️ Use HTTPS in production - Set SESSION_COOKIE_SECURE = True in Django
  • ⚠️ Validate session age - Consider setting MaxAge in ClientConfig
  • ⚠️ Database connection pooling - Configure pgxpool with appropriate pool settings

License

MIT License - See LICENSE file for details

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

Support

For issues, questions, or suggestions, please open an issue on GitHub.


Developed with assistance from Claude AI 🤖

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrSessionNotFound is returned when session is not found in database
	ErrSessionNotFound = errors.New("session not found")
	// ErrSessionExpired is returned when session has expired
	ErrSessionExpired = errors.New("session expired")
	// ErrInvalidSignature is returned when session signature is invalid
	ErrInvalidSignature = errors.New("invalid session signature")
	// ErrUserNotFound is returned when user is not found in database
	ErrUserNotFound = errors.New("user not found")
)

Functions

func AuthMiddleware

func AuthMiddleware(config MiddlewareConfig) gin.HandlerFunc

AuthMiddleware creates a Gin middleware that validates Django sessions It only checks if session exists and is not expired, WITHOUT decoding the payload Redirects to login page if session is invalid or missing.

func DecodeSessionData

func DecodeSessionData(sessionData, secretKey string) (string, error)

DecodeSessionData decodes Django session data and returns the user ID Uses the default salt for Django sessions: "django.contrib.sessions.SessionStore"

func DecodeSessionDataWithMaxAge

func DecodeSessionDataWithMaxAge(sessionData, secretKey string, maxAgeSeconds int) (string, error)

DecodeSessionDataWithMaxAge decodes Django session data with timestamp validation

func DecodeSessionDataWithSalt

func DecodeSessionDataWithSalt(sessionData, secretKey, salt string, maxAgeSeconds int) (string, error)

DecodeSessionDataWithSalt decodes Django session data with custom salt and timestamp validation

func EncodeSessionData

func EncodeSessionData(userID string, secretKey string, additionalData map[string]interface{}) (string, error)

EncodeSessionData creates a new Django session with the given user ID and additional data

func EncodeSessionDataWithSalt

func EncodeSessionDataWithSalt(userID string, secretKey string, salt string, additionalData map[string]interface{}, compress bool) (string, error)

EncodeSessionDataWithSalt creates a new Django session with custom salt

func OptionalAuthMiddleware added in v1.1.0

func OptionalAuthMiddleware(config MiddlewareConfig) gin.HandlerFunc

OptionalAuthMiddleware creates a Gin middleware that validates Django sessions but does NOT redirect when session is missing or invalid. If session exists and is valid, it will be stored in context. If session is missing or invalid, the request continues without setting session in context.

func UpdateSessionData

func UpdateSessionData(sessionData string, secretKey string, updates map[string]interface{}) (string, error)

UpdateSessionData modifies an existing session by decoding, updating fields, and re-encoding

func UpdateSessionDataWithSalt

func UpdateSessionDataWithSalt(sessionData string, secretKey string, salt string, updates map[string]interface{}, compress bool) (string, error)

UpdateSessionDataWithSalt modifies an existing session with custom salt

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client provides methods to interact with Django sessions

func NewClient

func NewClient(config ClientConfig) (*Client, error)

NewClient creates a new Django session client

func (*Client) DecodeSessionUserID

func (c *Client) DecodeSessionUserID(sessionData string) (string, error)

DecodeSessionUserID decodes the session payload and extracts user ID Use this when you have a RawSession and need to get the user ID

func (*Client) GetRawSession

func (c *Client) GetRawSession(ctx context.Context, sessionKey string) (*RawSession, error)

GetRawSession retrieves and validates a Django session by session key WITHOUT decoding the payload. This is fast and used by middleware.

func (*Client) SessionCookieName

func (c *Client) SessionCookieName() string

SessionCookieName returns the configured session cookie name

type ClientConfig

type ClientConfig struct {
	DB                DBTX
	SecretKey         string
	SessionCookieName string
	MaxAge            time.Duration // Optional: max age for session validation
}

ClientConfig holds configuration for the Django session client

type DBTX added in v1.0.0

type DBTX interface {
	Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
	Query(context.Context, string, ...interface{}) (pgx.Rows, error)
	QueryRow(context.Context, string, ...interface{}) pgx.Row
	CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error)
}

DBTX is an interface compatible with *pgx.Conn, *pgxpool.Pool and the sqlc generated interfaces.

type DjangoSigner

type DjangoSigner struct {
	SecretKey string
	Salt      string
	Sep       string
	Algorithm string
}

DjangoSigner handles Django's cryptographic signing

func NewDjangoSigner

func NewDjangoSigner(secretKey string) *DjangoSigner

NewDjangoSigner creates a new signer with default values matching Django's TimestampSigner

func (*DjangoSigner) SignObject

func (ds *DjangoSigner) SignObject(obj map[string]interface{}, compress bool) (string, error)

SignObject encodes and signs a map as JSON with timestamp and optional compression

func (*DjangoSigner) SignTimestamp

func (ds *DjangoSigner) SignTimestamp(value string) string

SignTimestamp signs a value with a timestamp

func (*DjangoSigner) Unsign

func (ds *DjangoSigner) Unsign(signedValue string) (string, error)

Unsign verifies and extracts the original value from a signed string

func (*DjangoSigner) UnsignObject

func (ds *DjangoSigner) UnsignObject(signedObj string, maxAge *time.Duration) (map[string]interface{}, error)

UnsignObject decodes a signed object (JSON)

func (*DjangoSigner) UnsignTimestamp

func (ds *DjangoSigner) UnsignTimestamp(signedValue string, maxAge *time.Duration) (string, error)

UnsignTimestamp verifies and extracts value from a timestamped signed string

type MiddlewareConfig

type MiddlewareConfig struct {
	Client           *Client
	LoginRedirectURL string                          // URL to redirect when auth fails (default: "/account/login")
	SessionKey       string                          // Context key for storing session (default: "django_session")
	OnError          func(c *gin.Context, err error) // Optional: custom error handler
}

MiddlewareConfig configures the authentication middleware

type RawSession

type RawSession struct {
	SessionKey  string
	SessionData string
	ExpireDate  time.Time
}

RawSession represents a Django session without decoded payload (fast)

Directories

Path Synopsis
examples
basic command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL