최영범
07/19/2025, 7:57 PMMarvin
07/19/2025, 7:57 PMMarvin
07/19/2025, 7:58 PMyaml
name: Deploy Prefect Flow
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Set up Python
- uses: actions/setup-python@v4
with:
python-version: '3.10'
# Authenticate to Google Cloud
- id: 'auth'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GCP_SA_KEY }}'
# Get IAP authentication token
- name: Get IAP token
run: |
# Install necessary tools
pip install google-auth requests
# Generate IAP token and store it
python -c '
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import os
credentials = service_account.Credentials.from_service_account_info(
${{ secrets.GCP_SA_KEY }},
["<https://www.googleapis.com/auth/cloud-platform>"]
)
# Force token refresh
request = Request()
credentials.refresh(request)
# Set the token as an environment variable
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"IAP_TOKEN={credentials.token}\n")
'
# Set up Prefect
- name: Setup Prefect
run: |
pip install prefect
# Set your Prefect API URL
prefect config set PREFECT_API_URL=${{ secrets.PREFECT_API_URL }}
# Deploy your flow
- name: Deploy Flow
env:
# Make the token available to the Prefect CLI
PREFECT_API_HEADERS: '{"Authorization": "Bearer ${{ env.IAP_TOKEN }}"}'
run: |
prefect deploy ... # Your deployment command here
Key points to note:
1. You'll need to store these secrets in your GitHub repository:
- `GCP_SA_KEY`: Your service account key JSON
- `PREFECT_API_URL`: Your Prefect server URL
2. The crucial part is setting the PREFECT_API_HEADERS
environment variable with the IAP token. This tells Prefect CLI to include the IAP authentication token in its requests.
3. Make sure your service account has the necessary IAP permissions. It typically needs:
- roles/iap.httpsResourceAccessor
- Any additional roles needed for your specific Prefect server setup
4. The service account should be authorized to access the IAP-protected service (your Prefect server).
If you're still experiencing issues, you can debug the authentication by:
1. Adding debug logging to your Python script that generates the token
2. Checking the Cloud Run logs for authentication failures
3. Testing the token manually with a curl command before running Prefect commands
Would you like me to provide more details about any specific part of this solution?최영범
07/19/2025, 8:00 PMPREFECT_API_HEADERS
existing?Marvin
07/19/2025, 8:00 PMMarvin
07/19/2025, 8:01 PMPREFECT_API_HEADERS
. That was incorrect as this configuration option does not exist in Prefect.
For authenticating with IAP in Prefect, you'll need to use a different approach. Here are two possible solutions:
1. Use an HTTP Proxy:
python
# Set these environment variables
PREFECT_API_URL="your-server-url"
HTTPS_PROXY="your-proxy-that-handles-IAP-auth"
2. Create a Custom HTTP Client:
You can create a custom HTTP client that includes the IAP token in its requests. Here's how you might do this:
python
from prefect.settings import PREFECT_API_URL
from prefect.client.schemas.actions import ClientAPI
from prefect.client.utilities import get_client
from httpx import Client
# Create a custom client with IAP authentication
def get_iap_client():
credentials = service_account.Credentials.from_service_account_info(
# your service account info here
scopes=["<https://www.googleapis.com/auth/cloud-platform>"]
)
request = Request()
credentials.refresh(request)
headers = {
"Authorization": f"Bearer {credentials.token}"
}
client = Client(
base_url=PREFECT_API_URL.value(),
headers=headers
)
return client
# Use the custom client with Prefect
api_client = ClientAPI(client=get_iap_client())
For GitHub Actions specifically, you might want to:
1. Set up authentication using the Google Cloud Auth action
2. Use the authenticated Google Cloud SDK to generate IAP tokens
3. Use those tokens with a custom HTTP client in your Prefect scripts
Would you like me to provide more detailed examples of either of these approaches? Also, it would be helpful to know which specific Prefect commands you're trying to run in your GitHub Actions workflow.최영범
07/19/2025, 8:05 PMPREFECT_CLIENT_CUSTOM_HEADERS
? check this documentMarvin
07/19/2025, 8:05 PMMarvin
07/19/2025, 8:06 PMPREFECT_CLIENT_CUSTOM_HEADERS
setting is indeed the proper way to add custom headers to Prefect API requests.
For your GitHub Actions workflow with IAP authentication, you can modify your workflow to use this setting:
yaml
name: Deploy Prefect Flow
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Set up Python
- uses: actions/setup-python@v4
with:
python-version: '3.10'
# Authenticate to Google Cloud
- id: 'auth'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GCP_SA_KEY }}'
# Get IAP token
- name: Get IAP token
run: |
pip install google-auth requests
python -c '
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import os
import json
credentials = service_account.Credentials.from_service_account_info(
json.loads("""${{ secrets.GCP_SA_KEY }}"""),
["<https://www.googleapis.com/auth/cloud-platform>"]
)
request = Request()
credentials.refresh(request)
# Set the headers as a JSON string
headers = json.dumps({"Authorization": f"Bearer {credentials.token}"})
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={headers}\n")
'
# Set up Prefect
- name: Setup Prefect
run: |
pip install prefect
prefect config set PREFECT_API_URL=${{ secrets.PREFECT_API_URL }}
# Deploy your flow
- name: Deploy Flow
run: |
prefect deploy ... # Your deployment command here
Key differences from my previous incorrect response:
1. Uses PREFECT_CLIENT_CUSTOM_HEADERS
instead of the non-existent PREFECT_API_HEADERS
2. The headers need to be provided as a JSON string
3. This setting will be automatically picked up by all Prefect client operations
This is a much cleaner solution as it uses the officially supported method for adding custom headers to Prefect API requests. The setting will be used for all API calls made by the Prefect client, ensuring your IAP authentication is properly handled.
Would you like me to explain any part of this solution in more detail?최영범
07/19/2025, 8:08 PMprefect deploy ~
require IAP login.Marvin
07/19/2025, 8:08 PMMarvin
07/19/2025, 8:08 PMPREFECT_CLIENT_CUSTOM_HEADERS
, this suggests that either:
1. The IAP token isn't being properly generated/set, or
2. The token isn't being properly formatted in the headers
Let's try to debug this. Could you:
1. First, verify that the IAP token is being generated correctly by adding some debug output:
yaml
- name: Get IAP token and debug
run: |
python -c '
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import os
import json
credentials = service_account.Credentials.from_service_account_info(
json.loads("""${{ secrets.GCP_SA_KEY }}"""),
["<https://www.googleapis.com/auth/cloud-platform>"]
)
request = Request()
credentials.refresh(request)
# Debug print the token (careful with sensitive info in logs)
print("Token generated successfully:", bool(credentials.token))
print("Token length:", len(credentials.token) if credentials.token else 0)
# You might also want to verify the token format
headers = json.dumps({"Authorization": f"Bearer {credentials.token}"})
print("Headers JSON format:", headers)
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={headers}\n")
'
2. Could you also try making a test request to your IAP-protected endpoint using curl with the same token to verify it works:
yaml
- name: Test IAP token
run: |
TOKEN=$(python -c '
# ... same token generation code as above ...
print(credentials.token)
')
curl -v -H "Authorization: Bearer $TOKEN" ${{ secrets.PREFECT_API_URL }}/api/health
3. Additionally, you might want to try setting the audience explicitly when generating the token:
python
credentials = service_account.Credentials.from_service_account_info(
json.loads("""${{ secrets.GCP_SA_KEY }}"""),
["<https://www.googleapis.com/auth/cloud-platform>"],
target_audience=f"<https://iap.googleapis.com/projects/{PROJECT_NUMBER}/global/backend-services/{BACKEND_SERVICE_ID}>"
)
Could you:
1. Confirm if you see the token being generated successfully in the GitHub Actions logs?
2. Share if you're able to make a successful curl request to your endpoint using the same token?
3. Let me know if you're using any specific IAP audience configuration?
This will help us narrow down where exactly the authentication is failing.Marvin
07/20/2025, 7:19 AMMarvin
07/20/2025, 7:19 AMbash
# In your GitHub Actions workflow, verify no API key is set
echo "Checking for PREFECT_API_KEY..."
env | grep PREFECT_API_KEY
If it exists, remove it as it might interfere with the authentication.
2. Set up Basic Authentication:
If your self-hosted Prefect server uses basic authentication, you need to configure it in your GitHub Actions:
yaml
- name: Setup Prefect
run: |
pip install prefect
prefect config set PREFECT_API_URL=${{ secrets.PREFECT_API_URL }}
# If using basic auth, set the credentials
prefect config set PREFECT_API_AUTH_CREDENTIALS=${{ secrets.PREFECT_API_AUTH_CREDENTIALS }} # format: "username:password"
3. Verify Server Configuration:
On your Prefect server, ensure you have the correct authentication settings. If using basic auth, your server configuration should include:
python
server:
api:
auth_string: "admin:password" # Replace with your actual credentials
4. Check Headers:
Let's verify the headers being sent with your requests:
yaml
- name: Debug Headers
run: |
python -c '
import os
import json
# Print current headers
headers = os.getenv("PREFECT_CLIENT_CUSTOM_HEADERS", "{}")
print("Current headers:", headers)
# For basic auth + IAP, you might need to combine both
combined_headers = {
"Authorization": "Bearer YOUR_IAP_TOKEN",
# Add basic auth if needed
# "Authorization-Basic": "base64_encoded_credentials"
}
# Set the combined headers
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={json.dumps(combined_headers)}\n")
'
Could you please:
1. Confirm if your Prefect server is using any authentication method besides IAP (like basic auth)?
2. Check if PREFECT_API_KEY
is set in your environment?
3. Try running a simple Prefect command with debug logging enabled:
bash
PREFECT_LOGGING_LEVEL=DEBUG prefect work-pool ls
This will show more detailed information about the request and response.
The error suggests that while IAP authentication succeeded, there might be an additional layer of authentication needed for the Prefect server itself. Let me know which of these you'd like to try first or if you need more details about any of these approaches.최영범
07/20/2025, 7:21 AMMarvin
07/20/2025, 7:21 AMMarvin
07/20/2025, 7:22 AMPREFECT_API_KEY
set can cause authentication issues with a self-hosted server.
Here's what you should do:
1. Remove API Key if Set:
bash
# Check if API key is set
unset PREFECT_API_KEY
# Also remove it from any environment variables in GitHub Actions if present
2. Use Basic Authentication Instead:
For a self-hosted server, you should use basic authentication. This involves:
On the server side:
python
# In your server configuration
server:
api:
auth_string: "admin:yourpassword" # Replace with your desired credentials
On the client side (GitHub Actions):
yaml
- name: Setup Prefect
run: |
prefect config set PREFECT_API_URL=${{ secrets.PREFECT_API_URL }}
prefect config set PREFECT_API_AUTH_CREDENTIALS=${{ secrets.PREFECT_API_AUTH_CREDENTIALS }} # format: "username:password"
3. Combine with IAP:
Since you're using IAP, you'll need both the IAP token and basic auth. Your headers should look like:
yaml
- name: Set Headers
run: |
python -c '
import json
import os
# Your existing IAP token generation code here
# ...
headers = {
"Authorization": f"Bearer {credentials.token}"
}
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={json.dumps(headers)}\n")
'
The key points are:
1. Do not use PREFECT_API_KEY
with self-hosted server
2. Use basic authentication if you need authentication
3. Combine basic auth with your IAP token for the complete authentication chain
Would you like me to provide more details about setting up basic authentication for your self-hosted server, or would you prefer to try removing the API key first to see if that resolves the issue?최영범
07/20/2025, 10:40 AMMarvin
07/20/2025, 10:40 AMMarvin
07/20/2025, 10:42 AMyaml
- name: Debug Authentication
run: |
python -c '
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import os
import json
import requests
# Load credentials
credentials = service_account.Credentials.from_service_account_info(
json.loads("""${{ secrets.GCP_SA_KEY }}"""),
["<https://www.googleapis.com/auth/cloud-platform>"]
)
request = Request()
credentials.refresh(request)
# Print token info (safely)
print("Token exists:", bool(credentials.token))
print("Token length:", len(credentials.token) if credentials.token else 0)
# Test the token with a direct request
headers = {"Authorization": f"Bearer {credentials.token}"}
test_url = "${{ secrets.PREFECT_API_URL }}/api/health"
print("\nTesting direct request to health endpoint...")
response = requests.get(test_url, headers=headers)
print(f"Status code: {response.status_code}")
# Set headers for Prefect
headers_json = json.dumps(headers)
print("\nSetting headers:", headers_json)
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={headers_json}\n")
'
2. Then, let's verify the Prefect configuration:
yaml
- name: Debug Prefect Config
run: |
echo "Current Prefect configuration:"
prefect config view
# Try a simple API request with debug logging
PREFECT_LOGGING_LEVEL=DEBUG prefect work-pool ls
3. Also, could you verify your server configuration? On your self-hosted Prefect server, check:
python
# prefect server configuration
server:
api:
# Check if any authentication is configured here
auth_string: null # or "username:password" if using basic auth
4. Try setting these environment variables explicitly:
yaml
- name: Setup Prefect with Debug
env:
PREFECT_LOGGING_LEVEL: DEBUG
PREFECT_CLIENT_CONNECT_TIMEOUT: 60 # Increase timeout for debugging
run: |
# Clear any existing API key
unset PREFECT_API_KEY
# Set the API URL
prefect config set PREFECT_API_URL=${{ secrets.PREFECT_API_URL }}
# Try the command
prefect work-pool ls
Could you please:
1. Run these debug steps and share the output (excluding sensitive information)?
2. Confirm if your Prefect server is using any authentication method besides IAP?
3. Can you access the Prefect UI through your browser when using IAP authentication?
This will help us understand:
- If the IAP token is being generated correctly
- If the token is being properly passed to Prefect
- If there are any additional authentication layers we need to handle
- If there might be an issue with the token scope or permissions최영범
07/20/2025, 10:53 AMMarvin
07/20/2025, 10:53 AMMarvin
07/20/2025, 10:53 AMjson
api: {
"auth_string": null, // Basic auth is disabled
"key": null, // API key auth is disabled
...
}
Both basic authentication (auth_string: null
) and API key authentication (key: null
) are disabled on your server. This means:
1. The server is relying solely on IAP for authentication
2. No additional Prefect-level authentication is enabled
3. You should NOT set:
- PREFECT_API_AUTH_CREDENTIALS
- PREFECT_API_KEY
For your GitHub Actions workflow, you only need to handle the IAP authentication. Let's modify your setup to be cleaner:
yaml
- name: Setup IAP Authentication
run: |
python -c '
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import os
import json
# Generate IAP token
credentials = service_account.Credentials.from_service_account_info(
json.loads("""${{ secrets.GCP_SA_KEY }}"""),
["<https://www.googleapis.com/auth/cloud-platform>"]
)
request = Request()
credentials.refresh(request)
# Set only the IAP header
headers = {
"Authorization": f"Bearer {credentials.token}"
}
# Set for Prefect client
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={json.dumps(headers)}\n")
'
- name: Setup Prefect
run: |
# Only set the API URL
prefect config set PREFECT_API_URL=${{ secrets.PREFECT_API_URL }}
- name: Deploy Flow
run: |
prefect deploy ... # Your deployment command
Since your server is configured to use only IAP authentication, we should focus on ensuring the IAP token is correct and properly formatted. Could you try this simplified setup and let me know if you still get the 401 error?
Also, to verify the IAP token is working correctly, you could try a test request:
yaml
- name: Test IAP Authentication
run: |
python -c '
import requests
import os
import json
headers = json.loads(os.environ["PREFECT_CLIENT_CUSTOM_HEADERS"])
response = requests.get(
"${{ secrets.PREFECT_API_URL }}/api/health",
headers=headers
)
print(f"Status code: {response.status_code}")
print(f"Response: {response.text if response.ok else "Failed"}")
'
This will help verify if the IAP token is working correctly before attempting Prefect commands.최영범
07/20/2025, 11:52 AMMarvin
07/20/2025, 11:52 AMMarvin
07/20/2025, 11:52 AMpython
"<https://prefect-server-1030217702417.asia-northeast3.run.app/api/api/health>"
^^^^^^^^
The correct URL should be:
python
"<https://prefect-server-1030217702417.asia-northeast3.run.app/api/health>"
Let's modify the test script to use the correct URL and add more debugging information:
yaml
- name: Test IAP Authentication
run: |
python -c '
import requests
import os
import json
# Get the base URL from environment
base_url = os.environ["PREFECT_API_URL"]
# Remove any trailing /api if present
if base_url.endswith("/api"):
base_url = base_url[:-4]
# Construct the health check URL
health_url = f"{base_url}/api/health"
# Get headers
headers = json.loads(os.environ["PREFECT_CLIENT_CUSTOM_HEADERS"])
print(f"Testing URL: {health_url}")
print(f"Headers (safely): {json.dumps({k: "***" if k.lower() == "authorization" else v for k, v in headers.items()})}")
# Make request with debug info
try:
response = requests.get(
health_url,
headers=headers,
timeout=30
)
print(f"\nStatus code: {response.status_code}")
print(f"Response headers: {dict(response.headers)}")
print(f"Response: {response.text if response.ok else "Failed"}")
except Exception as e:
print(f"Request failed with error: {str(e)}")
'
Also, let's verify the IAP token generation process:
yaml
- name: Debug IAP Token Generation
run: |
python -c '
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import os
import json
# Load credentials
credentials = service_account.Credentials.from_service_account_info(
json.loads(open(os.environ["GOOGLE_APPLICATION_CREDENTIALS"]).read()),
["<https://www.googleapis.com/auth/cloud-platform>"]
)
# Add IAP audience
credentials = credentials.with_audience(
f"<https://iap.googleapis.com/projects/{os.environ[>"GOOGLE_CLOUD_PROJECT"]}/global/backendServices/YOUR_BACKEND_SERVICE_ID"
)
request = Request()
credentials.refresh(request)
print("Token generation successful")
print(f"Token type: {type(credentials.token)}")
print(f"Token length: {len(credentials.token) if credentials.token else 0}")
# Set headers with the new token
headers = {
"Authorization": f"Bearer {credentials.token}"
}
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={json.dumps(headers)}\n")
'
Could you please:
1. Replace YOUR_BACKEND_SERVICE_ID
with your actual backend service ID from Cloud Run
2. Run both scripts
3. Share the output (excluding any sensitive information)
This will help us:
1. Verify the correct URL is being used
2. Ensure the IAP token is being generated with the correct audience
3. See any additional error information from the server
Also, could you verify:
1. Can you access the Prefect UI through your browser?
2. What's the exact value of your PREFECT_API_URL
environment variable?최영범
07/20/2025, 11:55 AMMarvin
07/20/2025, 11:55 AMMarvin
07/20/2025, 11:55 AMyaml
- name: Generate IAP Token
run: |
python -c '
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import os
import json
# Load credentials
credentials = service_account.Credentials.from_service_account_info(
json.loads(open(os.environ["GOOGLE_APPLICATION_CREDENTIALS"]).read()),
["<https://www.googleapis.com/auth/cloud-platform>"]
)
# For Cloud Run with IAP, the audience format is:
# <https://YOUR_REGION-run.googleapis.com/projects/PROJECT_NUMBER/locations/REGION/services/SERVICE_NAME>
project_number = "1030217702417" # From your URL
region = "asia-northeast3" # From your URL
service_name = "prefect-server" # From your URL
audience = f"https://{region}-<http://run.googleapis.com/projects/{project_number}/locations/{region}/services/{service_name}|run.googleapis.com/projects/{project_number}/locations/{region}/services/{service_name}>"
# Add the correct audience
credentials = credentials.with_audience(audience)
request = Request()
credentials.refresh(request)
print("Token details:")
print(f"Audience: {audience}")
print(f"Token length: {len(credentials.token) if credentials.token else 0}")
# Set headers with the new token
headers = {
"Authorization": f"Bearer {credentials.token}"
}
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={json.dumps(headers)}\n")
'
- name: Test IAP Authentication
run: |
python -c '
import requests
import os
import json
url = "<https://prefect-server-1030217702417.asia-northeast3.run.app/api/health>"
headers = json.loads(os.environ["PREFECT_CLIENT_CUSTOM_HEADERS"])
print(f"Testing URL: {url}")
print(f"Headers present: {list(headers.keys())}")
response = requests.get(
url,
headers=headers,
timeout=30
)
print(f"\nStatus code: {response.status_code}")
if not response.ok:
print(f"Response headers: {dict(response.headers)}")
print(f"Response body: {response.text}")
'
The key changes are:
1. Added proper audience format for Cloud Run with IAP
2. Using the correct project number, region, and service name from your URL
3. Added more detailed error output
Could you try this updated version? Also, to verify:
1. Can you confirm if these values are correct:
- Project number: 1030217702417
- Region: asia-northeast3
- Service name: prefect-server
2. Can you access the Prefect UI through your browser with IAP authentication?
If you're able to access the UI but the API calls are failing, we might need to check:
1. The IAM roles assigned to the service account
2. The OAuth consent screen configuration
3. The IAP settings in Cloud Console최영범
07/20/2025, 2:34 PM- id: 'auth'
uses: 'google-github-actions/auth@v2'
with:
project_id: '***'
workload_identity_provider: 'projects/***/locations/global/workloadIdentityPools/ml-team-pool/providers/github-action'
service_account: 'ml-github-sa@***.<http://iam.gserviceaccount.com|iam.gserviceaccount.com>'
token_format: 'id_token'
id_token_audience: '<https://prefect-server>-***.<http://asia-northeast3.run.app|asia-northeast3.run.app>'
Marvin
07/20/2025, 2:34 PMMarvin
07/20/2025, 2:34 PMgoogle-github-actions/auth
with token_format: 'id_token'
, we need to set the token differently in the Prefect headers. Here's how we should modify your workflow:
yaml
- id: 'auth'
uses: 'google-github-actions/auth@v2'
with:
project_id: '***'
workload_identity_provider: 'projects/***/locations/global/workloadIdentityPools/ml-team-pool/providers/github-action'
service_account: 'ml-github-sa@***.<http://iam.gserviceaccount.com|iam.gserviceaccount.com>'
token_format: 'id_token'
id_token_audience: '<https://prefect-server>-***.<http://asia-northeast3.run.app|asia-northeast3.run.app>'
- name: Set Prefect Headers
run: |
python -c '
import os
import json
# Get the ID token from the previous step
id_token = """${{ steps.auth.outputs.id_token }}"""
# Set the headers with the ID token
headers = {
"Authorization": f"Bearer {id_token}"
}
# Set for Prefect client
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"PREFECT_CLIENT_CUSTOM_HEADERS={json.dumps(headers)}\n")
'
- name: Test IAP Authentication
run: |
python -c '
import requests
import os
import json
url = "${{ secrets.PREFECT_API_URL }}/health"
headers = json.loads(os.environ["PREFECT_CLIENT_CUSTOM_HEADERS"])
print(f"Testing URL: {url}")
print(f"Headers present: {list(headers.keys())}")
try:
response = requests.get(
url,
headers=headers,
timeout=30
)
print(f"\nStatus code: {response.status_code}")
if not response.ok:
print(f"Response headers: {dict(response.headers)}")
except Exception as e:
print(f"Request failed: {str(e)}")
'
- name: Setup Prefect
run: |
prefect config set PREFECT_API_URL=${{ secrets.PREFECT_API_URL }}
Key changes:
1. Using the ID token directly from the google-github-actions/auth
output
2. Setting it as a Bearer token in the Authorization
header
3. No need for additional token generation since we're using the ID token from the auth action
Also, please verify:
1. The IAP settings in Cloud Console match the id_token_audience
you've configured
2. The service account has the necessary IAP-secured Web App User role (roles/iap.httpsResourceAccessor
)
3. The OAuth consent screen is properly configured for the domain
Would you like me to provide more details about any of these aspects?최영범
07/20/2025, 2:37 PMPREFECT_CLIENT_CUSTOM_HEADERS
from github action like below
- name: Set Prefect Custom Headers with ID Token
run: |
unset PREFECT_API_KEY
echo "PREFECT_CLIENT_CUSTOM_HEADERS={\"Authorization\":\"Bearer ${{ steps.auth.outputs.id_token }}\"}" >> $GITHUB_ENV
Marvin
07/20/2025, 2:37 PMMarvin
07/20/2025, 2:37 PMPREFECT_CLIENT_CUSTOM_HEADERS
correctly. Let's debug why it's still returning a 401 error.
First, let's verify the headers are being set and passed correctly:
yaml
- name: Debug Headers and Test Connection
run: |
python -c '
import os
import json
import requests
from urllib.parse import urljoin
# Debug current environment
headers = json.loads(os.environ.get("PREFECT_CLIENT_CUSTOM_HEADERS", "{}"))
api_url = os.environ.get("PREFECT_API_URL", "")
print("Debug Information:")
print(f"API URL: {api_url}")
print(f"Headers present: {list(headers.keys())}")
print(f"Authorization header starts with: {headers.get('Authorization', '')[:20]}...")
# Test basic endpoint
health_url = urljoin(api_url, "health")
print(f"\nTesting health endpoint: {health_url}")
try:
response = requests.get(
health_url,
headers=headers,
timeout=30
)
print(f"Status code: {response.status_code}")
print(f"Response headers: {dict(response.headers)}")
if response.status_code == 401:
print("\nPossible issues:")
print("1. Token might be expired")
print("2. Token audience might not match")
print("3. IAP might be expecting different header format")
except Exception as e:
print(f"Request failed: {str(e)}")
'
Also, let's verify the complete workflow order:
yaml
- uses: 'google-github-actions/auth@v2'
id: 'auth'
with:
project_id: '***'
workload_identity_provider: 'projects/***/locations/global/workloadIdentityPools/ml-team-pool/providers/github-action'
service_account: 'ml-github-sa@***.<http://iam.gserviceaccount.com|iam.gserviceaccount.com>'
token_format: 'id_token'
id_token_audience: '<https://prefect-server>-***.<http://asia-northeast3.run.app|asia-northeast3.run.app>'
- name: Set Prefect Custom Headers with ID Token
run: |
unset PREFECT_API_KEY
echo "PREFECT_CLIENT_CUSTOM_HEADERS={\"Authorization\":\"Bearer ${{ steps.auth.outputs.id_token }}\"}" >> $GITHUB_ENV
- name: Configure Prefect
run: |
prefect config set PREFECT_API_URL=${{ secrets.PREFECT_API_URL }}
# Debug Prefect configuration
echo "Current Prefect Configuration:"
prefect config view
Could you also try making a direct curl request to verify the token:
yaml
- name: Test with curl
run: |
# Test the health endpoint
curl -v -H "Authorization: Bearer ${{ steps.auth.outputs.id_token }}" ${{ secrets.PREFECT_API_URL }}/health
Questions to help debug:
1. Can you confirm that the id_token_audience
exactly matches your Cloud Run service URL?
2. Are you able to access other IAP-protected endpoints using the same token?
3. Have you tried accessing the Prefect API endpoint directly through curl or Postman with the same token to verify it works outside of Prefect?
4. Could you check the Cloud Run logs to see if there are any specific error messages when the 401 occurs?
This will help us understand:
- If the token is being properly generated and formatted
- If the token is valid for your IAP configuration
- If there's an issue specific to how Prefect is handling the headers
- If there might be an issue with the IAP configuration itself최영범
07/20/2025, 2:42 PM> GET /api/health HTTP/2
> Host: prefect-server-***.<http://asia-northeast3.run.app|asia-northeast3.run.app>
> User-Agent: curl/8.5.0
> Accept: */*
> Authorization: ***
>
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [282 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [282 bytes data]
* old SSL session ID is stale, removing
{ [5 bytes data]
< HTTP/2 401
< x-goog-iap-generated-response: true
< date: Sun, 20 Jul 2025 14:40:48 GMT
< content-type: text/html
< server: Google Frontend
< content-length: 57
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
<
{ [57 bytes data]
100 57 100 57 0 0 243 0 --:--:-- --:--:-- --:--:-- 243
* Connection #0 to host prefect-server-***.<http://asia-northeast3.run.app|asia-northeast3.run.app> left intact
Invalid IAP credentials: JWT 'email' claim isn't a string
Marvin
07/20/2025, 2:42 PMMarvin
07/20/2025, 2:42 PMInvalid IAP credentials: JWT 'email' claim isn't a string
. This indicates that the ID token being generated doesn't have the email claim that IAP expects.
When using IAP with ID tokens, we need to include specific claims in the token. Let's modify your auth configuration:
yaml
- uses: 'google-github-actions/auth@v2'
id: 'auth'
with:
project_id: '***'
workload_identity_provider: 'projects/***/locations/global/workloadIdentityPools/ml-team-pool/providers/github-action'
service_account: 'ml-github-sa@***.<http://iam.gserviceaccount.com|iam.gserviceaccount.com>'
token_format: 'id_token'
id_token_audience: '<https://prefect-server>-***.<http://asia-northeast3.run.app|asia-northeast3.run.app>'
# Add these claims that IAP requires
id_token_include_email: true
additional_claims: |
{
"email": "${{ vars.SERVICE_ACCOUNT_EMAIL }}",
"sub": "${{ vars.SERVICE_ACCOUNT_EMAIL }}"
}
- name: Set Prefect Custom Headers with ID Token
run: |
unset PREFECT_API_KEY
echo "PREFECT_CLIENT_CUSTOM_HEADERS={\"Authorization\":\"Bearer ${{ steps.auth.outputs.id_token }}\"}" >> $GITHUB_ENV
Key changes:
1. Added id_token_include_email: true
to include the email claim
2. Added additional_claims
to explicitly set the email and subject claims
3. Using the service account email as both the email and subject claims
Make sure to:
1. Set the SERVICE_ACCOUNT_EMAIL
variable in your GitHub repository variables with the full service account email (ml-github-sa@***.<http://iam.gserviceaccount.com|iam.gserviceaccount.com>
)
2. Verify that the service account has the roles/iap.httpsResourceAccessor
role
This should resolve the "JWT 'email' claim isn't a string" error from IAP. Would you like to try this modified configuration?최영범
07/20/2025, 2:46 PM> GET /api/health HTTP/2
> Host: prefect-server-***.<http://asia-northeast3.run.app|asia-northeast3.run.app>
> User-Agent: curl/8.5.0
> Accept: */*
> Authorization: ***
>
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [282 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [282 bytes data]
* old SSL session ID is stale, removing
{ [5 bytes data]
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0< HTTP/2 401
< x-goog-iap-generated-response: true
< date: Sun, 20 Jul 2025 14:43:57 GMT
< content-type: text/html
< server: Google Frontend
< content-length: 68
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
<
{ [68 bytes data]
100 68 100 68 0 0 177 0 --:--:-- --:--:-- --:--:-- 177
* Connection #0 to host prefect-server-***.<http://asia-northeast3.run.app|asia-northeast3.run.app> left intact
Invalid IAP credentials: Invalid bearer token. Invalid JWT audience.
Marvin
07/20/2025, 2:46 PMMarvin
07/20/2025, 2:47 PMInvalid IAP credentials: Invalid bearer token. Invalid JWT audience
. This means we need to adjust the id_token_audience
format.
For Google Cloud IAP, the audience should follow a specific format. Let's modify your auth configuration:
yaml
- uses: 'google-github-actions/auth@v2'
id: 'auth'
with:
project_id: '***'
workload_identity_provider: 'projects/***/locations/global/workloadIdentityPools/ml-team-pool/providers/github-action'
service_account: 'ml-github-sa@***.<http://iam.gserviceaccount.com|iam.gserviceaccount.com>'
token_format: 'id_token'
id_token_include_email: true
# Update the audience format for IAP
id_token_audience: '/projects/YOUR_PROJECT_NUMBER/global/backendServices/YOUR_BACKEND_SERVICE_ID'
- name: Set Prefect Custom Headers with ID Token
run: |
unset PREFECT_API_KEY
echo "PREFECT_CLIENT_CUSTOM_HEADERS={\"Authorization\":\"Bearer ${{ steps.auth.outputs.id_token }}\"}" >> $GITHUB_ENV
To get the correct audience value:
1. Go to the Google Cloud Console
2. Navigate to Security → Identity-Aware Proxy
3. Find your Cloud Run service
4. Click on the "OAuth 2.0 Client ID" link
5. In the URL or details, you should see the Backend Service ID
Alternatively, you can get it via gcloud:
bash
gcloud iap oauth-clients list --project=YOUR_PROJECT_ID
The audience format should be:
/projects/PROJECT_NUMBER/global/backendServices/BACKEND_SERVICE_ID
Could you:
1. Get your Backend Service ID from the IAP settings
2. Update the id_token_audience
with the correct format
3. Try the request again
This should resolve the "Invalid JWT audience" error. Would you like me to help you find the correct Backend Service ID?