API & Service Auth Lab
Two services talking to each other is where most real breaches start — the wrong auth mechanism, or a secret left somewhere it leaks. Work a queue of service-to-service scenarios and make the call: on a mechanism card, choose between an API key, OAuth 2.0 client credentials, mutual TLS and workload identity — guided by the one constraint that decides it; on a handling card, flag the mishandled credential — a key committed to a repo, in a URL, in a client bundle, in logs, long-lived, shared or over-scoped — or call it healthy. You are scored on right calls against false positives, because flagging a correctly-handledcredential (a publishable key, a pre-signed URL) is its own failure. Clear the queue clean.
The real job. More noise, a tighter clock.
Service-auth queue
Two calls per card type: on a mechanism card pick the right machine-auth mechanism; on a handling card flag the credential defect or call it OK.
#1
Backend → third-party SMS provider
Your server sends transactional SMS through a third-party provider’s REST API. No end user is in the flow; the provider is a simple external SaaS and the call is server-to-server.
Deciding constraint
The provider issues only a static API key — no OAuth, no client certificates. The key is held in your secret manager and sent server-side.
#2
Service → your own internal Orders API
The Checkout service calls your internal Orders API. Both are yours; no user is present (pure machine-to-machine). You already run an OAuth 2.0 authorization server for the platform.
Deciding constraint
You need short-lived, per-service, scoped tokens that expire and can be revoked centrally, plus an audit trail of which service called what.
#3
Service-to-service inside a zero-trust mesh
A fixed set of microservices on a flat internal network must authenticate each other. Security wants cryptographic peer identity at the transport layer and no shared bearer secret that could be replayed if it leaked.
Deciding constraint
Both ends must prove identity to each other, and the network is treated as hostile (zero trust).
#4
EKS pod needs to read an S3 bucket
A pod running in your EKS cluster reads objects from an S3 bucket. You want NO long-lived AWS keys stored in the pod, in a Kubernetes Secret, or baked into the image.
Deciding constraint
The workload runs on a platform that can attest its identity, and the goal is zero stored secrets.
#5
Secret in the repository
A config file checked into the main branch — and present throughout git history — carries a live third-party API secret.
# config/production.env (committed to the repo) PAYMENTS_API_SECRET=sk_live_4eC39HqEXAMPLEKEY_NOTREAL
#6
Key in the query string
A backend calls a vendor API by putting the reusable key straight in the URL.
GET https://api.vendor.example/v1/records?api_key=AKIAEXAMPLE1234_NOTREAL&limit=50
#7
Admin key in the browser bundle
A single-page app ships this in its compiled JavaScript, served to every visitor.
// app.bundle.js (served to browsers)
const ADMIN_API_KEY = "sk_live_adminEXAMPLE0000_NOTREAL";
fetch("/admin/export", { headers: { Authorization: "Bearer " + ADMIN_API_KEY } });#8
Token written to logs
An HTTP client logs full request details on every call.
logger.info("calling partner API, headers=" + JSON.stringify(req.headers));
// -> headers={"authorization":"Bearer eyJhbGEXAMPLE.tokenNOTREAL", ...}#9
GitHub Actions deploys to GCP
A CI workflow in GitHub Actions deploys infrastructure to Google Cloud. Today it authenticates with a downloaded service-account JSON key stored as a repo secret.
Deciding constraint
The runner can present a signed OIDC token, and you want to stop storing a long-lived service-account key anywhere.
#10
Partner backend calls your public API
A business partner’s server calls your API on its own behalf — no end user, no delegated consent. You onboard many partners and must be able to turn any one off independently.
Deciding constraint
Each partner needs its own revocable identity and its own scopes, issued by your authorization server.
#11
Bank mandates client-certificate auth
Your payout service must call a bank’s API. The bank requires you to authenticate with a specific client certificate it has registered, and it pins that certificate.
Deciding constraint
The counterparty dictates the mechanism: a registered client certificate, mutually verified.
#12
Nightly job calls a data-enrichment SaaS
A nightly batch job enriches records via a third-party data vendor. The vendor is a plain external SaaS; the job runs server-side on a trusted host.
Deciding constraint
The vendor supports only API keys; you control storage and rotate the key automatically every 90 days.
#13
Access key from three years ago
A service authenticates to a cloud provider with a static access key created over three years ago. It has never been rotated and has no expiry.
AWS_ACCESS_KEY_ID=AKIAEXAMPLESTATIC01_NOTREAL # created 2023-02-01 · last rotated: never · expiry: none
#14
One key, six services
The same API key is configured in six different services. When an engineer suspects it leaked, nobody can tell which service exposed it, and rotating it means coordinating an outage across all six at once.
#15
Read one bucket, granted everything
A reporting service that only needs to read objects from a single S3 bucket is attached a policy granting full administrative access.
{ "Effect": "Allow", "Action": "*", "Resource": "*" } // attached to the reporting service#16
Token fetched at runtime
A service fetches a short-lived token from the OAuth server at startup and on expiry, holds it only in memory, sends it in the Authorization header, and never logs it. The client secret is read from a secret manager.
Right calls vs. false positives
Call every scenario, then grade. So far 0/16 answered, 0/8 mechanisms, 0/7 leaks caught, 0 false positive(s).
How it is scored
Each scenario has one correct answer — the right mechanism, or the specific credential finding (OK included).
A false positive (flagging a correctly-handled credential) takes an extra hygiene bite — a review that cries wolf is as useless as one that misses a real leak.
Harder tiers add trust-boundary mechanism traps and deliberate false positives (publishable keys, pre-signed URLs, auto-rotated managed keys).
Live arena
4 activeadversary staging…
What this lab is
- A free, browser-based service-to-service authentication lab: work a queue of ~24 machine-auth scenarios and make the right call on each.
- Two linked skills in one queue. Mechanism cards: pick the right machine auth — API key vs OAuth 2.0 client credentials vs mutual TLS vs workload identity (SPIFFE/IRSA/managed identity) — driven by the deciding constraint. Handling cards: spot a leaked or mishandled credential — committed in a repo, in a URL, in a client bundle, in logs, long-lived, shared across services, or over-scoped — or call it OK.
- You are graded on right-answer rate weighed against false positives — flagging a correctly-handled credential takes an extra hygiene penalty, because a review that cries wolf is as useless as one that misses a real leak.
- Difficulty tiers add trust-boundary mechanism traps (a mesh cert that means nothing across a partner boundary; a platform role that removes a stored key) and deliberate false positives (a publishable pk_* key in the client, a pre-signed URL signature, an auto-rotated managed key).
- Pure client-side and deterministic: nothing is uploaded, no real credential is parsed, and every example key or token is an obviously fake placeholder.
Frequently asked questions
What is the API & Service Auth Lab?
A free, in-browser exercise where you work a queue of service-to-service authentication scenarios. On a mechanism card you choose the right machine auth for the situation — an API key, the OAuth 2.0 client-credentials grant, mutual TLS, or workload identity (SPIFFE/SVID, AWS IRSA, Azure/GCP managed identity) — based on the deciding constraint. On a handling card you judge how a credential is stored and transmitted and flag the defect: committed to a repo, passed in a URL, shipped in client-side code, written to logs, long-lived with no rotation, shared across services, or over-scoped — or call it OK.
How is it graded?
Each scenario has one correct answer. Your score is the share you call correctly, with mechanism and handling cards weighted equally. False positives — flagging a credential that is actually handled correctly — carry an extra hygiene penalty, because a review nobody can trust is as useless as one that misses a real leak. A perfect run calls every scenario exactly right, the clean ones included.
Which mechanism is "best"?
There is no single best — the right mechanism follows the constraint. If a counterparty only issues API keys, an API key (stored in a secret manager, scoped and rotated) is correct and OAuth is over-engineering. For first-party and partner app-to-API calls, OAuth 2.0 client credentials give short-lived, scoped, revocable tokens. For a fixed mesh, a mandated counterparty certificate, or a device fleet, mutual TLS gives replay-proof peer identity. When the workload runs somewhere the platform can attest it, workload identity removes the stored secret entirely — usually the strongest option when it is available.
Does it make any network or crypto calls?
No. Nothing is uploaded, no real token or certificate is parsed, and there is no crypto. The scenarios are authored, realistic training situations bundled with the page, and every example key or token is an obviously fake placeholder such as sk_live_...EXAMPLE...NOTREAL. It runs entirely in your browser — no account, no install, nothing sent anywhere.
Who is it for?
Platform, cloud and application engineers who wire services together, AppSec and security engineers who review how credentials are handled, and anyone prepping for a security or infrastructure interview who wants to feel why a committed key, a key in a URL, an admin key in a browser bundle or an over-scoped policy is a finding — and why a publishable key in the client or a pre-signed URL is not.