gopass OTP

Time-based one-time password management.

OTP / TOTP

Identify Your Secret Format

Format Looks Like Action

Base32 (common)

QTZV C4EV SRDD FTN7 (A-Z, 2-7)

Just remove spaces

Hex

DE AD BE EF 12 34 (0-9, A-F only)

Convert to Base32

Template: Base32 Key (Most Sites)

Sites like GitLab, GitHub, AWS give Base32 directly. Paste spaced key into KEY=:

# Fill these in - paste spaced key directly
SITE="GitLab"
USER="user@example.com"
GOPASS_PATH="v3/personal/social/gitlab/username-otp"
KEY="ABCD EFGH IJKL MNOP QRST UVWX YZ23 4567"

# Removes spaces automatically
SECRET="${KEY// /}"

gopass insert -m "$GOPASS_PATH" << EOF
otpauth://totp/${SITE}:${USER}?secret=${SECRET}&issuer=${SITE}
EOF

Template: Hex Key (Rare)

Only use this if the site gives you hex (0-9, A-F characters only):

# Fill these in
HEX="DE AD BE EF 12 34 56 78"
SITE="SomeService"
USER="username"
GOPASS_PATH="v3/personal/service/otp"

# Convert hex to base32
SECRET=$(echo "$HEX" | tr -d ' ' | xxd -r -p | base32 | tr -d '=')
gopass insert -m "$GOPASS_PATH" << EOF
otpauth://totp/${SITE}:${USER}?secret=${SECRET}&issuer=${SITE}
EOF

Generate OTP Code

gopass otp v3/personal/social/gitlab/username-otp

Troubleshooting: Invalid OTP Code

TOTP requires accurate system time. If codes are rejected, check NTP sync:

timedatectl status

If System clock synchronized: no or NTP service: inactive:

sudo timedatectl set-ntp true

Verify sync is active:

timedatectl status
# Should show: System clock synchronized: yes
# Should show: NTP service: active

Examples

GitLab (Base32 - most common)
SITE="GitLab"
USER="user@example.com"
GOPASS_PATH="v3/personal/social/gitlab/username-otp"
KEY="ABCD EFGH IJKL MNOP QRST UVWX YZ23 4567"

SECRET="${KEY// /}"
gopass insert -m "$GOPASS_PATH" << EOF
otpauth://totp/${SITE}:${USER}?secret=${SECRET}&issuer=${SITE}
EOF
GitHub (Base32)
SITE="GitHub"
USER="username"
GOPASS_PATH="v3/personal/social/github/username-otp"
KEY="WXYZ 2345 ABCD EFGH IJKL MNOP QRST UVWX"

SECRET="${KEY// /}"
gopass insert -m "$GOPASS_PATH" << EOF
otpauth://totp/${SITE}:${USER}?secret=${SECRET}&issuer=${SITE}
EOF
AWS (Base32)
SITE="AWS"
USER="user@example.com"
GOPASS_PATH="v3/personal/cloud/aws/username-otp"
KEY="MNOP QRST UVWX YZ23 4567 ABCD EFGH IJKL"

SECRET="${KEY// /}"
gopass insert -m "$GOPASS_PATH" << EOF
otpauth://totp/${SITE}:${USER}?secret=${SECRET}&issuer=${SITE}
EOF
Hex Key Example (rare)
HEX="DE AD BE EF CA FE BA BE"
SITE="LegacyService"
USER="admin"
GOPASS_PATH="v3/domains/d000/servers/legacy-otp"

SECRET=$(echo "$HEX" | tr -d ' ' | xxd -r -p | base32 | tr -d '=')
gopass insert -m "$GOPASS_PATH" << EOF
otpauth://totp/${SITE}:${USER}?secret=${SECRET}&issuer=${SITE}
EOF

Shell Helper Functions

Add to .bashrc or .zshrc later for convenience:

gopass-otp: Add OTP with one command
gopass-otp() {
    local SITE="$1"
    local USER="$2"
    local GOPASS_PATH="$3"
    local KEY="$4"

    # Remove spaces from key
    local SECRET="${KEY// /}"

    gopass insert -m "$GOPASS_PATH" << EOF
otpauth://totp/${SITE}:${USER}?secret=${SECRET}&issuer=${SITE}
EOF
    echo "Added OTP for $SITE"
}

# Usage:
gopass-otp "GitLab" "user@example.com" "v3/personal/social/gitlab/otp" "ABCD EFGH IJKL MNOP QRST UVWX YZ23 4567"
otp-clean: Just remove spaces from key
otp-clean() {
    echo "${1// /}"
}

# Usage:
SECRET=$(otp-clean "ABCD EFGH IJKL MNOP QRST UVWX YZ23 4567")
echo $SECRET
# Output: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567

OTP URI Format

otpauth://totp/ISSUER:USERNAME?secret=BASE32SECRET&issuer=ISSUER&digits=6&period=30
Parameter Description

secret

Base32-encoded secret (required)

issuer

Service name (recommended)

digits

Code length (default: 6)

period

Rotation seconds (default: 30)