LANG SELRCT

Apps Script Reference  (Create: Create new Spreadsheet | Create new Apps Script

Wednesday, July 17, 2019

freee APIでアクセストークンを取得したい


Google Apps Scriptで取得する手順を書いていきます。


公式のサイトでは外部ライブラリを使っていますが、この記事ではライブラリを使わない方法でアクセストークンを取得してみます。


大きく3つのSTEPで書いていきます。
  1. アプリの「Client ID」「Client Secret」を取得する
  2. コールバックURLをGASで作成する
  3. 連携してアクセストークンを取得する



STEP1 アプリの「Client ID」「Client Secret」を取得する


https://app.secure.freee.co.jp/ を開いて「開発者ページ」をクリックします。



「今すぐアプリを作成」をクリックします。



①「アプリ名」を決めます
②「概要」を入力します
③「利用規約」をチェックします
④「作成」をクリックします


「Client ID」「Client Secret」が表示されます。



STEP2 コールバックURLをGASで作成する


Google Apps Scriptの新規プロジェクトを作成します。
https://script.google.com/macros/create


このような画面が表示されます。


function myFunction() {〜}は削除して、以下のコード.gsをコピーして貼り付けます。


コピーして貼り付けるコード

コード.gs
var client_id = 'Client IDを貼り付けます';
var client_secret = 'Client Secretを貼り付けます';


/************************************
手順
1. 上部の1,2行目にアプリストアのClient ID, Client Secretを貼り付ける
2. 上部メニューの「公開」>「ウェブアプリケーションとして導入」 >「アプリケーションにアクセスできるユーザー」を全員(匿名ユーザを含む)で「更新」する
3. 「現在のウェブ アプリケーションの URL」をアプリストアの「コールバックURL」に貼り付けて「下書き保存」
4. 「Webアプリ認証用URL」をコピーしてブラウザの新規タブで開いて認証を行うとブラウザにアクセストークンが返ってくる

↓以下のコードは触らなくてOK
************************************/

function doGet(e) {// Web認証用URLを開いたときに動く処理
  var response = getAccessToken(e);
  return ContentService.createTextOutput(response);// ブラウザに表示する
}

var token_url = 'https://accounts.secure.freee.co.jp/public_api/token';

function getAccessToken(e) {// 認可コードを利用してトークン情報を取得して返す
  var code = e['parameter']['code'];
  var payload = {
    'grant_type': 'authorization_code',
    'client_id': client_id,
    'client_secret': client_secret,
    'code': code,
    'redirect_uri': ScriptApp.getService().getUrl()
  }
  
  var options = {
    'method': 'post',
    'contentType': 'application/x-www-form-urlencoded',
    'payload': payload
  };
  var response = UrlFetchApp.fetch(token_url, options);
  return response;
}




「ファイル」>「保存」を選択します。


任意の「プロジェクト名」を入力して「OK」をクリックします。


client_idに「Client ID」、client_secretに「Client Secret」の値を入れて保存します。


「公開」>「ウェブアプリケーションとして導入」をクリックします。


「アプリケーションにアクセスできるユーザー」>「全員(匿名ユーザーを含む)」>「導入」



「許可を確認」をクリックします。


自分のアカウントを選択します。


「詳細」をクリックします。


下の方にプロジェクト名のリンクが表示されるのでクリックします。


「許可」をクリックします。


「現在のウェブアプリケーションのURL」が表示されます。



STEP3 連携してアクセストークンを取得する


コールバックURLにGASで作ったウェブアプリのURLを貼り付けて「下書き保存」します。


「Webアプリ認証用URL」をコピーしてブラウザの新規タブで開きます。



「許可する」をクリックします。


ブラウザにaccess_tokenなどが表示されます。


access_tokenの有効期間は24時間です。
refresh_tokenでaccess_tokenを更新することができます。


補足

コード.gs内の
'redirect_uri': ScriptApp.getService().getUrl()

がうまく行かない場合があります。
デプロイしたWebアプリのURLを取得しているのですが、以下のようなエラーが出ます。

https://accounts.secure.freee.co.jp のリクエストに失敗しました(エラー: 401)。サーバー応答の一部: {"error":"invalid_grant","error_description":"指定された認可グラントは不正か、有効期限切れか、無効か、リダイレクトURIが異なるか、もしくは別のクライアントに適用されています。"}(応答の全文を見るには muteHttpExceptions オプションを使用してください)(行 37、ファイル「コード」、プロジェクト「無題のプロジェクト」)


これは、G suiteで使う場合に、GASのWebアプリのURLが変わるのが原因のようです。
(URLにドメインが追加される)

  • 「導入」クリック後に表示されるURL
    • https://script.google.com/a/macros/s/WEBアプリのID/exec
  • それをブラウザで開いたときのURL
    • https://script.google.com/a/macros/ドメイン/s/WEBアプリのID/exec


その場合、「現在のウェブアプリケーションのURL」を一度ブラウザで開いてみて、
アドレスバーに表示されるURLを貼り付けてみてください。

コード.gsの
'redirect_uri': ScriptApp.getService().getUrl()

'redirect_uri': 'アドレスバーに表示されるURL'
に変えて

「公開」>「ウェブアプリケーションとして導入」でバージョンをあげて「導入(デプロイ)」して、

freeeのアプリストアのコールバックURLの欄にも アドレスバーに表示されるURL を入れて下書き保存するとうまくいくかもしれません。



APPENDIX

/usercallbackを使うと
Who has access to the app: Only myself
で良さそうです。


コード.gs
var client_id = 'CLIENT_ID';
var client_secret = 'CLIENT_SECRET';

var redirect_uri = getRedirectUri();

function getRedirectUri() {
  var appUrl = ScriptApp.getService().getUrl();// v8では末尾が/devになる→いつか/execになるかもしれない
  var splited = appUrl.split("/");
  var tail = splited[splited.length-1];// /で分けた最後の部分を取得する→dev
  var last = "/" + tail;// /devにする
  var redirect_uri = appUrl.replace(last, "/usercallback");// appUrlの末尾の/devを/usercallbackに変更する
  // 単純に appUrl.replace("/dev", "/usercallback") としても今はいいけれど、/dev が /execに変わったときにエラーで動かなくなるため、その未来のバグを回避する
  // バグ報告は上がっている→https://stackoverflow.com/questions/60232532/scriptapp-getservice-geturl-points-to-dev-url-how-can-i-get-it-to-point-to
  return redirect_uri;
}


function doGet(e) {// Web認証用URLを開いたときに動く処理
  var response = getAccessToken(e);
  return ContentService.createTextOutput(response);// ブラウザに表示する
}

var token_url = 'https://accounts.secure.freee.co.jp/public_api/token';

function getAccessToken(e) {// 認可コードを利用してトークン情報を取得して返す
  var code = e['parameter']['code'];
  var payload = {
    'grant_type': 'authorization_code',
    'client_id': client_id,
    'client_secret': client_secret,
    'code': code,
    'redirect_uri': redirect_uri
  }
  
  var options = {
    'method': 'post',
    'contentType': 'application/x-www-form-urlencoded',
    'payload': payload
  };
  var response = UrlFetchApp.fetch(token_url, options);
  return response;
}


さらにstateを渡してみる


コード.gs
var client_id = 'CLIENT_ID';
var client_secret = 'CLIENT_SECRET';

var redirect_uri = getRedirectUri();

/************************************
コールバックURLを作成する
************************************/
function getRedirectUri() {
  var appUrl = ScriptApp.getService().getUrl();// v8では末尾が/devになる→いつか/execになるかもしれない
  var splited = appUrl.split("/");
  var tail = splited[splited.length-1];// /で分けた最後の部分を取得する→dev
  var last = "/" + tail;// /devにする
  var redirect_url = appUrl.replace(last, "/usercallback");// appUrlの末尾の/devを/usercallbackに変更する
  // 単純に appUrl.replace("/dev", "/usercallback") としても今はいいけれど、/dev が /execに変わったときにエラーで動かなくなるため、その未来のバグを回避する
  // バグ報告は上がっている→https://stackoverflow.com/questions/60232532/scriptapp-getservice-geturl-points-to-dev-url-how-can-i-get-it-to-point-to
  Logger.log(redirect_url);
  return redirect_url;
}

/************************************
Webアプリ認証用URLにstateを追加したURLを生成する
生成されたURLを開いて認可を行う
************************************/
function generateAuthCodeUrl() {
  var authCodeUrl = "https://accounts.secure.freee.co.jp/public_api/authorize" +
                    "?client_id=" + client_id +
                    "&redirect_uri=" + redirect_uri +
                    "&response_type=" + "code" +
                    "&state=" + ScriptApp.newStateToken().withMethod("getAccessToken").withTimeout(120).createToken();
  Logger.log(authCodeUrl);
  return authCodeUrl;
}

/************************************
初回アクセストークンを取得する
************************************/
var token_url = 'https://accounts.secure.freee.co.jp/public_api/token';

function getAccessToken(e) {// 認可コードを利用してトークン情報を取得して返す
  var code = e['parameter']['code'];
  var payload = {
    'grant_type': 'authorization_code',
    'client_id': client_id,
    'client_secret': client_secret,
    'code': code,
    'redirect_uri': redirect_uri
  }
  
  var options = {
    'method': 'post',
    'contentType': 'application/x-www-form-urlencoded',
    'payload': payload
  };
  var response = UrlFetchApp.fetch(token_url, options);
  return ContentService.createTextOutput(response);
  return response;
}



Class StateTokenBuilder

stateとusercallbackの部分はこちらも参考になりました

Latest post

スプレッドシートA列にある複数のテキストをスライドに追加したい(Google Apps Script)

今回Google Apps Scriptでやりたいこと GoogleスプレッドシートA列にある複数の値を取得して Googleスライドに渡して 図形オブジェクトのテキストとして追加したい ① スプレッドシートのA列に値を入れておく ② Code.gsのinsertNewShape...