Skip to main content

Authentication Patterns

This document covers the authentication patterns EGI uses across projects. The right pattern depends on the application type, user base, and security requirements. When in doubt, default to OAuth 2.0 / OIDC for user-facing applications and API keys for service-to-service communication.

OAuth 2.0 / OpenID Connect (OIDC)

OAuth 2.0 with OIDC is the preferred pattern for user-facing applications where users sign in with an existing identity provider (Google, GitHub, etc.).

When to Use

  • Web applications with user login
  • Applications that need access to third-party APIs on behalf of the user (e.g., Google Sheets, GitHub)
  • Projects where social login is a requirement

How It Works

  1. The user clicks "Sign in with [Provider]"
  2. The application redirects to the identity provider's authorization endpoint
  3. The user authenticates with the provider and grants permissions
  4. The provider redirects back to the application with an authorization code
  5. The application exchanges the code for access and refresh tokens
  6. The application uses the access token to make API calls or establish a session

Implementation Guidelines

  • Always use the Authorization Code flow with PKCE (Proof Key for Code Exchange) for web applications
  • Never use the Implicit flow (it is deprecated and insecure)
  • Store tokens server-side whenever possible; avoid exposing tokens to the browser
  • Use short-lived access tokens (15-60 minutes) with refresh tokens for longer sessions
  • Validate ID tokens on the server, checking issuer, audience, and expiration

API Keys

API keys are simple, static credentials used for service-to-service communication or third-party integrations.

When to Use

  • Server-to-server API calls where no user context is needed
  • Third-party webhook integrations
  • Internal service communication within a trusted network

Implementation Guidelines

  • Generate API keys with sufficient entropy (minimum 32 random bytes, base64-encoded)
  • Store API keys as environment variables; never hardcode them in source code
  • Scope keys to specific actions or resources when the service supports it
  • Implement rate limiting on API key-authenticated endpoints
  • Rotate API keys on a regular schedule (at minimum quarterly) and immediately if compromised
  • Log API key usage for audit purposes (log the key prefix, never the full key)

API Key Security Rules

RuleRationale
Never commit API keys to GitExposed keys in version history are a common attack vector
Use .env files locally, environment variables in productionKeeps secrets out of the codebase
Never log full API keysPrevents exposure through log aggregation systems
Rotate on team member departureDeparting employees may retain access to keys

JSON Web Tokens (JWT)

JWTs are signed tokens that encode user identity and claims. They are commonly used as the transport format for access tokens in OAuth 2.0 flows and as session tokens in custom auth implementations.

When to Use

  • Stateless authentication where the server does not need to maintain session state
  • Microservice architectures where services need to verify user identity without calling a central auth server
  • Short-lived access tokens in OAuth 2.0 flows

Implementation Guidelines

  • Always sign JWTs using an asymmetric algorithm (RS256 or ES256) for production systems; HS256 is acceptable only for single-service applications
  • Set short expiration times (15-60 minutes for access tokens)
  • Include only necessary claims in the payload (user ID, roles, expiration); never include sensitive data (passwords, PII)
  • Validate all claims on every request: issuer, audience, expiration, and signature
  • Implement token refresh using secure, httpOnly refresh tokens

Common Pitfalls

  • No expiration: Always set the exp claim
  • Storing JWTs in localStorage: Use httpOnly cookies instead to prevent XSS attacks
  • Not validating signatures: Always verify the signature before trusting the token contents
  • Including too much data: JWTs are not a database; keep payloads small

Session-Based Authentication

Session-based auth uses a server-side session store (database, Redis, or in-memory) with a session ID cookie sent to the client.

When to Use

  • Traditional web applications with server-rendered pages
  • Applications that need immediate session revocation (e.g., "log out everywhere")
  • When you need to store significant session state server-side

Implementation Guidelines

  • Use httpOnly, Secure, SameSite=Lax (or Strict) cookies for session IDs
  • Generate session IDs with a cryptographically secure random number generator
  • Set appropriate session timeouts (idle timeout and absolute timeout)
  • Implement session revocation on logout and password change
  • Store sessions in a persistent store (database or Redis) for production; in-memory stores do not survive server restarts

NextAuth.js Patterns for Next.js

Most EGI web applications are built with Next.js, and NextAuth.js (Auth.js) is the standard library for handling authentication in these projects.

Standard Setup

  1. Install NextAuth.js and the relevant provider packages
  2. Configure providers in the NextAuth route handler (e.g., Google, GitHub)
  3. Use the getServerSession helper in server components and API routes to access the current user
  4. Protect pages and API routes by checking for a valid session

Configuration Pattern

// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

export const authOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
callbacks: {
async session({ session, token }) {
// Add custom claims (e.g., role) to the session
session.user.role = token.role;
return session;
},
},
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

Key Guidelines

  • Store OAuth client IDs and secrets in environment variables, never in source code
  • Use the database adapter (Prisma adapter recommended) for persistent sessions when the project uses a database
  • Implement role-based access by extending the session callback to include user roles
  • Protect API routes by checking getServerSession and returning 401 if no session exists
  • Document the auth configuration as part of the Anchor handoff process (see Documenting Auth)

Choosing the Right Pattern

ScenarioRecommended Pattern
User-facing web app with loginOAuth 2.0 / OIDC via NextAuth.js
API consumed by a frontend SPAJWT (issued via OAuth 2.0 flow)
Server-to-server integrationAPI keys
Webhook receiverAPI key or HMAC signature verification
Internal tool with simple access controlSession-based auth or NextAuth.js
Mobile applicationOAuth 2.0 with PKCE