49 lines
2.2 KiB
Bash
Executable File
49 lines
2.2 KiB
Bash
Executable File
#!/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"])'
|