この記事はで読むことができます。
はじめに
GASで作成したWebサイトはデフォルトで「自分のみ」「Googleアカウントを持つ全員」「全員」という公開範囲を設定可能です。
さらにアクセスしているユーザの情報を取得することで細かなアクセス制御も可能ですが、Googleアカウントの情報を取得するため厳密に制限ができる反面、事前にアクセスを許可するメールアドレスをリストに追加し、閲覧ユーザにユーザ情報の取得を許可してもらう必要があります。
今回紹介する方法では事前のメールアドレス把握やユーザ情報の取得は不要です。
ただし通常のbasic認証同様、IDとPWがわかればアクセスできてしまう点にはご注意ください。
この記事では以下のステップでコードを解説しています。
1. 仕組み
ID/PWの認証にはGASのProperties Serviceクラスの「スクリプトプロパティ」と「ユーザプロパティ」を使用します。
スクリプトプロパティは全てのユーザがアクセスできるプロパティ、ユーザプロパティはスクリプトを動かすユーザのみがアクセスできるプロパティです。
スクリプトプロパティへ事前にID/PWを設定しておき、ユーザプロパティへユーザが入力したID/PWを保存をします。
GET時にID/PWの認証を行い、結果に応じて認証画面と認証後の画面を出し分けます。
認証が成功した場合にユーザプロパティにID/PWを保存して、次回以降はGET時に保存した認証データを参照します。
2. コード一覧
「ファイルを追加」から以下のファイルを作成し、各々コードを書き換えてください。
※ ID/PWをコードに直接記述すると、ライブラリとして公開されるURLを利用した際に外部のユーザが取得できてしまうため、{ID} {PW}から変更しないでください
- gas.gs
- auth.html
- index.html
gas.gs
サーバー側の処理部分になります
function setup() {
PropertiesService.getScriptProperties().setProperty('auth', JSON.stringify({id:"{ID}", pw:"{PW}"}));
}
function doGet() {
const authOwn = JSON.parse(PropertiesService.getUserProperties().getProperty('auth_users'));
if (tryAuth(authOwn) == "success"){
var htmlOutput = HtmlService.createTemplateFromFile("index").evaluate();
} else {
var htmlOutput = HtmlService.createTemplateFromFile("auth").evaluate();
}
return htmlOutput;
}
function tryAuth(authOwn) {
if (authOwn != null){
const auth = JSON.parse(PropertiesService.getScriptProperties().getProperty('auth'));
if (auth.id == authOwn.id && auth.pw == authOwn.pw) {
PropertiesService.getUserProperties().setProperty('auth_users', JSON.stringify(authOwn));
return "success";
}
}
return "fail";
}
function deleteAuth(){
PropertiesService.getUserProperties().deleteAllProperties();
}
auth.html
認証画面としてフロントに表示するものです
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<form onsubmit="handleSubmit()">
<input name="id" type="text" placeholder="ID" required="required" autocomplete="off">
<input name="pw" type="password" placeholder="PW" required="required" autocomplete="off">
<input type="submit" value="認証">
</form>
<script>
window.addEventListener("load", function(){
document.querySelector("form").addEventListener("submit", function (event) {
event.preventDefault();
});
});
function handleSubmit() {
document.querySelector("input[type=submit]").disabled = true;
const authOwn = document.querySelector("form");
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(onSuccess)
.tryAuth(authOwn);
}
function onSuccess(rslt){
if (rslt == "success"){
window.open("<?= ScriptApp.getService().getUrl() ?>", "_top");
} else {
alert("認証情報が一致しません");
document.querySelector("input[type=submit]").disabled = false;
}
}
function onFailure(){
alert("正常に完了しませんでした");
document.querySelector("input[type=submit]").disabled = false;
}
</script>
</body>
</html>
index.html
認証済の場合にフロントで表示するものです
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
認証成功
<button type="button" onclick="reset()">認証情報削除</button>
<script>
function reset(){
google.script.run
.withFailureHandler(reload)
.withSuccessHandler(reload)
.deleteAuth();
}
function reload(){
window.open("<?= ScriptApp.getService().getUrl() ?>", "_top");
}
</script>
</body>
</html>
3. セットアップ方法
setup()
を実行※ 必ず「ウェブアプリケーションにアクセスしているユーザー」で実行するように設定してください
4. コード解説
※ JavaScriptの挙動については詳細は割愛しています
gas.gs
gas.gsに記述するコードについて解説します。
PropertiesServiceクラスのgetScriptProperties()
メソッドでスクリプトプロパティを取得
取得したプロパティに対してPropertiesクラスのsetProperty(key, value)
メソッドでキーauthに対してidとpwを保存
このとき、Object型ではプロパティの値として扱えないため、JSON形式に変換を行う
PropertiesService.getScriptProperties().setProperty('auth', JSON.stringify({id:"{ID}", pw:"{PW}"}));
webサイト読み込み時(GET時)に動作
PropertiesServiceクラスのgetUserProperties()
メソッドでユーザプロパティを取得
取得したプロパティに対してPropertiesクラスのgetProperty(key)
メソッドでキーauth_usersに紐づく値を取得
JSON形式で保存されているため、Object型に戻して変数authOwnへ格納
const authOwn = JSON.parse(PropertiesService.getUserProperties().getProperty('auth_users'));
関数tryAuthへ変数authOwnで認証をリクエスト
戻り値に応じたhtmlファイルを変数htmlOutputにHtmlOutputオブジェクトとして格納
HtmlOutputオブジェクトはHtmlServiceクラスのcreateTemplateFromFile(filename)
メソッドでhtmlファイルをHtmlTemplateオブジェクトに変換し、HtmlTemplateクラスのevaluate()
メソッドで変換して生成
※ 今回はhtml内にGASコードが含まれるためcreateTemplateFromFile(filename)
を使用
if (tryAuth(authOwn) == "success"){
var htmlOutput = HtmlService.createTemplateFromFile("index").evaluate();
} else {
var htmlOutput = HtmlService.createTemplateFromFile("auth").evaluate();
}
フロントで出力する内容として変数htmlOutputを返す
return htmlOutput;
変数authOwnが空でない場合、
PropertiesServiceクラスのgetScriptProperties()
メソッドでスクリプトプロパティを取得
取得したプロパティに対してPropertiesクラスのgetProperty(key)
メソッドでキーauthに紐づく値を取得
JSON形式で保存されているため、Object型に戻して変数authへ格納
さらに変数authOwnと変数authのid/pwがどちらも一致した場合、
PropertiesServiceクラスのgetUserProperties()
メソッドでユーザープロパティを取得
取得したプロパティに対してPropertiesクラスのsetProperty(key, value)
メソッドでキーauth_usersに対してidとpwを保存
このとき、Object型ではプロパティの値として扱えないため、JSON形式に変換を行う
認証結果としてsuccessを返す
if (authOwn != null){
const auth = JSON.parse(PropertiesService.getScriptProperties().getProperty('auth'));
if (auth.id == authOwn.id && auth.pw == authOwn.pw) {
PropertiesService.getUserProperties().setProperty('auth_users', JSON.stringify(authOwn));
return "success";
}
}
変数authOwnが空の場合もしくは変数authOwnと変数authのidとpwが一致しなかった場合、認証結果としてfailを返す
return "fail";
PropertiesServiceクラスのgetUserProperties()
メソッドでユーザープロパティを取得
取得したプロパティに対してPropertiesクラスのdeleteAllProperties()
メソッドで全てのキーとそれに紐づく値を削除
PropertiesService.getUserProperties().deleteAllProperties();
auth.html
auth.htmlに記述するJavascriptについて解説します。
画面読み込み完了後に自動実行
form要素のsubmit時にsubmitのデフォルトイベントをキャンセルするイベントを追加
document.querySelector("form").addEventListener("submit", function (event) {
event.preventDefault();
});
重複送信防止の為、submitボタンを非活性化
document.querySelector("input[type=submit]").disabled = true;
変数authOwnにform要素を格納
const authOwn = document.querySelector("form");
GAS側の関数tryAuthへ変数authOwnで認証をリクエスト
認証結果を問わず関数実行が失敗した場合、関数onFailureを動作させる
認証結果を問わず関数実行が成功した場合、関数onSuccessを動作させる
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(onSuccess)
.tryAuth(authOwn);
認証が成功した場合、現在のURLに再アクセスを行いGAS側の関数doGetを動作させる
現在のURLはGASのScriptAppクラスのgetService()
メソッドでウェブアプリのServiceオブジェクトを取得し、getUrl()
メソッドで取得
※ GASのwebサイトはiframe構造のためJSのlocation.href
では取得できません
失敗した場合、認証が失敗した旨をアラートで通知し、submitボタンを活性化
if (rslt == "success"){
window.open("<?= ScriptApp.getService().getUrl() ?>", "_top");
} else {
alert("認証情報が一致しません");
document.querySelector("input[type=submit]").disabled = false;
}
関数実行が失敗した旨をアラートで通知
alert("正常に完了しませんでした");
submitボタンを活性化
document.querySelector("input[type=submit]").disabled = false;
index.html
index.htmlに記述するJavascriptについて解説します。
GAS側の関数deleteAuthへ認証情報削除をリクエスト
関数実行結果を問わず、関数reloadを動作させる
google.script.run
.withFailureHandler(reload)
.withSuccessHandler(reload)
.deleteAuth();
現在のURLに再アクセスを行いGAS側の関数doGetを動作させる
現在のURLはGASのScriptAppクラスのgetService()
メソッドでウェブアプリのServiceオブジェクトを取得し、getUrl()
メソッドで取得
※ GASのwebサイトはiframe構造のためJSのlocation.href
では取得できません
window.open("<?= ScriptApp.getService().getUrl() ?>", "_top");
まとめ
GASでBasic認証もどきを実装する方法を解説しました。
コピペだけで実装できますので、社内や仲間内での共有などにぜひ活用してみてください。