Implement GitOps workflows with ArgoCD and Flux for automated, declarative Kubernetes...
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 applyorterraform 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
- Your agent or skill calls
verify.shwith the user's ID and their code - The skill validates the code (TOTP or YubiKey)
- If valid, verification state is recorded with a timestamp
- Other scripts can check
check-status.shto see if verification is still fresh - Verification expires after a configured interval (default: 24 hours)
Installation
Via ClawHub (recommended)
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
- Go to https://upgrade.yubico.com/getapikey/
- Enter your email address
- Touch your YubiKey to generate an OTP in the form field
- 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:
- Install YubiKey Manager from https://www.yubico.com/support/download/yubikey-manager/
- Open it, go to Applications → OTP → Configure Slot 1
- Select Yubico OTP and check "Upload to Yubico"
- This re-registers your key with Yubico's servers
- 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
- Store secrets securely: Use 1Password/Bitwarden references, not plaintext in config
- Short expiry: Keep
intervalHoursreasonable (8-24 hours) - Audit logs: Skills should log verification events
- Scope carefully: Only require OTP for truly sensitive actions
- 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
- RFC 6238 - TOTP specification
- Yubico OTP - YubiKey OTP documentation
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.