Appearance
Полный пример: вход через curl от начала до конца
Один сквозной прогон Authorization Code Flow с PKCE через curl — от генерации PKCE-пары до /userinfo и обновления токена. Полезно, чтобы один раз увидеть весь флоу целиком одной последовательностью запросов, прежде чем реализовывать его в своём приложении (шаги по отдельности и объяснения — в 03-web-app.md).
Логин и экран согласия на этом сервере — обычные HTML-формы без JS, поэтому весь флоу проходится curl с файлом cookie-джара, без браузера. Того же результата в питоне (с обработкой MFA) — в файле oauth_flow.ipynb (docs-site/integration/, в репозитории рядом с этой документацией).
Понадобится: client_id/client_secret уже созданного клиента (02-client.md), рабочий логин/пароль пользователя в SSO, jq для разбора JSON-ответов (необязательно, но короче).
0. Переменные и PKCE-пара
bash
BASE="http://localhost:3002"
CLIENT_ID="my-app-client"
CLIENT_SECRET="<ваш client_secret>"
REDIRECT_URI="http://localhost:4000/callback"
SCOPE="openid profile email offline_access"
EMAIL="user@example.com"
PASSWORD="<пароль пользователя>"
COOKIES=$(mktemp)
CODE_VERIFIER=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=')
STATE=$(openssl rand -hex 8)1. Логин
POST /login с теми же query-параметрами, что были бы у /authorize, и учётными данными в теле. При успехе сервер ставит cookie сессии и (если MFA не требуется) редиректит на /consent.
bash
QS="response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&scope=${SCOPE// /+}&state=$STATE&code_challenge=$CODE_CHALLENGE&code_challenge_method=S256"
curl -s -c "$COOKIES" -b "$COOKIES" -L \
-d "email=$EMAIL" -d "password=$PASSWORD" -d "remember=1" \
-o /tmp/login_response.html -w "%{url_effective}\n" \
"$BASE/login?$QS"Если итоговый URL не похож на .../consent... — скорее всего сработала MFA (редирект на /mfa/verify) или неверные учётные данные. Пройти MFA без браузера curl'ом неудобно — для сценариев с MFA проще воспользоваться oauth_flow.ipynb (там это уже сделано для TOTP/backup-кода/email OTP) либо временно выключить MFA у тестового пользователя.
2. Забрать _csrf со страницы согласия
bash
CONSENT_HTML=$(curl -s -c "$COOKIES" -b "$COOKIES" "$BASE/consent?$QS")
CSRF=$(echo "$CONSENT_HTML" | grep -oP 'name="_csrf" value="\K[^"]+' | head -1)
echo "csrf: $CSRF"3. Подтвердить согласие и получить code
Редиректы не разрешаем (-i, без -L) — redirect_uri ведёт на несуществующий callback, код нужно вытащить из заголовка Location ответа, не переходя по нему.
bash
LOCATION=$(curl -s -i -c "$COOKIES" -b "$COOKIES" \
--data-urlencode "_csrf=$CSRF" \
--data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "redirect_uri=$REDIRECT_URI" \
--data-urlencode "state=$STATE" \
--data-urlencode "code_challenge=$CODE_CHALLENGE" \
--data-urlencode "code_challenge_method=S256" \
--data-urlencode "approved_scopes[]=openid" \
--data-urlencode "approved_scopes[]=profile" \
--data-urlencode "approved_scopes[]=email" \
--data-urlencode "approved_scopes[]=offline_access" \
--data-urlencode "action=approve" \
"$BASE/consent" | grep -i '^location:' | tr -d '\r')
echo "$LOCATION"
CODE=$(echo "$LOCATION" | grep -oP 'code=\K[^&]+')
echo "code: $CODE"4. Обменять code на токены
bash
TOKENS=$(curl -s -X POST "$BASE/token" \
-d "grant_type=authorization_code" \
-d "code=$CODE" \
-d "redirect_uri=$REDIRECT_URI" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "code_verifier=$CODE_VERIFIER")
echo "$TOKENS" | jq .
ACCESS_TOKEN=$(echo "$TOKENS" | jq -r .access_token)
REFRESH_TOKEN=$(echo "$TOKENS" | jq -r .refresh_token)5. Userinfo
bash
curl -s "$BASE/userinfo" -H "Authorization: Bearer $ACCESS_TOKEN" | jq .6. Обновить токен (когда access_token истечёт)
Отдельный шаг, не требует нового code — можно запускать независимо от остального флоу, пока жив refresh_token.
bash
curl -s -X POST "$BASE/token" \
-d "grant_type=refresh_token" \
-d "refresh_token=$REFRESH_TOKEN" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" | jq .Полный флоу целиком, без разбивки по шагам, — этот же сценарий одним запуском:
bash
bash <<'EOF'
set -euo pipefail
BASE="http://localhost:3002"
CLIENT_ID="my-app-client"
CLIENT_SECRET="<ваш client_secret>"
REDIRECT_URI="http://localhost:4000/callback"
EMAIL="user@example.com"
PASSWORD="<пароль пользователя>"
COOKIES=$(mktemp)
CODE_VERIFIER=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=')
STATE=$(openssl rand -hex 8)
QS="response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&scope=openid+profile+email+offline_access&state=$STATE&code_challenge=$CODE_CHALLENGE&code_challenge_method=S256"
curl -s -c "$COOKIES" -b "$COOKIES" -L -d "email=$EMAIL" -d "password=$PASSWORD" \
-o /dev/null "$BASE/login?$QS"
CSRF=$(curl -s -c "$COOKIES" -b "$COOKIES" "$BASE/consent?$QS" | grep -oP 'name="_csrf" value="\K[^"]+' | head -1)
CODE=$(curl -s -i -c "$COOKIES" -b "$COOKIES" \
--data-urlencode "_csrf=$CSRF" --data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "redirect_uri=$REDIRECT_URI" --data-urlencode "state=$STATE" \
--data-urlencode "code_challenge=$CODE_CHALLENGE" --data-urlencode "code_challenge_method=S256" \
--data-urlencode "approved_scopes[]=openid" --data-urlencode "approved_scopes[]=profile" \
--data-urlencode "approved_scopes[]=email" --data-urlencode "approved_scopes[]=offline_access" \
--data-urlencode "action=approve" "$BASE/consent" \
| grep -i '^location:' | grep -oP 'code=\K[^&]+')
curl -s -X POST "$BASE/token" -d "grant_type=authorization_code" -d "code=$CODE" \
-d "redirect_uri=$REDIRECT_URI" -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" \
-d "code_verifier=$CODE_VERIFIER" | jq .
EOF