mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-02-04 09:56:47 +00:00
docs: docs website + installation (#778)
* feat: WIP doc (vibe started and iterated) * install from scratch docs * caddyfile.example * gitignore * authentik script * authentik script * authentik script * llm doc * authentik ongoing * more daily setup logs * doc website * gpu self hosted setup guide (no-mistakes) * doc review round * doc review round * doc review round * update doc site sidebars * feat(docs): add mermaid diagram support * docs polishing * live pipeline doc * move pipeline dev docs to dev docs location * doc pr review iteration * dockerfile healthcheck * docs/pr-comments * remove jwt comment * llm suggestion * pr comments * pr comments * document auto migrations * cleanup docs --------- Co-authored-by: Mathieu Virbel <mat@meltingrocks.com> Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com>
This commit is contained in:
258
scripts/setup-authentik-oauth.sh
Executable file
258
scripts/setup-authentik-oauth.sh
Executable file
@@ -0,0 +1,258 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Setup Authentik OAuth provider for Reflector
|
||||
#
|
||||
# IMPORTANT: Run this script from your Reflector repository directory (cd ~/reflector)
|
||||
# The script creates files using relative paths: server/reflector/auth/jwt/keys/
|
||||
#
|
||||
# Usage: ./setup-authentik-oauth.sh <authentik-url> <admin-password> <frontend-url>
|
||||
# Example: ./setup-authentik-oauth.sh https://authentik.example.com MyPassword123 https://app.example.com
|
||||
|
||||
AUTHENTIK_URL="${1:-}"
|
||||
ADMIN_PASSWORD="${2:-}"
|
||||
FRONTEND_URL="${3:-}"
|
||||
|
||||
if [ -z "$AUTHENTIK_URL" ] || [ -z "$ADMIN_PASSWORD" ] || [ -z "$FRONTEND_URL" ]; then
|
||||
echo "Usage: $0 <authentik-url> <admin-password> <frontend-url>"
|
||||
echo "Example: $0 https://authentik.example.com MyPassword123 https://app.example.com"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove trailing slash from URLs
|
||||
AUTHENTIK_URL="${AUTHENTIK_URL%/}"
|
||||
FRONTEND_URL="${FRONTEND_URL%/}"
|
||||
|
||||
echo "==========================================="
|
||||
echo "Authentik OAuth Setup for Reflector"
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "Authentik URL: $AUTHENTIK_URL"
|
||||
echo "Frontend URL: $FRONTEND_URL"
|
||||
echo ""
|
||||
|
||||
# Step 1: Create API token via Django shell
|
||||
echo "Creating API token..."
|
||||
cd ~/authentik || { echo "Error: ~/authentik directory not found"; exit 1; }
|
||||
|
||||
API_TOKEN=$(sudo docker compose exec -T server python -m manage shell 2>&1 << 'PYTHON' | grep "^TOKEN:" | cut -d: -f2
|
||||
from authentik.core.models import User, Token, TokenIntents
|
||||
|
||||
user = User.objects.get(username='akadmin')
|
||||
token, created = Token.objects.update_or_create(
|
||||
identifier='reflector-setup',
|
||||
defaults={
|
||||
'user': user,
|
||||
'intent': TokenIntents.INTENT_API,
|
||||
'description': 'Reflector setup token',
|
||||
'expiring': False
|
||||
}
|
||||
)
|
||||
print(f"TOKEN:{token.key}")
|
||||
PYTHON
|
||||
)
|
||||
|
||||
cd - > /dev/null
|
||||
|
||||
if [ -z "$API_TOKEN" ] || [ "$API_TOKEN" = "null" ]; then
|
||||
echo "Error: Failed to create API token"
|
||||
echo "Make sure Authentik is fully started and akadmin user exists"
|
||||
exit 1
|
||||
fi
|
||||
echo " -> Got API token"
|
||||
|
||||
# Step 2: Get authorization flow UUID
|
||||
echo "Getting authorization flow..."
|
||||
FLOW_RESPONSE=$(curl -s "$AUTHENTIK_URL/api/v3/flows/instances/?slug=default-provider-authorization-implicit-consent" \
|
||||
-H "Authorization: Bearer $API_TOKEN")
|
||||
|
||||
FLOW_UUID=$(echo "$FLOW_RESPONSE" | jq -r '.results[0].pk')
|
||||
if [ -z "$FLOW_UUID" ] || [ "$FLOW_UUID" = "null" ]; then
|
||||
echo "Error: Could not find authorization flow"
|
||||
echo "Response: $FLOW_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
echo " -> Flow UUID: $FLOW_UUID"
|
||||
|
||||
# Step 3: Get invalidation flow UUID
|
||||
echo "Getting invalidation flow..."
|
||||
INVALIDATION_RESPONSE=$(curl -s "$AUTHENTIK_URL/api/v3/flows/instances/?slug=default-provider-invalidation-flow" \
|
||||
-H "Authorization: Bearer $API_TOKEN")
|
||||
|
||||
INVALIDATION_UUID=$(echo "$INVALIDATION_RESPONSE" | jq -r '.results[0].pk')
|
||||
if [ -z "$INVALIDATION_UUID" ] || [ "$INVALIDATION_UUID" = "null" ]; then
|
||||
echo "Warning: Could not find invalidation flow, using authorization flow"
|
||||
INVALIDATION_UUID="$FLOW_UUID"
|
||||
fi
|
||||
echo " -> Invalidation UUID: $INVALIDATION_UUID"
|
||||
|
||||
# Step 4: Get scope mappings (email, openid, profile)
|
||||
echo "Getting scope mappings..."
|
||||
SCOPE_RESPONSE=$(curl -s "$AUTHENTIK_URL/api/v3/propertymappings/all/" \
|
||||
-H "Authorization: Bearer $API_TOKEN")
|
||||
|
||||
EMAIL_SCOPE=$(echo "$SCOPE_RESPONSE" | jq -r '.results[] | select(.name == "authentik default OAuth Mapping: OpenID '\''email'\''") | .pk')
|
||||
OPENID_SCOPE=$(echo "$SCOPE_RESPONSE" | jq -r '.results[] | select(.name == "authentik default OAuth Mapping: OpenID '\''openid'\''") | .pk')
|
||||
PROFILE_SCOPE=$(echo "$SCOPE_RESPONSE" | jq -r '.results[] | select(.name == "authentik default OAuth Mapping: OpenID '\''profile'\''") | .pk')
|
||||
echo " -> email: $EMAIL_SCOPE"
|
||||
echo " -> openid: $OPENID_SCOPE"
|
||||
echo " -> profile: $PROFILE_SCOPE"
|
||||
|
||||
# Step 5: Get signing key
|
||||
echo "Getting signing key..."
|
||||
CERT_RESPONSE=$(curl -s "$AUTHENTIK_URL/api/v3/crypto/certificatekeypairs/" \
|
||||
-H "Authorization: Bearer $API_TOKEN")
|
||||
SIGNING_KEY=$(echo "$CERT_RESPONSE" | jq -r '.results[0].pk')
|
||||
echo " -> Signing key: $SIGNING_KEY"
|
||||
|
||||
# Step 6: Generate client credentials
|
||||
CLIENT_ID="reflector"
|
||||
CLIENT_SECRET=$(openssl rand -hex 32)
|
||||
|
||||
# Step 7: Create OAuth2 provider
|
||||
echo "Creating OAuth2 provider..."
|
||||
PROVIDER_RESPONSE=$(curl -s -X POST "$AUTHENTIK_URL/api/v3/providers/oauth2/" \
|
||||
-H "Authorization: Bearer $API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"Reflector\",
|
||||
\"authorization_flow\": \"$FLOW_UUID\",
|
||||
\"invalidation_flow\": \"$INVALIDATION_UUID\",
|
||||
\"client_type\": \"confidential\",
|
||||
\"client_id\": \"$CLIENT_ID\",
|
||||
\"client_secret\": \"$CLIENT_SECRET\",
|
||||
\"redirect_uris\": [{
|
||||
\"matching_mode\": \"strict\",
|
||||
\"url\": \"$FRONTEND_URL/api/auth/callback/authentik\"
|
||||
}],
|
||||
\"property_mappings\": [\"$EMAIL_SCOPE\", \"$OPENID_SCOPE\", \"$PROFILE_SCOPE\"],
|
||||
\"signing_key\": \"$SIGNING_KEY\",
|
||||
\"access_token_validity\": \"hours=1\",
|
||||
\"refresh_token_validity\": \"days=30\"
|
||||
}")
|
||||
|
||||
PROVIDER_ID=$(echo "$PROVIDER_RESPONSE" | jq -r '.pk')
|
||||
if [ -z "$PROVIDER_ID" ] || [ "$PROVIDER_ID" = "null" ]; then
|
||||
# Check if provider already exists
|
||||
if echo "$PROVIDER_RESPONSE" | grep -q "already exists"; then
|
||||
echo " -> Provider already exists, updating..."
|
||||
EXISTING=$(curl -s "$AUTHENTIK_URL/api/v3/providers/oauth2/?name=Reflector" \
|
||||
-H "Authorization: Bearer $API_TOKEN")
|
||||
PROVIDER_ID=$(echo "$EXISTING" | jq -r '.results[0].pk')
|
||||
CLIENT_ID=$(echo "$EXISTING" | jq -r '.results[0].client_id')
|
||||
# Update secret and scopes
|
||||
curl -s -X PATCH "$AUTHENTIK_URL/api/v3/providers/oauth2/$PROVIDER_ID/" \
|
||||
-H "Authorization: Bearer $API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"client_secret\": \"$CLIENT_SECRET\",
|
||||
\"property_mappings\": [\"$EMAIL_SCOPE\", \"$OPENID_SCOPE\", \"$PROFILE_SCOPE\"],
|
||||
\"signing_key\": \"$SIGNING_KEY\"
|
||||
}" > /dev/null
|
||||
else
|
||||
echo "Error: Failed to create provider"
|
||||
echo "Response: $PROVIDER_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo " -> Provider ID: $PROVIDER_ID"
|
||||
|
||||
# Step 8: Create application
|
||||
echo "Creating application..."
|
||||
APP_RESPONSE=$(curl -s -X POST "$AUTHENTIK_URL/api/v3/core/applications/" \
|
||||
-H "Authorization: Bearer $API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"Reflector\",
|
||||
\"slug\": \"reflector\",
|
||||
\"provider\": $PROVIDER_ID
|
||||
}")
|
||||
|
||||
if echo "$APP_RESPONSE" | grep -q "already exists"; then
|
||||
echo " -> Application already exists"
|
||||
else
|
||||
APP_SLUG=$(echo "$APP_RESPONSE" | jq -r '.slug')
|
||||
if [ -z "$APP_SLUG" ] || [ "$APP_SLUG" = "null" ]; then
|
||||
echo "Error: Failed to create application"
|
||||
echo "Response: $APP_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
echo " -> Application created: $APP_SLUG"
|
||||
fi
|
||||
|
||||
# Step 9: Extract public key for JWT verification
|
||||
echo "Extracting public key for JWT verification..."
|
||||
mkdir -p server/reflector/auth/jwt/keys
|
||||
curl -s "$AUTHENTIK_URL/application/o/reflector/jwks/" | \
|
||||
jq -r '.keys[0].x5c[0]' | \
|
||||
base64 -d | \
|
||||
openssl x509 -pubkey -noout > server/reflector/auth/jwt/keys/authentik_public.pem
|
||||
|
||||
if [ ! -s server/reflector/auth/jwt/keys/authentik_public.pem ]; then
|
||||
echo "Error: Failed to extract public key"
|
||||
exit 1
|
||||
fi
|
||||
echo " -> Saved to server/reflector/auth/jwt/keys/authentik_public.pem"
|
||||
|
||||
# Step 10: Update environment files automatically
|
||||
echo "Updating environment files..."
|
||||
|
||||
# Update server/.env
|
||||
cat >> server/.env << EOF
|
||||
|
||||
# --- Authentik OAuth (added by setup script) ---
|
||||
AUTH_BACKEND=jwt
|
||||
AUTH_JWT_AUDIENCE=$CLIENT_ID
|
||||
AUTH_JWT_PUBLIC_KEY=authentik_public.pem
|
||||
# --- End JWT Configuration ---
|
||||
EOF
|
||||
echo " -> Updated server/.env"
|
||||
|
||||
# Update www/.env
|
||||
cat >> www/.env << EOF
|
||||
|
||||
# --- Authentik OAuth (added by setup script) ---
|
||||
FEATURE_REQUIRE_LOGIN=true
|
||||
AUTHENTIK_ISSUER=$AUTHENTIK_URL/application/o/reflector
|
||||
AUTHENTIK_REFRESH_TOKEN_URL=$AUTHENTIK_URL/application/o/token/
|
||||
AUTHENTIK_CLIENT_ID=$CLIENT_ID
|
||||
AUTHENTIK_CLIENT_SECRET=$CLIENT_SECRET
|
||||
# --- End Authentik Configuration ---
|
||||
EOF
|
||||
echo " -> Updated www/.env"
|
||||
|
||||
# Step 11: Restart Reflector services
|
||||
echo "Restarting Reflector services..."
|
||||
docker compose -f docker-compose.prod.yml up -d server worker web
|
||||
|
||||
echo ""
|
||||
echo "==========================================="
|
||||
echo "Setup complete!"
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "Authentik admin: $AUTHENTIK_URL"
|
||||
echo " Username: akadmin"
|
||||
echo " Password: (provided as argument)"
|
||||
echo ""
|
||||
echo "Frontend: $FRONTEND_URL"
|
||||
echo " Authentication is now required"
|
||||
echo ""
|
||||
echo "Note: Public key saved to server/reflector/auth/jwt/keys/authentik_public.pem"
|
||||
echo " and mounted via docker-compose volume."
|
||||
echo ""
|
||||
echo "==========================================="
|
||||
echo "Configuration values (for reference):"
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "# server/.env"
|
||||
echo "AUTH_BACKEND=jwt"
|
||||
echo "AUTH_JWT_AUDIENCE=$CLIENT_ID"
|
||||
echo "AUTH_JWT_PUBLIC_KEY=authentik_public.pem"
|
||||
echo ""
|
||||
echo "# www/.env"
|
||||
echo "FEATURE_REQUIRE_LOGIN=true"
|
||||
echo "AUTHENTIK_ISSUER=$AUTHENTIK_URL/application/o/reflector"
|
||||
echo "AUTHENTIK_REFRESH_TOKEN_URL=$AUTHENTIK_URL/application/o/token/"
|
||||
echo "AUTHENTIK_CLIENT_ID=$CLIENT_ID"
|
||||
echo "AUTHENTIK_CLIENT_SECRET=$CLIENT_SECRET"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user