What API Authentication Is

Why an API must prove who is calling it, and the vocabulary used in every scheme that follows.

A web page can hide a button. An API cannot hide anything — it is a URL, and anyone can send it a request with curl. The only thing standing between your data and the internet is the API's ability to answer one question on every single request: who is calling?

The shape of every scheme

All of the mechanisms in this course — Basic, API keys, cookies, bearer tokens, OAuth, mTLS — are variations on the same three steps.

  1. Present a credential. The client sends something only it should have: a password, a key, a signed token, a certificate.
  2. Verify it. The server checks the credential against a database, a signature, or an identity provider.
  3. Attach an identity. The verified user id is bound to this request, and the handler uses it to decide what data to return.

What changes between schemes is what is presented, where it is stored, and who issued it. That is the whole subject.

Credential, verification and identity on every requestClientholds a credentialAPIverifies itHandlerknows user_idrequest + prooftrusted identityEvery request re-proves who is calling
HTTP is stateless: nothing carries over from the last request unless the client sends it again.

Stateless means "every request"

There is no such thing as "already logged in" at the HTTP level. A session cookie is sent again on request #2, and #3, and #400. A bearer token is attached to every call. If a request arrives without proof, it is anonymous — no matter what happened a second earlier.

The words you will keep seeing

  • Credential — the secret being presented (password, key, token, private key).
  • Principal / subject — who the credential identifies. A human user, or a machine.
  • Issuer — who created the credential and vouches for it.
  • Audience — which API the credential was meant for. A token issued for one API must not be accepted by another.
  • Scope — the subset of actions the credential permits.

Example

Example · bash
# An unauthenticated request: the API has no idea who this is
curl https://dfg.com/api/orders
# → 401 Unauthorized

# The same request carrying a credential
curl https://dfg.com/api/orders \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# → 200 OK  [{"id":1,"total":49.90}]

# Same URL. The only difference is proof of identity.

When to use it

  • A mobile app calls the same public API as the website, so identity must ride on the request itself rather than on anything the browser remembers.
  • A partner integration hits your endpoints from a server with no browser at all, which rules out any scheme that depends on cookies or redirects.
  • An internal reporting job runs at 3am with no human present, so it authenticates as a machine identity instead of as a person.

More examples

The three steps, in one Express handler

The identity comes from the verified credential and nowhere else. The moment a handler trusts a client-supplied user id, the whole scheme collapses.

Example · javascript
// 1. present  → the client sent "Authorization: Bearer <token>"
// 2. verify   → we check the signature and expiry
// 3. attach   → req.user is now trusted for the rest of the request
function authenticate(req, res, next) {
  const header = req.get('authorization') || '';
  const token = header.startsWith('Bearer ') ? header.slice(7) : null;

  if (!token) {
    res.set('WWW-Authenticate', 'Bearer realm="api"');
    return res.status(401).json({ error: 'authentication_required' });
  }

  try {
    const claims = verifyToken(token);        // throws if forged or expired
    req.user = { id: claims.sub, scopes: claims.scope.split(' ') };
    next();
  } catch {
    return res.status(401).json({ error: 'invalid_token' });
  }
}

app.get('/api/orders', authenticate, (req, res) => {
  // Never read the user id from the query string or body — only from req.user.
  res.json(ordersFor(req.user.id));
});

The mistake this course exists to prevent

This is called an insecure direct object reference, and it is still one of the most common API vulnerabilities found in the wild.

Example · javascript
// ❌ NEVER: the client tells the server who it is
app.get('/api/orders', (req, res) => {
  res.json(ordersFor(req.query.user_id));   // /api/orders?user_id=7
});

// Anyone can read anyone's orders by changing a number:
//   curl https://dfg.com/api/orders?user_id=8

// ✅ ALWAYS: the server derives the id from a verified credential
app.get('/api/orders', authenticate, (req, res) => {
  res.json(ordersFor(req.user.id));
});

Discussion

  • Be the first to comment on this lesson.