Microsoft Entra ID (旧称 Azure AD) との OIDC / OAuth 2.0 連携時の簡易的な検証のやり方 その (2)

# はじめに
こんにちは。またもや久しぶりの投稿になってしまいました。
今年こそは毎日、毎週とは言わずとも月に一本ぐらいは記事を書きたいと思っていました。
気付けば 2024 年も師走になってしまいました。

やっぱり思っているだけではなくて行動に移さなければならないですね。
社会人としての人生も折り返しを過ぎてますし、悔いが残らないように 2025 年こそは頑張ります。

さて、今回は 2 年越しの企画…というわけではありませんが、下記の記事の続きを書きます。
前回はアクセス トークンを取得して Graph API を実行するところまでをまとめました。今回は、アクセス トークンを取得する際に更新トークンも取得して、取得した更新トークンを利用してアクセス トークンを取得するところをやってみます。

[Azure AD との OIDC / OAuth 2.0 連携時の簡易的な検証のやり方](https://qiita.com/makotsur/items/cc42fda61d048d94384b)

# 前提
今回の記事を読む前に、まずは上記の記事に目を通していただけるとありがたいです。
本記事は、上記記事の内容を少し変更して、アクセス トークンと更新トークンを取得する流れとなります。

# 更新トークンを取得してみる
前回の記事ではアクセス トークンを取得するために scope に "User.Read" を指定した例で説明しました。
細かい話は今回も割愛しますが、更新トークンも併せて取得する場合は、scope に "offline_access" も併せて指定することで、アクセス トークンの取得と同時に更新トークンも取得することが可能です。

[Microsoft ID プラットフォームでのスコープとアクセス許可](https://learn.microsoft.com/ja-jp/entra/identity-platform/scopes-oidc)

> offline_access スコープ
> …中略…
> ユーザーが offline_access スコープを承認すると、アプリは Microsoft ID プラットフォーム トークン エンドポイントから更新トークンを取得できます。 更新トークンの有効期間は長期です。 アプリは、古いアクセス トークンの有効期限が切れると、新しいアクセス トークンを取得できます。

## 認可コードの取得
以下のように変更して認可コードを取得してみましょう。
scope に指定する内容を変更するだけで、あとは前回の記事から変更点はありません。
なお、"%20" は半角スペースを URL エンコードしたときのコードであり、"User.Read%20offline_access" は "User.Read offline_access" を URL エンコードしたものです。

    GET https://login.microsoftonline.com/{ディレクトリ (テナント) ID}/oauth2/v2.0/authorize? 
    client_id={アプリケーション (クライアント) ID}
    &response_type=code
    &redirect_uri={リダイレクト URI}
    &scope=User.Read%20offline_access

## アクセス トークンと更新トークンの取得
問題なく認可コードを取得できましたよね？
では、取得した認可コードを利用して、アクセス トークンと更新トークンを取得してみましょう。
ここも前回からの変更点は scope に指定する内容を変更するのみです。

    POST https://login.microsoftonline.com/{ディレクトリ (テナント) ID}/oauth2/v2.0/token
    Host: https://login.microsoftonline.com
    Content-Type: application/x-www-form-urlencoded
    
    client_id={アプリケーション (クライアント) ID}
    &scope=User.Read%20offline_access
    &code={取得した認可コード}
    &redirect_uri={リダイレクト URI}
    &grant_type=authorization_code
    &client_secret={クライアント シークレット}

前回のように PowerShell で試してみると、以下のように "refresh_token" も含まれた情報が取得されることを確認いただけるかと思います。

    token_type     : Bearer
    scope          : User.Read profile openid email
    expires_in     : 5059
    ext_expires_in : 5059
    access_token   : eyJ0eXAiOiJKV1QiLCJub25jZSI6InR...yO4UaD2tGXcEfGO04YWy_6TH3_441xg
    refresh_token  : 1.AXEAt3GEN3K9vES5gj1Z6FI7Npr-z...4FCk8Ujv6fwsD6wKjWyvojbgIIwXHUk

# 更新トークンでアクセス トークンを取得してみる
前置きが長かったですが、今回の記事の本題にようやくたどり着きました😂

取得した更新トークンを利用してアクセス トークンを取得してみましょう。
ポイントは、grant_type に "refresh_token" を指定して、refresh_token に取得している更新トークンを指定しているところです。

[アクセス トークンを更新する](https://learn.microsoft.com/ja-jp/entra/identity-platform/v2-oauth2-auth-code-flow#refresh-the-access-token)

    POST https://login.microsoftonline.com/{ディレクトリ (テナント) ID}/oauth2/v2.0/token
    Host: https://login.microsoftonline.com
    Content-Type: application/x-www-form-urlencoded

    client_id={アプリケーション (クライアント) ID}
    &grant_type=refresh_token
    &scope={リソースへのアクセスに必要なアクセス許可の一覧}
    &client_secret={クライアント シークレット}
    &refresh_token={取得した更新トークン}

以下はそれぞれの情報を埋め込んだ例です。

    https://login.microsoftonline.com/ac7b3f7e-0000-1111-2222-6ce0f67a4d44/oauth2/v2.0/token

    client_id=ca8f4006-1deb-4ba4-9dc1-b1353702c294
    &grant_type=refresh_token
    &scope=User.Read%20offline_access
    &client_secret=zlm8Q~CTbLQmIjr3a...BhvvLCo8KQngc3YTb63
    &refresh_token=1.AXEAt3GEN3K9vES5gj1Z6FI7Npr-z...4FCk8Ujv6fwsD6wKjWyvojbgIIwXHUk

上記の POST リクエストを送信することで新しいアクセス トークンと新しい更新トークンが得られます。
更新トークンの有効期限は 90 日となりますが、新しい更新トークンは、トークンが発行されたタイミングから改めて 90 日間の有効期限が設定されます。

なお、更新前の古い更新トークンはもともとの有効期限のままとなります。
良くある状況として、アプリケーション側で保持しているアクセス トークンは定期的に更新しているものの、更新トークンは古いままで更新していない、というケースが良くあります。古い更新トークンの有効期限内であればアクセス トークンを取得できますが、90 日が経過して古い更新トークンの有効期限が切れるとアクセス トークンを取得することができなくなります。
上記のとおり、アクセス トークンを更新すると新しい更新トークンも発行されるため、アプリケーションで保持している更新トークンも新しいものへ忘れることなく更新しましょう。

PowerShell を活用した方法も紹介します。
Postman など、もし使い慣れたツールがあればそちらでも構いません。

## PowerShell を利用した方法
アクセス トークンを取得するときと大きな変更点はありません。
上記にも記載のとおり grant_type に "refresh_token" を指定して、refresh_token に取得している更新トークンを指定しているところがポイントです。

    $tenantId = '{テナント ID}'
    $tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
    $parameters = @{
        client_id='{アプリケーション (クライアント) ID}';
        grant_type='refresh_token';
        scope='{リソースへのアクセスに必要なアクセス許可の一覧}';
        client_secret='{クライアント シークレット}';
        refresh_token='{更新トークン}'
    }
    $ret = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -Body $parameters
    $ret

コマンドの実行に成功すると、新しい更新トークンと新しい更新トークンが発行されることが確認できるかと思います😊

## 更新トークンを無効にした時にどうなるか
更新トークンの有効期限である 90 日を迎える以外に、いくつかの条件で、発行済みの更新トークンが無効化されます。

[Microsoft ID プラットフォームの更新トークン - トークンの失効](https://learn.microsoft.com/ja-jp/entra/identity-platform/refresh-tokens#token-revocation)

この中でも Azure ポータル上から無効化する方法が検証するうえでは容易な方法かと思います。

[Auzre ポータル] > [Microsoft Entra ID] > [ユーザー] > 対象のユーザー > [セッションの取り消し]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320657/f96d7b2b-cc93-256a-1798-c37e31c3376d.png)

この状態で以前取得した更新トークンを利用してアクセス トークンを取得しようとすると AADSTS50173 のエラーが発生します。もしこのようなエラーが発生した場合は、今回の例では、認可コードの再取得からの実施が必要です。

> "AADSTS50173: The provided grant has expired due to it being revoked, a fresh auth token is needed. The user might have changed or reset their password. The grant was issued on '2024-12-01T14:48:52.3012096Z' and the TokensValidFrom date (before which tokens are not valid) for this user is '2024-12-01T15:52:18.0000000Z'

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2320657/8c8371ce-e38b-f996-5d14-5a0e7a02f03b.png)

今日の記事は以上となります。

いかがでしたでしょうか。
前回の記事と合わせてご確認いただくことで、認可フローをより深く理解する一助となればうれしいです。
このような基本的な部分を知っているかどうかで、マイクロソフトが提供している MSAL (Microsoft Authentication Library) など、各種ライブラリについても、より正しく理解することができるようになると思います。

では、次は来年 2025 年 1 月に何か書きます…！が、がんばる…！