ryancnelson

otp-challenger

0
0
# Install this skill:
npx skills add ryancnelson/otp-challenger

Or install specific skill: npx add-skill https://github.com/ryancnelson/otp-challenger

# Description

Enable agents and skills to challenge users for fresh two-factor authentication proof (TOTP or YubiKey) before executing sensitive actions. Use this for identity verification in approval workflows - deploy commands, financial operations, data access, admin operations, and change control.

# SKILL.md


name: otp-challenger
version: 1.0.3
description: Enable agents and skills to challenge users for fresh two-factor authentication proof (TOTP or YubiKey) before executing sensitive actions. Use this for identity verification in approval workflows - deploy commands, financial operations, data access, admin operations, and change control.
metadata: {"openclaw": {"emoji": "🔐", "homepage": "https://github.com/ryancnelson/otp-challenger", "requires": {"bins": ["jq", "python3"], "anyBins": ["oathtool", "node"]}, "install": [{"id": "jq", "kind": "brew", "formula": "jq", "bins": ["jq"], "label": "Install jq via Homebrew", "os": ["darwin", "linux"]}, {"id": "python3", "kind": "brew", "formula": "python3", "bins": ["python3"], "label": "Install Python 3 via Homebrew", "os": ["darwin", "linux"]}, {"id": "oathtool", "kind": "brew", "formula": "oath-toolkit", "bins": ["oathtool"], "label": "Install OATH Toolkit via Homebrew", "os": ["darwin", "linux"]}]}}


OTP Identity Challenge Skill

Challenge users for fresh two-factor authentication before sensitive actions.

When to Use

Require OTP verification before:
- Deploy commands (kubectl apply, terraform apply)
- Financial operations (transfers, payment approvals)
- Data access (PII exports, customer data)
- Admin operations (user modifications, permission changes)

Scripts

verify.sh

Verify a user's OTP code and record verification state.

./verify.sh <user_id> <code>

Parameters:
- user_id - Identifier for the user (e.g., email, username)
- code - Either 6-digit TOTP or 44-character YubiKey OTP

Exit codes:
- 0 - Verification successful
- 1 - Invalid code or rate limited
- 2 - Configuration error (missing secret, invalid format)

Output on success:

✅ OTP verified for <user_id> (valid for 24 hours)
✅ YubiKey verified for <user_id> (valid for 24 hours)

Output on failure:

❌ Invalid OTP code
❌ Too many attempts. Try again in X minutes.
❌ Invalid code format. Expected 6-digit TOTP or 44-character YubiKey OTP.

check-status.sh

Check if a user's verification is still valid.

./check-status.sh <user_id>

Exit codes:
- 0 - User has valid (non-expired) verification
- 1 - User not verified or verification expired

Output:

✅ Valid for 23 more hours
⚠️ Expired 2 hours ago
❌ Never verified

generate-secret.sh

Generate a new TOTP secret with QR code.

./generate-secret.sh <account_name>

Usage Pattern

#!/bin/bash
source ../otp/verify.sh

if ! verify_otp "$USER_ID" "$OTP_CODE"; then
  echo "🔒 This action requires OTP verification"
  exit 1
fi

# Proceed with sensitive action

Configuration

Required for TOTP:
- OTP_SECRET - Base32 TOTP secret

Required for YubiKey:
- YUBIKEY_CLIENT_ID - Yubico API client ID
- YUBIKEY_SECRET_KEY - Yubico API secret key (base64)

Optional:
- OTP_INTERVAL_HOURS - Verification expiry (default: 24)
- OTP_MAX_FAILURES - Failed attempts before rate limiting (default: 3)
- OTP_STATE_FILE - State file path (default: memory/otp-state.json)

Configuration can be set via environment variables or in ~/.openclaw/config.yaml:

security:
  otp:
    secret: "BASE32_SECRET"
  yubikey:
    clientId: "12345"
    secretKey: "base64secret"

Code Format Detection

The script auto-detects code type:
- 6 digits (123456) → TOTP validation
- 44 ModHex characters (cccccc...) → YubiKey validation

ModHex alphabet: cbdefghijklnrtuv

State File

Verification state stored in memory/otp-state.json. Contains only timestamps, no secrets.

Human Documentation

See README.md for:
- Installation instructions
- Setup guides (TOTP and YubiKey)
- Security considerations
- Troubleshooting
- Examples

# README.md

OTP Identity Challenge

Identity verification for approval workflows in OpenClaw.

This skill enables agents to challenge users for fresh two-factor authentication proof before executing sensitive actions—deployments, financial operations, data access, or any command with change-control concerns.

Purpose

This is not chat channel security—it's identity verification for specific actions.

When your agent needs to execute something sensitive, it challenges the user to prove their identity with a time-based one-time password (TOTP) or YubiKey, providing cryptographic proof they authorized the action.

Quick Start

# Install via ClawHub
clawhub install otp

# Or clone manually
cd ~/.openclaw/skills
git clone https://github.com/ryancnelson/otp-skill.git otp

Use Cases

  • Deploy commands: Require fresh 2FA before kubectl apply or terraform apply
  • Financial operations: Verify identity before wire transfers or payment approvals
  • Data access: Challenge before exporting customer data or PII
  • Admin operations: Verify before user account modifications or permission changes
  • Change control: Enforce approval workflows with cryptographic proof of identity

How It Works

  1. Your agent or skill calls verify.sh with the user's ID and their code
  2. The skill validates the code (TOTP or YubiKey)
  3. If valid, verification state is recorded with a timestamp
  4. Other scripts can check check-status.sh to see if verification is still fresh
  5. Verification expires after a configured interval (default: 24 hours)

Installation

clawhub install otp

Manual

cd ~/.openclaw/skills
git clone https://github.com/ryancnelson/otp-skill.git otp

Check Dependencies

After installation, verify required dependencies:

# Check what's available
which jq && echo "✅ jq available" || echo "❌ Install: brew install jq"
which python3 && echo "✅ python3 available" || echo "❌ Install: brew install python3"
which oathtool && echo "✅ oathtool available" || echo "❌ Install: brew install oath-toolkit"

Note: oathtool is optional - the skill includes a built-in TOTP generator, but oathtool provides additional validation.


TOTP Setup

1. Generate a TOTP Secret

Use the included secret generator:

cd ~/.openclaw/skills/otp
./generate-secret.sh "[email protected]"

This will display:
- A QR code to scan with your authenticator app
- The base32 secret for manual entry
- Configuration instructions

Alternative: Use any other TOTP secret generator. You need a base32-encoded secret.

2. Scan QR Code

Add the secret to your authenticator app:
- Google Authenticator
- Authy
- 1Password
- Bitwarden
- Any RFC 6238 compatible app

3. Store the Secret

Option A: In your OpenClaw config

# ~/.openclaw/config.yaml
security:
  otp:
    secret: "YOUR_BASE32_SECRET_HERE"
    accountName: "[email protected]"
    issuer: "OpenClaw"
    intervalHours: 24  # Re-verify every 24 hours

Option B: In environment variable

export OTP_SECRET="YOUR_BASE32_SECRET_HERE"

Option C: In 1Password (recommended for security)

security:
  otp:
    secret: "op://vault/OpenClaw OTP/totp"

4. Test Your Setup

# Get current code from your authenticator app (6 digits)
./verify.sh "testuser" "123456"  # Replace with actual code
# Should show: ✅ OTP verified for testuser (valid for 24 hours)

# Check verification status
./check-status.sh "testuser"
# Should show: ✅ Valid for 23 more hours

YubiKey Setup (Alternative to TOTP)

If you have a YubiKey, you can use touch-to-verify instead of typing 6-digit codes.

1. Get Yubico API Credentials

  1. Go to https://upgrade.yubico.com/getapikey/
  2. Enter your email address
  3. Touch your YubiKey to generate an OTP in the form field
  4. Submit — you'll receive a Client ID and Secret Key

Troubleshooting "Invalid OTP" during registration:

If Yubico's site rejects your OTP, your key may not be registered with Yubico's cloud:

  1. Install YubiKey Manager from https://www.yubico.com/support/download/yubikey-manager/
  2. Open it, go to Applications → OTP → Configure Slot 1
  3. Select Yubico OTP and check "Upload to Yubico"
  4. This re-registers your key with Yubico's servers
  5. Try getting API credentials again

2. Configure Credentials

Option A: In your OpenClaw config

# ~/.openclaw/config.yaml
security:
  yubikey:
    clientId: "12345"
    secretKey: "your-base64-secret-key"

Option B: In environment variables

export YUBIKEY_CLIENT_ID="12345"
export YUBIKEY_SECRET_KEY="your-base64-secret-key"

3. Test YubiKey Verification

# Touch your YubiKey when prompted
./verify.sh "testuser" "cccccccccccc..."  # paste YubiKey output
# Should show: ✅ YubiKey verified for testuser (valid for 24 hours)

Using Both TOTP and YubiKey

You can configure both methods. The script auto-detects which to use based on the code format:
- 6 digits → TOTP validation
- 44 characters → YubiKey validation

This lets you use TOTP on your phone and YubiKey at your desk.


Configuration Reference

Set these in your OpenClaw config or environment:

Variable Description Default
OTP_SECRET Base32 TOTP secret (required for TOTP)
YUBIKEY_CLIENT_ID Yubico API client ID (required for YubiKey)
YUBIKEY_SECRET_KEY Yubico API secret key (base64) (required for YubiKey)
OTP_INTERVAL_HOURS Verification expiry 24
OTP_GRACE_PERIOD_MINUTES Grace period after expiry 15
OTP_STATE_FILE State file path memory/otp-state.json
OTP_MAX_FAILURES Failed attempts before rate limiting 3
OTP_FAILURE_HOOK Script to run on failures (none)
OTP_AUDIT_LOG Audit log file path (none)

Usage Examples

For Skill Authors

When your skill needs to verify user identity:

#!/bin/bash
# In your sensitive-action.sh

# Source the OTP skill
source ../otp/verify.sh

USER_ID="$1"
OTP_CODE="$2"

# Challenge the user
if ! verify_otp "$USER_ID" "$OTP_CODE"; then
  echo "❌ OTP verification failed. Run: /otp <code>"
  exit 1
fi

# Proceed with sensitive action
echo "✅ Identity verified. Proceeding with deployment..."
kubectl apply -f production.yaml

Checking Verification Status

#!/bin/bash
source ../otp/check-status.sh

if check_otp_status "$USER_ID"; then
  echo "✅ User verified within last 24 hours"
else
  echo "⚠️ Verification expired. User must verify again."
fi

For End Users

When prompted by a skill:

User: deploy to production
Agent: 🔒 This action requires identity verification. Please provide your OTP code.

User: /otp 123456
Agent: ✅ Identity verified. Deploying to production...

Deploy Command with OTP

#!/bin/bash
# skills/deploy/production.sh

source ../otp/verify.sh

USER="$1"
CODE="$2"
SERVICE="$3"

# Require OTP for production deploys
if ! verify_otp "$USER" "$CODE"; then
  echo "🔒 Production deployment requires OTP verification"
  echo "Usage: deploy production <service> --otp <code>"
  exit 1
fi

echo "✅ Identity verified. Deploying $SERVICE to production..."
# ... deployment logic ...

Payment Authorization

#!/bin/bash
# skills/finance/transfer.sh

source ../otp/check-status.sh

USER="$1"
AMOUNT="$2"
RECIPIENT="$3"

# Check if user verified recently
if ! check_otp_status "$USER"; then
  echo "💳 Large transfer requires fresh identity verification"
  echo "Please verify with: /otp <code>"
  exit 1
fi

echo "✅ Processing transfer of \$$AMOUNT to $RECIPIENT"
# ... transfer logic ...

Failure Hook - Alert on Failed Attempts

Configure OTP_FAILURE_HOOK to run a script when verification fails:

# Example: Shut down OpenClaw after 3 failures (impersonation defense)
export OTP_FAILURE_HOOK="/path/to/shutdown-if-impersonator.sh"

The hook receives these environment variables:
- OTP_HOOK_EVENT - VERIFY_FAIL (bad code) or RATE_LIMIT_HIT (too many failures)
- OTP_HOOK_USER - The user ID that failed
- OTP_HOOK_FAILURE_COUNT - Number of consecutive failures
- OTP_HOOK_TIMESTAMP - ISO 8601 timestamp

#!/bin/bash
# shutdown-if-impersonator.sh
if [ "$OTP_HOOK_EVENT" = "RATE_LIMIT_HIT" ]; then
  echo "🚨 OTP rate limit hit for $OTP_HOOK_USER at $OTP_HOOK_TIMESTAMP" | \
    slack-notify "#alerts"
  pkill -f openclaw
fi

Security Considerations

What This Protects Against

  • Session hijacking: Even if someone steals your chat session, they can't execute protected actions without your physical device
  • Replay attacks: Codes are time-based and expire quickly
  • Unauthorized actions: Cryptographic proof that you authorized the specific action

What This Doesn't Protect Against

  • Compromised agent: If someone has shell access to your OpenClaw instance, they can read the secret
  • Phishing: Users can still be tricked into providing codes to malicious actors
  • Device theft: If someone has your authenticator device, they can generate codes

Best Practices

  1. Store secrets securely: Use 1Password/Bitwarden references, not plaintext in config
  2. Short expiry: Keep intervalHours reasonable (8-24 hours)
  3. Audit logs: Skills should log verification events
  4. Scope carefully: Only require OTP for truly sensitive actions
  5. Clear prompts: Always tell users WHY they're being asked for OTP

Technical Details

TOTP Implementation

  • Standard: RFC 6238 (Time-Based One-Time Password)
  • Algorithm: HMAC-SHA1 (standard TOTP)
  • Time window: 30 seconds (configurable)
  • Code length: 6 digits
  • Clock skew: ±1 window tolerance (90 seconds total)

YubiKey OTP Implementation

  • API: Yubico Cloud (api.yubico.com)
  • Protocol: HMAC-SHA1 signed requests
  • OTP Format: 44-character ModHex (alphabet: cbdefghijklnrtuv)
  • Public ID: First 12 characters identify the physical key
  • Replay Protection: Handled by Yubico servers
  • Network: Requires HTTPS to api.yubico.com

State Management

Verification state is stored in memory/otp-state.json:

{
  "verifications": {
    "[email protected]": {
      "verifiedAt": 1706745600000,
      "expiresAt": 1706832000000
    }
  }
}

No secrets are stored in state—only timestamps.

Dependencies

Required:
- jq - for JSON state file manipulation
- python3 - for secure YAML config parsing

Optional:
- oathtool - provides additional TOTP validation (skill has built-in generator)
- Node.js - only needed for totp.mjs standalone CLI
- bats - for running test suite


Troubleshooting

TOTP Issues

"OTP verification failed"
- Check your authenticator app has the correct secret
- Verify system time is synchronized (NTP)
- Try the code from the previous/next 30-second window

"Secret not configured"
- Set OTP_SECRET environment variable
- Or configure security.otp.secret in OpenClaw config

"State file not found"
- First verification creates the file
- Check memory/ directory permissions

YubiKey Issues

"YUBIKEY_CLIENT_ID not set"
- Get API credentials from https://upgrade.yubico.com/getapikey/
- Set YUBIKEY_CLIENT_ID and YUBIKEY_SECRET_KEY in environment or config

"Invalid OTP" when getting API key
- Your YubiKey may not be registered with Yubico's cloud
- Use YubiKey Manager to reconfigure Slot 1 with "Upload to Yubico" checked

"YubiKey API signature mismatch"
- Check that YUBIKEY_SECRET_KEY is correct (should be base64)
- Try regenerating API credentials from Yubico

"Failed to contact Yubico API"
- Check internet connectivity
- Yubico API requires HTTPS (port 443)
- Try: curl -I https://api.yubico.com/wsapi/2.0/verify

"YubiKey OTP already used"
- Each YubiKey press generates a unique code
- Touch your YubiKey again to generate a fresh code
- Don't copy-paste old codes


Philosophy

OTP should be invisible when not needed, obvious when required.

Don't force users to verify for every action—that trains them to treat it as a meaningless ritual. Only challenge when:
1. The action has real-world consequences
2. The risk justifies the friction
3. You need cryptographic proof of intent

Think of OTP like sudo—you use it before commands that matter, not every command.


Agent Documentation

For agent/skill integration details, see SKILL.md.

Full Source & Tests

Complete repository with test suite: https://github.com/ryancnelson/otp-challenger

See Also

License

MIT

Author

Ryan Nelson

Contributing

Issues and PRs welcome at: https://github.com/ryancnelson/otp-challenger

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.