GASをガスって読むのは中の人からするとあまり好きじゃないらしいですね。大橋です。
TypetalkAppの時も少し言及したかもですが、
最近GASでOAuth2がやりやすくなりました。
ただし
やりやすくなったと言っても、
UrlFetchAppがOAuth1ように備えていたような、
自動でほぼほぼやってくれるみたいなレベルでは無いのですが、
それでもかなり楽になったのは確かです。
ただはっきりした話がまだどこにも書いてない気がするので
今回はその話を書きたいと思います。
おち
GASでOAuth2をやる場合はScriptApp.newStateToken()
を使って、/usercallback
にstate
パラメータで渡す
準備
いかがGASでOAuth2する上で必要なものになります。
- ClientId/ClientSecret
- Service Provider(authorization server)で取得して下さい。
今回はGoogleのAPIを叩くので、Developers Consoleから取得して下さい。
ただたいていのService ProviderではClientIdやCilentSecretを取得する際にredirect_uri
の設定が必要になります。
細かい説明は後半でしますが、redirect_uri
にはGASをWebアプリケーション化した際に利用できるURLの末尾を/usercallback
にしたものを登録して下さい。
例:
GASのWEBアプリケーションURL
https://script.google.com/macros/s/ABCDEFG-abcdefg-123456789/dev
↓
登録するredirect_uri
https://script.google.com/macros/s/ABCDEFG-abcdefg-123456789/usrcallback
詳細
大きく分けて2つの処理が必要です。
- Service Provider(今回はGoogle)の認可画面へユーザを遷移させるためのリンクを表示する画面を表示する処理(大体はdoGet)
- もっと言うと 認可画面へユーザを遷移させるためのURLを作る処理
- Service Providerから認可後戻ってきた際に処理を行うcallback処理
- この中では認可コードを元にAccessTokenを取得する。
認可画面へユーザを遷移させるためのURLを作るには、
認可画面へ言った後、GASのWebアプリケーションに戻ってきて何かしらの処理をする必要があります。
OAuth2であれば大体の場合は認可コードが渡されるのでそれを元にAccessTokenを取得するとかですね。
この何かしらの処理は自身で作成する必要があるのですが、それをdoGetの中に書いてしまうと、色々複雑になりますよね。
なので、GASでは先ほどredirect_uri
で登録したような
/usercallback
という他のアプリケーションから戻ってきた際に処理を行うようのURLがあります。
/usercallback
はGASで用意されている特別なURIでとあるstate
と呼ばれるパラメータと一緒に利用すると、
そのパラメータを元にGAS中に記載された任意の関数を呼び出すことが可能です。
ここでこのとあるパラメータを作るのに必要になるのが、ScriptApp.newStateToken()
から返却されるStateTokenBuilder
です。
StateTokenBuilderは/usercallback
でどのような処理をさせるかを記述した文字列を作成することができるサービスで、
以下の様なメソッドを持っています。
- StateTokenBuilder.withMethod(method)
-
/usercallback
に遷移後呼び出される関数名を文字列で指定します。 - 関数内でHtmlServiceやUiAppを返却するとそのままUIを表示できます。
-
- StateTokenBuilder.withTimeout(seconds)
- このstateパラメータの生存期間を設定できます。設定値は秒です。
- StateTokenBuilder.withArgument(name, value)
- withMethodで指定した関数に渡す引数を設定できます。
- withMethodで設定された関数(コールバック関数)には
doGet
同様にパラメータが渡されます。仮にコールバック関数でeという引数をとり、withArgumentでname=hoge, value=fugaと設定した場合、e.parameter.hoge
でfuga
が取得できます。
上記を用いて、GoogleのOAuth2用URLを作り、かつそのリンクをHTMLServiceを使って表示すると以下の様な感じになります。
function doGet() {
//clientIdの保存用
var prop = PropertiesService.getScriptProperties().getProperties();
//GoogleでOAuth2する場合のエントリーポイント
var url = "https://accounts.google.com/o/oauth2/auth";
//必要なパラメータ郡
var param = {
"response_type" : "code",
"client_id" : prop.clientId,
"redirect_uri" : getCallbackURL(), //←は↓に書いてあります
"state" : ScriptApp.newStateToken().withMethod("callback").withArgument("name", "value").withTimeout(2000).createToken() //←の指定で/usercallbackが呼び出された後、callback関数を呼び出し、その際nameというパラメータでvalueという値を渡し 2000秒でタイムアウトになるという設定が可能になります。
"scope" : "https://www.googleapis.com/auth/compute",
"access_type" : "offline"
};
var params = [];
for(var name in param){
params.push(name + "=" + encodeURIComponent(param[name]));
}
url = url + "?" + params.join("&");
return HtmlService.createHtmlOutput('<a href="' + url + '">認証</a>')
}
function getCallbackURL() {
// https://script.google.com/****/usercallbackを作成します。
var url = ScriptApp.getService().getUrl();
if (url.indexOf("/exec") >= 0) {
return url.slice(0, -4) + 'usercallback';
}
return url.slice(0, -3) + 'usercallback';
}
function callback(e) {
return HtmlService.createHtmlOutput("<pre>" + JSON.stringify(e, "",2) + "</pre>");
//実際にAccessTokenを取得する場合は以下の様な感じになります。
// var code = e.parameter.code;
// var credentials = fetchAccessToken(code);
// var properties = PropertiesService.getScriptProperties();
// properties.setProperty("credentials", JSON.stringify(credentials));
// var prop = properties.getProperties();
// var res = UrlFetchApp.fetch("https://www.googleapis.com/compute/v1/projects/" + prop.projectId , {
// headers : {
// "Authorization": "Bearer " + credentials.access_token
// }
//
// });
//
//
//
//
// return HtmlService.createHtmlOutput("<pre>" + res.getContentText() + "</pre>");
}
function fetchAccessToken(code) {
var prop = PropertiesService.getScriptProperties().getProperties();
var res = UrlFetchApp.fetch("https://accounts.google.com/o/oauth2/token", {
"method" : "POST",
payload : {
"code" : code,
"client_id" : prop.clientId,
"client_secret" : prop.clientSecret,
"redirect_uri" : getCallbackURL(),
"grant_type" : "authorization_code"
},
muteHttpExceptions : true
});
return JSON.parse(res.getContentText());
}
まとめ
ということでざっくりですがStateTokenBuilderの使い方について書きました。
これを利用すればFacebookAPIとかもかなり簡単にAccessTokenが取得可能になります。
※後半疲れてふっ飛ばしました...