#!/bin/bash # Enroll TOTP for a Cloudron identity (Cloudron 10 API: profile-scoped routes). # Prereq: cloudron-oidc-login.sh has run (bearer at /tmp/.oidc-bearer-). # usage: cloudron-totp-enroll.sh [vault-item] # Flow: POST /profile/totp_secret -> seed -> TOTP code -> POST /profile/totp_enable # -> seed into vault -> end-to-end verify via fresh TOTP login. set -euo pipefail U="${1:?usage: cloudron-totp-enroll.sh [vault-item]}" ITEM="${2:-$U Cloudron}" BASE="https://my.knownelement.com" BEARER_FILE="/tmp/.oidc-bearer-$U" [ -f "$BEARER_FILE" ] || { echo "FAIL: run cloudron-oidc-login.sh $U first" >&2; exit 1; } ACCESS=$(cat "$BEARER_FILE") SECRET=$(curl -sk -X POST "$BASE/api/v1/profile/totp_secret" \ -H "Authorization: Bearer $ACCESS" -H 'Content-Type: application/json' -d '{}' \ | sed -n 's/.*"secret":[[:space:]]*"\([^"]*\)".*/\1/p') [ -n "$SECRET" ] || { echo "FAIL: no totp secret returned (bearer stale? re-login)" >&2; exit 3; } CODE=$(bash "$(dirname "$0")/totp.sh" "$SECRET") ENABLED=$(curl -sk -X POST "$BASE/api/v1/profile/totp_enable" \ -H "Authorization: Bearer $ACCESS" -H 'Content-Type: application/json' \ -d "{\"totpToken\":\"$CODE\"}" -w '|%{http_code}') printf '%s' "$ENABLED" | grep -q '|200$' || { echo "FAIL: totp_enable said: $(printf '%s' "$ENABLED" | head -c 150)" >&2; exit 4; } SM() { if [ "$(id -un)" = "TSGCOO" ]; then /data2/TSGCOO/.local/bin/sm "$@" /dev/null 2>&1; then STORED=1 else VU=$(SM get "$ITEM" --field username); VP=$(SM get "$ITEM" --field password); VR=$(SM get "$ITEM" --field uri 2>/dev/null || true) [ -n "$VR" ] || VR="$BASE" SM rm "$ITEM" >/dev/null 2>&1 SM set "$ITEM" "username=$VU" "password=$VP" "uri=$VR" "totp_seed=$SECRET" >/dev/null 2>&1 && STORED=1 fi [ "$STORED" = 1 ] || { echo "FAIL: could not store seed in vault ($ITEM)" >&2; exit 5; } [ "$(SM get "$ITEM" --field totp_seed)" = "$SECRET" ] || { echo "FAIL: seed readback mismatch ($ITEM)" >&2; exit 5; } # end-to-end verify: fresh login WITH the stored seed lands a working bearer bash "$(dirname "$0")/cloudron-oidc-login.sh" "$U" "$ITEM" >/dev/null 2>&1 if [ -s "$BEARER_FILE" ]; then PROF=$(curl -sk "$BASE/api/v1/profile" -H "Authorization: Bearer $(cat "$BEARER_FILE")") WHO=$(printf '%s' "$PROF" | sed -n 's/.*"username":[[:space:]]*"\([^"]*\)".*/\1/p') TOTP_ON=$(printf '%s' "$PROF" | sed -n 's/.*"totpEnabled":[[:space:]]*\(true\|false\).*/\1/p') if [ "$WHO" = "$U" ] && [ "$TOTP_ON" = "true" ]; then echo "OK: $U 2FA enrolled (totpEnabled=true), seed in vault ($ITEM), TOTP login verified" exit 0 fi fi echo "PARTIAL: verify failed — check state for $U"