前回 に続いてWindows Live ID SDKよりWindows Live ID Delegated Authentication(委任認証)です。前回 は、ユーザーに対して承認の要求処理までを紹介しました。今回は承認情報の受信処理についてです。
承認要求ページと承認管理ページ
はじめにユーザーが目にする承認要求ページについて確認しておきましょう。Webサイト(アプリケーションプロバイダ)が示した承認要求ページのリンクをクリックすることでユーザーは承認要求ページ(図1 )に移動します。
図1 承認要求ページ
承認要求ページでは、アプリケーションプロバイダが要求したオファーおよびアクションに対してアクセスの許可ができます。一度許可を行い再び承認要求ページへ移動した場合は、既に許可したその許可がまだ有効な期間にあるオファーおよびアクションについては表示されません。ページへ移動したときすべて有効なオファーおよびアクションであった場合は、すぐにアプリケーションプロバイダの戻り先URLへリダイレクトされます。
ユーザーがアクセス許可を取り消したい場合は、承認管理ページ (図2 )で行います。
図2 承認管理ページ
承認情報の受信
それでは、承認要求ページからリダイレクトされたときの処理を説明します。承認サービスは戻り先URLにHTTP POSTにより承認情報を返します。戻り先のページでは次の4個のパラメータが含まれているかを確認し処理する必要があります。
ResponseCode
承認要求の状態を表す応答コードです。次のいずれかが格納されています。
値
説明
RequestApproved
ユーザーは承認要求プロセスを完了
RequestRejected
ユーザーは承認要求プロセスをキャンセル
アプリケーションプロバイダが要求した許可に対して、すべてまたは一部を許可した場合に「RequestApproved」の文字列が格納されています。
ConsentToken
承認トークンです。承認要求を完了した場合のみ返されます。このトークンにはさらに複数のパラメータが含まれています。アプリケーションプロバイダはこの情報を使用してリソースプロバイダにアクセスします。構造の解析はこの後説明します。
action
ユーザーの実行中の操作を表す文字列が格納されています。現在のところ格納される可能性がある値は次のひとつのみです。
値
説明
delauth
ユーザーが承認要求のプロセス完了に成功
appctx
承認の要求時に指定したappctxパラメータの値が格納されています。
ResponseCodeの値が「RequestApproved」の場合、ConsentTokenに格納されている承認トークンの解析処理に進みます。
委任認証プロセスにおいて必須の処理の中で、ここだけPOSTデータを処理する必要がありJavaScriptのみでの利用が不可能になっています。
承認トークンの構造
承認トークンの文字列はURLデコードすると「delt=…&reft=…&skey=…&offer=…&exp=…&lid=…」のようにパラメータ名とその値のペアがアンパサンド(&)で区切られたクエリ文字列として構成されています。この文字列に含まれているパラメータは次のものがあります。格納されている値の使い方はこの後に説明します。
eact
暗号化された承認トークンです。アプリケーション検証トークンを指定した場合もしくはアプリケーション登録がされている場合は、承認トークンが暗号化されています。この値を復号すると次のパラメータがさらに格納されています。
delt
委任トークンと呼ばれる文字列です。アプリケーションプロバイダがリソースプロバイダに渡すトークンになり、リソースプロバイダはこのトークンを使用して承認情報を検証します。この委任トークンはすぐに有効期限切れになり使用できなくなります。
reft
更新トークンと呼ばれる文字列です。委任トークンの有効期限が切れた場合、承認を更新し、新たな委任トークンを取得するために使用するトークンになります。アプリケーション検証トークンを使用せずに承認トークンを受信した場合、この更新トークンがない場合があるようです。
skey
セッションキーと呼ばれる文字列になります。アプリケーションプロバイダはこの値を必要としません。
offer
ユーザーが承認したオファーとアクション、その有効期限が格納されています。例を以下に示します。
SpacesPhotos.ReadWrite:1249915138;ContactsSync.FullSync:1218985740;ApplicationStorage.ReadWrite:1249929098
オファーとアクションはピリオド(.)で結ばれ、その後ろにコロン(:)が付き1970年1月1日(UTC)からの経過秒として有効期限が表わされています。複数のオファーとアクションがある場合は、セミコロンで(;)で区切られています。
exp
委任トークンの有効期限が1970年1月1日(UTC)からの経過秒として格納されています。委任トークンの有効期限が切れた場合、更新トークンを使い新しい委任トークンの取得ができます。
lid
リソースプロバイダのユーザーデータの場所を表す識別子になります。当然ながらユーザーごとに異なり、この値によりユーザー個人のデータにアクセスできます。この値からユーザーのWindows Live IDアカウントは類推できません。
パラメータは以上です。承認トークンは、パラメータのeactがひとつだけ含まれている場合と、eactを除く各パラメータが含まれている場合の2種類になります。
承認トークンの復号化
アプリケーション検証トークンを使用した場合などPOSTデータに含まれている承認トークンが暗号化されている場合がありました。ここでは承認トークンの復号手順をコードで示します。言語はVB.NET(ASP.NET)を使用しています。
まずは利便性のために「delt=…&reft=…」のような形式の文字列をパラメータ名とその値のペアのコレクションに変換して返すメソッドを作ります。
Public Shared Function Parse(ByVal input As String ) As Specialized.NameValueCollection
Dim pairs As New Specialized.NameValueCollection
Dim pairValues() As String = input.Split(New Char () {"&" c})
For Each value As String In pairValues
Dim separator As Integer = value.IndexOf("=" )
If separator = -1 OrElse separator = value.Length Then
Continue For
End If
pairs(value.Substring(0 , separator)) = value.Substring(separator + 1 )
Next
Return pairs
End Function
復号にはAES(Rijndael)アルゴリズムを使います。キーサイズは128bit、共有キーはアプリケーション登録時に指定したシークレットキーから得られるバイト配列値を指定します。また、暗号化の処理のときデータブロックのバイト数が必要な数だけ満たしていないときに使用する埋め込み文字列の種類にはPKCS #7という方法を指定します。初期化ベクタには、暗号化されたトークンをURLデコードおよびBase64デコードして得られた先頭16バイトを指定します。
共有キーの作成は、アプリケーション検証トークンを作成するときに使用したDeriveメソッドを使用します。接頭辞に「ENCRYPTION」を指定します。以下のDeriveメソッドは前回のものと同一です。
Public Shared Function Derive ( ByVal secretKey As String , ByVal prefix As String ) As Byte ()
Dim hashAlgorithm As New SHA256Managed
Dim buffer () As Byte = System . Text . Encoding . Default . GetBytes ( prefix & secretKey )
Dim hashOutput () As Byte = hashAlgorithm . ComputeHash ( buffer )
Dim byteKey ( 15 ) As Byte
Array . Copy ( hashOutput , byteKey , byteKey . Length )
Return byteKey
End Function
以上を踏まえた承認トークンを復号化するメソッドは次のようになります。例外処理は省略しています。
Public Shared Function DecryptToken(ByVal token As String , ByVal secretKey As String ) As String
Dim aesAlg As New RijndaelManaged
aesAlg.KeySize = 128
aesAlg.Key = Derive(secretKey, "ENCRYPTION" )
aesAlg.Padding = PaddingMode.PKCS7
Dim ivAndEncryptedValue() As Byte = Convert.FromBase64String(HttpUtility.UrlDecode(token))
Dim memoryStream As New System.IO.MemoryStream(ivAndEncryptedValue)
Dim iv(15) As Byte
memoryStream.Read(iv, 0 , iv.Length)
aesAlg.IV = iv
Using cryptoStream As New CryptoStream(memoryStream, aesAlg.CreateDecryptor, CryptoStreamMode.Read), _
streamReader As New System.IO.StreamReader(cryptoStream, System.Text.Encoding.ASCII)
Return streamReader.ReadToEnd
End Using
End Function
Webページ上に取得した承認トークンの各パラメータ値を表示するコードは次のようになります。
<div >
<%
Dim consentToken As String = Request.Params("ConsentToken" )
If consentToken <> "" Then
Dim pairs As Specialized.NameValueCollection = Parse(HttpUtility.UrlDecode(consentToken))
For Each key As String In pairs.Keys
Page.Response.Write(String .Format("{0}: {1}<br />" , key, pairs(key)))
Next
If pairs("eact" ) <> "" Then
consentToken = DecryptToken(pairs("eact" ), "****SecretKey****" )
pairs = Parse(HttpUtility.UrlDecode(consentToken))
For Each key As String In pairs.Keys
Page.Response.Write(String.Format("{0}: {1}<br />" , key, pairs(key)))
Next
End If
End If
%>
</div >
承認の更新
承認トークン内に含まれていた委任トークンは、リソースプロバイダと通信する際に必要になります。そのユーザーデータへ実際にアクセスする方法は、次回以降に紹介します。ここでは先に承認を更新する方法を紹介します。
通常は取得した委任トークンをアプリケーションプロバイダが保存しておき、ユーザーデータへアクセスする際に使用しますが、委任トークンには有効期限があります。ユーザーがオファーとアクションに対して承認した有効期限よりも短く、数時間程度が有効期限として設定されているようです。委任トークンは承認トークンに含まれていた更新トークンを使用して新たに有効な委任トークンを取得できます。これを承認の更新と呼び、この更新方法を説明します。
新しい委任トークンを取得するには、次の承認サービスの承認更新用のURL「https://consent.live.com/RefreshToken.aspx」に必要なパラメータを指定してアクセスします。承認要求と似た操作になりますが、大きく次のふたつの点で異なります。
承認要求では、ユーザーがアクセス許可を行う承認要求ページに移動しユーザーの操作が必要でしたが、委任トークンの更新ではアプリケーションプロバイダとWindows Live ID承認サービスとの間でのみ完了し、ユーザーの操作は必要ありません。また、承認要求の情報はPOSTデータとして承認サービスより取得していましたが、委任トークンの更新では承認更新用のURLへアクセスし、その応答にJSON形式のデータを受け取ります。
以上のように更新処理はJavaScriptのみを使用したやり取りが可能になり、アプリケーション設計においての利便性が考えられますが、実際にはアプリケーション登録(アプリケーションIDを取得)をしていない場合、更新トークンが取得できなかったり、登録している場合は更新情報の復号化処理が必要であったりとあまり利便性はないように思います。
さて、承認更新用のURLのパラメータには次のものを指定します。前回 の記事にて紹介した承認の要求時に使用したパラメータと同じものもあります。併せて確認してください。
ru
更新された委任トークンを受け取るURL指定します。URLは完全修飾のドメイン名(例: www.gihyo.jp)を使用し、クエリ文字列のパラメータが含まれていてはいけません。このパラメータはアプリケーション登録がされている場合、省略できます。
ps
アプリケーションプロバイダが必要とする許可(オファーとアクション)を表す文字列を指定します。
reft
更新トークンを指定します。今回紹介した承認情報の取得にて得られた承認トークンに含まれている更新トークンと呼ばれる文字列を指定します。
app
アプリケーション検証トークンを指定します。これは承認要求のときにも指定したものです。アプリケーションが登録してありアプリケーション検証を必須に設定してある場合にこのパラメータが必要になります。アプリケーション検証トークンの作成方法は前回紹介しています。
承認更新情報の受信
承認の更新時、承認サービスからはJSON形式の値が応答されます。値は次のいずれかが格納されています。
ConsentToken
承認トークンです。構造は先に示したものと同じです。有効な期限付きの委任トークンが含まれています。実際にはJSON形式のため次のような形式の文字列として取得されます。
{"ConsentToken":"eact%3D…"}
error
承認更新時に何らかのエラーが発生した場合は、そのエラー内容を示す番号が返ってきます。例えば次のような文字列です。
{"error":"2000"}
エラー番号の内容は、MSDNライブラリ内のエラーコード を参照してください。
次に承認更新を行うVB.NET(ASP.NET)のコード例を示します。ページ中に承認サービスより返ってきたJSON形式の文字列を表示するだけのコードになります。実際のアプリケーションでは、委任トークンの有効期限が切れていた場合、更新トークンを用いて委任トークンを更新するという処理が必要になります。
Dim ps As String = "SpacesPhotos.ReadWrite,ContactsSync.FullSync,ApplicationStorage.ReadWrite"
Dim reft As String = HttpUtility.UrlDecode("更新トークン" )
Dim app As String = GetAppVerifierToken("****Application ID****" , "****SecretKey****" , GetTimestamp)
Dim path As String = String .Format("https://consent.live.com/RefreshToken.aspx?ps={0}&reft={1}&app={2}" , ps, reft, app)
Dim req As HttpWebRequest = DirectCast(WebRequest.Create(path), HttpWebRequest)
Try
Dim json As String
Using response As HttpWebResponse = DirectCast(req.GetResponse, HttpWebResponse), _
stream As New System.IO.StreamReader(response.GetResponseStream)
json = stream.ReadToEnd
End Using
Response.Write(json)
Catch webEx As WebException
Response.Write(webEx.Message)
Catch ex As Exception
Response.Write(ex.Message)
End Try
サンプルコード
最後に委任認証のサンプルコードについて紹介しておきます。Windows Live IDDelegated Authentication SDK 1.0という各種言語を使用した委任認証のサンプルコードが提供されており、ダウンロードセンター からダウンロードができます。C#、Java、Perl、PHP、Python、Rubyのコードが用意されています。各サンプルの実行は、MSDNライブラリ内の委任認証のサンプル を参照してください。認証部分はライブラリとして使用できるようにサンプルページと分離されています。また、サンプルは委任認証以外にWeb認証と呼ばれるWindows Liveサービスも使用しています。すこしわかりにくいかもしれませんが、暗号・復号化部分など参考になると思います。
委任認証については今回でおわりです。委任認証の使い方は理解いただけたでしょうか。前回と今回の内容だけではアプリケーションとしてはまだ何もできません。次回から委任認証を利用してWindows Live サービス上のユーザーデータにアクセスしてみたいと思います。