私が達成しようとしているのは、Force.com から、既に設定されているサービス アカウントで保持されている Google Fusion Table にデータをアップロードして、そのデータに基づいてネットワーク グラフの視覚化を生成し、 Force.com 内の VF ページ。
以下で構成される Apex クラスを作成しました。
public class NetworkGraphTestController {
public static String base64URLEncode(Blob input) {
String output = encodingUtil.base64Encode(input);
output = output.replace('+', '-');
output = output.replace('/', '_');
while (output.endsWith('=')) {
output = output.subString(0, output.length()-1);
}
return output;
}
public static Long findSecondsBetween2DateTimes(DateTime dt1, DateTime dt2)
{
Integer intDays = dt1.Date().daysBetween(dt2.Date());
Long seconds = dt2.getTime() - dt1.getTime();
Long daysToSeconds = intDays * 24 * 60 * 60;
return daysToSeconds + seconds;
}
public static void generateJWT() {
String pkCS8PrivateKey = 'PRIVATE KEY OBTAINED VIA OPENSSL';
DateTime utc0 = DateTime.newInstance(1970, 1, 1, 0, 0, 0);
DateTime issueTime = DateTime.now();
JSONGenerator gen = JSON.createGenerator(false);
gen.writeStartObject();
gen.writeStringField('iss', 'xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@developer.gserviceaccount.com');
gen.writeStringField('scope', 'https:\\/\\/www.googleapis.com\\/auth\\/fusiontables');
gen.writeStringField('aud', 'https:\\/\\/accounts.google.com\\/o\\/oauth2\\/token');
Long now = findSecondsBetween2DateTimes(utc0, issueTime);
gen.writeNumberField('exp', now + 3500);
gen.writeNumberField('iat', now);
String claimSet = gen.getAsString().trim();
System.debug(gen.getAsString());
System.debug('Gen trimmed: ' + gen.getAsString().trim());
String header = '{"alg":"RS256", "typ":"JWT"}';
String signatureInput = base64URLEncode(blob.valueOf(header)) + '.' + base64URLEncode(blob.valueOf(claimSet));
Blob signature = Crypto.sign('RSA', blob.valueOf(signatureInput), encodingUtil.base64decode(pkCS8PrivateKey));
String jwt = signatureInput+'.'+base64URLEncode(signature);
//System.debug(jwt);
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setMethod('POST');
req.setBody('grant_type=' + encodingUtil.urlEncode('urn:ietf:params:oauth:grant-type:jwt-bearer', 'UTF-8')+'&assertion=' + encodingUtil.urlEncode(jwt, 'UTF-8'));
req.setEndpoint('https://accounts.google.com/o/oauth2/token');
System.debug('Request: ' + req + ' BODY: ' + req.getBody());
//System.debug(req.toString());
HttpResponse res = h.send(req);
System.debug(res + ' BODY: ' + res.getBody() + ' STATUS: ' + res.getStatus());
}
}
サービス アカウントはすでに設定されており、Fusion Tables API は Google API コンソールで有効になっています。Google が OpenSSL 経由で提供した PKCS12 p12 秘密鍵ファイルから PKCS8 キーを抽出しましたが、このプロセスに慣れていないため、間違いがあった可能性があります。私が気付いたのは、その秘密鍵の値を抽出しようと何度も試みた結果、現在 pkCS8PrivateKey に格納されている値を取得するまで、Crypto クラスが鍵の値を受け入れないことでした。プロセスを正しく覚えていれば、最初に PKCS12 ファイルを中間の pem ファイルに変換し、次に別の pem ファイルに出力された PKCS8 形式の秘密鍵を抽出しました。
発生した 400 'Bad Request' エラーの本文は次のとおりです。
{
"error" : "invalid_grant"
}
iss および scope パラメータのスラッシュもエスケープしないようにしましたが、違いはありません。また、リモート サイト セキュリティ設定で「 https://accounts.google.com 」へのアクセスを許可しました。
このコードを書くための私の最大の情報源は次の質問です: Google API oAuth 2.0 JWT を生成するための Force.com Apex コード
その質問の作成者は、Google で認証しようとすると、invalid_grant エラーしか取得できないという点で、私と同じ状況に陥りました。残念ながら、彼の質問には答えられなかったので、ここにいます。
私が直面している問題に関して、何か助けがあればよろしくお願いします。