Google Apps ScriptからプライベートなサービスのAPIを呼び出す場合、何らかの認証が必要になるので、GASから取得したOAuth2アクセストークンを検証するプロキシサーバを作ってみた。
仕組み
ScriptApp.getOAuthToken()を使うと有効なユーザーの OAuth 2.0 アクセス トークンを取得できる。※appscript.jsonの修正が必要

// appscript.json { "timeZone": "Asia/Tokyo", "dependencies": {}, "exceptionLogging": "STACKDRIVER", "runtimeVersion": "V8", "oauthScopes": [ "openid", "email", "https://www.googleapis.com/auth/script.external_request" ] }
トークンを使ってさらにユーザー情報を取得できる。
$ curl -H "Authorization: Bearer ${TOKEN}" https://openidconnect.googleapis.com/v1/userinfo { "sub": "...", "picture": "..., "email": "sugawara@winebarrel.jp", "email_verified": true, "hd": "winebarrel.jp" }
作成したプロキシサーバではユーザー情報のemailが許可されているかを検証する。
使い方
$ docker run --rm ghcr.io/winebarrel/gap --help Usage: gap --backend=BACKEND --port=UINT --header-name=STRING --allow-list=ALLOW-LIST,... [flags] Flags: -h, --help Show help. -b, --backend=BACKEND Backend URL ($GAP_BACKEND). -p, --port=UINT Listening port ($GAP_PORT). -n, --header-name=STRING Header name to pass the access token ($GAP_HEADER). -e, --allow-list=ALLOW-LIST,... Allowed email list that may contain wildcards ($GAP_ALLOW_LIST). --version
バックエンドのURL、リッスンポート、アクセストークンを渡すヘッダ名、許可するメールアドレスを指定してプロキシサーバを起動する。
$ docker run --rm -p 8080:8080 ghcr.io/winebarrel/gap -b https://example.com -e '*@winebarrel.jp' -p 8080 -n x-my-gap-token
正しいアクセストークンを渡すとプロキシサーバからバックエンドにアクセスできる。
$ curl -s -H "x-my-gap-token: ${TOKEN}" localhost:8080 <!doctype html> <html> <head> <title>Example Domain</title>
間違ったアクセストークンを渡すとはじかれる。
$ curl -s -H "x-my-gap-token: ${TOKEN}x" localhost:8080 forbidden
GASからはUrlFetchApp.fetch()でアクセスできる。
function myFunction() { const token = ScriptApp.getOAuthToken(); const response = UrlFetchApp.fetch('https://api.my-private.example.com/foo/bar/zoo', { headers: { "x-my-gap-token": token } }); console.log(response.getContentText()); }
GASをスプレッドシートのカスタム関数として使っている場合、関数から直接アクセストークンを取得することはできないが、メニューのアイテムとしてScriptApp.getOAuthToken()を呼び出してキャッシュに保存することで、カスタム関数内でアクセストークンを使うことができる。
function onOpen() { const ui = SpreadsheetApp.getUi(); ui.createMenu("認証").addItem("認証", "auth").addToUi(); } function auth() { const cache = CacheService.getUserCache(); const token = ScriptApp.getOAuthToken(); cache.put("token", token); } function myFunction() { const cache = CacheService.getUserCache(); const token = cache.get("token"); // ...
