OAuthでapps scriptとApp Engineをつなぐ

Ken published on
7 min, 1327 words

Categories: Programming

先日、Google Apps ScriptからGoogle App Engineのサービスを利用する際に管理者の判断を行いたかったのでそれについて調べていた。 自分ではある程度解決したと思っている。

発端

そんなとき、こんなmentionが飛んできた。

結局 @teaplanet の大将はAppsScriptからのappengineへのLoginつきアクセスってできたのかな?less than a minute ago via twicli Favorite Retweet Reply

来月辺り、@vvakameにふやかされる可能性があるので、今のうちに親切に恩を売っておく目的でこれ書いてる。

今のところこうなのかな?と思っている

以下の確認はスクリプトエディタから実行したので、トリガーなどからの実行は未確認です。 テスト中なので一部は "???" 表記にしてます。

まずはOAuthによる認証について。

  • ???.appspot.comではOAuthによる認証が出来た
  • ???.mydomain.xyzではOAuthによる認証が出来なかった

続いて、OAuthが通った場合のユーザ情報について。

  • AppEngine側ではOAuthServiceFactoryからユーザ情報が取得できた
  • AppEngine側ではUserServiceFactoryからユーザ情報が取得できなかった
  • UserServiceFactoryからUser情報が取得できなかったので、管理者かどうか判断できない

所感

mydomain.xyzで認証が通らないのはそのドメインの信頼性を確保できないからということなのだろうか? だとしたら、証明書を登録することによってmydomain.xyzでもOAuthによる認証が通るのかな?と思いつつ、試してない。時間があったらやってみたいところだけど、優先度が下がってる。 mydomain.xyzのToken(key, secret)使ってても、appspot.comでの認証が通ってる。mydomain.xyzはDNSでgoogleの方を向いているから結局は同じということなのだろうか?これはなんとなくありのような、無しのような、微妙な感覚。

ソースコード(apps script)

以下がapps scriptのソースコード。
function initApps_() {
  ScriptProperties.setProperty("http", "https://");
  ScriptProperties.setProperty("appid", "?????");
  ScriptProperties.setProperty("d", "appspot.com");
  ScriptProperties.setProperty("consumerKey", "?????.appspot.com");
  ScriptProperties.setProperty("consumerSecret", "appssecret");
}

function initMydomain_() {
  ScriptProperties.setProperty("http", "https://");
  ScriptProperties.setProperty("appid", "?????");
  ScriptProperties.setProperty("d", "mydomain.xyz");
  ScriptProperties.setProperty("consumerKey", "?????.mydomain.xyz");
  ScriptProperties.setProperty("sonsumerSecret", "mydomainsecret");
}

function initOAuthUrl_() {
  var http = ScriptProperties.getProperty("http");
  var appid = ScriptProperties.getProperty("appid");
  var domain = ScriptProperties.getProperty("d");
  
  var ah = http + appid + "." + domain + "/_ah/";
  ScriptProperties.setProperty("accessTokenUrl", ah + "OAuthGetAccessToken");
  ScriptProperties.setProperty("requestTokenUrl", ah + "OAuthGetRequestToken");
  ScriptProperties.setProperty("authUrl", ah + "OAuthAuthorizeToken");
}

// appspotへOAuth --> oauth: ken@mydomain.xyz / user: unknown
function oAuthAppspot() {
  initApps_();
  var url = "http://?????.appspot.com/oauth/auth";
  oAuthTest_(url);
}

// mydomainへOAuth --> Unknown(OAuthRequestExceptionが発生している)
function oAuthmydomain.xyz() {
  initMydomain_();
  var url = "http://?????.mydomain.xyz/oauth/auth";
  oAuthTest_(url);
}

// appspotへOAuth(mydomainのtoken) --> oauth: ken@mydomain.xyz / user: unknown
function oAuthAppspotMyKey() {
  initMydomain_();
  var url = "http://?????.appspot.com/oauth/auth";
  oAuthTest_(url);
}

// mydomainへOAuth(appspotのtoken) --> Unknown(OAuthRequestExceptionが発生している)
function oAuthmydomain.xyzAppspotKey() {
  initApps_();
  var url = "http://?????.mydomain.xyz/oauth/auth";
  oAuthTest_(url);
}

function oAuthTest_(url) {
  url += "?" + Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd HH:mm:ss");
  Logger.log(url);
  try {
    var response = UrlFetchApp.fetch(url, option_());
    Browser.msgBox(response.getContentText());
  } catch(e) {
    var msg = "";
    for (var i in e) {
      msg += e[i] + "n";
    }
    Browser.msgBox(msg);
  }
}

function option_() {
  initOAuthUrl_();
  var oAuthServiceName = "apps";
  var accessTokenUrl = ScriptProperties.getProperty("accessTokenUrl");
  var requestTokenUrl = ScriptProperties.getProperty("requestTokenUrl");
  var authUrl = ScriptProperties.getProperty("authUrl");
  var key = ScriptProperties.getProperty("consumerKey");
  var secret = ScriptProperties.getProperty("consumerSecret");

  var oAuthConfig = UrlFetchApp.addOAuthService(oAuthServiceName);
  oAuthConfig.setAccessTokenUrl(accessTokenUrl);
  oAuthConfig.setRequestTokenUrl(requestTokenUrl);
  oAuthConfig.setAuthorizationUrl(authUrl);
  oAuthConfig.setConsumerKey(key);
  oAuthConfig.setConsumerSecret(secret);

  var options = {
      "oAuthServiceName" : oAuthServiceName,
      "oAuthUseToken" : "always"
  };
  
  return options;
}

oAuthTest_メソッドでURLに時刻を付けてるのは、キャッシュを返されるような動作があったので、それを回避するため。

スクリプトでひとつ疑問。 ScriptPropertiesで "domain" をキーに指定した場合、getPropertyするとnullになる。これはうちだけ?

ソースコード(App Engine)

以下がApp Engineのソースコード。 言語はJavaじゃなくて、Scala使ってるし、オレオレフレームワーク使ってる。
class OauthController(request:Request, response:Response) extends Controller(request, response) {

	def auth = {
		try {
			val oauth = OAuthServiceFactory.getOAuthService();
			val oauthUser = oauth.getCurrentUser match {
				case user if user != null => user.getNickname;
				case _ => "unknown"
			}
			val user = UserServiceFactory.getUserService.getCurrentUser match {
				case user if user != null => user.getNickname
				case _ => "unknown"
			}
			val msg = "oauth: %s  /  user: %s".format(oauthUser, user)
			response.getWriter.write(msg)
		} catch {
			case e:OAuthRequestException =>
				response.getWriter.write(e.getMessage)
		}
	}
}

まとめ

今のところ、管理者かどうか判断は出来ていないけど、ユーザは判断できているので、それで凌ごうと思っている。必ずしも開発者(管理者)がアプリの管理者とは限らないので、これはこれでいいや…と思ってる。 お仕事じゃないので、うるさく言う人いないし(笑)