Fix Cloudflare Access IDP lookup and JSON body issues #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy to Cloudflare Pages | |
| on: | |
| push: | |
| branches: ["main"] | |
| workflow_dispatch: | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| deployments: write | |
| name: Deploy to Cloudflare Pages | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v3 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: '20' | |
| - name: Install Wrangler | |
| run: npm install -g wrangler | |
| - name: Install jq | |
| run: sudo apt-get update && sudo apt-get install -y jq | |
| - name: Determine Project Name | |
| id: meta | |
| run: echo "NAME=$(echo ${{ github.repository }} | cut -d'/' -f2)" >> $GITHUB_OUTPUT | |
| - name: Create Project (if not exists) | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.WEBSITE_STARTER_CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| PROJECT_NAME: ${{ steps.meta.outputs.NAME }} | |
| run: wrangler pages project create "$PROJECT_NAME" --production-branch main || true | |
| - name: Publish to Cloudflare Pages | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.WEBSITE_STARTER_CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| PROJECT_NAME: ${{ steps.meta.outputs.NAME }} | |
| run: wrangler pages deploy . --project-name "$PROJECT_NAME" --branch main | |
| - name: Get Assigned Pages Subdomain | |
| id: project_details | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.WEBSITE_STARTER_CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| PROJECT_NAME: ${{ steps.meta.outputs.NAME }} | |
| run: | | |
| RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$PROJECT_NAME" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json") | |
| SUBDOMAIN=$(echo "$RESPONSE" | jq -r '.result.subdomain') | |
| echo "Cloudflare assigned subdomain: $SUBDOMAIN" | |
| echo "SUBDOMAIN=$SUBDOMAIN" >> $GITHUB_OUTPUT | |
| - name: Assign Custom Domain (via API) | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.WEBSITE_STARTER_CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| PROJECT_NAME: ${{ steps.meta.outputs.NAME }} | |
| DOMAIN: "${{ steps.meta.outputs.NAME }}.apps.owid.io" | |
| run: | | |
| echo "Linking domain $DOMAIN to project $PROJECT_NAME..." | |
| curl -f -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$PROJECT_NAME/domains" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| --data "{\"name\":\"$DOMAIN\"}" || true | |
| - name: Force DNS Record Creation | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.WEBSITE_STARTER_CLOUDFLARE_API_TOKEN }} | |
| DOMAIN: "${{ steps.meta.outputs.NAME }}.apps.owid.io" | |
| TARGET: ${{ steps.project_details.outputs.SUBDOMAIN }} | |
| ZONE_NAME: "owid.io" | |
| run: | | |
| echo "Ensuring DNS record for $DOMAIN points to $TARGET..." | |
| ZONE_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$ZONE_NAME" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json" | jq -r '.result[0].id') | |
| RECORD_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$DOMAIN" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json" | jq -r '.result[0].id') | |
| if [ "$RECORD_ID" == "null" ]; then | |
| echo "Creating CNAME: $DOMAIN -> $TARGET" | |
| curl -f -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| --data "{\"type\":\"CNAME\",\"name\":\"$DOMAIN\",\"content\":\"$TARGET\",\"proxied\":true,\"ttl\":1}" | |
| else | |
| echo "Record exists. Skipping." | |
| fi | |
| - name: Restrict Access to OWID Staff | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.WEBSITE_STARTER_CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| DOMAIN: "${{ steps.meta.outputs.NAME }}.apps.owid.io" | |
| run: | | |
| # Check if an Access application already exists for this domain | |
| EXISTING_APP=$(curl -s -X GET \ | |
| "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| | jq -r --arg domain "$DOMAIN" '.result[] | select(.domain == $domain) | .id') | |
| # Find the Google identity provider ID (skip the OTP provider) | |
| IDP_RESPONSE=$(curl -s -X GET \ | |
| "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/identity_providers" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json") | |
| echo "Identity providers response: $(echo "$IDP_RESPONSE" | jq -c '{success: .success, result_count: (.result | length), types: [.result[]?.type]}')" | |
| GOOGLE_IDP=$(echo "$IDP_RESPONSE" \ | |
| | jq -r '(.result // [])[] | select(.type == "google" or .type == "google-apps" or (.name | test("google";"i"))) | .id' | head -1) | |
| if [ -n "$GOOGLE_IDP" ]; then | |
| echo "Found Google identity provider: $GOOGLE_IDP" | |
| IDP_EXTRA="\"allowed_idps\": [\"$GOOGLE_IDP\"], \"auto_redirect_to_identity\": true," | |
| else | |
| echo "Warning: Could not find Google identity provider. Login screen will show all providers." | |
| IDP_EXTRA="" | |
| fi | |
| if [ -n "$EXISTING_APP" ]; then | |
| echo "Access application already exists for $DOMAIN (ID: $EXISTING_APP). Updating to restrict identity providers..." | |
| curl -f -s -X PUT \ | |
| "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$EXISTING_APP" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| --data "{ | |
| \"name\": \"$DOMAIN\", | |
| \"domain\": \"$DOMAIN\", | |
| \"type\": \"self_hosted\", | |
| \"session_duration\": \"24h\", | |
| $IDP_EXTRA | |
| \"tags\": [] | |
| }" | |
| echo "Updated Access application." | |
| exit 0 | |
| fi | |
| echo "Creating Access application for $DOMAIN..." | |
| APP_RESPONSE=$(curl -f -s -X POST \ | |
| "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| --data "{ | |
| \"name\": \"$DOMAIN\", | |
| \"domain\": \"$DOMAIN\", | |
| \"type\": \"self_hosted\", | |
| \"session_duration\": \"24h\", | |
| $IDP_EXTRA | |
| \"tags\": [] | |
| }") | |
| APP_ID=$(echo "$APP_RESPONSE" | jq -r '.result.id') | |
| echo "Created Access application: $APP_ID" | |
| echo "Adding policy to allow @ourworldindata.org emails..." | |
| curl -f -s -X POST \ | |
| "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/access/apps/$APP_ID/policies" \ | |
| -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| --data "{ | |
| \"name\": \"Allow OWID Staff\", | |
| \"decision\": \"allow\", | |
| \"include\": [{ | |
| \"email_domain\": { | |
| \"domain\": \"ourworldindata.org\" | |
| } | |
| }] | |
| }" | |
| echo "Access restriction enabled for $DOMAIN" |