A third party ATProto appview
1#!/bin/bash
2
3# A pure Bash script to generate a standalone oauth-keyset.json file
4# using a P-256 (prime256v1) key for ES256, suitable for AT Protocol OAuth client authentication.
5#
6# NOTE: ES256 (P-256) is required for private_key_jwt client authentication in ATProto OAuth.
7# ES256K (secp256k1) is used for other ATProto operations, but NOT for OAuth client auth.
8#
9# Dependencies: openssl, jq, xxd
10
11set -e
12
13echo "🔐 OAuth Keyset Generator"
14echo "==========================="
15echo ""
16
17# --- Dependency Check ---
18for cmd in openssl jq xxd; do
19 if ! command -v $cmd &> /dev/null; then
20 echo "❌ Missing required dependency: $cmd"
21 exit 1
22 fi
23done
24
25# --- Key Generation ---
26echo "🔑 Generating P-256 (ES256) key pair for OAuth client authentication..."
27# 1. Generate a P-256 (prime256v1) private key in the legacy format
28openssl ecparam -name prime256v1 -genkey -noout -out private-legacy.pem
29
30# 2. Convert the legacy key to the required PKCS#8 format
31openssl pkcs8 -topk8 -nocrypt -in private-legacy.pem -out private-pkcs8.pem
32
33# 3. Generate the corresponding public key
34openssl ec -in private-legacy.pem -pubout -out public.pem 2>/dev/null
35
36# Read PEM file contents into variables (using the corrected PKCS#8 private key)
37PRIVATE_KEY_PEM=$(cat private-pkcs8.pem)
38PUBLIC_KEY_PEM=$(cat public.pem)
39
40echo "⚙️ Formatting key into JWK format..."
41# Extract components from the original key for JWK
42KEY_COMPONENTS_HEX=$(openssl ec -in private-legacy.pem -text -noout)
43
44# Isolate and clean up each component
45PRIV_HEX=$(echo "$KEY_COMPONENTS_HEX" | grep priv -A 3 | tail -n +2 | tr -d ' \n:')
46PUB_HEX=$(echo "$KEY_COMPONENTS_HEX" | grep pub -A 5 | tail -n +2 | tr -d ' \n:')
47X_HEX=$(echo "$PUB_HEX" | cut -c 3-66)
48Y_HEX=$(echo "$PUB_HEX" | cut -c 67-130)
49
50# Convert hex components to base64url for the JWK
51D_B64URL=$(echo -n "$PRIV_HEX" | xxd -r -p | base64 | tr '/+' '_-' | tr -d '=')
52X_B64URL=$(echo -n "$X_HEX" | xxd -r -p | base64 | tr '/+' '_-' | tr -d '=')
53Y_B64URL=$(echo -n "$Y_HEX" | xxd -r -p | base64 | tr '/+' '_-' | tr -d '=')
54
55# Generate a unique Key ID (kid)
56KID="$(date +%s)-$(openssl rand -hex 4)"
57
58# --- File Creation ---
59echo "📄 Creating oauth-keyset.json file..."
60jq -n \
61 --arg kid "$KID" \
62 --arg pkpem "$PRIVATE_KEY_PEM" \
63 --arg pubpem "$PUBLIC_KEY_PEM" \
64 --arg d "$D_B64URL" \
65 --arg x "$X_B64URL" \
66 --arg y "$Y_B64URL" \
67 '{
68 kid: $kid,
69 privateKeyPem: $pkpem,
70 publicKeyPem: $pubpem,
71 jwk: {
72 kid: $kid,
73 kty: "EC",
74 crv: "P-256",
75 alg: "ES256",
76 use: "sig",
77 d: $d,
78 x: $x,
79 y: $y
80 }
81 }' > oauth-keyset.json
82
83# --- Cleanup ---
84rm private-legacy.pem private-pkcs8.pem public.pem
85
86echo ""
87echo "✅ Success! oauth-keyset.json generated with ES256 (P-256) key for OAuth client authentication."
88echo ""