1Password CLIで認証情報をセキュア&便利に使おう！

# 1Passwordとは
1Password はパスワードマネージャーとして有名な製品です。

https://1password.com/jp

単にID/PWだけでなく、MFAやパスキーにも対応し、SSHの鍵情報やAPIのアクセスキーも管理できます。

また、これらを開発者がより簡単に利用するための[1Password CLI](https://developer.1password.com/docs/cli)も提供されています。

https://developer.1password.com/docs/cli

本記事では [1Password CLI](https://developer.1password.com/docs/cli) を使うことで、認証情報を**セキュアに**、かつ**便利に**扱う方法を紹介します。


# お題: APIトークンを挿入する
APIトークンなどの認証情報は流出したら一大事です。
これら認証情報を扱うときはみなさん細心の注意を払っていると思います。

とはいえ人間は完璧ではありません。
どんなに気をつけていても、一瞬の気の緩みから流出事故につながることがあります。

以下は[GitHub REST APIでissueを取得](https://docs.github.com/ja/rest/issues/issues?apiVersion=2022-11-28)して表示するNode.jsのコード例です。
`<token>` 部分に https://github.com/settings/personal-access-tokens/new で生成できるトークンが入ります。
```javascript:api-client.js
(async () => {
    // USERはユーザー名、REPOはリポジトリ名
    const response = await fetch("https://api.github.com/repos/USER/REPO/issues",{
        method: "GET",
        headers: {
            "Accept": "application/vnd.github+json",
            "Authorization": "Bearer <token>",
            "X-GitHub-Api-Version": "2022-11-28",
        }
    });
    const data = await response.json();
    console.log(data);
})();
```
`<token>` にトークンをベタ書きすればもちろんAPIを叩けますが、実際の開発現場でそのようのことは許されないでしょう。

# よく使われる方法
## ①環境変数からトークンを取得する
以下のようなコードで、実行時に環境変数にトークンを渡します。
```javascript:api-client.js
(async () => {
    // USERはユーザー名、REPOはリポジトリ名
    const response = await fetch("https://api.github.com/repos/USER/REPO/issues",{
        method: "GET",
        headers: {
            "Accept": "application/vnd.github+json",
            "Authorization": `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`,
            "X-GitHub-Api-Version": "2022-11-28",
        }
    });
    const data = await response.json();
    console.log(data);
})();
```
```sh
# 実行時にセット
GITHUB_ACCESS_TOKEN=github_pat_XXXXXXXXXXXXXXXXXXXXXXXXXX node api-client.js

# またはセッション内で環境変数にセット
export GITHUB_ACCESS_TOKEN=github_pat_XXXXXXXXXXXXXXXXXXXXXXXXXX
node api-client.js
```
この方法の面倒なところは、**実行時 or セッションが変わるたび**に毎回トークンの入力が必要なところです。

## ②`.env` にトークンを記載する
①のデメリットを補う方法として、`.env` がよく使われると思います。
以下のような `.env` ファイルを作成し実行時に環境変数にセットすることができます。

ここでは [env-cmd](https://github.com/toddbluhm/env-cmd) を使っています。
```config:.env
GITHUB_ACCESS_TOKEN=github_pat_XXXXXXXXXXXXXXXXXXXXXXXXXX
```
実行コマンド。`api-client.js`の中身は①と同じです。
```shell-session
npx env-cmd -f .env node api-client.js
```
利便性から、この方法は多くの開発現場で採用されているのではないでしょうか。

しかしこの方法には弱点があります。
それは`.env`ファイルは**絶対にコミットしてはいけない**ことです。

開発者は常に気をつけているはずですが、typoで`.gitignore`されてなくてコミットしてしまったなどミスが起こる可能性を常にはらんでいます。

またそもそも、こうしたトークンが**各開発者PCで平文で管理されている**事自体、流出のリスクを上げています。

最近は[GitHub Copilot](https://github.com/features/copilot)を導入している現場も増えていると思いますが、平文で管理していると、Copilotが秘密情報を含んだ情報を外部に送信しないような追加の設定も必要になります。
設定する前に`.env`に秘密情報を記載してしまったら、コミットしなくても外部に送信はされてしまいます。

# 1Password CLI でトークンを安全に取得する
本題です。[1Password CLI](https://developer.1password.com/docs/cli)を使って、1Passwordの保管庫に保存されたトークンを実行時に環境変数にセットすることができます。

https://developer.1password.com/docs/cli/secrets-environment-variables

ベースは `env-cmd` と同じですが、`.env`ファイルに記載する内容と実行時のコマンドが変わります。

## まずは結論
```config:.env
# 1Passwordに保存したアイテムの秘密参照を記載する
GITHUB_ACCESS_TOKEN="op://Personal/GitHub Access Token/credential"
```
実行コマンド
```sh
op run --env-file=".env" -- node api-client.js
```
以下が `env-cmd` との差分です。
* `.env`にトークンをベタ書き
→**1Passwordに保存したアイテムの秘密参照**
* `npx env-cmd -f .env`
→ `op run --env-file=".env" --`

以下で方法を詳しく説明します。

## トークンを1Passwordアイテムとして保存する
「新規アイテム」から「API認証情報」を選択、「認証情報」の欄にトークンを入れて保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408546/8c936448-8564-7849-97cf-30b126affab1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408546/1aec5d79-d7c9-8755-ebfd-c45dbb1363d6.png)

## アイテムの秘密参照をコピーする
先程作成したアイテムを開き、右の三角記号から「秘密参照をコピーする」を選択して秘密参照をコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408546/57fb5ad3-6750-158b-d634-7cb99a4830a1.png)

## `.env` ファイルに秘密参照を貼り付ける
コピーした秘密参照を `.env` ファイルに貼り付けます。
```config:.env
# 1Passwordに保存したアイテムの秘密参照を記載する
GITHUB_ACCESS_TOKEN="op://Personal/GitHub Access Token/credential"
```

## 実行する
```sh
op run --env-file=".env" -- node api-client.js
```

# 1Password CLIを使うと何が嬉しいか
`env-cmd` と比較して 1Password CLI が嬉しい点は以下です。

* `env-cmd`と遜色ない利便性を保てる
* トークンを**セキュア**に管理できる
* 各開発者の実行前の`.env`の**セットアップが不要**になる

それぞれ詳しく見ていきます。

## `env-cmd`と遜色ない**利便性**を保てる
比較のため2つの方法を並べると、記載内容やコマンドが異なるだけで、実行時の利便性はほとんど変わらないことがわかります。
```config:.env
# 1Password
GITHUB_ACCESS_TOKEN="op://Personal/GitHub Access Token/credential"
# env-cmd
GITHUB_ACCESS_TOKEN=github_pat_XXXXXXXXXXXXXXXXXXXXXXXXXX
```
実行コマンド
```sh
# 1Password
op run --env-file=".env" -- node api-client.js
# env-cmd
npx env-cmd -f .env node api-client.js
```

## トークンをセキュアに管理できる
`.env`にトークンを直接記載する方法だと以下のリスクがあります。
* 設定ミスや作業ミスなどで誤って`.env`をコミットしてプッシュしてしまう
* 設定ミスにより GitHub Copilot などが `.env` の中身を外部に送信してしまう
* 各開発者がそれぞれトークンを管理することになるため、開発者が多くなるほど流出リスクが増大する
* トークンが1つしかない場合の共有方法がグレー

4つ目について、使用したいトークンが一つしか用意されず、各開発者でトークンを共有して使用する必要がある場合、そのトークンはどのように共有するのでしょうか。
おそらくチームメンバーしか閲覧することのできないページなどに記載しておくか、チャットアプリのDMでやりとりするなどの方法が取られると思います。
しかしそれはセキュリティ的な観点からすれば**かなりグレー**な方法だと思います。

**1Password CLIを使うことで上記のリスクを回避**できます。
* `.env`に記載するものは参照パスであり、**コミットして問題ない**
* トークンは1Passwordの保管庫で管理されているので安全
* 共有するトークンは共有保管庫に保存すれば開発者間で利用可能

## 各開発者の実行前の`.env`のセットアップが不要になる
`env-cmd`の場合、`.env`はコミットされていないため、中身は各開発者がそれぞれ作成する必要があります。

しかし1Password CLIの場合は、`.env`に記載するものは参照パスであり、**コミットして問題ありません。**
つまり、アイテムが共有保管庫に保存されている場合、秘密参照が記載された`.env`をコミットしておけば、クローンしてきたら認証情報のセットアップすることなく、**即座に実行可能**です。

# まとめ
本記事では [1Password CLI](https://developer.1password.com/docs/cli) を利用することで、扱いに気を使う秘密情報を**セキュアに**そして**便利に**扱う方法を紹介しました。

開発中の秘密情報の扱いは開発者が常に神経を使う部分です。
1Password CLIを利用すれば秘密情報を格段に安全に扱えるだけでなく、開発者体験も向上して**開発スピードが上がる**ことも期待できます。