oath-toolkit で CLI から OTP を取得する - 自動化時代の2FA対応

oath-toolkit で CLI から OTP を取得する - 自動化時代の2FA対応

作成日:
更新日:

なぜ CLI で OTP が必要なのか

最近、AI エージェントでのブラウザ操作Playwright でのテスト自動化など、プログラムからブラウザを操作する機会が増えてきました。

これらの自動化で厄介なのが2要素認証(2FA)のログイン処理です。Google Authenticator などのスマホアプリを毎回手動で確認するのは、せっかくの自動化が台無しです。

oath-toolkit を使えば、ターミナルから OTP を取得できるため:

  • Playwright / Puppeteer のE2Eテストで2FAログインを自動化
  • Cursor / Claude などのAIエージェントがブラウザ操作する際の認証
  • CI/CD パイプラインでのセキュアな自動デプロイ
  • 単純にターミナルから離れずにOTPを取得したい

といったユースケースに対応できます。

oath-toolkit とは

oath-toolkit は、OATH(Initiative for Open Authentication)標準に準拠した OTP 生成ツールです。TOTP(時間ベース)と HOTP(カウンタベース)の両方に対応しています。

インストール

macOS(Homebrew)

brew install oath-toolkit

Ubuntu / Debian

sudo apt update
sudo apt install oathtool

RHEL / CentOS / Fedora

# RHEL/CentOS 8以降、Fedora
sudo dnf install oathtool

# CentOS 7以前
sudo yum install oathtool

インストール確認

oathtool --version

シークレットキーの取得

Google Authenticator に登録する際に表示されるシークレットキー(Base32形式の文字列)が必要です。

状況 対応方法
新規登録時 QRコードの下に表示される「セットアップキー」や「シークレットキー」をメモしておく
既存のアカウント サービス側で2FAを一度解除し、再設定する際にシークレットキーを控える

⚠️ 注意: Google Authenticator アプリからシークレットキーを直接取り出すことはできません。新規登録時に必ずメモしておきましょう。

基本的な使い方

OTP を生成

# TOTP(時間ベースのOTP)を生成
oathtool --totp --base32 "YOUR_SECRET_KEY"

実行例

# 例: シークレットキーが "JBSWY3DPEHPK3PXP" の場合
$ oathtool --totp --base32 "JBSWY3DPEHPK3PXP"
123456

クリップボードにコピー

# macOS
oathtool --totp --base32 "YOUR_SECRET_KEY" | pbcopy

# Linux(xclipを使用)
oathtool --totp --base32 "YOUR_SECRET_KEY" | xclip -selection clipboard

便利なシェル設定

エイリアスを設定

~/.zshrc に追加:

# 特定サービス用のOTP取得エイリアス
alias otp-github='oathtool --totp --base32 "YOUR_GITHUB_SECRET"'
alias otp-aws='oathtool --totp --base32 "YOUR_AWS_SECRET"'

# クリップボードにコピーするバージョン(macOS)
alias otpc-github='oathtool --totp --base32 "YOUR_GITHUB_SECRET" | pbcopy && echo "Copied!"'

シークレットファイルで複数サービスを管理

より実用的な方法として、シークレットファイルとシェル関数を組み合わせます。

1. シークレットファイルを作成

# ~/.otp_secrets ファイルを作成(パーミッションを制限)
touch ~/.otp_secrets
chmod 600 ~/.otp_secrets

2. サービスを登録

github:JBSWY3DPEHPK3PXP
aws:ANOTHERSECRETKEY
sakura-vps:SECRETKEYHERE

3. シェル関数を定義

~/.zshrc に追加:

# OTP取得関数
otp() {
  local secrets_file="$HOME/.otp_secrets"
  
  if [[ -z "$1" ]]; then
    echo "Usage: otp <service_name>"
    echo "Available services:"
    cut -d: -f1 "$secrets_file" | sed 's/^/  - /'
    return 1
  fi
  
  local secret=$(grep "^$1:" "$secrets_file" | cut -d: -f2)
  
  if [[ -z "$secret" ]]; then
    echo "Service '$1' not found"
    return 1
  fi
  
  local code=$(oathtool --totp --base32 "$secret")
  
  # クリップボードにコピー(OS判定)
  if [[ "$OSTYPE" == "darwin"* ]]; then
    echo "$code" | pbcopy
  elif command -v xclip &> /dev/null; then
    echo "$code" | xclip -selection clipboard
  fi
  
  echo "OTP: $code (copied to clipboard)"
}

# 補完設定
_otp_completion() {
  local secrets_file="$HOME/.otp_secrets"
  if [[ -f "$secrets_file" ]]; then
    compadd $(cut -d: -f1 "$secrets_file")
  fi
}
compdef _otp_completion otp

使用例

# 利用可能なサービス一覧を表示
$ otp
Usage: otp <service_name>
Available services:
  - github
  - aws
  - sakura-vps

# GitHubのOTPを取得(自動でクリップボードにコピー)
$ otp github
OTP: 123456 (copied to clipboard)

Playwright での活用例

E2E テストで 2FA ログインを自動化する例:

import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';

// OTPを取得するヘルパー関数
function getOTP(service: string): string {
  const secretsFile = `${process.env.HOME}/.otp_secrets`;
  const secret = execSync(`grep "^${service}:" ${secretsFile} | cut -d: -f2`)
    .toString()
    .trim();
  return execSync(`oathtool --totp --base32 "${secret}"`)
    .toString()
    .trim();
}

test('2FA login', async ({ page }) => {
  await page.goto('https://example.com/login');
  
  // 通常のログイン
  await page.fill('[name="email"]', 'user@example.com');
  await page.fill('[name="password"]', 'your-password');
  await page.click('button[type="submit"]');
  
  // 2FAページでOTPを入力
  await page.waitForSelector('[name="otp"]');
  const otp = getOTP('example-service');
  await page.fill('[name="otp"]', otp);
  await page.click('button[type="submit"]');
  
  // ログイン成功を確認
  await expect(page).toHaveURL('/dashboard');
});

セキュリティ上の注意

項目 説明
シークレットキーの保護 シークレットキーがあれば誰でもOTPを生成できる。絶対に他人に見せない
ファイルパーミッション chmod 600 で自分だけ読み書き可能に設定
Git管理しない .gitignore にシークレットファイルを追加
CI/CD での扱い GitHub Secrets や環境変数として安全に管理

.gitignore への追加

# OTPシークレット
.otp_secrets

トラブルシューティング

OTP が一致しない場合

時刻がずれている可能性があります:

# システム時刻を確認
date

# NTPで同期(macOS)
sudo sntp -sS time.apple.com

# NTPで同期(Linux)
sudo timedatectl set-ntp true

シークレットキーが無効な場合

  • Base32形式であることを確認(A-Z, 2-7 の文字のみ)
  • スペースやハイフンを削除
  • 大文字に統一

コマンドリファレンス

# 基本的なTOTP生成
oathtool --totp --base32 "SECRET"

# 8桁のコード(一部サービスで使用)
oathtool --totp --base32 --digits=8 "SECRET"

# 時間ステップを変更(デフォルトは30秒)
oathtool --totp --base32 --time-step-size=60 "SECRET"

# 次のOTPも表示(切り替わるタイミング用)
oathtool --totp --base32 -w 1 "SECRET"

まとめ

oath-toolkit を使えば、CLI から簡単に OTP を取得できます。

  1. インストール: brew install oath-toolkit(macOS)
  2. シークレットキー取得: 2FA設定時に必ずメモ
  3. OTP生成: oathtool --totp --base32 "SECRET"
  4. 自動化: シェル関数や Playwright と組み合わせ

AI エージェントや E2E テストでの 2FA 対応が必要な場面で、ぜひ活用してみてください。


参考