Help us understand the problem. What is going on with this article?

Google Apps Script DE OAuth2 ~ GASで直接触れない Google APIを叩いてみるお~

More than 5 years have passed since last update.

GASをガスって読むのは中の人からするとあまり好きじゃないらしいですね。大橋です。

TypetalkAppの時も少し言及したかもですが、
最近GASでOAuth2がやりやすくなりました。

ただし
やりやすくなったと言っても、
UrlFetchAppがOAuth1ように備えていたような、
自動でほぼほぼやってくれるみたいなレベルでは無いのですが、
それでもかなり楽になったのは確かです。

ただはっきりした話がまだどこにも書いてない気がするので
今回はその話を書きたいと思います。

おち

GASでOAuth2をやる場合はScriptApp.newStateToken()を使って、/usercallbackstateパラメータで渡す

準備

いかが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つの処理が必要です。

  1. Service Provider(今回はGoogle)の認可画面へユーザを遷移させるためのリンクを表示する画面を表示する処理(大体はdoGet)
    • もっと言うと 認可画面へユーザを遷移させるためのURLを作る処理
  2. 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.hogefugaが取得できます。

上記を用いて、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が取得可能になります。

※後半疲れてふっ飛ばしました...

soundTricker
Google API、GSuite、GCP、Angular(1&2)、Google Apps Scriptらへんの人 一応Google Developer Expert(Apps Script)です。 https://developers.google.com/community/experts/directory/profile/profile-keisuke_oohashi
https://plus.google.com/u/0/112329532641745322160/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away