Files
xamxam/scripts/app-token.sh
T
Pontoporeia 7b6d79c133 diag: invalid_grant is SSO auth-method mismatch, not bad creds
- feat: creds-test.sh gum probe for SMTP vs PeerTube auth + PeerTubeService::probeAuth()
- feat: app-token.sh gum probe for long-lived PeerTube app token (client_credentials)
- docs: add copy-paste proof commands to demonstrate the SSO break to admins
2026-08-24 11:33:34 +02:00

154 lines
7.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# app-token.sh — can we obtain a long-lived PeerTube app token?
#
# A long-lived ("app") token comes from PeerTube's `client_credentials` OAuth2
# grant, which is only allowed for APPLICATION OAuth clients (not the built-in
# `local` client). To create one you first need an admin to run PeerTube's
# create-client script ON the instance, then paste the client_id/client_secret
# here.
#
# This script:
# 1. Probes API reachability of the instance.
# 2. Tries `client_credentials` with the given (or optional) app client.
# 3. Detects whether the instance is behind an SSO/OIDC provider.
# 4. Prints the exact admin command to create an app client, and how to use it
# here afterward.
#
# Usage:
# scripts/app-token.sh [--instance https://videos.erg.be] [--client <id> --secret <secret>]
#
# --instance PeerTube base URL (default: read from xamxam DB, else https://videos.erg.be)
# --client OAuth client_id of an application client (if you already have one)
# --secret matching client_secret
#
# Exit 0 => a token was minted (printed to stdout); 1 => could not.
# =============================================================================
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
DB_PATH="$REPO_ROOT/app/storage/xamxam.db"
INSTANCE=""
CLIENT_ID=""
CLIENT_SECRET=""
while [[ $# -gt 0 ]]; do
case "$1" in
--instance) INSTANCE="$2"; shift 2 ;;
--client) CLIENT_ID="$2"; shift 2 ;;
--secret) CLIENT_SECRET="$2"; shift 2 ;;
-h|--help)
gum style "Usage:" --bold
gum style " scripts/app-token.sh [--instance <url>] [--client <id>] [--secret <secret>]"
exit 0 ;;
*) echo "Unknown arg: $1" >&2; exit 2 ;;
esac
done
command -v gum >/dev/null || { echo "gum is required"; exit 2; }
command -v curl >/dev/null || { echo "curl is required"; exit 2; }
command -v jq >/dev/null || { echo "jq is required (brew install jq)"; exit 2; }
# Default instance from the DB if not given.
if [[ -z "$INSTANCE" ]]; then
if [[ -f "$DB_PATH" ]] && command -v sqlite3 >/dev/null; then
INSTANCE="$(sqlite3 "$DB_PATH" "SELECT instance_url FROM peertube_settings WHERE id=1;" 2>/dev/null)"
INSTANCE="${INSTANCE:-https://videos.erg.be}"
else
INSTANCE="https://videos.erg.be"
fi
fi
INSTANCE="${INSTANCE%/}"
API="$INSTANCE/api/v1"
gum style "── XAMXAM · PeerTube app-token probe ──────────────────────" \
--border double --padding "1 2" --foreground 213
gum style "Instance : $INSTANCE" --foreground 214
# ── 1. API reachability ────────────────────────────────────────────────────────
resp="$(curl -sS -m 20 -w $'\n%{http_code}' "$API/config" 2>/dev/null)"
code="${resp##*$'\n'}"
if [[ "$code" != "200" ]]; then
gum style "✗ API unreachable (HTTP $code)" --foreground 196
echo "$resp" | head -1 >&2
exit 1
fi
gum style "✓ API reachable" --foreground 42
# ── 2. Detect SSO / OIDC ───────────────────────────────────────────────────────
# Plugin listing often requires auth. Only report OIDC presence if we actually got
# a data list; otherwise state that it can't be determined anonymously.
oidc_flag="unknown"
oidc_list="$(curl -sS -m 20 "$API/plugins?pluginType=2&count=100" 2>/dev/null)"
if echo "$oidc_list" | grep -q '"total"'; then
if echo "$oidc_list" | jq -e '.data[]? | select(.name | contains("oidc"))' >/dev/null 2>&1; then
oidc_flag="enabled"; gum style "⚠ OIDC/SSO plugin detected on the instance" --foreground 220
else
oidc_flag="none"; gum style "✓ No OIDC plugin in the auth-plugin list" --foreground 42
fi
else
gum style "? OIDC/SSO presence not determinable anonymously (plugin list needs admin auth)" --foreground 240
fi
# ── 3. Try client_credentials ──────────────────────────────────────────────────
# PeerTube only allows client_credentials on APPLICATION clients. The built-in
# `local` client rejects it; if that's the only one we have, say so.
if [[ -z "$CLIENT_ID" || -z "$CLIENT_SECRET" ]]; then
gum style "── app client ──" --padding "0 1" --foreground 240 --bold
gum style "No application client_id/client_secret supplied." --foreground 214
gum style "Fetching the built-in 'local' client to demonstrate it is NOT enough:" --foreground 240
local_client="$(curl -sS -m 20 "$API/oauth-clients/local" 2>/dev/null)"
if command -v jq >/dev/null; then
CLIENT_ID="$(echo "$local_client" | jq -r '.client_id // empty')"
CLIENT_SECRET="$(echo "$local_client" | jq -r '.client_secret // empty')"
else
CLIENT_ID="$(echo "$local_client" | sed -n 's/.*"client_id":"\([^"]*\)".*/\1/p')"
CLIENT_SECRET="$(echo "$local_client" | sed -n 's/.*"client_secret":"\([^"]*\)".*/\1/p')"
fi
fi
grant="$(curl -sS -m 20 -X POST "$API/users/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET" 2>/dev/null)"
if echo "$grant" | grep -q '"access_token"'; then
token="$(echo "$grant" | jq -r '.access_token')"
expires="$(echo "$grant" | jq -r '.expires_in // "n/a"')"
gum style "✓ client_credentials OK — minted access token" --foreground 42
gum style " expires_in: $expires s" --foreground 240
gum style "Access token:" --foreground 214 --bold
echo "$token"
exit 0
else
errcode="$(echo "$grant" | jq -r '.code // empty' 2>/dev/null)"
errmsg="$(echo "$grant" | jq -r '.detail // .error // "unknown"' 2>/dev/null)"
if [[ "$errcode" == "unsupported_grant_type" ]]; then
gum style "✗ client_credentials REJECTED (unsupported_grant_type)" --foreground 196
gum style " The '$CLIENT_ID' client is not an application client — it can only do" --foreground 214
gum style " password grant. You need an ADMIN-CREATED app client (see below)." --foreground 214
else
gum style "✗ client_credentials failed: $errcode $errmsg" --foreground 196
fi
fi
# ── 4. Guide: how an admin creates an app client ───────────────────────────────
gum style "── How to get a long-lived app token ──" --padding "0 1" --foreground 240 --bold
gum style "Run this ON the PeerTube server (as an admin):" --foreground 214
gum style "" --foreground 240
gum style " cd /var/www/peertube/prod" --foreground 240
gum style " sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/prod/config \\" --foreground 240
gum style " NODE_ENV=production node -r dotenv/config tools/create-client.js" --foreground 240
gum style "" --foreground 240
gum style "It prints a client_id and client_secret." --foreground 240
gum style "Then mint a long-lived token right here:" --foreground 214
gum style "" --foreground 240
gum style " bash $SCRIPT_DIR/app-token.sh --client <client_id> --secret <client_secret>" --foreground 240
gum style "" --foreground 240
gum style "Note: the app client may still need the app authorized to act on behalf of" --foreground 240
gum style "a user; PeerTube issues the token with the client's own permission scope." --foreground 240
exit 1