特長と用途
API版は、WebAPIを通してFIDO認証を実施する形態です。
PKaaSの認証画面は表示されず、IdP側で用意した認証画面からFIDO登録・認証を実施できます。認証画面やWebAuthn実装を自前で柔軟に制御したい場合に適した利用方法です。
API版を利用する場合には、下記の開発が必要です。
- IdPの認証画面へJavaScriptを配置
- JavaScriptが呼び出すWebAuthnのエンドポイントおよびPKaaS APIの呼び出し
API版では、FIDO登録とFIDO認証のそれぞれで、チャレンジの発行と電子署名の検証を順番に実行します。公開鍵の保存や署名検証など、FIDO認証に必要な機能をPKaaSへ委譲できるため、IdPは従来に比べて容易にFIDO認証を利用者へ提供できます。
この図は、API版を利用した際の認証システム全体の構成例です。開発者は自分の認証ページを用意するドメイン上にFIDO認証用のエンドポイントを設置し、ブラウザからのリクエストを受けてPKaaS APIへ連携します。
- ①チャレンジの発行: チャレンジと呼ばれる乱数を発行します。
- ②署名検証: 登録・認証時には①で発行したチャレンジを含んだ文書を、利用者のPCやスマホが適切な公開鍵を使って電子署名します。PKaaSはそれぞれの形式に沿って署名を検証し、その結果を返します。
API版とは別に開発環境でのみ利用いただけるローカル開発API版があります。
API版利用の流れ
1. Yahoo! JAPAN IDを取得する
最初に「Yahoo! JAPAN ID」を取得してください。
2. アプリケーションを登録する
手順1で取得した「Yahoo! JAPAN ID」でログインして、下記ページからアプリケーションを登録してください。
3. Client IDを利用して認証用エンドポイントを開発する
手順2で登録したClient ID(アプリケーションID)は認証用エンドポイントを設置するサーバーからAPIをリクエストする際に利用します。
4. PKaaS APIの事前利用連絡を行う(現在募集していません)
PKaaSを利用するためには、個別のドメインや通信について事前にご連絡いただいたうえで、許諾可能かどうかを判断いたします。現在利用連絡は限定したお客様以外から受け付けておりません。
API版リファレンス
PKaaSが提供するAPIは以下の通りです。
PKaaSはFIDO登録とFIDO認証に関する2対のAPIを提供しています。必ず「登録のチャレンジ発行」→「登録の署名検証」もしくは「認証のチャレンジ発行」→「認証の署名検証」の順番で実行してください。
| エンドポイント | 概要 |
|---|---|
/api/attestation/options |
登録のチャレンジ発行 |
/api/attestation/result |
登録の署名検証 |
/api/assertion/options |
認証のチャレンジ発行 |
/api/assertion/result |
認証の署名検証 |
共通ヘッダー
これらすべてのAPIリクエストには下記のヘッダーを付与してください。
| パラメータ | 必須 | 値 | 説明 |
|---|---|---|---|
| User-agent | ◯ | string | "Yahoo AppID:"に続けて、前の手続きで取得したClientIDを入力してください。 |
| origin | ◯ | string | 前の手順にて申請済みの開発者サイトのドメインを入力してください。 |
登録のチャレンジ生成
- リクエストURL
https://pkaas.yahooapis.jp/api/attestation/options- メソッド
- POST
- Content-Type
- application/json
リクエストパラメータ
標準的なブラウザの実装により対応状況が異なります。MDNなどを参考にしてください。
| パラメータ | 必須 | 値 | 説明 |
|---|---|---|---|
| username | ◯ | string | PKaaSがユーザーを識別するための識別子です。開発者サイトで利用しているユーザー名と"#"に続けて、申請済みの開発者サイトのドメインを結合してください。例えばユーザー名が「sample1234」、開発者サイトのドメインが「example.com」の場合にはsample1234#example.comとしてください。 |
| displayName | string | ユーザーの表示に用いるためのニックネームです。 | |
| authenticatorSelection | array(string) | MDNを参照してください。 | |
| attestation | string | MDNを参照してください。 |
サンプルリクエスト
curl 'https://pkaas.yahooapis.jp/api/attestation/options' \
-H "Content-Type: application/json" \
-H "User-agent: Yahoo AppID:<Client ID>" \
-H "Origin: <YOUR DOMAIN>" \
--data-raw '{"username":"sample1234#example.com","displayName":"sample1234#example.com","authenticatorSelection":{"requireResidentKey":true,"userVerification":"preferred","authenticatorAttachment":"platform"},"attestation":"none"}'
レスポンスパラメータ
エラー時のレスポンスはYahoo!デベロッパーネットワークのエラーメッセージもしくは、LINE FIDO2 serverのエラーメッセージに準じます。
| パラメータ | 値 | 説明 |
|---|---|---|
| status | string | 処理結果を示します。"ok": 処理に成功、"failed":処理に失敗 |
| errorMessage | array(string) | LINE FIDO2 serverの内部エラーに準じたエラーメッセージです。 |
| sessionId | string | セッションIDです。 |
| (rp以下) | Navigator.credentials.create()のパラメータに準じます。登録するサイトのドメインやチャレンジなどが含まれています。詳しくはMDNなどを参照してください。 |
サンプルレスポンス
{
"status": "ok",
"errorMessage": "",
"sessionId": "",
"rp": {
"name": "PKaaS Demo",
"icon": "",
"id": "example.com"
},
"user": {
"name": "sample1234#example.com",
"icon": "",
"id": "n99PZyE5b_muRkeyRUyvCgg4XsXg5YA816PVVO0UqBI",
"displayName": "sample1234#example.com"
},
"challenge": "PTlwETmKOkcS1m3__3__1LDf1kWX8OZIZCZYSqdWzoJRWUAkvRb9velN98Wvdgd0VwNCwBpX_-UKEA3Fv4dRRA",
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -65535
},
{
"type": "public-key",
"alg": -257
}
// ... (その他のパラメータ)
],
"timeout": 180000,
"excludeCredentials": [],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"requireResidentKey": true,
"userVerification": "preferred"
},
"attestation": "none",
"extensions": {
"credProps": true
}
}
登録の署名検証
- リクエストURL
https://pkaas.yahooapis.jp/api/attestation/result- メソッド
- POST
- Content-Type
- application/json
リクエストパラメータ
標準的なブラウザの実装により対応状況が異なります。MDNなどを参考にしてください。
| パラメータ | 必須 | 値 | 説明 |
|---|---|---|---|
| rawId | ◯ | MDNを参照してください。 | |
| id | ◯ | MDNを参照してください。 | |
| response | ◯ | MDNを参照してください。 | |
| type | ◯ | string | "public-key"を指定してください。 |
| extensions | MDNを参照してください。 |
サンプルリクエスト
curl 'https://pkaas.yahooapis.jp/api/attestation/result' \
-H "Content-Type: application/json" \
-H "User-agent: Yahoo AppID:<Client ID>" \
-H "Origin: <YOUR DOMAIN>" \
--data-raw '{"rawId":"sampleSample","id":"sampleSample","response":{"clientDataJSON":"eyJ....","transports":["hybrid","internal"]},"type":"public-key","extensions":{"credProps":{"rk":true}}}'
レスポンスパラメータ
| パラメータ | 値 | 説明 |
|---|---|---|
| status | string | 処理結果を示します。"ok": 処理に成功、"failed":処理に失敗 |
| errorMessage | array(string) | LINE FIDO2 serverの内部エラーに準じたエラーメッセージです。詳しくはこちらを参照してください。 |
| sessionId | string | セッションIDです。 |
サンプルレスポンス
{"status":"ok","errorMessage":"","sessionId":""}
認証のチャレンジ発行
- リクエストURL
https://pkaas.yahooapis.jp/api/assertion/options- メソッド
- POST
- Content-Type
- application/json
リクエストパラメータ
標準的なブラウザの実装により対応状況が異なります。MDNなどを参考にしてください。
| パラメータ | 必須 | 値 | 説明 |
|---|---|---|---|
| username | ◯ | string | PKaaSがユーザーを識別するための識別子です。開発者サイトで利用しているユーザー名と"#"に続けて、申請済みの開発者サイトのドメインを結合してください。例えばユーザー名が「sample1234」、開発者サイトのドメインが「example.com」の場合にはsample1234#example.comとしてください。 |
| userVerification | string | 認証器の検証方法を指定します。 "required" 必ずユーザー検証を伴う認証器を使用します。 "preferred" ユーザー検証が可能であれば行いますが、必須ではありません。 "discouraged" ユーザー検証を行わない認証器を優先します。 |
サンプルリクエスト
curl 'https://pkaas.yahooapis.jp/api/assertion/options' \
-H "Content-Type: application/json" \
-H "User-agent: Yahoo AppID:<Client ID>" \
-H "Origin: <YOUR DOMAIN>" \
--data-raw '{"username":"sample1234#example.com","userVerification":"preferred"}'
レスポンスパラメータ
| パラメータ | 値 | 説明 |
|---|---|---|
| status | string | 処理結果を示します。"ok": 処理に成功、"failed":処理に失敗 |
| errorMessage | array(string) | LINE FIDO2 serverの内部エラーに準じたエラーメッセージです。 |
| sessionId | string | セッションIDです。 |
| (challenge以下) | Navigator.credentials.get()のパラメータに準じます。登録するサイトのドメインやチャレンジなどが含まれています。詳しくはMDNなどを参照してください。 |
サンプルレスポンス
{
"status": "ok",
"errorMessage": "",
"sessionId": "",
"challenge": "ssSafaXDbmLDxtQnFPALcUYWDdOT180yOoay-P0EdeDVj6uKvo28chUZOU2fLc_7LJK1wlj-ebg02iRQQPgVtw",
"timeout": 180000,
"rpId": "example.com",
"allowCredentials": [
{
"type": "public-key",
"id": "ugHWbvJQEDUeqLzA5wZqO6KEnT-lRLtMHbLbboNIDDc",
"transports": [
"internal"
]
}
],
"userVerification": "preferred",
"extensions": {}
}
認証の署名検証
- リクエストURL
https://pkaas.yahooapis.jp/api/assertion/result- メソッド
- POST
- Content-Type
- application/json
リクエストパラメータ
標準的なブラウザの実装により対応状況が異なります。MDNなどを参考にしてください。
| パラメータ | 必須 | 値 | 説明 |
|---|---|---|---|
| rawId | ◯ | string | クレデンシャル識別子(credentialId) |
| id | ◯ | string | クレデンシャル識別子(credentialId)のBase64URLエンコードした文字列 |
| response | ◯ | list | Navigator.credentials.get()のパラメータに準じます。登録するサイトのドメインやチャレンジなどが含まれています。詳しくはMDNなどを参照してください。 |
サンプルリクエスト
curl 'https://pkaas.yahooapis.jp/api/assertion/result' \
-H "Content-Type: application/json" \
-H "User-agent: Yahoo AppID:<Client ID>" \
-H "Origin: <YOUR DOMAIN>" \
--data-raw '{"rawId":"sampleSample","id":"sampleSample","response":{"clientDataJSON":"eyJ...","signature":"...","authenticatorData":"..."},"type":"public-key","extensions":{}}'
レスポンスパラメータ
| パラメータ | 値 | 説明 |
|---|---|---|
| status | string | 処理結果を示します。"ok": 処理に成功、"failed":処理に失敗 |
| errorMessage | array(string) | LINE FIDO2 serverの内部エラーに準じたエラーメッセージです。 |
| sessionId | string | セッションIDです。 |
サンプルレスポンス
{"status":"ok","errorMessage":"","sessionId":""}
API版サンプルコード
API版PKaaSを利用する際のサンプルコードを示します。
API版PKaaSの実装例はこちらを参照してください。
PKaaSデモの対応機種・ブラウザ
- • Android: バージョン9.0以上、画面ロックを設定済み
- • iOS/iPadOS: バージョン16以上、画面ロックを設定済み
- • macOS: バージョン13(macOS Ventura)以上、画面ロックを設定済み
- • 推奨ブラウザ:
- - Google Chrome
- - Microsoft Edge
- - Safari
- *一部のバージョンは非対応の場合があります。
開発者のサーバーに設置するもの
認証用ページ(HTML)
まず、認証用ページとして、以下のようなHTMLを設置します。
FIDO認証を実現するために、「ユーザー名」を入れるだけでFIDO登録・認証を行うことを目的としています。
実際に導入をご検討いただく際には、ご自身のサイトに合わせた適切なデザインや設計が必要です。
この設計は、利用者による不正を防ぐために重要な役割を果たします。例えば、既存の認証方法にPKaaSを利用したFIDO認証を追加する場合、既存の認証方法で成功したユーザーにのみFIDO登録を行わせるように設計することで、他人のアカウントに対してFIDO登録することを防ぐことが可能です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PKaaS Demo</title>
<style>
body { font-family: sans-serif; margin: 24px; }
label, input, button { font-size: 16px; }
input { margin: 0 0 12px; padding: 6px 8px; width: 320px; max-width: 100%; display: block; }
button { margin-right: 8px; padding: 8px 14px; }
#status { white-space: pre-wrap; margin-top: 16px; border: 1px solid #ccc; padding: 12px; }
.hidden { display: none; }
</style>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="/index.js"></script>
</head>
<body>
<h1>PKaaSデモ検証用サイト</h1>
<div>
<label for="username">ユーザー名:</label>
<input type="text" id="username" name="username">
</div>
<div>
<label for="userDisplayName">表示名:</label>
<input type="text" id="userDisplayName" name="userDisplayName">
</div>
<input type="hidden" name="prfFirst" value="">
<input type="hidden" name="prfSecond" value="">
<div>
<button id="register">登録</button>
<button id="authenticate">認証</button>
</div>
<pre id="status" class="hidden"></pre>
</body>
</html>
認証用ページから読み込むJavaScript
FIDO認証をブラウザ上で実現する仕組みは、Web Authentication(WebAuthn)と呼ばれます。WebAuthnでは、JavaScriptを用いて、ブラウザから生体認証などの認証機能を呼び出したり、生成した電子署名をFIDOサーバーへ送信したりします。今回は、オープンソースソフトウェア(OSS)として公開されている以下のライブラリに含まれるJavaScriptを利用します。
JavaScript内から呼び出すためのエンドポイント
前の手順で設置したJavaScriptからFIDOサーバーと通信する際、ブラウザは同一のドメインに設置されたエンドポイントへの通信のみを許可しています。これは、ページに埋め込まれた悪意あるJavaScriptが、ユーザーの意図しない形で攻撃者の用意した別ドメインへ通信するのを防ぐため、ブラウザによって課されている セキュリティ制限です。
そのため、JavaScriptはまず自身のWebサイト内のFIDOサーバーへリクエストを送信する必要があります。受け取ったリクエストは、ご自身のサーバーからPKaaSへ転送することで、チャレンジの発行や署名検証の結果を取得できます。この際、ブラウザからご自身のサーバーが受け取ったリクエストに、適切なAppIDを付加する必要があります。
以下に、Node.jsを用いて開発者サイトにFIDO認証用のエンドポイントを実装する際のサンプルコードを示します。
// Node.jsサンプルコード
const http = require("http");
const fs = require("fs");
const path = require("path");
const httpProxy = require("http-proxy");
const PORT = Number(process.env.PORT || 3000);
const TARGET_URL = process.env.PKAAS_TARGET_URL || "https://pkaas.yahooapis.jp";
const CLIENT_ID = process.env.PKAAS_CLIENT_ID || "YOUR_CLIENT_ID"; // Replace with your actual client ID
const INDEX_HTML_PATH = path.join(__dirname, "index.html");
const INDEX_JS_PATH = path.join(__dirname, "index.js");
const proxy = httpProxy.createProxyServer({
target: TARGET_URL,
changeOrigin: true
});
proxy.on("proxyReq", (proxyReq, req) => {
proxyReq.setHeader("User-Agent", `Yahoo AppID:${CLIENT_ID}`);
const originHeader = req.headers.origin;
if (originHeader) {
proxyReq.setHeader("Origin", originHeader);
}
});
proxy.on("error", (error, _req, res) => {
console.error("プロキシエラー:", error);
if (!res.headersSent) {
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
}
res.end("プロキシエラーが発生しました。");
});
const server = http.createServer((req, res) => {
const requestPath = new URL(req.url, `http://${req.headers.host || `localhost:${PORT}`}`).pathname;
if (requestPath === "/") {
serveFile(res, INDEX_HTML_PATH, "text/html; charset=utf-8");
return;
}
if (requestPath === "/index.js") {
serveFile(res, INDEX_JS_PATH, "application/javascript; charset=utf-8");
return;
}
if (requestPath.startsWith("/assertion/") || requestPath.startsWith("/attestation/")) {
req.url = rewriteProxyPath(req.url, true);
proxy.web(req, res);
return;
}
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
res.end("ページが見つかりません。");
});
server.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
console.log(`Proxy target: ${TARGET_URL}`);
});
function serveFile(res, filePath, contentType) {
fs.readFile(filePath, (error, content) => {
if (error) {
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
res.end("サーバエラーが発生しました。");
return;
}
res.writeHead(200, { "Content-Type": contentType });
res.end(content);
});
}
function rewriteProxyPath(originalUrl, addApiPrefix) {
const url = new URL(originalUrl, `http://localhost:${PORT}`);
if (addApiPrefix) {
url.pathname = `/api${url.pathname}`;
}
return `${url.pathname}${url.search}`;
}