- Home
- HTTP Headers
- Authentication-Info Header
Header
Authentication-Info Header
Learn how Authentication-Info provides additional authentication data in responses to successful requests. Covers digest authentication and session info.
Authentication-Info Header
TL;DR: Provides additional authentication information after successful requests, mainly used with Digest authentication to supply the next nonce and reduce round trips.
What is Authentication-Info?
The Authentication-Info header provides additional authentication-related information in responses to successfully authenticated requests. It’s like a receipt that confirms your authentication and may include extra details like the next nonce for digest authentication.
This header is primarily used with HTTP Digest Authentication to provide information for subsequent requests.
How Authentication-Info Works
Client sends authenticated request:
GET /api/protected HTTP/1.1
Host: api.example.com
Authorization: Digest username="user", realm="api", nonce="abc123",
uri="/api/protected", response="def456"
```text
**Server responds with authentication info:**
```http
HTTP/1.1 200 OK
Authentication-Info: nextnonce="xyz789", qop=auth, rspauth="ghi012",
cnonce="jkl345", nc=00000001
Content-Type: application/json
{"data": "protected content"}
Syntax
Authentication-Info: <directive>=<value>
Authentication-Info: <directive>=<value>, <directive>=<value>, ...
```text
### Common Directives
- **nextnonce** - Next nonce value to use for subsequent requests
- **qop** - Quality of protection (auth, auth-int)
- **rspauth** - Response authentication digest
- **cnonce** - Client nonce echo
- **nc** - Nonce count echo
## Common Examples
### Basic Authentication Info
```http
Authentication-Info: nextnonce="abc123xyz"
Server provides the next nonce for the client to use.
With Quality of Protection
Authentication-Info: nextnonce="abc123xyz", qop=auth
```text
Indicates the quality of protection used.
### Full Digest Authentication
```http
Authentication-Info: nextnonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
qop=auth, rspauth="ca842f5d7b13f9c76c1d74b53e8b4f73",
cnonce="0a4f113b", nc=00000001
Complete authentication info with response authentication.
Real-World Scenarios
Digest Authentication Flow
# 1. Client makes initial request
GET /api/account HTTP/1.1
Host: api.example.com
# 2. Server challenges
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm="api", qop="auth",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"
# 3. Client responds with credentials
GET /api/account HTTP/1.1
Authorization: Digest username="alice", realm="api",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/api/account",
qop=auth, nc=00000001, cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1"
# 4. Server responds with authentication info
HTTP/1.1 200 OK
Authentication-Info: nextnonce="082c875dcb2ca740c07f1d8cf8f6a9c6",
qop=auth, rspauth="c4f38c31e6f3e25b8e3f2a3c8f5a9e8d",
cnonce="0a4f113b", nc=00000001
{"account": "alice", "balance": 1000}
```text
### Session Extension
```http
GET /api/data HTTP/1.1
Authorization: Digest username="bob", realm="api", nonce="old-nonce",
uri="/api/data", response="hash123"
HTTP/1.1 200 OK
Authentication-Info: nextnonce="new-nonce-for-next-request"
{"data": "content"}
Mutual Authentication
# Client authenticates to server
GET /secure HTTP/1.1
Authorization: Digest username="user", nonce="client-nonce", response="hash1"
# Server authenticates to client
HTTP/1.1 200 OK
Authentication-Info: rspauth="hash2", nextnonce="new-nonce"
```javascript
## Server Implementation
### Node.js (Express)
```javascript
const express = require('express')
const crypto = require('crypto')
const app = express()
// Digest authentication handler
function generateNonce() {
return crypto.randomBytes(16).toString('hex')
}
function generateResponse(username, realm, nonce, uri, method, password) {
const ha1 = crypto.createHash('md5').update(`${username}:${realm}:${password}`).digest('hex')
const ha2 = crypto.createHash('md5').update(`${method}:${uri}`).digest('hex')
return crypto.createHash('md5').update(`${ha1}:${nonce}:${ha2}`).digest('hex')
}
app.get('/api/protected', (req, res) => {
const authHeader = req.headers.authorization
if (!authHeader || !authHeader.startsWith('Digest ')) {
res.setHeader('WWW-Authenticate', `Digest realm="api", qop="auth", nonce="${generateNonce()}"`)
return res.status(401).send('Unauthorized')
}
// Parse digest auth header
// Verify credentials
// ...
// Generate next nonce
const nextNonce = generateNonce()
// Generate response auth
const rspauth = generateResponse(username, realm, nonce, uri, 'GET', password)
// Send authentication info
res.setHeader('Authentication-Info', `nextnonce="${nextNonce}", qop=auth, rspauth="${rspauth}"`)
res.json({ data: 'protected content' })
})
Python (Flask)
from flask import Flask, request, jsonify, make_response
import hashlib
import secrets
app = Flask(__name__)
def generate_nonce():
return secrets.token_hex(16)
def generate_response_auth(username, realm, nonce, uri, method, password):
ha1 = hashlib.md5(f"{username}:{realm}:{password}".encode()).hexdigest()
ha2 = hashlib.md5(f"{method}:{uri}".encode()).hexdigest()
return hashlib.md5(f"{ha1}:{nonce}:{ha2}".encode()).hexdigest()
@app.route('/api/protected')
def protected():
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Digest '):
response = make_response('Unauthorized', 401)
response.headers['WWW-Authenticate'] = \
f'Digest realm="api", qop="auth", nonce="{generate_nonce()}"'
return response
# Parse and verify digest auth
# ...
# Generate authentication info
next_nonce = generate_nonce()
rspauth = generate_response_auth(username, realm, nonce, uri, 'GET', password)
response = make_response(jsonify({'data': 'protected content'}))
response.headers['Authentication-Info'] = \
f'nextnonce="{next_nonce}", qop=auth, rspauth="{rspauth}"'
return response
```javascript
### Apache Configuration
```apache
<Directory "/var/www/protected">
AuthType Digest
AuthName "api"
AuthDigestDomain /protected/
AuthDigestProvider file
AuthUserFile /etc/apache2/.htdigest
Require valid-user
# Apache automatically sends Authentication-Info
</Directory>
Best Practices
For Servers
1. Always provide nextnonce for digest auth
# ✅ Provide next nonce
Authentication-Info: nextnonce="new-nonce-value"
# ❌ Don't leave client without next nonce
# (forces client to start new auth)
```javascript
**2. Generate cryptographically secure nonces**
```javascript
// ✅ Secure random nonce
const nonce = crypto.randomBytes(16).toString('hex')
// ❌ Predictable nonce
const nonce = Date.now().toString()
3. Include response authentication for mutual auth
Authentication-Info: rspauth="hash-value", nextnonce="next-nonce"
```javascript
**4. Match qop from request**
```javascript
// Echo back the qop that was used
const qop = digestParams.qop // from Authorization header
res.setHeader('Authentication-Info', `qop=${qop}, nextnonce="${nonce}"`)
5. Set nonce expiration
const nonces = new Map()
function generateNonce() {
const nonce = crypto.randomBytes(16).toString('hex')
const expiry = Date.now() + 300000 // 5 minutes
nonces.set(nonce, expiry)
return nonce
}
function isNonceValid(nonce) {
const expiry = nonces.get(nonce)
return expiry && expiry > Date.now()
}
```javascript
### For Clients
**1. Store and use nextnonce for subsequent requests**
```javascript
let currentNonce = null
async function makeAuthenticatedRequest(url) {
const nonce = currentNonce || (await getNonceFromChallenge())
const response = await fetch(url, {
headers: {
Authorization: `Digest username="user", nonce="${nonce}", ...`
}
})
// Extract and store next nonce
const authInfo = response.headers.get('Authentication-Info')
if (authInfo) {
const match = authInfo.match(/nextnonce="([^"]+)"/)
if (match) {
currentNonce = match[1]
}
}
return response
}
2. Verify response authentication
function verifyResponseAuth(authInfo, expectedHash) {
const match = authInfo.match(/rspauth="([^"]+)"/)
if (!match) {
throw new Error('No response authentication')
}
if (match[1] !== expectedHash) {
throw new Error('Response authentication failed')
}
}
```javascript
**3. Handle nonce count properly**
```javascript
let nonceCount = 1
function getNextNonceCount() {
const nc = nonceCount.toString(16).padStart(8, '0')
nonceCount++
return nc
}
Authentication-Info vs WWW-Authenticate
WWW-Authenticate (Challenge)
# Used in 401 response to challenge client
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm="api", nonce="xyz", qop="auth"
```text
### Authentication-Info (Success)
```http
# Used in 200 response after successful auth
HTTP/1.1 200 OK
Authentication-Info: nextnonce="abc", qop=auth
Security Considerations
Nonce Management
// ✅ Good: Unique, expiring nonces
const nonce = {
value: crypto.randomBytes(16).toString('hex'),
expires: Date.now() + 300000,
count: 0
}
// ❌ Bad: Reusing nonces indefinitely
const nonce = 'static-nonce'
```text
### Response Authentication
```http
# ✅ Include rspauth for mutual authentication
Authentication-Info: rspauth="hash", nextnonce="nonce"
# ⚠️ Without rspauth, client can't verify server
Authentication-Info: nextnonce="nonce"
Nonce Count Tracking
// Prevent replay attacks
const nonceData = noncesMap.get(nonce)
if (nc <= nonceData.lastNc) {
return res.status(401).send('Replay attack detected')
}
nonceData.lastNc = nc
```text
## Testing Authentication-Info
### Using curl
```bash
# Digest authentication request
curl -v --digest --user username:password \
https://api.example.com/protected
# Look for Authentication-Info in response headers
Using JavaScript
fetch('https://api.example.com/protected', {
headers: {
Authorization: 'Digest username="user", nonce="abc", response="hash"'
}
}).then((response) => {
const authInfo = response.headers.get('Authentication-Info')
console.log('Authentication-Info:', authInfo)
// Parse nextnonce for next request
const match = authInfo.match(/nextnonce="([^"]+)"/)
if (match) {
console.log('Next nonce:', match[1])
}
})
Related Headers
- Authorization - Client sends credentials
- WWW-Authenticate - Server challenges for authentication
- Proxy-Authenticate - Proxy challenges for authentication
- Proxy-Authorization - Client sends proxy credentials
nextnonce and Replay Attack Prevention
The nextnonce parameter in Authentication-Info is the mechanism that prevents replay attacks in Digest authentication. Each nonce is valid for only one request. After a successful authentication, the server provides the next nonce the client should use. If an attacker captures a valid Authorization header and replays it, the server rejects it because the nonce has already been used.
The nonce count (nc) parameter works alongside nextnonce to provide additional replay protection. The client increments the nonce count with each request using the same nonce, and the server tracks which counts it has seen. A replayed request with the same nonce and count is rejected. This is why Authentication-Info is primarily a Digest authentication feature — Bearer token authentication handles replay prevention differently, through token expiration and revocation rather than per-request nonces.
Frequently Asked Questions
What is Authentication-Info?
Authentication-Info is sent by servers after successful authentication to provide additional information like a new nonce for the next request in Digest authentication.
When is Authentication-Info used?
It is primarily used with Digest authentication to provide mutual authentication proof and supply the next nonce, reducing round trips for subsequent requests.
What is the nextnonce parameter?
nextnonce provides a new nonce for the client to use in the next request. This allows the server to rotate nonces without requiring a new 401 challenge.
Is Authentication-Info commonly used?
No, it is rarely used because Digest authentication is uncommon. Most modern APIs use Bearer tokens or OAuth which do not use Authentication-Info.