シングルサインオン認証の中核となるのは、システムから取得したログイン要求をZendeskが信頼できるようにするJSON Web Token(JWT)というテクノロジーです。詳細については、「JWT(JSON Webトークン)を使用したシングルサインオンの設定」を参照してください。
JSON Webトークン認証リクエストは次のようになります。
https://joeandco.zendesk.com/access/jwt?jwt=eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpYXQiOjEzNzIxMTMzMDUsImp0aSI6ODg4MzM2MjUzMTE5Ni4zMjYsIm5hbWUiOiJUZXN0IFVzZXIiLCJlbWFpbCI6InR1c2VyQGV4YW1wbGUub3JnIiwiZXh0ZXJuYWxfaWQiOiI1Njc4Iiwib3JnYW5pemF0aW9uIjoiQXBwbGUiLCJ0YWdzIjoidmlwX3VzZXIiLCJyZW1vdGVfcGhvdG9fdXJsIjoiaHR0cDovL21pdC56ZW5mcy5jb20vMjA2LzIwMTEvMDUvQmFybmFieV9NYXR0X2Nyb3BwZWQuanBnIiwibG9jYWxlX2lkIjoiOCJ9.Zv9P7PNIcgHfxZaMwQtMpty3TZnmVHRWcsmAMM-mNHg
これをブラウザのURLバーに入力すると、Zendeskインスタンスにログインできました。
操作はこれだけです。他に何もする必要はありません。 見えないところで、サーバーどうしがやりとりすることもありません。 すべてはURL内で行われます。
リモート認証スクリプトは、単にURL(またはそれに類するもの)を構築し、ユーザーをそこに向けるだけのものです。
トークンを分解してみましょう。
https://joeandco.zendesk.com/access/jwt?jwt=
最初のビットは、リモート認証リクエストが送信されるURLエンドポイントで、その後にパラメータを追加していることを示す疑問符(?)が続きます。パラメータは、宛先エンドポイント(https://joeandco.zendesk.com/access/jwt)でスクリプトに情報を渡します。スクリプトは、これらのパラメータを受け入れるように設計する必要があります。そのため、ランダムアドレスではなく/access/jwtに送信する必要があります。
疑問符の後の「jwt」はパラメータの名前であり、「=」は後続の文字列がパラメータの値であることを示します。
残りの部分はデータです。よく見ると、ピリオド(フルストップ)ごとにチャンクに分割されていることがわかります。各チャンクを順番に見ていきます。
チャンク1:JWTヘッダー
最初のチャンクはJWTヘッダーです。これは、JWTリクエストであることを示し、使用されているハッシュアルゴリズムのタイプを示します。(詳細は後述します。)
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
この部分(および残りのデータ)の大きな特徴は、base64でエンコードされていることです。これは実際には暗号化されていないので、次のようなツールで簡単にデコードできます。
- Webツール:http://www.base64decode.org/
- ターミナル:http://drewsymo.com/how-to/quick-and-simple-base64-encode-on-mac-osx-terminal/
デコードされた文字列は次のようになります。
{"typ":"JWT",
"alg":"HS256"}
JSON構造をとり、実質的に型を意味する2つのキーと値のペア、type:JWT
とAlgorithm:HMAC SHA 256
があることがわかります。 SHA256は、米国家安全保障局によって設計された256ビット暗号化アルゴリズムです。第3のチャンク、つまり署名を生成するために使用されます。これについては、後で説明します。
チャンク2:JWTクレームセット/ペイロード
2番目のチャンクはペイロードを含むため、かなり長くなります。これは「JWTクレームセット」として知られています。
eyJpYXQiOjEzNzIxMTMzMDUsImp0aSI6ODg4MzM2MjUzMTE5Ni4zMjYsIm5hbWUiOiJUZXN0IFVzZXIiLCJlbWFpbCI6InR1c2VyQGV4YW1wbGUub3JnIiwiZXh0ZXJuYWxfaWQiOiI1Njc4Iiwib3JnYW5pemF0aW9uIjoiQXBwbGUiLCJ0YWdzIjoidmlwX3VzZXIiLCJyZW1vdGVfcGhvdG9fdXJsIjoiaHR0cDovL21pdC56ZW5mcy5jb20vMjA2LzIwMTEvMDUvQmFybmFieV9NYXR0X2Nyb3BwZWQuanBnIiwibG9jYWxlX2lkIjoiOCJ9
これには、タイムスタンプ、ランダム値、ユーザー名、メールアドレス、外部ID、タグが含まれます。ほかにも使用できるオプションがあります。ペイロードを理解しやすいように、ここではbase64をデコードします。ここでは、読みやすいように改行しています。
{
"iat":1372113305,
"jti":8883362531196.326,
"name":"Test User",
"email":"tuser@example.org",
"external_id":"5678",
"organization":"Apple",
"tags":"vip_user",
"remote_photo_url":"http://mit.zenfs.com/206/2011/05/Barnaby_Matt_cropped.jpg",
"locale_id":"8"
}
必須
iat
最初のキーはiatで、issued at(発行時刻):を表します。これは、1970年1月1日以降の整数秒としてフォーマットされたタイムスタンプであり、UNIXの標準的な時刻表現です。タイムスタンプは整数(小数なし)でなければならず、UTCである必要があります。また、Zendeskサーバーが受信した現在時刻から3分以内でなければなりません。これにより、各リモート認証リクエストに自己破壊メカニズムが追加され、生成されてから3分以上経過した単一のリクエストは使用されなくなります。
jti
2番目のキーjtiは、JSONトークンIDを表します。これは単なるランダムな文字列です。このため、再び使用される可能性が非常に低くなるように、十分に長くランダムである必要があります。偶然、別の認証リクエストに再利用された場合、そのリクエストは失敗します。これは使い捨ての鍵です。このようなランダム値を(強制的に)含めることで、同一の認証リクエストがないことを保証します。これにより、有効なリクエストURLが再利用されることがなくなります。たとえば、誰かがコンピュータまたはネットワークにマルウェアをインストールし、トラフィックログの記録を開始したとします。この誰かは、ユーザーがクリックしたすべてのURLを見ることができます。Zendeskへのログイン用に発行されたURLを見つけて取得した場合、この使い捨てキーがなかったら、3分以内にあなたの振りをしてログインできてしまいます。
name
次はスペースを含むユーザーのフルネームです。Zendeskがここで受け取るものはすべて、以前に別の名前が設定されていたとしても、ユーザー名として設定されます。
その後はユーザーのメールアドレスです。外部IDを受け取らない限り、ユーザーの一意の識別子として使用されます*。つまり、メールアドレスと外部IDを受信した場合、最初にIDの照合を試みます。 その場合、指定されたメールアドレスでそのユーザーを更新します。
*メモ: オプション「外部IDの更新を許可しますか?」がZendeskで有効になっている場合、外部IDを受信しても引き続きメールアドレスによる特定を行います。メールと外部IDが異なる場合、外部IDを更新します。
オプション
external_ID
外部IDは、メールアドレスを使用する代わりにユーザーを識別するために使用できるオプションのIDです(上述)。
組織
また、organization値を渡してユーザーを組織に追加することもできます。指定された組織はすでに存在し、名前が正確に一致している必要があります。それ以外の場合、アクションは実行されません。
タグ
tagsキーを使用すると、ログインしているユーザーにタグを設定できます。tagsキーは、ユーザーの既存のタグを指定したタグに置き換えるため、慎重に使用してください。空のタグパラメーターを渡すと、ユーザーからすべてのタグが削除されます。
remote_photo_url
また、remote_photo_urlの値を渡すこともできます。この値は、写真を含むパブリックURLを受け取り、この写真をユーザーのプロフィール写真として設定します。
locale(言語)
locale_idの値を渡して、Zendeskで認証済みユーザーの言語を設定または更新できます。この値は、Zendeskで現在アクティブになっているロケールに一致する数値である必要があります。次のZendesk APIのlocales.jsonエンドポイントを使用してロケールを検索できます。http://developer.zendesk.com/documentation/rest_api/locales.html#list-locales
ユーザーフィールド(上述の例には含まれない)
これは、各フィールドキーと値のキーと値のペアを含むJSONオブジェクトである必要があります。フィールドキーは、ユーザーフィールドインターフェイスで検索または定義できます。なお、渡すことができるのはカスタムユーザーフィールドのみです。
例:
"user_fields": {"checked": false,"date_joined": "2013-08-14T00:00:00+00:00","region": "EMEA","text_field": null}
チェックボックスはブール値を使用し、日付コードは上記の例に従います。ドロップダウンにはオプションの名前を指定します。テキストフィールドには文字列を指定します。
電話番号(上述の例には含まれない)
電話番号を示す文字列を指定します。電話番号を指定する際は、所定の形式に従ってください。詳細については、「Talkで利用できる電話番号の形式について」を参照してください。
チャンク3:JWS署名
リクエストの最後のチャンクは暗号化されています。それほど難しく考える必要はなく、基本的には上記のすべての情報(iat、jti、名前、メールなど)を共有シークレットとともに取得し、そのすべてから暗号化された文字列を生成します。次に、JWT標準に従って、その暗号化された文字列のチャンク(チェックサムと呼ばれる)が取得されます。それがJWS署名です。
では、どのように安全が保たれるのでしょうか。
暗号化された有効な文字列を生成するには、共有シークレットを知る必要があります。
暗号化は、暗号化された文字列からデータおよびキーまで逆方向に処理できないように設計されています。データの内容があっても、キーを推定することは事実上不可能です。
ただし、送り側と受け手側にそれぞれキーがあり、暗号化するのと同じデータを暗号化せずに送信するため、署名を自分で作成して、送信したものと一致することを確認できます。
また、転送中のデータを誰も改ざんできないことも保証されます。
擬似コードでは、次のように構築します。
URLBase64Encode(
HMAC-SHA256(
URLBase64Encode( header_json ).URLBase64Encode( payload_json )
)
)
エンコードしたヘッダーとペイロードをHMAC-SHA256で生成するときには、共有シークレットを含めます。
結果は以下のようになります。
Zv9P7PNIcgHfxZaMwQtMpty3TZnmVHRWcsmAMM-mNHg