正直な所、他のウェブサービス(ツイッターやらなにやら)は、業務では使用しないので、OAuth認証系に関しては、必要ない・もしくは必要となっても、oAuthConfigとUrlfetchAppを使ったOAuth1の認証で十分と思ってました。特に業務では、PDF化などに於いて、Google Appsなどでは使う機会があるのですが、既に紹介した方法で可能です。が、しかし、1点問題があるのです。この認証は、スクリプトエディタに入って1度実行しないと認証画面が出てくれない&実行ユーザ毎に認証が必要(但し、ScriptTriggerによる自動実行は設置者が1回やれば問題ない)、更にはユーザにそんな作業をやらせないといけないと、問題がオオアリなわけです。普通の認証画面の用に実行時に出てくれたら良いのですが、なぜか、このOAuth認証は出てくれないから困るわけで。
ということで、調べて見たところ、この問題の解決およびついでに外部のAPIを叩いてGoogle Apps ScriptでOAuth2.0認証を画面認証させる&PDFを作れるようにする為に四苦八苦して、なんとか出来るようになりました。これによって、ダイアログで認証作業をさせることが出来、また、これまでのようにPDFを作れるので、少しこれで様子を見てみようと思っています。また、Googleの様々なAPI自体もUrlfetchAppのように、外部からのアクセスという形の場合には、このOAuthの認証が必要になることがあります。
PDF作成では、UrlfetchAppに続けて、oauthconfigのパラメータを渡して、認証を行わせてPDFを作りました。
今回、OAuth2.0認証の場合には、少々面倒な作業が待っています。しかし、Googleも推奨しているようにOAuth1.0から2.0へ移行しなさいという警告および、他のウェブサービスが2.0へ移行していることもあって、これから先のGASアプリケーションを作る上では、結構避けられない部分があったりします。しかし、このOAuth2.0は仕組みも結構複雑なので、少々理解するのに時間が必要かもしれません。
OAuth2.0を使用する為にはいくつかの情報が必要です。その1つがウェブアプリケーションとして公開した時のURLです。スプレッドシートなどのURLとは異なり、このURLは次の項目であるクライアントIDの作成上必要なものです。以下の手順で公開し、URLをメモしておきます。
Google の各種APIを今回は使用するので、Google Developer Consoleにて、クライアントIDを作成します。以下の手順で作成します。
図:クライアントIDの作成中の画面
いくつか、認証してアクセストークンを取得するコードがネットにあるのですが、自分としてはこれがいちばんフィットしたというのがありましたので、今回はこのサイトのコードを利用させていただきました。そのコードについて見てゆきたいと思います。
//クライアントID関係のグローバル変数var CLIENT_ID = 'ここにクライアントIDを入力する';var CLIENT_SECRET='ここにクライアントシークレットコードを入力する';var REDIRECT_URL= ScriptApp.getService().getUrl();//OAuth関係のグローバル変数var AUTHORIZE_URL = 'https://accounts.google.com/o/oauth2/auth'; var TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'; var tokenPropertyName = 'GOOGLE_OAUTH_TOKEN'; var baseURLPropertyName = 'GOOGLE_INSTANCE_URL'; //プログラムのメインコードfunction doGet(e) { var HTMLToOutput; if(e.parameters.code){//if we get "code" as a parameter in, then this is a callback. we can make this more explicit getAndStoreAccessToken(e.parameters.code); HTMLToOutput = '<html><h1>OAuth認証が完了しました。</h1></html>'; } else if(isTokenValid()){//if we already have a valid token, go off and start working with data HTMLToOutput = '<html><h1>既にOAuth認証完了してますよ</h1></html>'; } else {//we are starting from scratch or resetting return HtmlService.createHtmlOutput("<html><h1>Lets start with oAuth</h1><a href='"+getURLForAuthorization()+"'>click here to start</a></html>"); } HTMLToOutput += getData(); return HtmlService.createHtmlOutput(HTMLToOutput);}//データの取得
function getData(){ var getDataURL = 'https://www.googleapis.com/oauth2/v1/userinfo'; var dataResponse = UrlFetchApp.fetch(getDataURL,getUrlFetchOptions()).getContentText(); return dataResponse;}//URLの組み立てとスコープの指定function getURLForAuthorization(){ return AUTHORIZE_URL + '?response_type=code&client_id='+CLIENT_ID+'&redirect_uri='+REDIRECT_URL + '&scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile&state=/profile https://docs.google.com/feeds'; }//組み立てたURLをPOSTで投げて、コールバックデータをJSON形式で受けて取り出す。function getAndStoreAccessToken(code){ var parameters = { method : 'post', payload : 'client_id='+CLIENT_ID+'&client_secret='+CLIENT_SECRET+'&grant_type=authorization_code&redirect_uri='+REDIRECT_URL+'&code=' + code }; var response = UrlFetchApp.fetch(TOKEN_URL,parameters).getContentText(); var tokenResponse = JSON.parse(response); //アクセストークンをユーザプロパティに格納 UserProperties.setProperty(tokenPropertyName, tokenResponse.access_token);}//Urlfetchオプションの指定function getUrlFetchOptions() { var token = UserProperties.getProperty(tokenPropertyName); return { "contentType" : "application/json", "headers" : { "Authorization" : "Bearer " + token, "Accept" : "application/json" } };}//トークン取得しているかどうかの確認function isTokenValid() { var token = UserProperties.getProperty(tokenPropertyName); if(!token){ //if its empty or undefined return false; } return true; //naive check }試しに自分はこの認証をユーザ側で簡単に行える用に、インターフェースを用意してみました。
予め、特定のユーザプロパティを取得して、トークンを持っているかどうかを確認し、トークンを持っていない場合には、起動時にこの画面を出すようにしています。もちろん、持っている場合には、特に何もしません。もちろん、インターフェースはCSSなどでちょっと格好良くして、表示させてそれっぽく見せます。承認ボタンを押すと、新しいタブでURLが開かれて、hrefに指定しておいたURLが開くようになっています。
図:試しにつくってみたインターフェース(HTML Servicesで作成したダイアログ)
既に、上記のコードでアクセストークンを取得してたら、以降はその取得済みアクセストークンをパラメータとしてUrlfetchAppへ渡して上げれば良いです。これまでは、この段階でUrlfetchAppの持つOAuth1認証機能を利用して、oAuthConfigにパラメータやオプションを設定していましたが、今回は不要です。
function sheet2pdf2mail2() { //アクティブシートのIDとGIDを取得する var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheet = ss.getActiveSheet(); var sheetID = sheet.getSheetId(); var key = ss.getId(); var token = ScriptApp.getOAuthToken(); //PDF生成するURLをfetchする var newFileId = "ここにPDF化したいファイルのIDを入れる"; var url = "https://docs.google.com/a/spreadsheets/d/"+newFileId+"/export?format=pdf&portrait=true&size=A4&gridlines=false&fitw=true"; var pdf = UrlFetchApp.fetch(url, {headers: {'Authorization': 'Bearer ' + token}}).getBlob().setName("test" + ".pdf"); //作成したPDFファイルをメールに添付して送る var mail = '送信先メールアドレスをここに入れます' var subject = 'PDF送るよ' var body = 'まぁ、送るんで見てくれ' MailApp.sendEmail(mail, subject, body, {attachments:pdf});}