HTTP Authentication Methods and Best Practices

TL;DR: Pick the auth mechanism that matches your trust boundary and operational needs. Basic Auth is for narrow controlled cases, API keys are for service access, Bearer tokens are common for APIs, and session cookies are often the simplest fit for traditional web apps. Use HTTPS in every case.

HTTP authentication is the process of proving who a client is before the server decides what that client may do. In practice, most teams are not choosing an abstract security concept. They are choosing how browsers, mobile apps, CLIs, workers, and services present credentials on every request.

Introduction#

Every time you log into a site, call an internal API, or send a request with an API key, you are in authentication territory. Authentication answers “who are you?” Authorization answers “what are you allowed to do?” Mixing those two is one of the fastest ways to make an auth system confusing.

Why authentication matters:

  • Protects sensitive data and operations
  • Enables personalized user experiences
  • Prevents unauthorized access to resources
  • Provides audit trails for security compliance
  • Enables rate limiting and usage tracking

Common Authentication Methods#

1. Basic Authentication#

Basic Auth sends credentials as a Base64-encoded string in the Authorization header.

How it works:

GET /api/users HTTP/1.1
Host: api.example.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Copy

The string dXNlcm5hbWU6cGFzc3dvcmQ= is Base64 encoding of username:password.

Implementation:

// Server (Node.js/Express)
app.use((req, res, next) => {
  const auth = req.headers.authorization

  if (!auth || !auth.startsWith('Basic ')) {
    return res.status(401).set('WWW-Authenticate', 'Basic').send('Authentication required')
  }

  const credentials = Buffer.from(auth.slice(6), 'base64').toString()
  const [username, password] = credentials.split(':')

  if (username === 'admin' && password === 'secret') {
    next()
  } else {
    res.status(401).send('Invalid credentials')
  }
})

// Client
fetch('https://api.example.com/users', {
  headers: {
    Authorization: 'Basic ' + btoa('username:password')
  }
})

Copy

Pros:

  • Simple to implement
  • Widely supported
  • No additional infrastructure needed

Cons:

  • Credentials sent with every request
  • Vulnerable if not used over HTTPS
  • No built-in expiration
  • Not suitable for most browser-based product UX

2. Bearer Token Authentication#

Bearer tokens (like JWTs) are passed in the Authorization header and represent proof of authentication.

How it works:

GET /api/protected HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Copy

JWT (JSON Web Token) Implementation:

const jwt = require('jsonwebtoken')

// Generate token on login
app.post('/login', (req, res) => {
  const { username, password } = req.body

  // Verify credentials...
  if (validCredentials(username, password)) {
    const token = jwt.sign({ userId: 123, username }, process.env.JWT_SECRET, { expiresIn: '24h' })

    res.json({ token })
  } else {
    res.status(401).json({ error: 'Invalid credentials' })
  }
})

// Verify token on protected routes
const authMiddleware = (req, res, next) => {
  const auth = req.headers.authorization

  if (!auth || !auth.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'No token provided' })
  }

  const token = auth.slice(7)

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET)
    req.user = decoded
    next()
  } catch (err) {
    res.status(401).json({ error: 'Invalid token' })
  }
}

app.get('/api/protected', authMiddleware, (req, res) => {
  res.json({ message: `Hello ${req.user.username}` })
})

Copy

Client usage:

// Store token from login
const token = await login('username', 'password')
localStorage.setItem('token', token)

// Use token for API requests
fetch('https://api.example.com/protected', {
  headers: {
    Authorization: `Bearer ${localStorage.getItem('token')}`
  }
})

Copy

The tradeoff here is operational, not just syntactic: bearer tokens are convenient because any holder of the token can use it. That simplicity is why they fit APIs well and why token storage decisions matter so much.

Pros:

  • Stateless (no server-side session storage)
  • Can include user data and permissions
  • Supports expiration
  • Works well with SPAs and mobile apps

Cons:

  • Larger than session IDs
  • Cannot be invalidated without additional infrastructure
  • Vulnerable to XSS if stored in localStorage
  • Requires careful secret management

3. API Key Authentication#

API keys are long-lived credentials used for server-to-server or application authentication.

Common patterns:

# Header-based
GET /api/data HTTP/1.1
X-API-Key: sk_live_abc123xyz

# Query parameter (less secure)
GET /api/data?api_key=sk_live_abc123xyz HTTP/1.1

# Custom authentication scheme
Authorization: ApiKey sk_live_abc123xyz

Copy

Implementation:

app.use('/api', (req, res, next) => {
  const apiKey = req.headers['x-api-key']

  if (!apiKey) {
    return res.status(401).json({ error: 'API key required' })
  }

  // Validate against database
  const isValid = await validateApiKey(apiKey)

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid API key' })
  }

  next()
})

Copy

Best practices:

  • Use different keys for development/production
  • Implement rate limiting per key
  • Allow key rotation
  • Prefix keys to identify type (e.g., pk_ for publishable, sk_ for secret)
  • Never commit keys to version control

Server stores session data and sends a session ID to the client as a cookie.

Flow:

1. User logs in with credentials
2. Server creates session and stores in database/memory
3. Server sends session ID as HttpOnly cookie
4. Browser automatically includes cookie in subsequent requests
5. Server validates session ID and retrieves user data

Copy

Implementation:

const session = require('express-session')

app.use(
  session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
      secure: true, // HTTPS only
      httpOnly: true, // No JavaScript access
      sameSite: 'strict', // CSRF protection
      maxAge: 86400000 // 24 hours
    }
  })
)

app.post('/login', (req, res) => {
  const { username, password } = req.body

  if (validCredentials(username, password)) {
    req.session.userId = getUserId(username)
    req.session.username = username
    res.json({ success: true })
  } else {
    res.status(401).json({ error: 'Invalid credentials' })
  }
})

app.get('/api/profile', (req, res) => {
  if (!req.session.userId) {
    return res.status(401).json({ error: 'Not authenticated' })
  }

  res.json({ userId: req.session.userId, username: req.session.username })
})

app.post('/logout', (req, res) => {
  req.session.destroy()
  res.json({ success: true })
})

Copy

Pros:

  • Secure (HttpOnly cookies prevent XSS)
  • Server can invalidate sessions
  • Automatic browser handling
  • Natural CSRF protection with SameSite

Cons:

  • Requires server-side storage
  • Doesn’t work well with distributed systems (without shared session store)
  • CORS complications for cross-domain requests

5. OAuth 2.0#

OAuth is a delegation protocol that allows third-party applications to access user data without sharing passwords.

Common flows:

Authorization Code Flow (for web apps):

1. User clicks "Login with Google"
2. Redirect to Google with client_id and redirect_uri
3. User logs in and grants permission
4. Google redirects back with authorization code
5. Exchange code for access token (server-side)
6. Use access token to access user data

Copy

Implementation example (simplified):

// Step 1: Redirect to OAuth provider
app.get('/auth/google', (req, res) => {
  const authUrl =
    `https://accounts.google.com/o/oauth2/v2/auth?` +
    `client_id=${process.env.GOOGLE_CLIENT_ID}&` +
    `redirect_uri=${encodeURIComponent('http://localhost:3000/auth/callback')}&` +
    `response_type=code&` +
    `scope=profile email`

  res.redirect(authUrl)
})

// Step 2: Handle callback
app.get('/auth/callback', async (req, res) => {
  const { code } = req.query

  // Exchange code for 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: 'http://localhost:3000/auth/callback',
      grant_type: 'authorization_code'
    })
  })

  const { access_token } = await tokenResponse.json()

  // Use access token to get user info
  const userResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
    headers: { Authorization: `Bearer ${access_token}` }
  })

  const user = await userResponse.json()

  // Create session or JWT for user
  req.session.user = user
  res.redirect('/dashboard')
})
```text

## Security Best Practices

### 1. Always Use HTTPS

```javascript
// Enforce HTTPS
app.use((req, res, next) => {
  if (!req.secure && process.env.NODE_ENV === 'production') {
    return res.redirect(`https://${req.headers.host}${req.url}`)
  }
  next()
})

Copy

2. Implement Rate Limiting#

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

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

app.post('/login', authLimiter, loginHandler)
```javascript

### 3. Use Strong Passwords

```javascript
const bcrypt = require('bcrypt')

// Hash password on registration
const hashedPassword = await bcrypt.hash(password, 10)

// Verify on login
const isValid = await bcrypt.compare(password, hashedPassword)

Copy

4. Implement Multi-Factor Authentication (MFA)#

const speakeasy = require('speakeasy')

// Generate secret
const secret = speakeasy.generateSecret({ name: 'MyApp' })

// Verify TOTP code
const verified = speakeasy.totp.verify({
  secret: secret.base32,
  encoding: 'base32',
  token: userProvidedCode
})
```javascript

### 5. Protect Against Common Attacks

**CSRF Protection:**

```javascript
const csrf = require('csurf')
app.use(csrf({ cookie: true }))

// Include CSRF token in forms
<input type="hidden" name="_csrf" value="<%= csrfToken %>">

Copy

XSS Protection:

// Sanitize user input
const validator = require('validator')
const clean = validator.escape(userInput)

// Use HttpOnly cookies
res.cookie('session', sessionId, { httpOnly: true })
```text

## Troubleshooting Common Issues

**Issue: 401 Unauthorized on valid credentials**

```text
Check:
- Password hashing comparison
- Token expiration
- Clock skew (for JWT)
- Case sensitivity in credentials
```text

**Issue: CORS errors with credentials**

```javascript
app.use(
  cors({
    origin: 'https://yourfrontend.com',
    credentials: true
  })
)

Copy

Issue: Tokens not being sent

// Ensure credentials: 'include' for cookies
fetch('/api/protected', {
  credentials: 'include'
})

Copy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值