5.7kโ
by ryancnelson
otp-challenger โ OpenClaw Skill
otp-challenger is an OpenClaw Skills integration for coding workflows. 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 Snapshot
| name | 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. OpenClaw Skills integration. |
| owner | ryancnelson |
| repository | ryancnelson/otp-challenger |
| language | Markdown |
| license | MIT |
| topics | |
| security | L1 |
| install | openclaw add @ryancnelson/otp-challenger |
| last updated | Feb 7, 2026 |
Maintainer

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 successful1- Invalid code or rate limited2- 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) verification1- 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 (requires qrencode to be installed).
./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 IDYUBIKEY_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
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"
which qrencode && echo "โ
qrencode available" || echo "โ ๏ธ Optional: brew install qrencode (for QR codes)"
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 "your-email@example.com"
This will display:
- A QR code to scan with your authenticator app (if
qrencodeis installed) - The base32 secret for manual entry
- Configuration instructions
Note: QR code display requires the qrencode utility. Install with brew install qrencode (macOS) or apt install qrencode (Ubuntu). Without it, you'll only see the base32 secret for manual entry.
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: "user@example.com"
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) orRATE_LIMIT_HIT(too many failures)OTP_HOOK_USER- The user ID that failedOTP_HOOK_FAILURE_COUNT- Number of consecutive failuresOTP_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": {
"user@example.com": {
"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:
- qrencode - displays QR code during secret generation (without it, you'll only get the base32 secret)
- oathtool - provides additional TOTP validation (skill has built-in generator)
- Node.js - only needed for
totp.mjsstandalone 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_SECRETenvironment variable - Or configure
security.otp.secretin 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_IDandYUBIKEY_SECRET_KEYin 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_KEYis 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:
- The action has real-world consequences
- The risk justifies the friction
- 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
Permissions & Security
Security level L1: Low-risk skills with minimal permissions. Review inputs and outputs before running in production.
Requirements
- OpenClaw CLI installed and configured.
- Language: Markdown
- License: MIT
- Topics:
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`: ```yaml security: otp: secret: "BASE32_SECRET" yubikey: clientId: "12345" secretKey: "base64secret" ```
FAQ
How do I install otp-challenger?
Run openclaw add @ryancnelson/otp-challenger in your terminal. This installs otp-challenger into your OpenClaw Skills catalog.
Does this skill run locally or in the cloud?
OpenClaw Skills execute locally by default. Review the SKILL.md and permissions before running any skill.
Where can I verify the source code?
The source repository is available at https://github.com/openclaw/skills/tree/main/skills/ryancnelson/otp-challenger. Review commits and README documentation before installing.
