API Authentication

Advertisement

API Authentication: Basic, API Key, OAuth 2.0 & JWT

Nearly every API you test is protected, so authentication is the first thing to get right in any API suite — and one of the most valuable areas to test, because authentication bugs are security bugs. This guide explains the common methods (Basic auth, API keys, bearer tokens, OAuth 2.0 and JWT), how to use each in Postman and REST Assured, and the checks every tester should run.

Authentication vs Authorization

  Authentication Authorization
Question Who are you? What are you allowed to do?
Checks Identity (password, token, key) Permissions (roles, ownership)
Order First After authentication
Failure code 401 Unauthorized 403 Forbidden
Banking example Logging in Can this user view the account, transfer money, open admin pages?

The Methods at a Glance

Method What's sent Typical use
Basic auth Authorization: Basic base64(user:password) Internal tools, simple services, test environments
API key A key in a header (e.g. x-api-key) Server-to-server calls, public APIs with usage limits
Bearer token Authorization: Bearer <token> Most modern APIs — the token usually comes from a login or OAuth flow
OAuth 2.0 A framework for obtaining access tokens Delegated access, "Sign in with…", service-to-service
JWT A token format, usually sent as a bearer token Stateless APIs, microservices, SSO

They overlap: a common setup is OAuth 2.0 issuing a JWT access token that's sent as a Bearer token.

Basic Authentication

The client sends username:password encoded in Base64. For asha:Secret@1:

Authorization: Basic YXNoYTpTZWNyZXRAMQ==

Base64 is encoding, not encryption — anyone who sees the header can decode it instantly — so Basic auth is only acceptable over HTTPS.

// REST Assured
given().auth().preemptive().basic("asha", System.getenv("API_PASSWORD"))
.when().get("/account")
.then().statusCode(200);

preemptive() sends the header on the first request; without it, REST Assured waits for a 401 challenge first.

In Postman: Authorization → Basic Auth, and Postman builds the header for you.

API Key Authentication

A unique key identifies the calling application:

given().header("x-api-key", System.getenv("API_KEY"))
.when().get("/products")
.then().statusCode(200);
  • Send keys in headers, not query parameters — URLs end up in logs, browser history and analytics.
  • An API key usually identifies an application, not a user, so it's often combined with user authentication.
  • Keys should be rotatable, scoped and rate-limited. They're not automatically "more secure" than Basic auth — a leaked key is as bad as a leaked password.

Bearer Tokens

The client logs in once, receives a token, and sends it with every request. In tests, fetch the token once and reuse it:

String token = given().contentType(ContentType.JSON)
        .body(Map.of("username", "asha", "password", System.getenv("API_PASSWORD")))
    .when().post("/auth/login")
    .then().statusCode(200)
    .extract().path("accessToken");

given().auth().oauth2(token)                 // adds "Authorization: Bearer <token>"
.when().get("/orders")
.then().statusCode(200);

In Postman, save it in the login request's Tests tab:

pm.environment.set("token", pm.response.json().accessToken)

Then set the collection's authorization to Bearer Token with {{token}}.

More: Postman Authentication & Tokens.

OAuth 2.0

OAuth 2.0 is an authorization framework: instead of giving an app your password, an authorization server issues the app an access token with limited scope and lifetime (often with a refresh token to get a new one).

Grant type Used when
Authorization Code (+ PKCE) A user signs in through a browser — web and mobile apps
Client Credentials Service-to-service calls with no user — the easiest to automate in tests
Refresh Token Getting a new access token without logging in again
// Client credentials: get a token from the authorisation server
String accessToken = given()
        .formParam("grant_type", "client_credentials")
        .formParam("client_id", System.getenv("CLIENT_ID"))
        .formParam("client_secret", System.getenv("CLIENT_SECRET"))
        .formParam("scope", "orders.read")
    .when().post("https://auth.example.com/oauth/token")
    .then().statusCode(200)
    .extract().path("access_token");

"Sign in with Google" uses OpenID Connect, an identity layer built on OAuth 2.0. For browser-based flows in UI tests, testers usually get tokens through an API or a test identity provider rather than automating the third-party login page.

In Postman, Authorization → OAuth 2.0 → Get New Access Token handles the flow.

JWT (JSON Web Token)

A JWT has three Base64URL-encoded parts separated by dots — header.payload.signature:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMDEiLCJuYW1lIjoiQXNoYSIsInJvbGUiOiJ0ZXN0ZXIiLCJpYXQiOjE3OTAwMDAwMDAsImV4cCI6MTc5MDAwMDkwMH0
.ZPk0pMT0jIXeVoC-cekSTs-5fTJzNpbEToxDYBxcYhw

Decoding the middle part gives the payload (the claims):

{ "sub": "101", "name": "Asha", "role": "tester", "iat": 1790000000, "exp": 1790000900 }
  • Signed, not encrypted — anyone can read the payload, so it must never contain passwords or sensitive data. The signature only proves it hasn't been changed.
  • Stateless — the server validates the signature and expiry instead of looking up a session.
  • exp − iat = 900 seconds here: a 15-minute access token, renewed with a refresh token.

What to Test: Security Checklist ⭐

Test Expected
No token / no credentials 401
Invalid or malformed token 401
Expired token 401 (then refresh works)
Token with a tampered payload (e.g. role changed to admin) 401 — signature check fails
Token with "alg": "none" or a different algorithm 401
Valid user, wrong role (e.g. admin-only endpoint) 403
User A requests user B's data (/orders/{B's id}) 403 or 404 — a leak here is a serious bug (broken object-level authorization)
Logged-out or revoked token 401
Credentials or tokens in URLs, logs or error messages Never present
HTTP (not HTTPS) Rejected or redirected
Repeated failed logins Rate limiting or lockout (429)
given().when().get("/orders").then().statusCode(401);                                  // no token
given().auth().oauth2("not-a-real-token").when().get("/orders").then().statusCode(401); // invalid
given().auth().oauth2(userToken).when().get("/admin/users").then().statusCode(403);     // wrong role

Design full positive and negative sets for protected endpoints in the API Testing Scenario Lab.

More: REST Assured Authentication.

Best Practices

  • HTTPS for every authenticated request.
  • Short-lived access tokens with refresh tokens.
  • Never put credentials or tokens in URLs.
  • In test code, read secrets from environment variables or CI secrets — never commit them.
  • Fetch a token once per suite (or per user role) and reuse it, rather than logging in before every test.

From Real Projects

My API testing experience covers CRUD operations, HTTP methods, JSON path, and validating JSON and XML data. On Testsigma, a platform that itself automates API tests, understanding requests, responses and status codes was part of understanding the product. Whatever tool you use, validate the status code, the response structure and the values — not just that the call returned something. Never hard-code tokens in tests; request or read them at run time.

📚 Official documentation: MDN: HTTP · ISTQB Glossary of testing terms

FAQs

What are the main types of API authentication?

Basic auth, API keys, bearer tokens, OAuth 2.0 (to obtain tokens) and JWT (a token format).

Is Basic authentication secure?

Only over HTTPS — Base64 is encoding, not encryption, so the credentials are readable if intercepted.

What is the difference between OAuth 2.0 and JWT?

OAuth 2.0 is a framework for issuing access tokens; JWT is a format a token can take. OAuth access tokens are often JWTs.

Can anyone read a JWT?

Yes — the payload is only Base64URL-encoded. The signature prevents tampering, not reading, so JWTs must not carry sensitive data.

401 vs 403?

401: not authenticated (missing, invalid or expired credentials). 403: authenticated but not allowed.

How do you handle authentication in automated API tests?

Get a token once through the login or OAuth endpoint, store it (a variable or a shared request specification), and add negative tests for missing, invalid, expired and wrong-role tokens.