Endpoint uji untuk setiap skema autentikasi API
Basic, Digest, token bearer, API key, tanda tangan HMAC, JWT, OAuth 1.0a, dan OAuth 2.0 dengan PKCE, beserta kredensialnya.
Contoh terakhir dijalankan terhadap API pada
Untuk menguji autentikasi klien API tanpa menyiapkan identity provider, pakai endpoint uji publik di https://api.testingapis.com/api/authmasuk untuk melihat. Ada satu endpoint per skema: HTTP Basic, Digest, token bearer tetap, API key di header, query, atau cookie, dan penandatanganan request HMAC. Login JWT punya refresh token dan token kedaluwarsa untuk tes negatif, server OAuth 2.0 mendukung PKCE dan client credentials, dan server OAuth 1.0a memeriksa request bertanda tangan. Kredensialnya tetap dan publik, tercantum di bawah dan di GET /api/authmasuk untuk melihat. Kredensial yang salah mengembalikan 401 yang benar, dan token valid tanpa role yang tepat mengembalikan 403.
Endpoint dan kredensial
| Skema | Endpoint | Kredensial |
|---|---|---|
| HTTP Basic | GET /api/auth/basicmasuk untuk melihat | apibee / password123 |
| HTTP Digest (MD5, qop=auth) | GET /api/auth/digestmasuk untuk melihat | apibee / password123 |
| Token bearer | GET /api/auth/bearermasuk untuk melihat | Authorization: Bearer apibee-token-123 |
| API key, header | GET /api/auth/api-key/headermasuk untuk melihat | X-API-Key: apibee-key-123 |
| API key, query | GET /api/auth/api-key/querymasuk untuk melihat | ?api_key=apibee-key-123 |
| API key, cookie | GET /api/auth/api-key/cookiemasuk untuk melihat | Cookie: api_key=apibee-key-123 |
| Tanda tangan HMAC-SHA256 | POST /api/auth/hmacmasuk untuk melihat | Secret apibee-hmac-secret |
| Role dan scope | GET /api/auth/roles/adminmasuk untuk melihat | admin-token, user-token, readonly-token |
| JWT | POST /api/auth/jwt/loginmasuk untuk melihat | admin / admin123, user / user123 |
| OAuth 2.0 | /api/auth/oauth/*masuk untuk melihat | apibee-client / apibee-secret, klien publik apibee-public |
| OAuth 1.0a | /api/auth/oauth1/*masuk untuk melihat | Consumer apibee-consumer-key / apibee-consumer-secret, token apibee-access-token / apibee-token-secret |
Basic dan Digest
Password yang tidak ada atau salah mengembalikan 401 dengan header WWW-Authenticate, yang membuat browser dan library HTTP meminta kredensial atau mencoba lagi dengan kredensial:
$ curl -i -u apibee:password123 https://api.testingapis.com/api/auth/basic
HTTP/1.1 200 OK
{"authenticated":true,"scheme":"basic","user":"apibee"}
$ curl -i https://api.testingapis.com/api/auth/basic
HTTP/1.1 401 Unauthorized
www-authenticate: Basic realm="apibee", charset="UTF-8"
{"status":401,"error":"Unauthorized","message":"Missing or malformed Basic Authorization header."}Digest butuh dua kali bolak-balik. 401 pertama membawa nonce, dan klien menjawab dengan hash password, bukan password itu sendiri. --digest milik curl melakukan keduanya:
$ curl -i --digest -u apibee:password123 https://api.testingapis.com/api/auth/digest
HTTP/1.1 401 Unauthorized
www-authenticate: Digest realm="apibee", qop="auth", algorithm=MD5, nonce="MTc5...", opaque="069d..."
HTTP/1.1 200 OK
{"authenticated":true,"scheme":"digest","user":"apibee","qop":"auth","nc":"00000001"}Untuk menguji klien yang harus berurusan dengan server yang menyembunyikan resource terlindungi, /api/auth/basic-hidden/{user}/{pass}masuk untuk melihat mengembalikan 404, bukan 401, saat kredensial tidak cocok.
Token bearer dan API key
Ketiga endpoint API key menerima key yang sama di tempat berbeda, jadi Anda bisa memeriksa apakah klien Anda menaruhnya di tempat yang diharapkan API. Token bearer yang salah mengembalikan WWW-Authenticate: Bearer error="invalid_token".
/api/auth/api-key/header401 vs 403
401 berarti server tidak tahu siapa Anda: tanpa kredensial, atau kredensialnya buruk. 403 berarti server tahu persis siapa Anda, dan Anda tidak diizinkan. Klien harus menanganinya secara berbeda. Setelah 401, klien bisa login lagi atau memperbarui token. Setelah 403, login lagi tidak mengubah apa pun. Endpoint role mengembalikan masing-masing:
$ curl -i https://api.testingapis.com/api/auth/roles/admin
HTTP/1.1 401 Unauthorized
www-authenticate: Bearer realm="apibee"
$ curl -i -H "Authorization: Bearer user-token" https://api.testingapis.com/api/auth/roles/admin
HTTP/1.1 403 Forbidden
{"status":403,"error":"Forbidden","message":"This endpoint requires the 'admin' role.","roles":["user"],"scopes":["read","write"]}
$ curl -H "Authorization: Bearer admin-token" https://api.testingapis.com/api/auth/roles/admin
{"authorized":true,"user":"Alice Admin","roles":["admin","user"],"scopes":["read","write","delete"]}DELETE /api/auth/roles/resourcemasuk untuk melihat memeriksa scope, bukan role. readonly-token hanya punya read, jadi ia mendapat 403 yang menyebut scope delete yang kurang.
Penandatanganan request HMAC
API pembayaran dan webhook sering menandatangani request alih-alih mengirim secret. Di sini tanda tangannya adalah HMAC-SHA256 dalam hex huruf kecil dari timestamp + "." + raw body, dikirim di X-Signature dengan timestamp di X-Timestamp. Tandatangani byte persis yang Anda kirim. Menyerialisasi ulang JSON setelah menandatangani adalah cara paling umum merusaknya.
import { createHmac } from "node:crypto";
const secret = "apibee-hmac-secret";
const body = JSON.stringify({ orderId: 1001, amount: 49.99 });
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");
const res = await fetch("https://api.testingapis.com/api/auth/hmac", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Timestamp": timestamp,
"X-Signature": signature,
},
body,
});
console.log(res.status, await res.json());200 { authenticated: true, scheme: 'hmac-sha256', timestamp: 1791341858, bodyLength: 31 }
# The same signature sent with a different body:
401 {
status: 401,
error: 'Unauthorized',
message: 'Signature mismatch.',
hint: 'X-Signature = lowercase hex HMAC-SHA256(secret, X-Timestamp + "." + raw request body). Optional \'sha256=\' prefix.',
bodyLength: 30
}Untuk memeriksa tanda tangan secara manual, generator hash menghitung HMAC-SHA256 di browser.
Login JWT, kedaluwarsa, dan refresh
/api/auth/jwt/loginmasuk untuk melihat mengembalikan access token bertanda tangan yang berlaku 15 menit dan sebuah refresh token. Refresh token berotasi: setiap refresh mengembalikan yang baru, dan yang lama berhenti berfungsi. /api/auth/jwt/expiredmasuk untuk melihat memberikan token yang ditandatangani dengan benar tetapi sudah kedaluwarsa, agar Anda bisa menguji jalur kedaluwarsa tanpa menunggu 15 menit.
const BASE = "https://api.testingapis.com/api/auth/jwt";
const login = await fetch(`${BASE}/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "user", password: "user123" }),
});
const tokens = await login.json();
// { accessToken, tokenType: "Bearer", expiresIn: 900, refreshToken, user }
const auth = { Authorization: `Bearer ${tokens.accessToken}` };
console.log((await fetch(`${BASE}/me`, { headers: auth })).status); // 200
console.log((await fetch(`${BASE}/admin`, { headers: auth })).status); // 403: role is "user"
// A correctly signed token that expired an hour ago, for the negative test.
const { accessToken: expired } = await (await fetch(`${BASE}/expired`)).json();
const old = await fetch(`${BASE}/me`, { headers: { Authorization: `Bearer ${expired}` } });
console.log(old.status, old.headers.get("www-authenticate"));
// 401 Bearer error="invalid_token", error_description="The token expired at '...'"
// Refresh tokens rotate: use the new one next time.
const refreshed = await fetch(`${BASE}/refresh`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refreshToken: tokens.refreshToken }),
});
console.log(refreshed.status); // 200Tempel token mana pun ke decoder JWT untuk melihat claim dan waktu kedaluwarsanya.
OAuth 2.0 dengan PKCE
Server OAuth mendukung grant authorization code dengan PKCE, client credentials, password, dan refresh token, ditambah introspeksi dan pencabutan. Otorisasi disetujui otomatis sebagai pengguna apibee, jadi seluruh alur bisa dijalankan dalam satu skrip. Redirect URI di localhost atau 127.0.0.1 (port apa pun) dan callback Postman mendapat redirect 302 sungguhan.
Authorization code dengan PKCE
import { createHash, randomBytes } from "node:crypto";
const BASE = "https://api.testingapis.com/api/auth/oauth";
const redirectUri = "http://localhost:3000/callback";
// 1. A random code verifier and its S256 challenge.
const verifier = randomBytes(32).toString("base64url");
const challenge = createHash("sha256").update(verifier).digest("base64url");
// 2. Send the user to /authorize. This server approves at once and redirects
// to the redirect URI with ?code=...&state=...
const authorize = new URL(`${BASE}/authorize`);
authorize.search = new URLSearchParams({
response_type: "code",
client_id: "apibee-public",
redirect_uri: redirectUri,
scope: "read profile",
state: "xyz123",
code_challenge: challenge,
code_challenge_method: "S256",
});
const redirect = await fetch(authorize, { redirect: "manual" });
const code = new URL(redirect.headers.get("location")).searchParams.get("code");
// 3. Exchange the code and the verifier for tokens.
const tokenRes = await fetch(`${BASE}/token`, {
method: "POST",
body: new URLSearchParams({
grant_type: "authorization_code",
client_id: "apibee-public",
code,
redirect_uri: redirectUri,
code_verifier: verifier,
}),
});
const tokens = await tokenRes.json();
// 4. Call a protected endpoint.
const me = await fetch(`${BASE}/userinfo`, {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
console.log(me.status, await me.json());200 {
sub: 'apibee',
name: 'TestingAPIs Tester',
preferred_username: 'apibee',
email: null,
email_verified: null
}Dua kegagalan yang harus ditangani klien PKCE:
# Sending the same code a second time
400 { error: 'invalid_grant', error_description: 'Authorization code is invalid, expired or already used.' }
# Sending a code_verifier that doesn't match the challenge
400 { error: 'invalid_grant', error_description: 'PKCE verification failed: code_verifier does not match code_challenge.' }Client credentials
Untuk panggilan antarlayanan, tanpa melibatkan pengguna:
$ curl -X POST https://api.testingapis.com/api/auth/oauth/token \
-d "grant_type=client_credentials&client_id=apibee-client&client_secret=apibee-secret&scope=read"
{"access_token":"oat_3fc4...","token_type":"Bearer","expires_in":3600,"scope":"read"}Request bertanda tangan OAuth 1.0a
OAuth 1.0a menandatangani setiap request dengan dua secret, consumer secret dan token secret, alih-alih mengirim token saja. Server uji menerima tanda tangan HMAC-SHA1, HMAC-SHA256, HMAC-SHA512, dan PLAINTEXT. Pakai access token tetap apibee-access-token / apibee-token-secret yang tidak pernah kedaluwarsa, atau jalankan alur three-legged untuk mendapatkan token sendiri. Otorisasi disetujui otomatis sebagai pengguna apibee.
POST /api/auth/oauth1/request_tokenmasuk untuk melihat, ditandatangani hanya dengan consumer key dan secret, denganoauth_callbackdiisi URL atauoob.GET /api/auth/oauth1/authorize?oauth_token=…masuk untuk melihat me-redirect ke callback localhost atau Postman denganoauth_verifier, dan mengembalikannya sebagai JSON untukoob.POST /api/auth/oauth1/access_tokenmasuk untuk melihat, ditandatangani dengan request token dan secret-nya, ditambahoauth_verifier. Access token berlaku satu jam.
The protected resource, with the static access token:
1. New request: GET https://api.testingapis.com/api/auth/oauth1
2. Authorization → Type: OAuth 1.0
Signature Method: HMAC-SHA1
Consumer Key: apibee-consumer-key
Consumer Secret: apibee-consumer-secret
Access Token: apibee-access-token
Token Secret: apibee-token-secret
Add authorization data to: Request Headers
3. Send. The response has "authenticated": true.
The three-legged flow, with the same Authorization settings:
1. POST https://api.testingapis.com/api/auth/oauth1/request_token
Leave Access Token and Token Secret empty.
Advanced → Callback URL: oob
2. Open GET https://api.testingapis.com/api/auth/oauth1/authorize?oauth_token=<oauth_token from step 1>
and copy oauth_verifier from the JSON.
3. POST https://api.testingapis.com/api/auth/oauth1/access_token
Access Token: oauth_token from step 1
Token Secret: oauth_token_secret from step 1
Advanced → Verifier: oauth_verifier from step 2
4. Use the oauth_token and oauth_token_secret it returns as
Access Token and Token Secret for GET /api/auth/oauth1.PLAINTEXT, di tab cURL, tidak butuh library penandatanganan: tanda tangannya adalah signing key itu sendiri, consumer_secret&token_secret, dengan & dikirim sebagai %26. Pakai untuk memeriksa kredensial, lalu beralih ke HMAC-SHA1 untuk menguji kode penandatanganan Anda.
Saat tanda tangan tidak cocok, 401 menyertakan signature base string yang dibangun server, sehingga Anda bisa membandingkannya dengan milik klien Anda. Kegagalan yang layak diuji:
# Signed with the wrong token secret
401 signature_invalid: Signature mismatch.
details.signatureBaseString: GET&https%3A%2F%2Fapi.testingapis.com%2Fapi%2Fauth%2Foauth1&oauth_consumer_key%3D...
# The same oauth_nonce sent twice
401 nonce_used: This oauth_nonce was already used with this timestamp. Send a new nonce.
# The same request token exchanged twice
401 token_used: This request token was already exchanged for an access token.Aturan tanda tangan OAuth 1.0a membahas di mana parameter diletakkan, jendela timestamp, dan setiap kode oauth_problem.
Tes negatif yang layak ditulis
- Tanpa kredensial: harapkan 401 dan header
WWW-Authenticate. - Password salah, key salah, token diubah: 401, tidak pernah 500.
- Pengguna valid tanpa role atau scope: 403, dan klien tidak me-logout pengguna.
- JWT kedaluwarsa: klien melakukan refresh sekali, lalu mengulang request aslinya.
- Refresh token dipakai dua kali: pemakaian kedua gagal.
- Kode OAuth dipakai dua kali, atau dengan verifier yang salah:
invalid_grant. - Tanda tangan HMAC atas body yang berbeda: 401.
- Request OAuth 1.0a dengan nonce yang dipakai ulang atau tanda tangan salah: 401
nonce_usedatausignature_invalid.
Konsep di balik tes-tes ini ada di pelajaran autentikasi dan otorisasi (dalam bahasa Inggris).