Github PagesでGASを使ってキーワード認証を行う


# はじめに

Github Pages で公開したサイトでキーワード認証をしたい．
しかし，静的サイトなのでサーバー側で認証を行うことができません．また，キーワード認証を Github Pages だけで行おうとすると（javascript で行おうとすると）コードを見られたときに，キーワードがばれてしまいます．
そのため，無料で使える GAS（Google Apps Script）を使ってキーワード認証を行う方法を紹介します．

:::note warn
part1 はこちら
こちらで GAS を使ってデータを保存・取得する方法を紹介しています．
:::

# 今回の目標

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670528/a61a0d25-2eee-a30e-7e62-99ec949626e0.png)

データの保存・取得は part1 で紹介したので，今回は GAS でキーワード認証を行います．

イメージは，以下のようになります．

1. **キーワード**と**そのキーワードが送信されたときのレスポンス**を登録する
   1. POST リクエストで**キーワード**と**レスポンス**を登録する
   1. 例えば，キーワード : **password**, レスポンス : **認証成功**
   1. データベース（spreadsheet）に登録される
1. GithubPages でできたサイトで**キーワード**を送信する
   1. GET リクエストで**キーワード**を送信する
   1. データベース（spreadsheet）に登録されている**キーワード**と一致するかを確認する
   1. 一致したら，登録されている**レスポンス**を返す
1. GithubPages でできたサイトでレスポンスを確認できる

:::note info
簡単に言うと，**ログイン画面**のようなものです．パスワードを入力すると，認証が通るかどうかを確認できます．ただし，画面遷移は行いません．
:::

# GAS コードの作成

前回からの変更点は，GET と POST リクエストの方法です．

- GET リクエスト

  - クエリパラメータを使って，キーワードを渡します．
  - キーワードが一致したら，認証突破後のデータを返します．

- POST リクエスト

  - リクエストボディを使って，キーワードと情報を渡します．
  - キーワードと情報をペアで登録します．

- スプレッドシート
  - 今回は A 列にキーワード，B 列に情報を登録します．
  - 同じ行のものがペアになります．
  - 新しいものは最終行に追加されます．

プロジェクトによってシート名を変更できるようにするため，シート名は指定できるようにしますが，複数キーワードがある場合を考えて，セル名は指定しません．

## リクエストデータ

### GET

```
https://script.google.com/macros/s/GASのデプロイID/exec?sheetName=シート名&keyword=キーワード
```

クエリパラメータ部分は`?sheetName=シート名&keyword=キーワード`です

### POST

```json
{
  "sheet": "シート名",
  "keyword": "値",
  "resp": "レスポンス"
}
```

:::note warn
前回同様，データをエンコードしたほうがいいかもしれませんが，今回は省略します．
そのため，日本語でデータを**keyword**を送ると，文字化けするかもしれません．
その場合は，エンコードするか，英語などで送るようにしてください．
:::

## GAS コード

長くなるので POST と GET を分けて書きます．

### POST

```javascript
// スプレッドシートの最終行(空白の行)を取得
function findEmptyRow() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var columnA = sheet.getRange("A:A").getValues();

  var row = 0;
  for (var i = 0; i < columnA.length; i++) {
    if (columnA[i][0] == "") {
      row = i + 1;
      break;
    }
  }

  return row;
}

function doPost(e) {
  const string = e.postData.getDataAsString();
  const JsonData = JSON.parse(string);
  const sheetName = JsonData.sheet;
  const keyword = JsonData.keyword;
  const resp = JsonData.resp;

  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName(sheetName);
  let row = findEmptyRow();
  sheet.getRange("A" + row).setValue(keyword);
  sheet.getRange("B" + row).setValue(resp);
  var data = { valid: true };

  const response = ContentService.createTextOutput();
  response.setMimeType(ContentService.MimeType.JSON);
  response.setContent(JSON.stringify(data));

  return response;
}
```

このコードでは，最終行にキーワードとレスポンスを登録します．

### GET

```javascript
// vlookup関数
function vlookup(searchKey, range, index, isSorted) {
  for (var i = 0; i < range.length; i++) {
    if (range[i][0] === searchKey) {
      return range[i][index - 1];
    }
  }

  if (isSorted) {
    return "#N/A";
  }

  return null;
}

function doGet(e) {
  const sheetName = e.parameter.sheetName;
  const cell = e.parameter.cell;

  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName(sheetName);
  var range = sheet.getRange("A:B").getValues();
  var result = vlookup("searchKey", range, 2, false);

  var data = { valid: true, value: result };
  if (result === null) {
    data = { valid: false, value: "not found" };
  }

  const response = ContentService.createTextOutput();
  response.setMimeType(ContentService.MimeType.JSON);
  response.setContent(JSON.stringify(data));

  return response;
}
```

このコードでは，キーワードを A 列から探し，一致したら同じ行の B 列のデータを返します．

# Github Pages で使う

最後に，Github Pages で使うために，fetch を使ってデータを送受信します．

### fetch の使い方

```javascript
async function fetchData(url, options) {
  try {
    const response = await fetch(url, options);
    const data = await response.json();
  } catch (error) {
    console.error("Error:", error);
  }
}
```

### GET

```javascript
// GASのurlにクエリパラメータ（?sheetName=シート名&cell=セル名）を付ける
const url =
  "https://script.google.com/macros/s/GASのデプロイID/exec?sheetName=シート名&keyword=キーワード";
const options = {
  method: "GET",
  headers: {
    "Content-Type": "application/json",
  },
};
fetchData(url, options);
```

### POST

```javascript
// GASのurl
const url = "https://script.google.com/macros/s/GASのデプロイID/exec";
const options = {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    sheet: "シート名",
    keyword: "値",
    resp: "レスポンス",
  }),
};
fetchData(url, options);
```

# まとめ

GAS を使って，Github Pages サイトでキーワード認証を行う方法を紹介しました．
これで，**ログイン画面**も作れる！ということになりますが，画面遷移を行うと，次はその url にアクセスをすれば良くなってしまうので，注意が必要です．
また，Github の有料プランに入っていないと，コードが完全に公開されてしまうので，このサイトにどのようなパスがあるかが全て分かってしまいます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670528/177765bb-4c81-c960-da7a-2d3fbdc80d66.png)

# 次回

それでは，GithubPages でログイン画面を作るにはどうしたらよいのかを考えていきます．