Bluesky (AT Protocol) の認証関連 API のふるまい詳細メモ
Bluesky の SDK を使ったウェブアプリ (tweet-to-sky) の開発に際して、 Bluesky (正確には AT Protocol) の認証関連の API の詳細を調べることあったので、メモを残しておく。だって API Doc がないんだもん 🥺
認証関連の API
は正確には:
the atproto PDS server and account management APIs
セッションを作成する・ログインする
SDK
SDK ではこんな感じにセッションを作成(ログイン)する。
戻り値のオブジェクトは resumeSession
(後述) で使うための AtpSessionData
型とはちょっと違うのがややこしい…。
const agent = new AtpAgent({ service: "https://bsky.social" });
const sessionDataLike = agent.login({
identifier: "***",
password: "***",
});
SDK 内部 (atp-agent.ts#L308) では:
- API (com.atproto.server.createSession) を呼び出して
- agent の状態としてセッションを保存して
- agent の使用者側へ通知する
HTTP API
curl で com.atproto.server.createSession を実行する。
ID=***
PASS=***
curl --silent -X POST -H "Content-Type: application/json" https://bsky.social/xrpc/com.atproto.server.createSession -d "{\"identifier\":\"$ID\",\"password\":\"$PASS\"}" | jq
{
"did": "***",
"didDoc": { ... },
"handle": "***",
"email": "***",
"emailConfirmed": true,
"emailAuthFactor": false,
"accessJwt": "***",
"refreshJwt": "***",
"active": true
}
Bluesky における「セッション作成」の実態は「アクセストークンとリフレッシュトークンを取得する」ということ。
セッション情報を取得する・セッションを再開する
セッションを再開する という表現は少し変かもしれない。 ログインで取得したトークンに停止というものはなく、再度 Web API を呼ぶときは保存したトークンを使えばいいから。
SDK
SDK の場合、agent クラス (のインスタンス) をログイン状態にすることが「セッションを再開する」ことになる。
agent.resumeSession(savedSessionData);
内部(atp-agent.ts#L355)では:
- アクセストークンを使ってセッション情報を取得する
- トークンのエラーが出たら (ここの詳細は後述)
- リフレッシュトークンを使ってトークンを更新する
- セッション情報を取得する
HTTP API
セッション情報を取得する
は curl で com.atproto.server.getSession を実行する。
ACCESS=***
curl --silent -H "Authorization: Bearer $ACCESS" https://bsky.social/xrpc/com.atproto.server.getSession | jq
{
"handle": "***",
"did": "***",
"didDoc": { ... },
"email": "***",
"emailConfirmed": true,
"emailAuthFactor": false,
"active": true
}
セッションを削除する・ログアウトする
トークンを用いているのでサーバー側もステートレスかと思いきや、削除することができる。
SDK
agent.logout();
内部 (atp-agent.ts#L335)では:
- API (com.atproto.server.deleteSession) を呼び出して
- agent の状態を更新して
- agent の使用者側へ通知する
HTTP API
curl で com.atproto.server.deleteSession を実行する。 削除出来るのはアクセストークンではなく 1、リフレッシュトークンのみ。
☠️ access token を削除する機能ではない
ACCESS=***
curl --silent -X POST -H "Authorization: Bearer $ACCESS" https://bsky.social/xrpc/com.atproto.server.deleteSession
{"error":"InvalidToken","message":"Invalid token type"}
🥳 refresh token を削除する機能
REFRESH=***
curl --silent -X POST -H "Authorization: Bearer $REFRESH" https://bsky.social/xrpc/com.atproto.server.deleteSession
(内部的に) トークンをリフレッシュする
SDK
トークンのリフレッシュは然るべきタイミングで agent が自動的に実行するため、AtpAgent class の public API としては公開されていない2。
然るべきタイミング:
resumeSession
時にトークンが古かったらリフレッシュする (atp-agent.ts#L372)- API 呼び出したときに
ExpiredToken
エラーが返ってきたらリフレッシュする atp-agent.ts#L207)
HTTP API
curl で com.atproto.server.refreshSession を実行する。
REFRESH_TOKEN=***
curl --silent -X POST -H "Authorization: Bearer $REFRESH_TOKEN" https://bsky.social/xrpc/com.atproto.server.refreshSession | jq
{
"did": "***",
"didDoc": { ... },
"handle": "***",
"accessJwt": "***",
"refreshJwt": "***",
"active": true
}
deleteSession したあとは、refreshSession できない。
curl --silent -X POST -H "Authorization: Bearer $REFRESH" https://bsky.social/xrpc/com.atproto.server.deleteSession
curl --silent -X POST -H "Authorization: Bearer $REFRESH" https://bsky.social/xrpc/com.atproto.server.refreshSession | jq
{
"error": "ExpiredToken",
"message": "Token has been revoked"
}
リフレッシュトークンの有効期間は 3ヶ月 3 だが、1度使うと、有効期間がそこから2時間に短くなる。
$ curl --silent -X POST -H "Content-Type: application/json" https://bsky.social/xrpc/com.atproto.server.createSession -d "{\"identifier\":\"$ID\",\"password\":\"$PASS\"}" | jq "{accessJwt, refreshJwt}"
{
"accessJwt": "***",
"refreshJwt": "***"
}
👇️ リフレッシュトークン取得直後に使用する
$ date +%R && curl --silent -X POST -H "Authorization: Bearer $REFRESH" https://bsky.social/xrpc/com.atproto.server.refreshSession
10:32
{"did":"did:plc:...","didDoc":{"@context":["https://www.w3.org/ns/did/v1",
$ date +%R && curl --silent -X POST -H "Authorization: Bearer $REFRESH" https://bsky.social/xrpc/com.atproto.server.refreshSession
10:33
{"did":"did:plc:...","didDoc":{"@context":["https://www.w3.org/ns/did/v1",
$ date +%R && curl --silent -X POST -H "Authorization: Bearer $REFRESH" https://bsky.social/xrpc/com.atproto.server.refreshSession
12:31
{"did":"did:plc:...","didDoc":{"@context":["https://www.w3.org/ns/did/v1",
👇️ 2時間後に expire した!
$ date +%R && curl --silent -X POST -H "Authorization: Bearer $REFRESH" https://bsky.social/xrpc/com.atproto.server.refreshSession
12:32
{"error":"ExpiredToken","message":"Token has been revoked"}
(Personal Data Server が)リフレッシュトークンを管理する
前述の通り、Bluesky はトークンを使いつつもステートフルな認証を行っている:
- Personal Data Server で
deleteSession
ができる - Personal Data Server で
refreshSession
すると、リフレッシュトークンの有効期間が短くなる (JWT の内容より優先される時間がサーバー側にある)
リフレッシュトークンを削除する
Personal Data Server は deleteSession
を呼び出されると (deleteSession.ts#L18)、
DB からリフレッシュトークンを削除する (auth.ts#L178)。
リフレッシュトークンをローテートする
Personal Data Server は refreshToken
を呼び出されると、リフレッシュトークンをローテートする(refreshSession.ts#L42)。
ローテーション:
- DB からリフレッシュトークンを取得する (index.ts#L236)
- 新しい有効期限日 (2時間) を計算する (index.ts#L245-L252)
- 新しいアクセストークン・リフレッシュトークンを作成する (index.ts#L262-L268)
- DB 更新
- 既存のリフレッシュトークンの有効期限日を更新する(index.ts#L274-L278)
- 新しいリフレッシュトークンを保存する (index.ts#L279)
Footnotes
-
agent の使用者が呼び出せないわけではない (atp-agent.ts#L433) ↩
-
リフレッシュトークンは JWT なので、デコードすれば分かる。 ↩