Azure ADでリフレッシュトークンを取得してアクセストークンを更新する

2021.12.20

いわさです。

以前の記事で、Azure ADでアクセストークンを取得し、UserInfoエンドポイントへアクセスしてみました。

アクセストークンには有効期限があります。
上記記事で取得したアクセストークンの有効期限は約1~1.5時間ほどでした。
一定間隔でサインインが必要になってしまうので、今回はリフレッシュトークンを使ってみたいと思います。

リフレッシュトークンについては、稲葉純さんの以下の記事で触れられております。

また、Microsoft IDプラットフォームの更新トークンの既定の有効期間は90日で、かつ使用されるたびに新しいトークンに置き換えられます。
このあたりは、都元ダイスケさんが以下の記事内でもリフレッシュトークンの再発行によるセッション期間について言及されています。

リフレッシュトークンの取得条件

まず、前回の方法でアクセストークン取得時の状況を確認してみます。
どうやらリフレッシュトークンは取得されていないようですね。

リフレッシュトークンを取得するためにはいくつか条件があります。
まず、前回まで使用していた暗黙的な(インプリシット)フローではリフレッシュトークンは提供されません。
前回はauthorizeエンドポイントを使ってIDトークンとアクセストークンを同時に取得していましたが、tokenエンドポイントを使った承認コードフローを使う必要があります。

そして、更新トークンを受信するには、アプリがoffline_accessスコープを明示的に要求する必要があります。
指定しなかった場合は、エンドポイントからアクセストークンだけが取得されます。

リフレッシュトークンを取得する

スコープでoffline_accessを設定

先程の条件にあったようにoffline_accessをスコープで指定した時にリフレッシュトークンが提供されます。
Azureポータル上で許可設定を行っておきます。

承認コード取得

テナントIDとクライアントIDを指定してブラウザでアクセスします。
Azure ADのサインイン画面で認証操作を行うと、承認コードがパラメータに付与されてリダイレクトされると思います。

https://login.microsoftonline.com/11111111-2222-3333-4444-555555555555/oauth2/v2.0/authorize?client_id=66666666-7777-8888-9999-000000000000&response_type=code&response_mode=query&scope=user.read+openid+profile+email&state=12345

このコードを使って、次はアクセストークンを取得します。

アクセストークン & リフレッシュトークン取得

アクセス時にシークレットあるいは証明書が必要です。
ここではAzure ADアプリケーション上でシークレットを作成し、それを利用しました。

iwasa.takahito@hoge 20211220azuread % curl -X POST -d 'client_id=66666666-7777-8888-9999-000000000000' -d 'code=<承認コード>' -d 'grant_type=authorization_code' -d 'client_secret=<シークレット>' -d 'scope=user.read+openid+profile+email+offline_access' https://login.microsoftonline.com/11111111-2222-3333-4444-555555555555/oauth2/v2.0/token | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7080  100  5568  100  1512   8224   2233 --:--:-- --:--:-- --:--:-- 10442
{
  "token_type": "Bearer",
  "scope": "email openid profile User.Read",
  "expires_in": 5107,
  "ext_expires_in": 5107,
  "access_token": "<アクセストークン>",
  "refresh_token": "<リフレッシュトークン>",
  "id_token": "<IDトークン>"
}

アクセストークンにあわせてリフレッシュトークンが取得出来ました。
まずは、アクセストークンを解析してみましょう。

Microsoftより提供されているJWT.MSを使います。

{
  "typ": "JWT",
  ...

}.{
  "aud": "00000003-0000-0000-c000-000000000000",
  "iss": "https://sts.windows.net/",
  "iat": 1639980148,
  "nbf": 1639980148,
  "exp": 1639985556,
  "acct": 0,
  "acr": "1",
  "aio": "***",
  "altsecid": "1:live.com:*******",
  "amr": [
    "pwd",
    "mfa"
  ],

...

}.[Signature]

有効期限は、2021年12月20日 16:32:36でした。

リフレッシュトークンを使ってアクセストークンを更新する

リフレッシュトークンを使う際にはtokenエンドポイントにリフレッシュトークンをパラメータに設定し、grant_typerefresh_tokenを指定します。

iwasa.takahito@hoge 20211220azuread % curl -X POST -d 'client_id=66666666-7777-8888-9999-000000000000' -d 'refresh_token=<リフレッシュトークン>' -d 'grant_type=refresh_token' -d 'client_secret=<シークレット>' -d 'scope=user.read+openid+profile+email+offline_access' https://login.microsoftonline.com/11111111-2222-3333-4444-555555555555/oauth2/v2.0/token | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7492  100  5574  100  1918   8736   3006 --:--:-- --:--:-- --:--:-- 11724
{
  "token_type": "Bearer",
  "scope": "email openid profile User.Read",
  "expires_in": 5294,
  "ext_expires_in": 5294,
  "access_token": "<新アクセストークン>",
  "refresh_token": "<新リフレッシュトークン>",
  "id_token": "<IDトークン>"
}

新しいアクセストークンが取得出来ました。
先程と同じ手順で期限を確認してみると、2021年12月20日 17:08:18でした。

UserInfoエンドポイントへアクセスしてみましょう。

iwasa.takahito@hoge 20211220azuread % curl -H 'Authorization: Bearer <新しいアクセストークン>' https://graph.microsoft.com/oidc/userinfo | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   182    0   182    0     0    846      0 --:--:-- --:--:-- --:--:--   846
{
  "sub": "HOGE",
  "name": "いわさ",
  "family_name": "いわ",
  "given_name": "さ",
  "picture": "https://graph.microsoft.com/v1.0/me/photo/$value"
}

UserInfoを取得することが出来ましたね。
古いアクセストークンは使えるのでしょうか。

iwasa.takahito@hoge 20211220azuread % curl -H 'Authorization: Bearer <古いアクセストークン>' https://graph.microsoft.com/oidc/userinfo | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   182    0   182    0     0    579      0 --:--:-- --:--:-- --:--:--   579
{
  "sub": "HOGE",
  "name": "いわさ",
  "family_name": "いわ",
  "given_name": "さ",
  "picture": "https://graph.microsoft.com/v1.0/me/photo/$value"
}

古いアクセストークンも期限までは引き続きアクセス可能です。
また、新しいリフレッシュトークンも提供されていましたね。

Microsoft IDプラットフォームでは、期限内の古いトークンは無効になりません。
詳細は以下を参照頂くのが良いですが、新しいトークンを取得出来たらサーバー側で安全に削除してくださいね。となっています。

さいごに

本日は、Azure ADのアクセストークンをリフレッシュトークンを使って更新する方法を紹介しました。
Webでリフレッシュトークンについて調べると、更新に使ったリフレッシュトークンが無効になるIdPもありました。このあたりは実装によるようですね。
Microsoft IDプラットフォームでは無効にならない。とおぼえておきましょう。