API Authentication
API authentication is how an API decides who sent a request. There is no login screen between a client and an API, so the client attaches a credential to every request — an API key, a user-id and password (Basic) or a token (Bearer) — and the server checks it first. A mobile app calling GET /v1/orders/9021 sends Authorization: Bearer mF_9.B5f-4.1JqM; the API answers 200 for a valid token and 401 for a missing or invalid one.
This lesson covers what the three common schemes send, where a credential may travel and how to test that an API refuses everything else. What a known caller may do — authorization — comes later in the module.
Why it matters
An endpoint that skips the credential check hands its data or actions to anyone who can reach it. Normal use never shows the gap, because real clients always send a valid token; only a test that deliberately sends a missing, wrong or expired credential finds it. The OWASP API Security Top 10 (2023) lists Broken Authentication as API2.
Credentials also leak quietly: a key in a URL lands in access logs, a Basic header in a screenshot is a readable password, and a token that survives logout stays useful to whoever copied it. Knowing how each scheme works keeps the test team from becoming the leak.
API keys, Basic and Bearer: what each one sends
Most APIs use one of three schemes, and the contract says which. In an OpenAPI document, securitySchemes shows either type: apiKey with the header (or query parameter) that carries the key, or type: http with scheme: basic or scheme: bearer, which use the standard Authorization header.
All three travel in a request header and none of them protects itself: whoever copies the header can replay the request, so every scheme depends on HTTPS.
- API key: a long random string issued to a client application, sent in a header the API chooses, such as
X-API-Key. No single standard defines it. A key identifies the calling application rather than a person — OWASP says API keys should authenticate API clients, not users — and it usually stays valid until it is rotated (replaced with a new key) or revoked. - Basic authentication (RFC 7617): the client joins the user-id and password with a colon, encodes the result in Base64 and sends it after
Basic. Base64 is an encoding, not encryption: the value below decodes toqa.tester:S4mple-pass. The password goes with every request and works until it is changed; the RFC does not consider the scheme secure without TLS. - Bearer token (RFC 6750): the client first obtains a token, for example from a login endpoint or an OAuth 2.0 authorization server, and sends it after
Bearer. Any party in possession of a bearer token can use it, so the RFC recommends short-lived tokens (one hour or less) restricted to the API they are meant for. Many bearer tokens are JWTs, covered later in this module.
API keyNo single standard
A long random string issued to a client application, in a header the API names
X-API-Key: demo_7Hq2LmZ9pR4tKx
- Identifies:
- The calling application, not a person
- If someone copies it:
- It works for them until the key is rotated or revoked
- Challenge with a 401:
- No standard one: the contract decides
- First negative tests:
- Missing, revoked, sent in the query string
Basic authenticationRFC 7617
user-id:password, Base64-encoded, sent after Basic
Authorization: Basic cWEudGVzdGVyOlM0bXBsZS1wYXNz
- Identifies:
- The user whose user-id and password it carries
- If someone copies it:
- Decoding gives the password, usable until it is changed
- Challenge with a 401:
WWW-Authenticate: Basic realm="api"- First negative tests:
- Wrong password, unknown user, one error message for both
Bearer tokenRFC 6750
A token obtained first and sent after Bearer; whoever holds it can use it
Authorization: Bearer mF_9.B5f-4.1JqM
- Identifies:
- The user or client it was issued to
- If someone copies it:
- It works for them until it expires or is revoked
- Challenge with a 401:
WWW-Authenticate: Bearer realm="orders"- First negative tests:
- Missing, one character changed, expired, used after logout
Example values are invented for this lesson. Anyone who sees the request can replay any of the three, so each one needs HTTPS.
Text version
API key: X-API-Key: demo_7Hq2LmZ9pR4tKx
Basic: Authorization: Basic cWEudGVzdGVyOlM0bXBsZS1wYXNz
Bearer: Authorization: Bearer mF_9.B5f-4.1JqMWhere credentials travel: headers, never URLs
A credential in a URL is copied to places nobody protects: server and proxy access logs, monitoring tools, browser history and every ticket where someone pastes the link. RFC 9110 warns that many servers, proxies and browsers log or display the target URI, so sensitive information does not belong in it, and forbids senders to put user:password@ into http and https URLs; RFC 6750 says a bearer token SHOULD NOT be sent as a query parameter unless there is no other way; OWASP lists tokens and passwords in the URL as broken authentication.
The rule for a tester: the credential travels in a header, over HTTPS, and is never echoed in an error message or written to a log. If the API also accepts it in the query string, report that as a risk even though it "works".
# Leaks: the key is part of the URL and is written to access logs
GET /v1/products?api_key=demo_7Hq2LmZ9pR4tKx HTTP/1.1
Host: api.shop.test
# Expected: the key travels in a header, over HTTPS
GET /v1/products HTTP/1.1
Host: api.shop.test
X-API-Key: demo_7Hq2LmZ9pR4tKxWhat to test on every protected endpoint
As the lesson on authentication and authorization showed, RFC 9110 gives a request that lacks valid credentials 401 with a WWW-Authenticate challenge, and a request the server understood but refuses 403. For an API, the challenge names the scheme to use, such as WWW-Authenticate: Basic realm="api" or WWW-Authenticate: Bearer realm="orders", where realm names the protected area. API keys have no standard challenge, so the contract decides. A 403 for a known caller is an authorization decision, which has its own lesson; the cases below test authentication only.
- Missing: no credential → 401 with a challenge.
- Malformed or wrong scheme: an empty value,
Bearerwith no token,Basicsent to a Bearer-only API → 401, never 500. A contract that follows RFC 6750 may instead answer a malformed Bearer header with 400 anderror="invalid_request". - Invalid: a wrong password, an unknown user, a key or token with one character changed → 401.
- Expired or revoked: a token past its expiry, a revoked key, a token used after logout when the contract says logout revokes it → 401.
- Wrong place: the credential in the query string or under another header name → refused.
- Every endpoint and method: a protected GET proves nothing about the DELETE on the same path, or about older API versions that are still deployed.
- Safe errors: one message for an unknown user and a wrong password, no stack traces, and the credential never echoed back.
Worked example: an orders endpoint with Bearer tokens
The contract: "GET and DELETE /v1/orders/{id} require Authorization: Bearer <token>. Tokens expire 30 minutes after they are issued and are revoked at logout. A missing or invalid token returns 401." With test user A, write each case and its expected answer before you send anything:
Rows 5 and 6 need timing and a logout. Row 5 applies 2-value boundary value analysis to the 30-minute lifetime: 30 minutes is the first expired value, and a token at 29 minutes, the last valid value, must still return 200. If the server adds a grace period for clock differences, the contract should state it. Row 9 also checks state: a 401 that deleted the order anyway is a defect. User B's token on A's order is an authorization test for a later lesson. The bug report for a failed row names the exact request, the status and headers received and the expected answer, with the credential masked.
# Request to /v1/orders/9021 (A's order) Expected
1 GET, valid token of user A 200, order JSON
2 GET, no Authorization header 401, WWW-Authenticate: Bearer realm="orders"
3 GET, Authorization: Bearer (empty value) 401
4 GET, token with last character changed 401, error="invalid_token" in the challenge
5 GET, token issued 30 minutes ago 401, error="invalid_token" in the challenge
6 GET, token of A after A logged out 401
7 GET, Authorization: Basic (A's password) 401, challenge names Bearer
8 GET, valid token in ?access_token= 401; if accepted, report it as a risk
9 DELETE, no Authorization header 401, and order 9021 still existsCommon mistakes
- Testing only the happy path with a valid token and assuming the endpoint is protected.
- Treating Base64 as protection: a Basic header in a log or a screenshot is a readable password.
- Accepting any 4xx: 401 and 403 mean different things, and a 401 must carry
WWW-Authenticate. - Checking GET only, and forgetting the write methods and older API versions.
- Pasting real tokens, keys or passwords into bug reports, chat or shared collections instead of masking them.
- Never testing expiry and revocation, so a token that still works after logout goes unnoticed.
The QA perspective
Authentication questions belong in the contract review: which scheme, which header, how long a token lives, what logout does, what a 401 looks like. Ask the developer when the OpenAPI document leaves any of these open, and agree which test accounts and keys QA owns in each environment.
Record the credential type and the account used in each test, never the secret, and keep test keys in the team's secret store or in environment variables. Report an endpoint that accepts a request without a valid credential as a security defect, usually critical or blocker. Run authentication tests only on systems you own or have written permission to test.
Practice exercise
Review an authentication test log
A teammate ran authentication tests against the orders API from the worked example, with the same contract, and saved the log below.
Decide for each row whether the API behaved correctly, and for every defect state what was expected and how severe it is.
Orders API, test environment, endpoint /v1/orders/9021
# Method and credential Status Observed
1 GET, valid token of user A 200 order JSON
2 GET, no Authorization header 401 WWW-Authenticate: Bearer realm="orders"
3 GET, token with last character changed 200 order JSON
4 GET, token of A after A logged out 200 order JSON
5 GET, Authorization: Basic 500 stack trace in the body
cWEubGVhZDpXaW50ZXItVGVhLTQy
6 GET, ?access_token=<valid token of A> 200 order JSON
7 DELETE, no Authorization header 204 empty body
8 GET, token issued 35 minutes ago 401 no WWW-Authenticate header- Write the expected result for each row from the contract and RFC 9110.
- Mark each row as correct or as a defect.
- Decode the value in row 5 with a Base64 decoder and note what it means for the log itself.
- Order the defects by severity and write a bug title for the most severe one.
Show the model answer
Rows 1 and 2 are correct.
Row 3, critical: expected 401 with error="invalid_token", but a modified token was accepted, so tokens are not validated and a made-up token may work. Row 4, major: expected 401, but logout does not revoke the token, against the contract.
Row 5, major: expected 401 with WWW-Authenticate: Bearer, not a 500 that leaks a stack trace. The value decodes to qa.lead:Winter-Tea-42, so the log itself exposes a test password: mask credentials in logs and reports, and have that password changed.
Row 6, major: expected 401, but the token is accepted in the URL, where access logs keep it. The contract names the header only and RFC 6750 says SHOULD NOT, so report it as a security defect or as a risk for the team to decide.
Row 7 is the most severe, blocker or critical: an anonymous DELETE succeeded. Confirm that order 9021 is gone, then report "DELETE /v1/orders/{id} deletes the order without an Authorization header and returns 204 instead of 401".
Row 8: the refusal is right, but RFC 9110 requires WWW-Authenticate on every 401, so this is a minor defect. Suggested order: 7, 3, 4, 6, 5, 8.
Frequently asked questions
Is Basic authentication still acceptable for APIs?
Only over HTTPS, and mostly for server-to-server calls or internal tools. The password is sent with every request and is only Base64-encoded, so one leaked request or log line exposes it. Many teams prefer tokens or keys that can be revoked without changing a person's password.
Should a missing credential return 401 or 403?
401, as the lesson on authentication and authorization explains. For a Bearer API, RFC 6750 also separates the two kinds of 401: a request that sent no credential should get a challenge without an error code, and a request whose token is expired, revoked or invalid should get error="invalid_token" in the challenge. So a test for an expired token checks the challenge, not just the status.
Is an API key enough to protect user data?
Usually not on its own. A key identifies the calling application, not the person using it, and it lives for a long time. The OWASP API Security Top 10 (2023) says API keys should authenticate API clients, not users; user data needs user authentication, typically with a token.
What is the difference between a bearer token and a JWT?
"Bearer" describes how a token is used: whoever holds it can present it. JWT is a format for the token's content. A bearer token can be a JWT or a random string that only the server can look up. The JWT guide explains the format.
References
- RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token UsageHow a bearer token is sent (Authorization: Bearer …) and the error responses a resource server returns.
- RFC 7617: The 'Basic' HTTP Authentication SchemeHow Basic authentication encodes user-id:password in the Authorization header, and why it needs TLS.
- RFC 9110: HTTP SemanticsThe meaning of HTTP methods, header fields, status codes and content negotiation, independent of the wire version.
- OWASP API Security Top 10 (2023)The ten most common API weaknesses: broken object-level authorization, broken authentication, unrestricted resource consumption and more.
Practise with Mutqan tools
Test Bearer, Basic, API-key and custom-header authentication against an endpoint and compare authorised vs unauthorised responses.
Decode Base64 or Base64url strings to text, with automatic padding repair.
Encode text or files to Base64 (standard or URL-safe) in your browser.
Build request headers for REST APIs (auth, content negotiation, tracing) and export to cURL, fetch, axios or HTTP.
Build cURL commands from method, URL, headers, body and auth options.
Go deeper
- What is Base64? Encoding ExplainedBase64 is not encryption
- HTTP Status Codes ExplainedCodes people confuse
- What is API Testing? How to Test a REST APIHow to test a REST API, step by step
- What is a JWT? JSON Web Tokens ExplainedJWT explained simply: the three parts of a JSON Web Token, claims like exp and sub, how to decode and verify one, why decoding is not verifying, and security.
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.