0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Express.js】簡易なOAuth認可サーバを実装してみた

Posted at

OAuth 2.0 の勉強のために認可コードグラントに対応した簡易認可サーバをExpress.jsで実装してみました。

エンドポイント

下記のエンドポイントを実装しています。

  • 認可エンドポイント
  • トークンエンドポイント

トークンエンドポイントでは、リフレッシュトークンの発行にも対応させました。

config.js
const authzServer = {
  authorizationEndpoint: serverAddress + "/authorize",
  tokenEndpoint: serverAddress + "/token",
  responseType: ["code", "token"],
};

クライアント

クライアント情報は静的に登録しています。

config.js
const clients = [
  {
    client_id: "client_id",
    client_secret: "client_secret",
    redirect_uris: [clientAddress + "/callback"],
    scope: ["foo", "bar"],
  },
];

余力があれば、動的クライアント登録(RFC7592)にも対応させたいと思っています。

シーケンス図

シーケンスはこんな感じ。

それぞれのエンドポイントで何をやっているか簡単に説明していきます。

認可エンドポイント

認可コードを発行するエンドポイントです。

適切なクライアントからの認可リクエストを受付け、このクライアントに権限を委譲してよいかユーザーへ確認を行います。

ユーザーからクライアントへの権限委譲の許可を得られたら、その証として、認可コードを認可レスポンスとしてクライアントへ返します。

認可リクエスト

GET http://localhost:3000/authorize?client_id=client_id&redirect_url=http://localhost:9000/callback&response_type=code&scope=foo%20bar&state=201

認可エンドポイントでは、認可リクエストパラメータのチェックを行います。
すべてのチェックが正常に行われれば、認可ページをブラウザに返します。

処理順 パラメータ 必須 チェック内容 エラーレスポンス
1 client_id 登録されているクライアントIDと一致すること 認可サーバのページに「Unknown client」を表示
2 redirect_url 指定されている場合、クライアント情報に設定されているリダイレクトURLと一致すること 認可サーバのページに「Invalid redirect URI」を表示
3 response_type 認可サーバで対応している種別(codeのみ)と一致すること リダイレクトURLへ「unsupported_response_type」エラーを返す
4 scope ◯(※) 認可サーバで対応しているscope値とすべて一致すること。 リダイレクトURLへ「invalid_scope」エラーを返す

scopeは仕様では任意だが、今回作成した認可サーバでは、必須扱いとしています。

認可レスポンス

http://localhost:9000/callback?code=uEH2mhRn4Gfjthwh&state=201

認可ページでユーザーが認可を承認した場合、認可レスポンスとして、リダイレクトURLへのリダイレクトを返します。
そのとき、パラメータに発行した認可コードを付与します。

認可レスポンスではcodeパラメータは必須となり、stateパラメータは認可リクエストに含まれていた場合、必須となります。

トークンエンドポイント

アクセストークンおよびリフレッシュトークンを発行するエンドポイントです。
適切なクライアントからトークンリクエストを受付け、認可コードを元にトークンを発行します。

クライアント認証

トークンエンドポイントでは、クライアント認証も行います。
クライアント認証は、リクエストヘッダーやボディに含まれるクライアントIDとシークレットを検証することで行います。

Http Basic認証またはリクエストボディのいずれかの方法でclient_idclient_secretを渡せるようになっています。

トークンリクエスト

POST http://localhost:3000/token

トークンエンドポイントでは、トークンリクエストボディのパラメータチェックを行います。

すべてのチェックが正常に行われれば、トークンをクライアントに返します。

処理順 パラメータ 必須 チェック内容 エラーレスポンス
1 grant_type authorization_codeまたはrefresh_tokenであるかを確認 クライアントへ「unsupported_grant_type」エラーを返す
2 code 認可レスポンスで返した認可コードと一致すること クライアントへ「invalid_grant」エラーを返す
3 client_id 指定されている場合、認可リクエスト時のclient_idと一致すること クライアントへ「invalid_grant」エラーを返す

トークンレスポンス

{
    "access_token": "w1boanear7T92toNv7UYfEgMrTnfAigL",
    "refresh_token": "uCWgP7ONnbQaCAqrDifJbuLouPTYsygO",
    "token_type": "Bearer",
    "scope": "foo bar"
}

トークンレスポンスとして、トークン情報以外にも、token_typescopeを返します。

token_typeはアクセストークンがBearer(持参人)トークンとして、提示することを期待していることを表しています。
scopeは、アクセストークンがどのスコープで権限委譲されたかを表しています。

最後に

簡単に、今回実装した簡易認可サーバの仕様について説明しました。

OAuth 2.0 を知れば知るほど、わかってたつもりがよくわからなくなったりと、まだまだ完璧に理解した!とまで言えない奥が深い仕様だなーと改めて感じました。

まだまだ改善や拡張の残る状態ではありますが、そこについては今後の余力次第で対応していきたいと思っています。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?