あらゆる API 認証方式のテスト用エンドポイント

Basic、Digest、Bearer トークン、API キー、HMAC 署名、JWT、OAuth 1.0a、PKCE 付き OAuth 2.0 を、資格情報付きで。

例を API に対して最後に実行した日:

ID プロバイダーを用意せずに API クライアントの認証をテストするなら、https://api.testingapis.com/api/authサインインして表示 の公開テスト用エンドポイントを使ってください。方式ごとにエンドポイントが 1 つあります。HTTP Basic、Digest、固定の Bearer トークン、ヘッダー・クエリ・Cookie の API キー、HMAC によるリクエスト署名です。JWT のログインには refresh token とネガティブテスト用の期限切れトークンがあり、OAuth 2.0 サーバーは PKCE と client credentials に対応し、OAuth 1.0a サーバーは署名付きリクエストを検証します。資格情報は固定で 公開されており、下の表と GET /api/authサインインして表示 に載っています。資格情報が間違っていれば正しく 401 を返し、有効なトークンでも必要なロールがなければ 403 を返します。

エンドポイントと資格情報

方式エンドポイント資格情報
HTTP BasicGET /api/auth/basicサインインして表示apibee / password123
HTTP Digest(MD5、qop=auth)GET /api/auth/digestサインインして表示apibee / password123
Bearer トークンGET /api/auth/bearerサインインして表示Authorization: Bearer apibee-token-123
API キー(ヘッダー)GET /api/auth/api-key/headerサインインして表示X-API-Key: apibee-key-123
API キー(クエリ)GET /api/auth/api-key/queryサインインして表示?api_key=apibee-key-123
API キー(Cookie)GET /api/auth/api-key/cookieサインインして表示Cookie: api_key=apibee-key-123
HMAC-SHA256 署名POST /api/auth/hmacサインインして表示シークレット apibee-hmac-secret
ロールとスコープGET /api/auth/roles/adminサインインして表示admin-token, user-token, readonly-token
JWTPOST /api/auth/jwt/loginサインインして表示admin / admin123、user / user123
OAuth 2.0/api/auth/oauth/*サインインして表示apibee-client / apibee-secret、公開クライアント apibee-public
OAuth 1.0a/api/auth/oauth1/*サインインして表示コンシューマー apibee-consumer-key / apibee-consumer-secret、トークン apibee-access-token / apibee-token-secret

Basic と Digest

パスワードがない、または間違っていると、WWW-Authenticate ヘッダー付きの 401 が返ります。ブラウザや HTTP ライブラリが資格情報を求めたり、資格情報を付けてリトライしたりするのは、このヘッダーがあるからです。

Terminal
$ 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 は 2 往復かかります。最初の 401 に nonce が含まれ、クライアントはパスワードそのものではなくパスワードのハッシュで 応答します。curl の --digest はその両方を行います。

Terminal
$ 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"}
この例はサインインしたメンバー向けのエンドポイントを使います。サインイン無料アカウントを作成

保護されたリソースを隠すサーバーに対応しなければならないクライアントをテストするなら、/api/auth/basic-hidden/{user}/{pass}サインインして表示 は資格情報が一致しないとき 401 ではなく 404 を返します。

Bearer トークンと API キー

3 つの API キーのエンドポイントは、同じキーを別々の場所で受け付けるので、クライアントが API の期待する場所にキーを置いて いるかを確かめられます。間違った Bearer トークンには WWW-Authenticate: Bearer error="invalid_token" が返ります。

GET/api/auth/api-key/header
このリクエストはサインインしたメンバー向けのエンドポイントを使います。サインイン無料アカウントを作成

401 と 403 の違い

401 は、サーバーがあなたが誰かを知らないことを意味します。資格情報がないか、正しくありません。403 は、あなたが誰かをはっきり知ったうえで、許可されていないことを意味します。クライアントは両者を区別して扱うべきです。401 の後はログインし直すかトークンを更新できますが、403 の後にログインし直しても何も変わりません。ロールのエンドポイントは それぞれを返します。

Terminal
$ 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/resourceサインインして表示 はロールではなくスコープを確認します。readonly-token には read しかないので、足りない delete スコープを名指しした 403 が返ります。

HMAC によるリクエスト署名

決済や Webhook の API は、シークレットを送る代わりにリクエストに署名することがよくあります。ここでの署名は timestamp + "." + raw body の HMAC-SHA256 を小文字の 16 進数にしたもので、X-Signature に入れ、タイムスタンプは X-Timestamp に入れます。送るバイト列そのものに署名してください。署名した後に JSON をシリアライズし直すのが、署名を壊すいちばんよくある原因です。

JavaScript
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
}

署名を手で確かめるなら、ハッシュジェネレーターがブラウザで HMAC-SHA256 を計算します。

JWT のログイン、期限切れ、更新

/api/auth/jwt/loginサインインして表示 は 15 分有効な署名付きアクセストークンと refresh token を返します。refresh token はローテーションします。更新するたびに新しいものが返り、古いものは使えなくなります。/api/auth/jwt/expiredサインインして表示 は正しく署名されたうえで期限切れになったトークンを渡すので、15 分待たずに期限切れの経路をテストできます。

JavaScript
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); // 200
この例はサインインしたメンバー向けのエンドポイントを使います。サインイン無料アカウントを作成

これらのトークンを JWT デコーダーに貼り付ければ、クレームと有効期限を確認できます。

PKCE 付きの OAuth 2.0

OAuth サーバーは、PKCE 付きの authorization code、client credentials、password、refresh token の各グラントに加え、 イントロスペクションと失効に対応しています。認可はユーザー apibee として自動で承認されるので、フロー全体をスクリプトで実行できます。localhost や 127.0.0.1(ポートは任意)のリダイレクト URI と Postman のコールバックには、本物の 302 リダイレクトが返ります。

PKCE 付きの authorization code

JavaScript
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
}

PKCE クライアントが扱うべき 2 つの失敗です。

Text
# 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

ユーザーが関わらない、サービス間の呼び出し向けです。

Terminal
$ 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"}
この例はサインインしたメンバー向けのエンドポイントを使います。サインイン無料アカウントを作成

OAuth 1.0a の署名付きリクエスト

OAuth 1.0a は、トークンだけを送るのではなく、コンシューマーシークレットとトークンシークレットの 2 つでリクエストごとに署名します。テストサーバーは HMAC-SHA1、HMAC-SHA256、HMAC-SHA512、PLAINTEXT の署名を受け付けます。期限のない固定のアクセストークン apibee-access-token / apibee-token-secret を使うか、three-legged フローを実行して自分のトークンを取得してください。認可はユーザー apibee として自動で承認されます。

  1. POST /api/auth/oauth1/request_tokenサインインして表示。コンシューマーのキーとシークレットだけで署名し、oauth_callback には URL か oob を指定します。
  2. GET /api/auth/oauth1/authorize?oauth_token=…サインインして表示 は localhost か Postman のコールバックに oauth_verifier 付きでリダイレクトし、oob なら JSON で返します。
  3. POST /api/auth/oauth1/access_tokenサインインして表示。request token とそのシークレットで署名し、oauth_verifier を付けます。アクセストークンは 1 時間有効です。
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.

cURL タブの PLAINTEXT は署名ライブラリが要りません。署名は署名鍵そのもの、つまり consumer_secret&token_secret で、& は %26 として送ります。これで資格情報を確かめてから、HMAC-SHA1 に切り替えて自分の署名コードをテストしてください。

署名が一致しないとき、401 にはサーバーが組み立てた署名ベース文字列が含まれるので、クライアントのものと比較できます。 テストする価値のある失敗です。

Text
# 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.

OAuth 1.0a の署名ルールでは、パラメータの置き場所、タイムスタンプの許容幅、 すべての oauth_problem コードを説明しています。

書いておきたいネガティブテスト

  • 資格情報なし:401 と WWW-Authenticate ヘッダーを期待する。
  • 間違ったパスワード、間違ったキー、改ざんされたトークン:401。500 になってはいけない。
  • 有効なユーザーだがロールやスコープがない:403。クライアントはユーザーをログアウトさせない。
  • 期限切れの JWT:クライアントは 1 回更新してから、元のリクエストをリトライする。
  • 2 回使われた refresh token:2 回目は失敗する。
  • 2 回使われた OAuth のコード、または間違った verifier:invalid_grant。
  • 別のボディで計算した HMAC 署名:401。
  • nonce を使い回したか署名が間違った OAuth 1.0a のリクエスト:401 の nonce_used か signature_invalid。

これらのテストの背景にある考え方は、認証と認可のレッスン(英語)にあります。