Cloud Run is a serverless computing platform that allows you to deploy containerized applications with automatic scaling. However, securing these applications is crucial, especially when exposing them to the internet. OAuth2 and OpenID Connect (OIDC) provide robust authentication and authorization mechanisms to protect Cloud Run services. This blog post walks through implementing OAuth2 and OIDC authentication for a Cloud Run service.
What is OAuth2 and OpenID Connect?
- OAuth2: It is a standard authorization framework, giving third-party applications limited access to a web service without revealing the user’s credentials.
- OpenID Connect (OIDC): This is an identity layer on top of OAuth2 that offers authentication in addition to authorization.
You can use OIDC to authenticate your users to gain identity tokens proving the identity of users and is thus ideal for securing Cloud Run services.
Advantages of using OAuth2 and OpenID Connect (OIDC):
1. Enhanced Security
- Normalized Authentication: OAuth2 and OIDC standard industry mechanisms for authenticating and authorizing securely.
- Token-Based Security: They use access and ID tokens, reducing the need for storing sensitive user credentials.
- Single Sign-On (SSO): OIDC enables SSO, allowing users to log in once and access multiple services.
2. Seamless Integration with Identity Providers
- Works with Google Identity, Okta, Auth0, Azure AD, and other OAuth2/OIDC providers.
- Supports federated identity, allowing users to authenticate with social logins (Google, Facebook, GitHub, etc.).
3. Simplified Authentication for Cloud Run Services
- Identity-Aware Proxy (IAP): Google Cloud’s IAP can handle OAuth2/OIDC authentication without modifying your app.
- Automatic Token Verification: Cloud Run services can easily verify Google-issued ID tokens without additional libraries.
4. Scalability and Performance
- Stateless Authentication: No need to maintain session storage since OAuth2 tokens handle authentication.
- Efficient API Access: Using OAuth2, Cloud Run can securely call APIs (e.g., Google APIs) without requiring user credentials.
5. Role-Based Access Control (RBAC)
- OIDC allows you to extract user roles and permissions from identity providers, enabling fine-grained access control.
6. Reduced Development Overhead
- Google Cloud offers built-in support for OAuth2/OIDC, reducing the need for manual authentication handling.
- Managed services like Firebase Authentication or Cloud Identity Platform can be easily integrated.
7. Improved User Experience
- Enables secure and password-less authentication with providers like Google.
- Users can authenticate across multiple applications without re-entering credentials.
8. Compatibility with Service-to-Service Authentication
- OAuth2 client credentials flow allows Cloud Run services to securely communicate with other APIs or services.
- Google Cloud Service Accounts support workload identity federation for secure access.
Authenticating with Cloud Run
1. Select an Identity Provider (IdP)
Some well-known identity providers that support OAuth2 and OIDC are as follows:
- Google Identity Platform
- Auth0
- Okta
- Microsoft Azure AD
- Keycloak
For the purpose of this tutorial, we will use Google Identity Platform as the IdP.
2. Enable Identity-Aware Proxy (IAP)
Google’s Identity-Aware Proxy (IAP) helps you limit access to your Cloud Run service using Google authentication.
Steps to Enable IAP:
1. Enable IAP in your Google Cloud project:
gcloud services enable iap.googleapis.com
What Does It Do?
- This command activates the IAP API (iap.googleapis.com) for your Google Cloud project.
2. Deploy your Cloud Run service with authentication enabled:
gcloud run deploy my-service \
--add-cloudsql-instances=my-instance \
--service-account=my-service-account@my-project.iam.gserviceaccount.com
What Does It Do?
- gcloud run deploy my-service
- Deploys a new Cloud Run service named my-service.
- If the service already exists, this command updates it.
- –add-cloudsql-instances=my-instance
- Connects the Cloud Run service to a Cloud SQL instance named my-instance.
- This is required if your application needs to access a database hosted in Cloud SQL.
- –service-account=my-service-account@my-project.iam.gserviceaccount.com
- Assigns a specific service account to the Cloud Run service.
- This service account must have appropriate IAM roles to access Cloud SQL, such as roles/cloudsql.client.
3. Configure IAP by restricting access to authorized users:
gcloud projects add-iam-policy-binding my-project \
--member=user:example@gmail.com \
--role=roles/iap.httpsResourceAccessor
What Does It Do?
- gcloud projects add-iam-policy-binding my-project
- Adds an IAM policy binding (permission) to the Google Cloud project named my-project.
- Replace my-project with your actual Google Cloud project ID.
- –member=user:example@gmail.com
- Specifies the user who will receive the permission.
- –role=roles/iap.httpsResourceAccessor
- Grants the “IAP-secured Web App User” role (roles/iap.httpsResourceAccessor).
- This allows the specified user to access web applications protected by Identity-Aware Proxy (IAP).
- Without this role, the user will be blocked by IAP when trying to access a Cloud Run, App Engine, or Compute Engine backend.
3. Implement OAuth2 in Your Application
If you are not using IAP and want to implement OAuth2 authentication manually, follow these steps:
a. Register Your Application with Google OAuth
- Go to the Google Cloud Console.
- Navigate to APIs & Services > Credentials.
- Create a new OAuth 2.0 Client ID.
- Configure the Authorized Redirect URIs (e.g., https://your-service-url/callback).
- Note down the Client ID and Client Secret.
b. Implement OAuth2 Flow in Your Application
Your application should handle the OAuth2 authorization flow:
1. Redirect Users to the Authorization URL
from flask import Flask, redirect, request
import requests
import os
app = Flask(__name__)
CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
REDIRECT_URI = https://your-service-url/callback
AUTH_URL = https://accounts.google.com/o/oauth2/auth
@app.route("/login")
def login():
auth_url = (f"{AUTH_URL}?client_id={CLIENT_ID}&response_type=code"
f"&redirect_uri={REDIRECT_URI}&scope=openid email profile")
return redirect(auth_url)
What Does It Do?
1. Import Required Modules
from flask import Flask, redirect, request
import requests
import os
- Import all the necessary libraries.
2. Initialize Flask App
app = Flask(__name__)
- Creates an instance of a Flask web application.
3. Define OAuth2 Configuration Variables
CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
REDIRECT_URI = https://your-service-url/callback
AUTH_URL = "https://accounts.google.com/o/oauth2/auth" - CLIENT_ID: Retrieved from environment variables (GOOGLE_CLIENT_ID). This is the unique identifier for your app registered with Google.
- REDIRECT_URI: The callback URL where Google will redirect after authentication. This must be pre-configured in the Google OAuth settings.
- AUTH_URL: The Google OAuth2 authorization endpoint, used to initiate the authentication process.
4. Define the /login Route
@app.route("/login")
def login():
auth_url = (f"{AUTH_URL}?client_id={CLIENT_ID}&response_type=code"
f"&redirect_uri={REDIRECT_URI}&scope=openid email profile")
return redirect(auth_url) - Defines a /login route that users visit to start the authentication process.
- Builds the authorization URL dynamically:
- client_id={CLIENT_ID} → Specifies the registered application.
- response_type=code → Requests an authorization code (needed for exchanging tokens).
- redirect_uri={REDIRECT_URI} → Specifies where Google should send the user after authentication.
- scope=openid email profile → Requests access to the user’s OpenID, email, and profile information.
- Redirects the user to Google’s authentication page, where they can log in and approve access.
2. Handle the Callback and Exchange Code for Tokens
TOKEN_URL = https://oauth2.googleapis.com/token
CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
@app.route("/callback")
def callback():
code = request.args.get("code")
data = {
"code": code,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": REDIRECT_URI,
"grant_type": "authorization_code"
}
response = requests.post(TOKEN_URL, data=data)
tokens = response.json()
return tokens
What Does It Do?
1. Define the Token Endpoint and Client Secret
TOKEN_URL = https://oauth2.googleapis.com/token
CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
- TOKEN_URL: This is the endpoint provided by Google to exchange the authorization code for access and ID tokens.
- CLIENT_SECRET: Retrieved from environment variables (GOOGLE_CLIENT_SECRET), it is used to authenticate the application during the token request.
2. Define the /callback Route
@app.route("/callback")
def callback():
code = request.args.get("code") - callback route: This is the URL where Google redirects the user after they authenticate.
- request.args.get(“code”): Extracts the code query parameter from the URL, which was sent by Google after successful login.
3. Prepare Data for Token Exchange
data = {
"code": code,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": REDIRECT_URI,
"grant_type": "authorization_code"
} - This dictionary contains the required fields for exchanging the authorization code for tokens.
4. Send a Request to Exchange the Code for Tokens
response = requests.post(TOKEN_URL, data=data)
tokens = response.json()
- requests.post(TOKEN_URL, data=data): Sends a POST request to Google’s token endpoint with the required parameters.
- response.json(): Converts the response from Google into a JSON object containing authentication tokens.
5. Return the Tokens
return tokens
- Returns the tokens received from Google, which typically include:
- access_token → Used for API access.
- id_token → Contains user identity information (JWT format).
- expires_in → Token expiration time.
- refresh_token (if offline access is enabled) → Used to get a new access token without re-authentication.
4. Validate the ID Token
Once the access and ID tokens are obtained, you should validate the ID token to ensure its authenticity:
import jwt
from google.auth.transport import requests
from google.oauth2 import id_token
@app.route(“/profile”)
def profile():
token = request.headers.get(“Authorization”).split(“Bearer “)[1]
try:
id_info = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
return id_info
except Exception as e:
return {“error”: “Invalid token”}, 401
What Does It Do?
1. Import Required Modules
import jwt
from google.auth.transport import requests
from google.oauth2 import id_token
- Import all the necessary libraries.
2. Define the /profile Route
@app.route("/profile")
def profile(): - This route handles authenticated user requests to fetch profile information.
3. Extract the Token from the Request
token = request.headers.get("Authorization").split("Bearer ")[1] - Retrieves the Authorization header from the HTTP request.
The token is expected in the format:
Authorization: Bearer <id_token>
- .split(“Bearer “)[1] extracts the actual token by removing the “Bearer ” prefix.
4. Verify the ID Token
try:
id_info = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
- id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID):
- Validates the token’s signature using Google’s public keys.
- Ensures the token was issued for the correct CLIENT_ID and is not expired.
- Parses the token into a dictionary containing user identity data.
5. Return the User’s Information if Verified
return id_info
- If verification is successful, the decoded ID token (JSON format) is returned, typically containing:
{
"sub": "1234567890",
"name": "John Doe",
"email": "johndoe@example.com",
"picture": https://lh3.googleusercontent.com/a-/AOh14...
} 6. Handle Invalid or Expired Tokens
except Exception as e:
return {"error": "Invalid token"}, 401
- If verification fails due to an invalid or expired token, it returns a 401 Unauthorized error.
5. Deploy Your Application to Cloud Run
After implementing authentication, deploy the application to Cloud Run:
gcloud run deploy my-auth-service --source .
Conclusion
Implementing OAuth2 and OpenID Connect authentication in Cloud Run enhances security by ensuring only authenticated users can access your services. You can use Google Identity Platform and IAP for seamless authentication or implement OAuth2 manually using an external IdP. By following these steps, you can effectively secure your Cloud Run applications while providing a smooth authentication experience for users.
















