2014-04-05

PythonとかApexでJWT作ってGoInstantにぶっこんでみる。

SalesforceがGoInstant買収してどうのこうのって話らしいので、GoInstant試してます。

 

GoInstantでユーザ認証するには外部の認証プロバイダ(facebook, salesforce, github, google, twitter)

を使ってOpenID Connectやら何やらで認証するか

自前の認証プロバイダ(と言っても普通のログイン機能を有するアプリ)で

JWTをGoInstantに投げ入れて認証します。

 

前者の場合はそれぞれのアプリケーション設定して

ClientIDとかSecretを設定してJS叩くだけで超簡単に実装できます(リファレンス)。

後者のJWT生成もGoInstantのライブラリ(Ruby、Java、PHP、node.js)があって

ライブラリのない言語の場合もJWT生成のライブラリを使えば、こちらも楽勝。

 

んで、PythonでチャレンジしようとJWT生成のライブラリをインストールしてみたけど

うまくいかなかったのでJWTの勉強がてら、自前で生成してみた。

 

import base64
import json
import hmac
import hashlib

def base64url_decode(input):
    input += '=' * (4 - (len(input) % 4))
    return base64.urlsafe_b64decode(input)

def base64url_encode(input):
    return base64.urlsafe_b64encode(input).replace('=', '')

key = base64url_decode("Input Your App Key")

header = {
    'typ':'JWT',
    'alg':'HS256'
}

payload = {
    'iss':'localhost',
    'sub':'test@freedom-man.com',
    'dn':'freedom-man@display',
    'g' :None
}

header_payload = base64url_encode(json.dumps(header)) + "." + base64url_encode(json.dumps(payload))

sig = base64url_encode(hmac.new(key, header_payload, seed, hashlib.sha256).digest())
print seed + "." + sig

 

これでJWT生成はOK。

生成の流れとしては、ヘッダ(暗号化方式を記述)とクレーム(トークンのデータ)のそれぞれのJSON文字列を

urlsafeなbase64エンコードして、それをカンマでくっつけたものを

アプリケーション毎のキーでハッシュ作ってurlsafeなbase64エンコードしてSignatureを作る。

最後にヘッダ、クレーム、Signatureをカンマで連結(それぞれbase64エンコード)して終わり。

 

実際の実装はdjangoなりflaskなりでログイン認証した後に、認証ユーザに対する適切なJWTを作って

JSの変数にバインドして、GoInstantのJSでユーザプレゼンスを得るって感じになる。

 

Apex(force.com)だとこんな感じ。

/**
 * JWT生成
 */
public static String createJWT(String username, String displayname) {
    Map<String, String> header = new Map<String, String>{
        'typ' => 'JWT',
        'alg' => 'HS256'
    };

    Map<String, String> payload = new Map<String, String>{
        'iss' => 'salesforce.com',
        'sub' => username,
        'dn' => displayname,
        'g' => null
    };

    String key = 'Input Your App Key';
    String target = 
        encodeUrlsafeBase64(Blob.valueOf(JSON.serialize(header))) + '.' + 
        encodeUrlsafeBase64(Blob.valueOf(JSON.serialize(payload)));
    String signature = 
        encodeUrlsafeBase64(Crypto.generateMac('HmacSha256', Blob.valueOf(target), decodeUrlsafeBase64(key)));
    return target + '.' + signature;
}

/**
 * base64urlエンコード
 */
public static String encodeUrlsafeBase64(Blob target) {
    String ret = EncodingUtil.base64Encode(target);
    ret = ret.replace('+', '-');
    ret = ret.replace('/', '_');
    ret = ret.replace('=', '');
    return ret;
}

/**
 * base64urlデコード
 */
public static Blob decodeUrlsafeBase64(String target) {
    String ret = target;
    ret = ret.replace('-', '+');
    ret = ret.replace('_', '/');
    return EncodingUtil.base64Decode(ret);
}

 

Salesforceの場合、ログイン認証は既にWebアプリとして持っているため

ログインユーザに見せるVFページでJWTを発行すれば、GoInstant内でもユーザプレゼンスが確保されます。

 

このエントリーをはてなブックマークに追加