Por Performate
OAuth2 y API keys en k6: patrones de refresh de token que sobreviven la concurrencia
OAuth2 y API keys en k6 bajo concurrencia: refresh de token, client credentials, secretos por env—docs de auth HTTP k6 y patrones de carga realistas.
Tokens bearer estáticos en scripts k6 hacen que cada corrida parezca saludable—hasta que OAuth prod produce tormentas de refresh, endpoints token rate-limited y clock skew que generan picos 401 bajo carga. Clientes reales renuevan tokens; tu load test también debería.
OAuth2 y API keys en k6 no es «pegar el JWT de DevTools». Es modelar ciclo de vida token bajo concurrencia: client credentials para service accounts, refresh antes de expiración, y escenarios separados cuando API keys golpean límites RPM. En esta guía verás por qué auth colapsa bajo VUs, cómo estructurar setup y refresh en k6, y qué patrones van en smoke CI vs carga completa—no un secreto hard-coded en git.
Por qué auth falla bajo carga—no a escala single-request
Tests funcionales obtienen un token y lo reutilizan forever. k6 concurrente expone fallos distintos:
- Rate limits en endpoint token throttle refresh cuando muchos VUs renuevan simultáneamente.
- Access tokens short-lived expiran mid-escenario—ráfagas 401 si refresh es per-iteration pero sin caché.
- Tormentas client credentials stampede al IdP cuando cada VU llama
/tokenindependientemente. - Caps RPM API key devuelven
429mientras rutas business siguen «bien» a baja tasa. - Clock skew entre runners k6 y servidor auth causa errores «expired» prematuros.
Piensa tokens como credenciales de conferencia: una credencial por asistente funciona hasta que todos intentan re-imprimir en el mismo kiosk al cambio de sesión.
Referencia OAuth 2.0 overview y variables de entorno k6 para secretos. Empareja con secretos k6 en entornos prueba y gestionar env vars.
Implementación práctica en k6: refresh compartido con caché por VU
Usa setup() para un fetch client-credentials en smoke, o refresh a nivel módulo con lógica tipo mutex en corridas largas—fetch token una vez por batch de iteración VU, no una vez global forever.
Script de ejemplo (ilustrativo—no listo para producción). URLs IdP ficticias; adapta grant type a tu proveedor.
Qué demuestra este ejemplo:
- Client credentials en setup: un fetch token antes de escenarios cuando es aceptable para carga service-to-service.
- Caché token por VU con chequeo TTL: refresh cuando ventana
expires_inestá cerca—refleja comportamiento SDK. - Escenario separado ruta API key: tags
auth:api_keyvsauth:oauthpara triage. - Umbrales en fallos auth:
http_req_failed{route:token}detecta colapso IdP temprano.
import http from 'k6/http';
import { check, sleep } from 'k6';
const BASE = __ENV.API_BASE || 'https://staging.example.com';
const TOKEN_URL = __ENV.TOKEN_URL || 'https://idp.example.com/oauth/token';
const tokenCache = {};
function getAccessToken(vuId) {
const now = Date.now();
const cached = tokenCache[vuId];
if (cached && cached.expiresAt > now + 5000) {
return cached.accessToken;
}
const res = http.post(
TOKEN_URL,
{
grant_type: 'client_credentials',
client_id: __ENV.CLIENT_ID,
client_secret: __ENV.CLIENT_SECRET,
scope: 'api.read',
},
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, tags: { route: 'token' } },
);
check(res, { 'token 200': (r) => r.status === 200 });
const body = res.json();
tokenCache[vuId] = {
accessToken: body.access_token,
expiresAt: now + (body.expires_in || 3600) * 1000,
};
return tokenCache[vuId].accessToken;
}
export const options = {
scenarios: {
oauth_api: {
executor: 'constant-arrival-rate',
rate: Number(__ENV.API_RPS || 30),
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 15,
maxVUs: 60,
exec: 'callWithOAuth',
tags: { auth: 'oauth' },
},
api_key_route: {
executor: 'constant-arrival-rate',
rate: Number(__ENV.KEY_RPS || 10),
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 5,
maxVUs: 20,
exec: 'callWithApiKey',
tags: { auth: 'api_key' },
},
},
thresholds: {
'http_req_failed{route:token}': ['rate<0.02'],
'http_req_failed{auth:oauth}': ['rate<0.01'],
'http_req_duration{auth:oauth}': ['p(95)<600'],
},
};
export function callWithOAuth() {
const token = getAccessToken(__VU);
const res = http.get(`${BASE}/v1/orders`, {
headers: { Authorization: `Bearer ${token}` },
tags: { route: 'orders', auth: 'oauth' },
});
check(res, { 'orders 2xx': (r) => r.status >= 200 && r.status < 300 });
sleep(0.2);
}
export function callWithApiKey() {
const res = http.get(`${BASE}/v1/metrics`, {
headers: { 'X-API-Key': __ENV.API_KEY },
tags: { route: 'metrics', auth: 'api_key' },
});
check(res, { 'metrics not 429': (r) => r.status !== 429 });
sleep(0.3);
}
Patrones que funcionan
- Escalonar refresh token: jitter sleep antes de refresh cuando muchos VUs comparten ventanas expiry.
- Escenario token dedicado baja tasa para estresar IdP separado de APIs business.
- Nunca commitear secretos—
CLIENT_SECRETyAPI_KEYsolo vía CI/env (guía secretos k6). - Taggear path auth para dashboards—401 en rutas business vs ruta token difiere (tags observabilidad k6).
Anti-patrones a evitar
- Un token global para 500 VUs cuando proveedor emite límites per-client.
- Refresh en cada iteración sin importar TTL—crea carga IdP artificial.
- Ignorar
429en rutas API key—producto puede golpear límites antes de saturar servidores.
Pro tip (comando de ejemplo):
k6 run oauth-load.js -e CLIENT_ID=$ID -e CLIENT_SECRET=$SECRET -e API_KEY=$KEY
Qué demuestra este comando: secretos inyectados en runtime—mismo patrón que CI y staging local deben usar.
Marco de decisión: token estático vs refresh vs auth dual
| Situación | Acción recomendada |
|---|---|
| Smoke CI corto, token staging long-lived | Fetch setup una vez; rotar token out-of-band |
| OAuth production-like con TTL < 15m | Caché por VU + refresh antes expiry |
| API key en endpoints partner | Escenario separado; umbrales 429 |
| Rate limit IdP conocido | Cap VUs; jitter; escenario token dedicado opcional |
| Clientes mixtos OAuth + API key | Escenarios paralelos con tags auth:* |
Usa token setup estático si smoke es <2m e IdP owner aprueba token servicio compartido.
Usa refresh por VU si access tokens expiran durante duración test realista.
Usa escenarios duales si tráfico prod mezcla contexto OAuth usuario e integraciones API key.
Observabilidad, documentación y próximos pasos
Tests carga auth fallan silenciosamente cuando 401s se tratan como errores genéricos. Antes de escalar:
- Documenta grant type, scopes, TTL token y rate limits IdP en runbook.
- Alerta separadamente en fallos
route:tokenvs tasas 401 rutas business. - Correlaciona tags k6
auth:*con métricas IdP durante ventanas staging. - Automatiza smoke OAuth en CI con secret store rotado—no tokens commiteados (pruebas de carga en CI/CD).
- Archiva escenario y patrón inyección secretos por entorno (staging vs load lab).
Cómo Performate simplifica load testing OAuth2
Copy-paste bearer tokens desde devtools browser no escala entre escenarios. Abajo un ejemplo de flujo concreto para orders (OAuth) y metrics (API key).
Ejemplo: escenarios auth dual con secretos env
- Importa colección Postman con requests OAuth y API-key; guarda nombres env var en colección. Problema resuelto: rutas definidas una vez; secretos externos.
- Configura escenario
oauth_apia 30 req/s con nota pre-request enlazando token URL. Problema resuelto: mapa visual auth vs tráfico business. - Agrega escenario
api_key_routea 10 req/s con headerX-API-Keyligado a env. Problema resuelto: testing límite RPM sin fork script. - Aplica tags
auth:oauthyauth:api_keypara reportes filtrados. Problema resuelto: mismo modelo tags del ejemplo k6. - Corre en staging con secretos desde archivo env escritorio (nunca commiteado). Problema resuelto: ingeniería itera sin editar script en cada rotación token.
- Exporta script k6 para CI con inyección secret store matching nombres env var local. Problema resuelto: ejecución federada sin drift auth.
Ese flujo mapea al cta: escenarios ejecutables, umbrales y reportes sin días de glue code.
Cierre
OAuth en k6 es problema de ciclo de vida: refresh, rate limits y API keys necesitan escenarios y tags separados—no JWT estático del martes pasado.
Corre el load test de esta semana con refresh token habilitado—y observa si IdP o tu API golpea 429 primero al escalar VUs.
Try Performate free | Reserva una demo | Variables entorno k6
¿Listo para optimizar el rendimiento de tu API?
Usa Performate para convertir este playbook en escenarios k6 ejecutables, umbrales y reportes compartibles sin perder días en código pegamento.