keecode logokeecode
Beginner
api authentication
bearer token
api keys
jwt token
oauth explained
authentication methods

API Authentication Explained: Tokens, API Keys, and OAuth

Complete beginner's guide to API authentication. Learn about API keys, Bearer tokens, JWT, OAuth, and how to secure your API requests.

Updated January 15, 2025

API authentication is how you prove your identity when making API requests. This beginner-friendly guide explains different authentication methods, when to use each, and how to implement them.

Table of Contents

  1. What is API Authentication?
  2. Why Authentication Matters
  3. API Keys
  4. Bearer Tokens
  5. JWT (JSON Web Tokens)
  6. OAuth 2.0
  7. Basic Authentication
  8. Comparison and Best Practices

What is API Authentication?

Authentication is the process of verifying who you are. When you use an API, authentication proves that you're allowed to access it.

Authentication vs Authorization

Authentication: "Who are you?"
Authorization: "What are you allowed to do?"

AUTHENTICATION (Identity):
"I am John Doe" (prove it with password/token)
✅ Login successful

AUTHORIZATION (Permission):
"Can John Doe delete this file?"
✅ Yes, he's an admin
❌ No, he's only a viewer

How Authentication Works

1. CLIENT: "I want to access your API"
   
2. SERVER: "Show me your credentials"
   
3. CLIENT: Sends credentials (API key, token, etc.)
   
4. SERVER: Verifies credentials
   ├─ Valid → "Welcome! Here's your data"
   └─ Invalid → "Access denied" (401 Unauthorized)

5. CLIENT: Uses data

Why Authentication Matters

Without Authentication (Public API)

// Anyone can access - no security
fetch('https://api.example.com/weather')
  .then(res => res.json())
  .then(data => console.log(data));

// Problems:
// ❌ No usage limits (abuse possible)
// ❌ Can't track who's using it
// ❌ No personalized data
// ❌ Can't charge for usage
// ❌ Anyone can spam your server

With Authentication

// Must prove identity
fetch('https://api.example.com/user/profile', {
  headers: {
    'Authorization': 'Bearer your-token-here'
  }
})
.then(res => res.json())
.then(data => console.log(data));

// Benefits:
// ✅ Track usage per user
// ✅ Enforce rate limits
// ✅ Provide personalized data
// ✅ Charge based on usage
// ✅ Block abusive users
// ✅ Protect sensitive data

API Keys

API Keys are the simplest form of authentication. They're like passwords for your application.

How API Keys Work

1. Sign up for the service
2. Service gives you an API key: "abc123xyz789"
3. Include key in every API request
4. Server checks if key is valid
5. If valid → Access granted ✅
   If invalid → Access denied ❌

Using API Keys

Method 1: Query Parameter (Simple but less secure)

// In URL (visible in logs and browser history)
fetch('https://api.example.com/data?api_key=abc123xyz789')
  .then(res => res.json())
  .then(data => console.log(data));

// Example: Weather API
fetch('https://api.openweathermap.org/data/2.5/weather?q=London&appid=YOUR_API_KEY')
  .then(res => res.json())
  .then(data => console.log(data));

Method 2: Header (More secure)

// In header (not visible in URL)
fetch('https://api.example.com/data', {
  headers: {
    'X-API-Key': 'abc123xyz789'
  }
})
.then(res => res.json())
.then(data => console.log(data));

// or sometimes:
fetch('https://api.example.com/data', {
  headers: {
    'Authorization': 'ApiKey abc123xyz789'
  }
})
.then(res => res.json())
.then(data => console.log(data));

Real-World API Key Examples

// Google Maps API
const googleMapsKey = 'AIzaSyB...';
const script = document.createElement('script');
script.src = \`https://maps.googleapis.com/maps/api/js?key=\${googleMapsKey}\`;
document.head.appendChild(script);

// OpenWeatherMap API
const weatherKey = 'abc123xyz789';
fetch(\`https://api.openweathermap.org/data/2.5/weather?q=London&appid=\${weatherKey}\`)
  .then(res => res.json())
  .then(data => console.log(data));

// SendGrid Email API
fetch('https://api.sendgrid.com/v3/mail/send', {
  method: 'POST',
  headers: {
    'Authorization': \`Bearer \${sendgridApiKey}\`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    personalizations: [{ to: [{ email: 'user@example.com' }] }],
    from: { email: 'noreply@example.com' },
    subject: 'Hello',
    content: [{ type: 'text/plain', value: 'Hello, World!' }]
  })
});

API Key Best Practices

// ✅ GOOD - Store in environment variables
const API_KEY = process.env.API_KEY;
fetch(\`https://api.example.com/data?api_key=\${API_KEY}\`);

// ❌ BAD - Hardcoded in code
const API_KEY = 'abc123xyz789';  // Visible in Git!

// ✅ GOOD - Use .env file
// .env
API_KEY=abc123xyz789

// ❌ BAD - Expose in frontend (visible in browser)
// Client-side JavaScript - anyone can see your key!
fetch(\`https://api.example.com/data?api_key=abc123xyz789\`);

// ✅ GOOD - Use proxy server
// Frontend calls your server, server calls API with key
fetch('/api/weather?city=London')  // Your server
  .then(res => res.json());

// Your server (backend):
app.get('/api/weather', async (req, res) => {
  const response = await fetch(
    \`https://api.openweathermap.org/data/2.5/weather?q=\${req.query.city}&appid=\${process.env.API_KEY}\`
  );
  const data = await response.json();
  res.json(data);
});

When to Use API Keys

Good for:

  • Server-to-server communication
  • Backend applications
  • Development/testing
  • Simple authentication needs
  • Service-to-service authentication

Avoid for:

  • Frontend applications (keys exposed)
  • User-specific authentication
  • Complex permission systems
  • When you need expiration/refresh

Bearer Tokens

Bearer Tokens are strings that grant access to resources. "Bearer" means "whoever holds this token has access."

How Bearer Tokens Work

1. Login with username/password
   POST /api/login
   Body: { "email": "user@example.com", "password": "secret" }

2. Server verifies credentials
   ✅ Valid → Generate token

3. Server returns token
   Response: { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }

4. Client stores token (localStorage, cookie, etc.)

5. Client sends token with every request
   GET /api/profile
   Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

6. Server validates token
   ✅ Valid → Return data
   ❌ Invalid/Expired → 401 Unauthorized

Using Bearer Tokens

// Step 1: Login and get token
async function login(email, password) {
  const response = await fetch('https://api.example.com/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ email, password })
  });
  
  const data = await response.json();
  
  // Store token
  localStorage.setItem('token', data.token);
  
  return data.token;
}

// Step 2: Use token for authenticated requests
async function getUserProfile() {
  const token = localStorage.getItem('token');
  
  const response = await fetch('https://api.example.com/user/profile', {
    headers: {
      'Authorization': \`Bearer \${token}\`
    }
  });
  
  if (response.status === 401) {
    // Token expired or invalid
    // Redirect to login
    window.location.href = '/login';
    return;
  }
  
  const profile = await response.json();
  return profile;
}

// Step 3: Logout (remove token)
function logout() {
  localStorage.removeItem('token');
  window.location.href = '/login';
}

Complete Authentication Flow

class AuthService {
  constructor() {
    this.token = localStorage.getItem('token');
  }
  
  async login(email, password) {
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password })
      });
      
      if (!response.ok) {
        throw new Error('Login failed');
      }
      
      const data = await response.json();
      this.token = data.token;
      localStorage.setItem('token', data.token);
      
      return data;
    } catch (error) {
      console.error('Login error:', error);
      throw error;
    }
  }
  
  async fetchWithAuth(url, options = {}) {
    // Add token to every request
    const headers = {
      ...options.headers,
      'Authorization': \`Bearer \${this.token}\`
    };
    
    const response = await fetch(url, { ...options, headers });
    
    // Handle expired token
    if (response.status === 401) {
      this.logout();
      window.location.href = '/login';
      throw new Error('Authentication required');
    }
    
    return response;
  }
  
  logout() {
    this.token = null;
    localStorage.removeItem('token');
  }
  
  isAuthenticated() {
    return !!this.token;
  }
}

// Usage
const auth = new AuthService();

// Login
await auth.login('user@example.com', 'password123');

// Make authenticated requests
const response = await auth.fetchWithAuth('/api/profile');
const profile = await response.json();

// Logout
auth.logout();

JWT (JSON Web Tokens)

JWT is a specific type of bearer token that contains encoded information about the user.

What is JWT?

A JWT consists of three parts separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
└────────────────┬────────────────┘ └──────────────────────┬──────────────────────┘ └──────────────┬──────────────┘
        HEADER                              PAYLOAD                                    SIGNATURE

Header: Token type and algorithm

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload: User data and claims

{
  "sub": "1234567890",
  "name": "John Doe",
  "email": "john@example.com",
  "role": "admin",
  "iat": 1516239022,
  "exp": 1516242622
}

Signature: Ensures token hasn't been tampered with

JWT Advantages

Self-contained: All info is in the token (no database lookup needed)
Stateless: Server doesn't need to store sessions
Scalable: Works across multiple servers
Secure: Cryptographically signed
Expiration: Built-in expiry time

Creating and Using JWT (Node.js)

const jwt = require('jsonwebtoken');
const SECRET_KEY = process.env.JWT_SECRET;

// ===================================
// SERVER: Generate JWT on login
// ===================================

app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;
  
  // Verify credentials (check database)
  const user = await User.findOne({ email });
  if (!user || !await user.comparePassword(password)) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  // Generate JWT
  const token = jwt.sign(
    {
      userId: user.id,
      email: user.email,
      role: user.role
    },
    SECRET_KEY,
    {
      expiresIn: '24h'  // Token expires in 24 hours
    }
  );
  
  res.json({ token, user: { id: user.id, email: user.email } });
});

// ===================================
// SERVER: Verify JWT middleware
// ===================================

function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];  // "Bearer TOKEN"
  
  if (!token) {
    return res.status(401).json({ error: 'Token required' });
  }
  
  try {
    const decoded = jwt.verify(token, SECRET_KEY);
    req.user = decoded;  // Add user info to request
    next();
  } catch (error) {
    return res.status(403).json({ error: 'Invalid or expired token' });
  }
}

// ===================================
// SERVER: Protected routes
// ===================================

app.get('/api/profile', authenticateToken, (req, res) => {
  // req.user contains decoded JWT data
  res.json({
    userId: req.user.userId,
    email: req.user.email,
    role: req.user.role
  });
});

app.get('/api/admin', authenticateToken, (req, res) => {
  // Check role from JWT
  if (req.user.role !== 'admin') {
    return res.status(403).json({ error: 'Admin access required' });
  }
  
  res.json({ message: 'Admin data' });
});

JWT Client-Side Usage

// Login and store JWT
async function login(email, password) {
  const response = await fetch('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });
  
  const data = await response.json();
  
  // Store JWT
  localStorage.setItem('jwt', data.token);
  
  return data;
}

// Decode JWT to read user info (without verifying)
function parseJwt(token) {
  const base64Url = token.split('.')[1];
  const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
  const jsonPayload = decodeURIComponent(
    atob(base64)
      .split('')
      .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
      .join('')
  );
  
  return JSON.parse(jsonPayload);
}

// Check if token is expired
function isTokenExpired(token) {
  try {
    const decoded = parseJwt(token);
    const currentTime = Date.now() / 1000;
    return decoded.exp < currentTime;
  } catch {
    return true;
  }
}

// Use JWT for requests
async function fetchWithJWT(url, options = {}) {
  const token = localStorage.getItem('jwt');
  
  if (!token || isTokenExpired(token)) {
    // Redirect to login
    window.location.href = '/login';
    return;
  }
  
  return fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': \`Bearer \${token}\`
    }
  });
}

JWT Refresh Tokens

// ===================================
// SERVER: Generate access + refresh tokens
// ===================================

app.post('/api/login', async (req, res) => {
  const user = await authenticateUser(req.body);
  
  // Short-lived access token (15 minutes)
  const accessToken = jwt.sign(
    { userId: user.id },
    ACCESS_TOKEN_SECRET,
    { expiresIn: '15m' }
  );
  
  // Long-lived refresh token (7 days)
  const refreshToken = jwt.sign(
    { userId: user.id },
    REFRESH_TOKEN_SECRET,
    { expiresIn: '7d' }
  );
  
  // Store refresh token in database
  await RefreshToken.create({ userId: user.id, token: refreshToken });
  
  res.json({ accessToken, refreshToken });
});

// ===================================
// SERVER: Refresh access token
// ===================================

app.post('/api/refresh', async (req, res) => {
  const { refreshToken } = req.body;
  
  if (!refreshToken) {
    return res.status(401).json({ error: 'Refresh token required' });
  }
  
  // Verify refresh token
  try {
    const decoded = jwt.verify(refreshToken, REFRESH_TOKEN_SECRET);
    
    // Check if refresh token exists in database
    const storedToken = await RefreshToken.findOne({
      userId: decoded.userId,
      token: refreshToken
    });
    
    if (!storedToken) {
      return res.status(403).json({ error: 'Invalid refresh token' });
    }
    
    // Generate new access token
    const newAccessToken = jwt.sign(
      { userId: decoded.userId },
      ACCESS_TOKEN_SECRET,
      { expiresIn: '15m' }
    );
    
    res.json({ accessToken: newAccessToken });
  } catch (error) {
    return res.status(403).json({ error: 'Invalid refresh token' });
  }
});

// ===================================
// CLIENT: Auto-refresh access token
// ===================================

class AuthService {
  async fetchWithAuth(url, options = {}) {
    let accessToken = localStorage.getItem('accessToken');
    
    // Try request with current token
    let response = await fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        'Authorization': \`Bearer \${accessToken}\`
      }
    });
    
    // If 401, try to refresh
    if (response.status === 401) {
      const refreshToken = localStorage.getItem('refreshToken');
      
      // Get new access token
      const refreshResponse = await fetch('/api/refresh', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ refreshToken })
      });
      
      if (refreshResponse.ok) {
        const data = await refreshResponse.json();
        localStorage.setItem('accessToken', data.accessToken);
        
        // Retry original request with new token
        response = await fetch(url, {
          ...options,
          headers: {
            ...options.headers,
            'Authorization': \`Bearer \${data.accessToken}\`
          }
        });
      } else {
        // Refresh failed, logout
        this.logout();
        window.location.href = '/login';
      }
    }
    
    return response;
  }
}

OAuth 2.0

OAuth is an authorization framework that lets you grant access to your data on one site to another site without sharing your password.

How OAuth Works (Simplified)

Example: "Login with Google" on a website

1. YOU: Click "Login with Google" on MyApp.com
   
2. MyApp.com: Redirects you to Google
   
3. GOOGLE: Shows permission screen
   "MyApp wants to access your email and profile"
   [Allow] [Deny]
   
4. YOU: Click "Allow"
   
5. GOOGLE: Redirects back to MyApp.com with authorization code
   
6. MyApp.com: Exchanges code for access token (server-to-server)
   
7. MyApp.com: Uses token to get your data from Google
   
8. YOU: Logged in to MyApp.com (without giving MyApp your Google password!)

OAuth Flow Diagram

USER            YOUR APP         GOOGLE (OAuth Provider)
 │                  │                        │
 │  Click "Login"   │                        │
 ├─────────────────>│                        │
 │                  │  Redirect to Google    │
 │                  ├───────────────────────>│
 │                  │                        │
 │                  │  Show permission page  │
 │<──────────────────────────────────────────┤
 │                  │                        │
 │  User approves   │                        │
 ├──────────────────────────────────────────>│
 │                  │                        │
 │                  │  Redirect with code    │
 │<─────────────────┤                        │
 │                  │                        │
 │                  │  Exchange code for token
 │                  ├───────────────────────>│
 │                  │                        │
 │                  │  Return access token   │
 │                  │<───────────────────────┤
 │                  │                        │
 │                  │  Get user data         │
 │                  ├───────────────────────>│
 │                  │                        │
 │                  │  User data             │
 │                  │<───────────────────────┤
 │                  │                        │
 │  Logged in! ✅   │                        │
 │<─────────────────┤                        │

OAuth Example (Google Login)

// ===================================
// CLIENT: Initiate OAuth flow
// ===================================

const GOOGLE_CLIENT_ID = 'your-google-client-id';
const REDIRECT_URI = 'http://localhost:3000/auth/callback';

function loginWithGoogle() {
  const authUrl = 'https://accounts.google.com/o/oauth2/v2/auth?' + new URLSearchParams({
    client_id: GOOGLE_CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    response_type: 'code',
    scope: 'email profile',
    access_type: 'offline'
  });
  
  // Redirect to Google
  window.location.href = authUrl;
}

// ===================================
// SERVER: Handle callback
// ===================================

app.get('/auth/callback', async (req, res) => {
  const { code } = req.query;
  
  try {
    // Exchange authorization code for access token
    const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        code,
        client_id: process.env.GOOGLE_CLIENT_ID,
        client_secret: process.env.GOOGLE_CLIENT_SECRET,
        redirect_uri: REDIRECT_URI,
        grant_type: 'authorization_code'
      })
    });
    
    const tokens = await tokenResponse.json();
    // tokens.access_token, tokens.refresh_token, tokens.id_token
    
    // Get user info from Google
    const userResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
      headers: {
        'Authorization': \`Bearer \${tokens.access_token}\`
      }
    });
    
    const googleUser = await userResponse.json();
    // { id, email, name, picture }
    
    // Create or update user in your database
    let user = await User.findOne({ googleId: googleUser.id });
    if (!user) {
      user = await User.create({
        googleId: googleUser.id,
        email: googleUser.email,
        name: googleUser.name,
        picture: googleUser.picture
      });
    }
    
    // Create your own JWT for the user
    const jwt = createJWT(user);
    
    // Redirect to frontend with JWT
    res.redirect(\`http://localhost:3000?token=\${jwt}\`);
    
  } catch (error) {
    console.error('OAuth error:', error);
    res.redirect('http://localhost:3000/login?error=oauth_failed');
  }
});
// Google OAuth
https://accounts.google.com/o/oauth2/v2/auth

// GitHub OAuth
https://github.com/login/oauth/authorize

// Facebook OAuth
https://www.facebook.com/v12.0/dialog/oauth

// Twitter OAuth
https://twitter.com/i/oauth2/authorize

// Microsoft OAuth
https://login.microsoftonline.com/common/oauth2/v2.0/authorize

// Each has similar flow but different parameters

When to Use OAuth

Good for:

  • "Login with Google/Facebook/GitHub"
  • Accessing user's data from other services
  • Third-party integrations
  • When you don't want to store passwords

Overkill for:

  • Simple API authentication
  • Server-to-server communication
  • Internal applications
  • When OAuth provider isn't needed

Basic Authentication

Basic Auth sends username and password with every request. Simple but less secure.

How Basic Auth Works

# Credentials: username "john" password "secret123"

# 1. Combine with colon
john:secret123

# 2. Encode in Base64
am9objpzZWNyZXQxMjM=

# 3. Send in header
GET /api/users HTTP/1.1
Authorization: Basic am9objpzZWNyZXQxMjM=

Using Basic Auth

// JavaScript
const username = 'john';
const password = 'secret123';

// Encode credentials
const credentials = btoa(\`\${username}:\${password}\`);

fetch('https://api.example.com/data', {
  headers: {
    'Authorization': \`Basic \${credentials}\`
  }
})
.then(res => res.json())
.then(data => console.log(data));

// Or use fetch with credentials directly
fetch('https://api.example.com/data', {
  headers: {
    'Authorization': 'Basic ' + btoa('john:secret123')
  }
});

When to Use Basic Auth

Acceptable for:

  • Internal tools
  • Development/testing
  • Simple admin panels
  • Over HTTPS only

Avoid for:

  • Production user authentication
  • Public APIs
  • When security is critical
  • Mobile apps

Comparison and Best Practices

Authentication Methods Comparison

methodsecuritycomplexitybest Forexpiration
API Keys🟡 Medium✅ SimpleService-to-serviceManual
Bearer Tokens✅ Good🟡 MediumUser authenticationConfigurable
JWT✅ Good🟡 MediumStateless authBuilt-in
OAuth 2.0✅ Excellent❌ ComplexThird-party loginYes
Basic Auth❌ Poor✅ Very simpleInternal tools onlyNo

Best Practices

1. Always Use HTTPS

✅ https://api.example.com (Encrypted)
❌ http://api.example.com (Plaintext - credentials visible!)

All authentication requires HTTPS to prevent credentials from being intercepted.

2. Store Tokens Securely

// ✅ GOOD - HttpOnly cookie (can't be accessed by JavaScript)
res.cookie('token', jwt, {
  httpOnly: true,
  secure: true,  // HTTPS only
  sameSite: 'strict',
  maxAge: 24 * 60 * 60 * 1000  // 24 hours
});

// 🟡 OKAY - localStorage (accessible by JavaScript)
localStorage.setItem('token', jwt);

// ❌ BAD - Regular cookie (accessible by JavaScript, vulnerable to XSS)
document.cookie = \`token=\${jwt}\`;

3. Implement Token Expiration

// ✅ GOOD - Tokens expire
const token = jwt.sign(
  { userId: user.id },
  SECRET,
  { expiresIn: '15m' }  // Short-lived
);

// ❌ BAD - Tokens never expire
const token = jwt.sign({ userId: user.id }, SECRET);
// If stolen, attacker has access forever!

4. Use Refresh Tokens

✅ GOOD Pattern:
- Access token: Short-lived (15 minutes)
- Refresh token: Long-lived (7 days)
- Store refresh token securely (HttpOnly cookie or database)
- Automatically refresh access token when expired

❌ BAD Pattern:
- Long-lived access token (24 hours+)
- No refresh mechanism
- If stolen, attacker has extended access

5. Validate All Tokens

// ✅ GOOD - Verify on every request
function authenticateToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'Token required' });
  }
  
  try {
    const decoded = jwt.verify(token, SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(403).json({ error: 'Invalid token' });
  }
}

// ❌ BAD - Trust without verification
function authenticateToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  req.user = jwt.decode(token);  // Decoded but not verified!
  next();
}

6. Rate Limit Authentication Attempts

const rateLimit = require('express-rate-limit');

// Limit login attempts
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 minutes
  max: 5,  // 5 attempts
  message: 'Too many login attempts, please try again later'
});

app.post('/api/login', loginLimiter, async (req, res) => {
  // Login logic
});

7. Never Log Tokens

// ❌ BAD - Tokens in logs
console.log('User logged in:', token);
logger.info(\`Token: \${token}\`);

// ✅ GOOD - Log user ID only
console.log('User logged in:', userId);
logger.info(\`User \${userId} authenticated\`);

Summary

API authentication is essential for securing your applications and APIs. Choose the right method based on your needs, always use HTTPS, and follow security best practices.

Key Takeaways:

API Keys - Simple, for server-to-server
Bearer Tokens - User authentication, expires
JWT - Self-contained, stateless, popular
OAuth - Third-party login ("Login with Google")
Basic Auth - Simple but insecure (avoid in production)
✅ Always use HTTPS
✅ Store tokens securely (HttpOnly cookies preferred)
✅ Implement token expiration and refresh
✅ Validate tokens on every request
✅ Rate limit authentication attempts

Quick Decision Guide:

  • Building a public API? → API Keys
  • User login system? → JWT with refresh tokens
  • "Login with Google"? → OAuth 2.0
  • Simple internal tool? → Basic Auth (with HTTPS)