cfd79190ee
Adds TOTP (Time-based One-Time Password) two-factor authentication support including: - New TOTP service with secret generation, QR code provisioning, and code verification - New auth endpoints for enrollment, verification, status, and backup code management - New TOTP authentication method type and user methods for TOTP management - Backup codes generation and verification for account recovery - Updated OIDC endpoints with timezone-aware datetime handling and RFC-compliant responses - Added "roles" scope support for OIDC userinfo and ID tokens - New pyotp dependency for TOTP operations - Comprehensive unit tests for TOTP service
103 lines
2.8 KiB
Bash
103 lines
2.8 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ISSUER="https://oidctest.wsweet.org"
|
|
CLIENT_ID="secret"
|
|
CLIENT_SECRET="tardis"
|
|
REDIRECT_URI="http://127.0.0.1:5556/callback"
|
|
SCOPE="openid profile email offline_access"
|
|
|
|
# ---------------------------
|
|
# Discover OIDC endpoints
|
|
# ---------------------------
|
|
DISCOVERY=$(curl -s "$ISSUER/.well-known/openid-configuration")
|
|
|
|
AUTH_ENDPOINT=$(echo "$DISCOVERY" | jq -r .authorization_endpoint)
|
|
TOKEN_ENDPOINT=$(echo "$DISCOVERY" | jq -r .token_endpoint)
|
|
USERINFO_ENDPOINT=$(echo "$DISCOVERY" | jq -r .userinfo_endpoint)
|
|
|
|
echo "Auth endpoint : $AUTH_ENDPOINT"
|
|
echo "Token endpoint: $TOKEN_ENDPOINT"
|
|
echo
|
|
|
|
# ---------------------------
|
|
# PKCE
|
|
# ---------------------------
|
|
CODE_VERIFIER=$(openssl rand -base64 32 | tr -d '=+/')
|
|
CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr -d '=+/' | tr '/+' '_-')
|
|
|
|
STATE=$(openssl rand -hex 16)
|
|
NONCE=$(openssl rand -hex 16)
|
|
|
|
# ---------------------------
|
|
# Build auth URL
|
|
# ---------------------------
|
|
AUTH_URL="$AUTH_ENDPOINT?response_type=code\
|
|
&client_id=$CLIENT_ID\
|
|
&redirect_uri=$(printf '%s' "$REDIRECT_URI" | jq -s -R -r @uri)\
|
|
&scope=$(printf '%s' "$SCOPE" | jq -s -R -r @uri)\
|
|
&state=$STATE\
|
|
&nonce=$NONCE\
|
|
&code_challenge=$CODE_CHALLENGE\
|
|
&code_challenge_method=S256"
|
|
|
|
echo "Open this URL in a browser:"
|
|
echo
|
|
echo "$AUTH_URL"
|
|
echo
|
|
echo "After login you will be redirected to:"
|
|
echo "$REDIRECT_URI?code=XXXX&state=YYYY"
|
|
echo
|
|
read -p "Paste the full redirect URL: " REDIRECT
|
|
|
|
CODE=$(echo "$REDIRECT" | sed -n 's/.*code=\([^&]*\).*/\1/p')
|
|
RETURNED_STATE=$(echo "$REDIRECT" | sed -n 's/.*state=\([^&]*\).*/\1/p')
|
|
|
|
if [ "$RETURNED_STATE" != "$STATE" ]; then
|
|
echo "STATE MISMATCH"
|
|
exit 1
|
|
fi
|
|
|
|
# ---------------------------
|
|
# Exchange code for tokens
|
|
# ---------------------------
|
|
TOKENS=$(curl -s -X POST "$TOKEN_ENDPOINT" \
|
|
-u "$CLIENT_ID:$CLIENT_SECRET" \
|
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
-d "grant_type=authorization_code" \
|
|
-d "code=$CODE" \
|
|
-d "redirect_uri=$REDIRECT_URI" \
|
|
-d "code_verifier=$CODE_VERIFIER")
|
|
|
|
echo
|
|
echo "Token response:"
|
|
echo "$TOKENS" | jq .
|
|
|
|
ACCESS_TOKEN=$(echo "$TOKENS" | jq -r .access_token)
|
|
ID_TOKEN=$(echo "$TOKENS" | jq -r .id_token)
|
|
|
|
# ---------------------------
|
|
# JWT decode function
|
|
# ---------------------------
|
|
decode() {
|
|
echo "$1" | awk -F. '{print $2}' | tr '_-' '/+' | base64 -d 2>/dev/null | jq .
|
|
}
|
|
|
|
echo
|
|
echo "================ ID TOKEN ================"
|
|
decode "$ID_TOKEN"
|
|
|
|
echo
|
|
echo "============== ACCESS TOKEN =============="
|
|
decode "$ACCESS_TOKEN"
|
|
|
|
# ---------------------------
|
|
# Userinfo (optional)
|
|
# ---------------------------
|
|
if [ "$USERINFO_ENDPOINT" != "null" ]; then
|
|
echo
|
|
echo "=============== USERINFO ================="
|
|
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" "$USERINFO_ENDPOINT" | jq .
|
|
fi
|
|
|