Lesson 24 / 25
API Keys and Service Accounts
Credentials for software, not people.
Managing machine credentials
API keys identify a calling application. Generate them with a CSPRNG, show them once, and store only a hash (a fast hash is acceptable because keys are long and random). A visible prefix (for example sk_live_ style) helps users and secret scanners recognise leaked keys. Scope each key to specific permissions and, where possible, IPs or environments; support multiple active keys so customers can rotate without downtime; record last_used_at and revoke unused keys. Service accounts are non-human identities inside your platform; prefer short-lived credentials issued from workload identity (cloud IAM roles, Kubernetes service account tokens, OIDC federation from CI) over long-lived static secrets. Keep secrets in a secrets manager, never in source control.
Issuing and checking API keys
Node.js; the prefix and id let you look up the key without scanning hashes.
export async function createApiKey(accountId: string, scopes: string[]) {
const id = crypto.randomBytes(6).toString('hex');
const secret = crypto.randomBytes(32).toString('base64url');
await db.apiKeys.insert({ id, accountId, scopes, secretHash: sha256(secret), createdAt: new Date() });
return `ak_${id}_${secret}`; // shown to the user once, never stored in plain text
}
export async function apiKeyAuth(req: Request, res: Response, next: NextFunction) {
const m = /^ak_([0-9a-f]{12})_([A-Za-z0-9_-]+)$/.exec(req.get('x-api-key') ?? '');
const key = m && (await db.apiKeys.findActive(m[1]));
const ok = key && crypto.timingSafeEqual(Buffer.from(key.secretHash), Buffer.from(sha256(m![2])));
if (!ok) return res.status(401).end();
await db.apiKeys.touch(key.id); // last_used_at
req.client = { accountId: key.accountId, scopes: key.scopes };
next();
}Turn on secret scanning
Distinctive key prefixes let platforms such as GitHub secret scanning detect leaked keys in repositories. Have a documented process to revoke and reissue quickly when that happens.
Quick check: How should a server store API keys it has issued?
- Encrypted in the client's browser storage
- In plain text so support can read them back
- As hashes, showing the plaintext key to the user only once
- Inside the JWT payload of every response
Answer
As hashes, showing the plaintext key to the user only once — A database leak should not reveal working keys.