#!/usr/bin/env bash # Drive a complete authorization_code + PKCE flow with curl and print the access token. # # TOKEN=$(./scripts/user-token.sh) # # There is no browser here and none is needed: the "browser flow" is four HTTP requests and a # cookie jar. Doing it by hand once is the fastest way to understand what your SPA's OIDC # library is actually doing, and it makes every transcript in docs/output/ reproducible. set -eu AS=http://127.0.0.1:9000 JAR=$(mktemp) trap 'rm -f "$JAR"' EXIT # 1. PKCE: a random verifier, and its base64url-encoded SHA-256 as the challenge. VERIFIER=$(head -c 48 /dev/urandom | base64 | tr -d '=+/' | cut -c1-64) CHALLENGE=$(printf '%s' "$VERIFIER" | openssl dgst -binary -sha256 | base64 | tr '+/' '-_' | tr -d '=') AUTHORIZE="$AS/oauth2/authorize?response_type=code&client_id=spa-client\ &redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Fauthorized&scope=orders.read%20orders.write\ &code_challenge=$CHALLENGE&code_challenge_method=S256" # 2. Ask for the code. Unauthenticated, so this saves the request and redirects to /login. curl -s -o /dev/null -c "$JAR" -b "$JAR" "$AUTHORIZE" # 3. Log in. The login page is CSRF-protected, so read the token out of the form. CSRF=$(curl -s -c "$JAR" -b "$JAR" "$AS/login" \ | grep -oiE 'name="_csrf"[^>]*value="[^"]*"' | head -1 | sed 's/.*value="//; s/"//') curl -s -o /dev/null -c "$JAR" -b "$JAR" -X POST "$AS/login" \ -d "username=alice" -d "password=password" -d "_csrf=$CSRF" # 4. Follow the saved request. Now authenticated, so this redirects to the redirect_uri # carrying ?code=... We never let curl follow it; we just read the Location header. CODE=$(curl -s -o /dev/null -D- -c "$JAR" -b "$JAR" "$AUTHORIZE" \ | grep -i '^location:' | sed 's/.*code=//; s/[&\r].*//') if [ -z "$CODE" ]; then echo "no authorization code was issued - is the auth server up?" >&2 exit 1 fi # 5. Redeem it. A public client, so no client secret: the code_verifier is the proof. curl -s -X POST "$AS/oauth2/token" \ -d grant_type=authorization_code \ -d "code=$CODE" \ -d "redirect_uri=http://127.0.0.1:8080/authorized" \ -d client_id=spa-client \ -d "code_verifier=$VERIFIER" \ | python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])'