本記事は Microsoft Azure Tech Advent Calendar 2022 11 日目の記事です。
はじめに
初めまして。
年齢のせいか勉強してもすぐに忘れてしまうことが多くなってきたなぁ、、、と痛感している今日この頃です。
こういうのはあまり長続きしたことはないのですが、自分自身へのメモの意味も込めて Qiita に記事を残していこうかな…と思い立ちました。
どういう内容の記事にしようか考えていたのですが、栄えある初回の投稿は、現在自分が専門としている Azure AD とアプリケーションに関連した記事にしようかと思います。
Azure AD とアプリケーションと言っても範囲がかなり広いため、今回は、アプリケーションの登録と、普段は各種ライブラリで吹っ飛ばしてしまう Azure AD との OpenID Connect / OAuth 2.0 での認証認可のやり方について記事にしようと思います。
自分もそうですけど、初めて触るもの、よく分からないもの、って怖いですよね。
Azure AD にアプリケーションを登録するって怖い!認証認可ってよく分からないから怖い!って自分も昔は思っていました。
同じように感じている方がこの記事をきっかけに次のステップに進んでいただければとってもうれしいです。
Azure AD へのアプリケーションの登録
Azure AD にアプリケーションを登録する場合、以下の項目から登録することができます。
- [Azure ポータル] > [Azure Active Directory] > [アプリの登録]
- [Azure ポータル] > [Azure Active Directory] > [エンタープライズ アプリケーション]
それぞれの違いについてここでは詳細は触れませんが、いずれの方法でアプリケーションを登録したとしても、実はすべてのアプリケーションで Azure AD と OIDC / OAuth 2.0 での認証認可が可能だったりします。
今回はテストのやり方の紹介が目的のため、[アプリの登録] を利用した方法を記載します。
[エンタープライズ アプリケーション] についてはまた別の機会にでも。
なお、Azure AD に登録したアプリケーションはいらなくなったら削除することができるため、いくらでも検証用にアプリケーションを登録することができますよ。
Visual Studio 特典など、検証できるテナントをお持ちだったらぜひ試してみてください。
[アプリの登録] を利用した登録
[アプリの登録] からは以下の手順でアプリケーションを登録できます。
- [Azure ポータル] にグローバル管理者などでサインインします
- [Azure Active Directory] > [アプリの登録] の順に遷移します
- [+ 新規登録] をクリックします
- 任意の内容で登録します ("リダイレクト URI" は "Web" を選択して任意の URL を入力します (ここで入力した内容は後で利用するためメモします))
- 登録が完了すると、[概要] ページが表示されるため、ここに表示されている "アプリケーション (クライアント) ID" と "ディレクトリ (テナント) ID" をメモします
- 理由は触れませんが今回の方法ではアクセス トークン取得時にクライアント シークレットが必要になります。このため、左側のメニューから [証明書とシークレット] を選択し、[+ 新しいクライアント シークレット] からシークレットを追加します
- "値" に表示されている内容をメモします
まとめると、アプリケーションを登録する際に以下の四点はメモしておいてください。
- アプリケーション (クライアント) ID
- ディレクトリ (テナント) ID
- クライアント シークレット
- リダイレクト URI
認可フロー
OAuth 2.0 ではいくつかのフローが定義されています。
本記事ではそれぞれのフローの詳細については記載しません。もし詳細を知りたかったら OAuth 2.0 全フローの図解と動画 などがとってもわかりやすいなって個人的には思いました。ご興味があればぜひ。
- 認可コード フロー
- インプリシット フロー
- リソース オーナー パスワード クレデンシャルズ フロー
- クライアント クレデンシャルズ フロー
本記事では、上記のうち Web アプリケーションでも良く利用するであろう "認可コード フロー" のテストのやり方についてご紹介します。
認可コード フロー
認可コード フローは、その名のとおり、認可コードという情報を認可サーバーから受け取り、その認可コードをアクセス トークンと引き換えるフローです。
今回は Azure AD との連携となるため、マイクロソフトの資料では以下が参考になるかと思います。
Microsoft ID プラットフォームと OAuth 2.0 認証コード フロー
https://learn.microsoft.com/ja-jp/azure/active-directory/develop/v2-oauth2-auth-code-flow
認可コード フローは大まかには以下のような流れとなります。
- 認可エンドポイント (本記事の場合 Azure AD のエンドポイント) へ認可リクエストを送信します
- ユーザー名やパスワードが要求されたら入力します
- 認可エンドポイントから認可コードが発行されます
- トークン エンドポイントへ認可コードを提示してアクセス トークンをリクエストします
- トークン エンドポイントからアクセス トークンが発行されます
- リソースへのアクセス時にアクセス トークンを提示します
上記の流れのうち、ポイントは "認可コードの取得" と "アクセス トークンの取得" です。
本記事では簡易的にその動作を確認するための方法として、それぞれ以下の方法で取得する方法をご紹介します。
と言っても、いつも自分もやっている方法ではありますけどね。
- 認可コードの取得 : ブラウザーを利用する
- アクセス トークンの取得 : PowerShell を利用する
認可コードとアクセス トークンの取得
では、早速ですが、いつも自分がやっている方法を紹介します。
もっと楽で良い方法があるとは思うのですが、なるべくツールなどはインストールせずに、Windows に既定でインストールされているものだけで完結するように考えた結果、このような方法となっています。
認可コードの取得
まずは認可コードを取得してみましょう。
Azure AD から認可コードを取得するには以下のような GET リクエストを送信します。
{ディレクトリ (テナント) ID} や {アプリケーション (クライアント) ID}、{リダイレクト URI} は、事前にメモしていた内容で置き換えます。なお、{リダイレクト URI} は、URL エンコードした内容とします。
GET https://login.microsoftonline.com/{ディレクトリ (テナント) ID}/oauth2/v2.0/authorize?
client_id={アプリケーション (クライアント) ID}
&response_type=code
&redirect_uri={リダイレクト URI}
&scope={リソースへのアクセスに必要なアクセス許可の一覧}
以下はそれぞれの情報を埋め込み、改行を削除した URL の例です。
なお、今回は後述の処理を行うために scope に "User.Read" を指定してみました。
https://login.microsoftonline.com/ac7b3f7e-0000-1111-2222-6ce0f67a4d44/oauth2/v2.0/authorize?client_id=ca8f4006-1deb-4ba4-9dc1-b1353702c294&response_type=code&redirect_uri=http%3a%2f%2flocalhost&scope=User.Read
ブラウザーを Guest モードなどで起動し、上記で用意した URL をアドレス バーにペーストしてアクセスしてみましょう。 Azure AD のサインイン画面が表示されたら、早速ユーザー名とパスワードを入力して次に進みます。
すると、以下のように "要求されているアクセス許可" の画面が表示されたのではないかと思います。
この "要求されているアクセス許可" が表示される理由を話し出すととても長くなってしまいますため、ここでは割愛します。
[承諾] ボタンを押すと、以下のように http://localhst/?code=xxxxxxxxxx といった URL で画面遷移することが確認できるかと思います。
私のローカル環境では IIS が動作しているため、IIS のページが表示されましたが、環境によっては「ページを表示できません」が表示されるかと思います。これはこれで問題ありません。
重要なのは、アドレス バーに表示されている URL であり、この URL をコピーしてメモ帳などにペーストしてみましょう。
そうすると、以下のように code というパラメーターが付与されていることがわかると思います。
http://localhost/?code=0.AT0Afj97rJxwSEqE_Wzg9npNR...HaxtiRKYVf_0pACuowEWszorfmZbg&session_state=cf83585a-7b8c-4029-b790-e8f20f950233#
実は、この "code=" から続く文字列が、認可コードの正体です。この認可コードを使うことでアクセス トークンを取得することができるのです。この "code=" から続く文字列をコピーしましょう。
0.AT0Afj97rJxwSEqE_Wzg9npNR...HaxtiRKYVf_0pACuowEWszorfmZbg
アクセス トークンの取得
次に、認可コードを使ってアクセス トークンを取得してみましょう。
Azure AD からアクセス トークンを取得するには、Azure AD のトークン エンドポイントに対して以下のような POST リクエストを送信します。
{ディレクトリ (テナント) ID} や {アプリケーション (クライアント) ID}、{リダイレクト URI}、{取得した認可コード}、{クライアント シークレット} は、事前にメモしていた内容で置き換えます。なお、{リダイレクト URI} は、URL エンコードしなくて問題ありません。
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={リソースへのアクセスに必要なアクセス許可の一覧}
&code={取得した認可コード}
&redirect_uri={リダイレクト URI}
&grant_type=authorization_code
&client_secret={クライアント シークレット}
以下はそれぞれの情報を埋め込んだ例です。
https://login.microsoftonline.com/ac7b3f7e-0000-1111-2222-6ce0f67a4d44/oauth2/v2.0/token
client_id=ca8f4006-1deb-4ba4-9dc1-b1353702c294
&scope=User.Read
&code=0.AT0Afj97rJxwSEqE_Wzg9npNR...HaxtiRKYVf_0pACuowEWszorfmZbg
&redirect_uri=http://localhost
&grant_type=authorization_code
&client_secret=zlm8Q~CTbLQmIjr3a...BhvvLCo8KQngc3YTb63
上記の POST リクエストを送信することでアクセス トークンが得られます。
ただ、残念ながら認可コードを取得するときのようにブラウザーのアドレス バーにペーストするだけでは POST リクエストを送信することができません。
ブラウザー上で POST リクエストを送信するには <form> という要素が含まれた html を作成しなければならず、html を知らない人にとっては容易ではありません。
このため、何か良い方法がないかと思案した結果、Postman や PowerShell などでは割と簡単に POST リクエストを送信可能なことが分かり、自分はそれ以来、検証したいときはこの方法を利用しています。
ここでは PowerShell を活用した方法を紹介しますが、もし使い言慣れたツールがあればそちらでも構いません。
PowerShell を活用した方法
まず初めに、メモ帳 (notepad.exe) などのテキスト エディターを起動します。
以下の内容をコピーしてペーストします。
$tenantId = ''
$tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$getatkbody = @{
grant_type='authorization_code';
client_id='';
scope='';
redirect_uri='';
client_secret='';
code=''
}
$ret = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -Body $getatkbody
$ret
認可コードの取得のところで用意した内容をもとに空欄部分を埋めます。
必要な情報を埋め込んだ後、すべてを選択してコピーします。
$tenantId = 'ac7b3f7e-0000-1111-2222-6ce0f67a4d44'
$tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$getatkbody = @{
grant_type='authorization_code';
client_id='ca8f4006-1deb-4ba4-9dc1-b1353702c294';
scope='User.Read';
redirect_uri='http://localhost';
client_secret='zlm8Q~CTbLQmIjr3a...BhvvLCo8KQngc3YTb63';
code='0.AT0Afj97rJxwSEqE_Wzg9npNR...HaxtiRKYVf_0pACuowEWszorfmZbg'
}
$ret = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -Body $getatkbody
$ret
次に、PowerShell を起動し、コピーしていた内容をペーストして Enter キーで実行します。
処理に成功 (アクセス トークンの取得に成功) すると、以下のように取得したアクセス トークンの情報が表示されるかと思います。
token_type : Bearer
scope : User.Read profile openid email
expires_in : 4788
ext_expires_in : 4788
access_token : eyJ0eXAiOiJKV1QiLCJub2...fOFGeLy57ML4IuX6Tprl3jhGqu5Uu-klVaA
取得したアクセス トークンを確認する
以下のコマンドを実行し、取得したアクセス トークンをクリップ ボードにコピーします。
$ret.access_token | clip
ブラウザーで https://jwt.ms に接続し、コピーしたアクセス トークンを "Enter token blow" 欄にペーストすると、アクセス トークンの内容を確認いただけます。
アクセス トークンの内容の詳細は以下の資料が参考になるかと思います。
Microsoft ID プラットフォーム アクセス トークン
https://learn.microsoft.com/ja-jp/azure/active-directory/develop/access-tokens
Microsoft Graph API を呼び出してみる
せっかくなので、取得したアクセス トークンを利用して Microsoft Graph API を呼び出してみましょう。
PowerShell は起動したままと思いますため、まずは以下をそのまま実行してみてください。
Invoke-RestMethod -Method Get -Uri 'https://graph.microsoft.com/v1.0/me'
以下のようなエラーが出力されたのではないでしょうか?
これは、Microsoft Graph API を呼び出すために必要なアクセス トークンが提示されていないために発生しています。
Invoke-RestMethod : The remote server returned an error: (401) Unauthorized.
At line:1 char:1
+ Invoke-RestMethod -Method Get -Uri 'https://graph.microsoft.com/v1.0/ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId :
WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
このため、取得したアクセス トークンを利用して Microsoft Graph API を呼び出すように改造します。
$headers = @{ Authorization="Bearer $($ret.access_token)" }
Invoke-RestMethod -Method Get -Uri 'https://graph.microsoft.com/v1.0/me' -Headers $headers
今度はきちんと情報が取得できたのではないでしょうか。
@odata.context : https://graph.microsoft.com/v1.0/$metadata#users/$entity
businessPhones : {}
displayName : XXXXXXXX YYYYYYYY
givenName : YYYYYYYY
jobTitle :
mail : admin@zzzzzzzz.onmicrosoft.com
mobilePhone :
officeLocation :
preferredLanguage : ja-JP
surname : XXXXXXXX
userPrincipalName : admin@zzzzzzzz.onmicrosoft.com
id : f186cc0e-849f-4602-9dba-0bfda7cb4b58
今回実行した Microsoft Graph API は、自分自身の情報を取得するための API です。
この API の実行に必要なアクセス許可が "User.Read" だったため、認可コードおよびアクセス トークンを取得するときの scope パラメーターに "User.Read" を指定したのでした。
ユーザーの取得
https://learn.microsoft.com/ja-jp/graph/api/user-get?view=graph-rest-1.0&tabs=http
いかがでしたでしょうか。
いつもならライブラリを使ってサクッと実装する処理を、一つずつ手動でリクエストを送信して確認する方法を紹介しました。
少しでも皆さんの理解につながればうれしいです。
また、どこかでお会いしましょう :-)