まず初めにお断りしておきますが、公式リソースでは Windows Azure Active Directory の OAuth2.0 への対応自体はアナウンスされていますが、内容について明確には言及がない状態なので、あくまで今日時点で色々と探索した結果を書いており、間違いや今後の変更などは高い確率で発生すると思われます。あくまで、エンドポイントを探して叩いてみたらこんな結果が返ってきた、という状態であるとご理解ください。
さて、始めます。
WAAD でアプリケーションを追加すると様々なエンドポイントが付与されます。
その中には OAuth2.0 のトークン・エンドポイントも含まれており、OAuth2.0 プロトコルで使う client_id や client_secret の取得、redirect_uri 等の設定もアプリケーションの構成の中で行うことが出来ます。
さらに、現状ではプレビュー提供ですが protected resource として同じく WAAD 上に構成したアプリケーションを登録することも可能であり、OAuth2.0 を使って WAAD 上のアプリケーション(API)をマッシュアップしていくことも可能になっています。
基本的には WAAD Authentication Library(AAL)を使ってアクセストークンなどを取得することが前提のようですが、折角そこに OAuth のエンドポイントがあるので叩いてみたいと思います。
まず、叩き方ですが、先日日本語訳がされた OAuth2.0 の仕様(RFC6749)に定義されている各種フローをそれぞれ試してみます。
The OAuth 2.0 Authorization Framework(OpenID Foundation Japan 翻訳 WG 訳)
-
http://openid-foundation-japan.github.io/rfc6749.ja.html
試したのは、
・認可コードグラント
・インプリシットグラント
・リソースオーナーパスワードクレデンシャル
・クライアントクレデンシャル
です。
それぞれやってみます。
◆認可コードグラント
仕様上は、ざっくりいうと
1.クライアントが認可エンドポイントから認可コードを取得
2.トークンエンドポイントで認可コードとアクセストークンを交換
という流れになります。
ここで疑問。先ほど出てきた WAAD の OAuth2.0 エンドポイントにはトークンエンドポイントはありましたが、認可エンドポイントが出てきていませんでした。
トークンエンドポイントのアドレスが
https://login.windows.net/<テナントID>/oauth2/token?api-version=1.0
だったので、token ⇒ authorize に変えたら行けるかな?ということで
https://login.windows.net/<テナントID>/oauth2/authorize?api-version=1.0
を試してみます。意外と行けるもんです(笑)
ということで以下を試します。
1.認可コードの取得
エンドポイント:認可エンドポイント
メソッド:GET
パラメータ:
・response_type : code
・client_id : <WAADで取得した client_id>
こんなクエリになります。
GET /<テナントID>/oauth2/authorize?api-version=1.0&response_type=code&client_id=<client_id> HTTP/1.1
Host: login.windows.net
仕様上は HTTP 302 が返ってきて Location に redirect_uri?code=xxx という形で認可コードが返ってくるはずです。
結果、Microsoft Online アカウントでの認証が実行された後、ちゃんと認可コードが返ってきました。
2.認可コードとアクセストークンの交換
次は取得した認可コードをトークンエンドポイントへ投げてアクセストークンを取得します。
エンドポイント:トークンエンドポイント
メソッド:POST
パラメータ:
・grant_type : authorization_code
・code : <取得した認可コード>
・resource : アクセス対象の保護リソースの URI(WAAD 上で指定した WebAPI のURI)。今回は Graph API の URI(https://graph.windows.net)を指定します。
※OAuth2.0 の仕様上は resource パラメータは存在しませんが、WAAD の OAuth2.0 エンドポイントへアクセスする場合は必ず resource パラメータを要求されます。
POST /<テナントID>/oauth2/token HTTP/1.1 Host: login.windows.net
Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=<取得した認可コード>&resource=https%3A%2F%2Fgraph.windows.net
結果、残念ながら、「unsupported_grant_type」が返ってきてしまいました。
エラーの説明として「ACS70003: The access grant 'authorization_code' is not supported」とあるので、現状は認可コードグラントは使えない、ということです。
◆インプリシットグラント
次はインプリシットグラントです。こちらは非常に単純なフローで、認可エンドポイントに対してアクセストークンを直接要求します。
エンドポイント:認可エンドポイント
メソッド:GET
パラメータ:
・response_type : token
・client_id : <WAADで取得した client_id>
こんなクエリになります。
GET /<テナントID>/oauth2/authorize?api-version=1.0&response_type=token&client_id=<client_id> HTTP/1.1
Host: login.windows.net
仕様上は HTTP 302 が返ってきて Location に redirect_uri#access_token=xxx&token_type=xxx という形でフラグメントにアクセストークンが返ってくるはずです。
しかし、これも残念。「unsupported_response_type」といわれてしまいます。こちらも使えない、といことです。
◆リソースオーナーパスワードクレデンシャル
続いてリソースオーナーパスワードクレデンシャルです。こちらも単純でリソースオーナーのユーザ名とパスワードをクエリに埋め込んでアクセストークンを要求します。
エンドポイント:トークンエンドポイント
メソッド:POST
パラメータ:
・grant_type : password
・username : <WAADのユーザ名>
・password : <WAADのパスワード>
・resource : https://graph.windows.net
こんなクエリになります。
POST /<テナントID>/oauth2/token HTTP/1.1 Host: login.windows.net
Content-Type: application/x-www-form-urlencoded grant_type=password&username=<WAADのユーザ名>&password=<WAADのパスワード>&resource=https%3A%2F%2Fgraph.windows.net
こちらもうまくいけば HTTP 200 でアクセストークンが返ってくるはずです。
しかし、同じくこちらも残念ながら「unsupported_grant_type」です。なかなかうまく行かないもんです。
◆クライアントクレデンシャルグラント
最後はクライアントクレデンシャルグラントです。こちらはトークンエンドポイントへ直接トークンを要求します。
エンドポイント:トークンエンドポイント
メソッド:POST
パラメータ:
・grant_type : client_credentials
・client_id : <取得した client_id>
・client_secret : <取得した client_secret>
・resource : https://graph.windows.net
※こちらも仕様上は grant_type 以外のパラメータは必須ではありませんが、認証を行うことが必要なフローなので、WAAD では client_id と client_secret を投げてクライアント認証を行っているようです。
こんなクエリになります。
POST /<テナントID>/oauth2/token HTTP/1.1 Host: login.windows.net
Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&client_id=<取得した client_id>&client_secret=<取得した client_secret>&resource=https%3A%2F%2Fgraph.windows.net
うまくいけば HTTP 200 でアクセストークンが返ってくるはずです。
こちらは、、、、、うまく行きました。
{
"token_type":"Bearer",
"access_token":"eyJ0eX........(略)",
"expires_in":"43199",
"not_before":"1374294762",
"expires_on":"1374337962",
"resource":"https://graph.windows.net"
}
と、いうことで結果をまとめると下記の通りとなります。
まぁ、まだまだですねぇ。
フロー | 結果 | 備考 |
認可コードグラント | × | Code requetは成功
Token requestでunsupported_grant_type |
インプリシットグラント | × | unsupported_response_type |
リソースオーナーパスワードクレデンシャル | × | unsupported_grant_type |
クライアントクレデンシャルグラント | ○ | 以下のパラメータが要求される
Resource
Client_id
Client_secret |
◆おまけ(OpenID Connect)
ここからはおまけですが、WAAD も OpenID Connect に対応するという話があります。OpenID Connect は OAuth2.0 ベースなので少し試してみました。
試したのはインプリシットのみで、結果的に言うと id_token の取得が出来てしまいました。が、仕様への対応状況はまだかなり怪しい感じです。
エンドポイント:認可エンドポイント
メソッド:GET
パラメータ:
・response_type : id_token token
・client_id : <WAADで取得した client_id>
・scope : openid profile
・nonce : <任意の文字列>
こんなクエリになります。
GET /<テナントID>/oauth2/authorize?api-version=1.0&response_type=id_token%20token&client_id=<client_id>&scope=openid%20profile&nonce=<任意の文字列> HTTP/1.1
Host: login.windows.net
仕様上は HTTP 302 が返ってきて Location に redirect_uri#id_token=xxx&access_token=xxx&token_type=xxx という形でフラグメントに ID トークンとアクセストークンが返ってくるはずです。
しかし、先に述べたように response_type に token を指定するとエラーになるので、今回は response_type = id_token のみを指定してみました。
すると、フラグメントではなく、パラメータで id_token が返ってきました。
返ってきた id_token を BASE64 URL デコードしてみると、
{
"typ":"JWT",
"alg":"RS256",
"x5t":"NGTFvdK-fythEuLwjpwAJOM9n-A"
}
{
"aud":"<取得した client_id>",
"iss":"https://sts.windows.net/<テナント ID>/",
"nbf":1374284435,
"exp":1374313235,
"ver":"1.0",
"tid":"<テナントID>",
"oid":"<WAAD 上のユーザのオブジェクトID>",
"upn":"xxx@<テナントドメイン>.onmicrosoft.com",
"sub":"vqrPXdoNdGPRd5DBnH...(略)",
"family_name":"富士榮",
"given_name":"尚寛",
"nonce":"hoge"
}
という形になりました。
sub に入っている値が何なのか、Graph API 等で探ってみたのですが、利用者に見えるところには出てこない識別子のようです。
まだ、これで何か出来るわけではありませんが、Office Web App の開発リファレンスを見ているとトークンのデコードや検証のためのサンプルコードが出ていたりするので、同じ仕組みを使うことが出来るようです。
今回、色々と課題はあるものの WAAD が着々と OAuth2.0 や OpenID Connect に対応してきていることが見えてきました。
今後は社内の Active Directory と AD FS ⇒ WAAD を経由して OpenID Connect RP への認証連携なども実現できるようになってきそうなので、ますますコンシューマ系のサービスとの接続が容易になってくるのかも知れません。
引き続き目が離せないところです。