Introduction: Securing Your API Endpoints

Welcome back! In the previous lesson, you learned how to keep your application's secrets safe using secure credential management. Now, let's move on to another critical part of application security: protecting your API endpoints.

APIs are the gateways to your application's data and features. If you leave them unprotected, anyone can access or misuse your resources. In this lesson, you will learn how to control who can access your Cloud Functions endpoints using Firebase Authentication, Google's recommended authentication service. By the end, you will have built a real token-based authentication system and seen it in action.

Quick Recall: Cloud Functions and HTTP Requests

Before we dive in, let's quickly remind ourselves how Cloud Functions handle HTTP requests.

  • Cloud Functions are small pieces of code that run in the cloud. They can be triggered by HTTP requests.
  • When someone sends a request to your function's endpoint, Cloud Functions receives the HTTP request and passes it to your function.
  • Your function processes the request and returns a response.

For example, when someone visits your API endpoint, Cloud Functions calls your function with the request details, which handles the logic and sends back a result.

This flow is important to remember because we will be adding a security check right before your function processes the request.

What Is Firebase Authentication?

Firebase Authentication is Google's identity platform that validates user tokens for you. When a user signs in through Firebase, they receive an ID token (a JWT). This token proves the user's identity and can be validated by your Cloud Functions.

Here's how it works in simple terms:

  1. A user signs in through Firebase Auth and receives an ID token.
  2. The user sends a request to your Cloud Function with the token in the Authorization header.
  3. Your function validates the token with Firebase.
  4. If the token is valid, the request is allowed and your function processes it.
  5. If the token is missing or invalid, the request is denied.

A token is sent in the Authorization header, like this:

Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

Here's a visual representation of this authentication flow:

User Signs In ──► Firebase Issues Token


Client Request (Authorization: Bearer <token>)


Authentication Function ◄─── Validates with Firebase

   ┌───┴───┐
   ▼       ▼
 Valid   Invalid ──► 401


Process Request ──► 200 Response
Setting Up Firebase Authentication

Before we can validate tokens, we need to set up Firebase Authentication. Here's a quick setup:

# Install Firebase Admin SDK dependency
pip install firebase-admin

# Add to requirements.txt
echo "firebase-admin>=6.0.0" >> requirements.txt

For testing, create a test user in the Firebase Console:

  1. Navigate to AuthenticationUsers
  2. Click Add user
  3. Enter email and password (e.g., test@example.com / testpass123)

You'll also need your Firebase Web API Key from Project Settings for getting test tokens.

Building the Authentication Function Step-by-Step

Let's build the authentication function together, step by step. We'll create a Cloud Function that validates Firebase tokens before processing requests.

1. Initializing Firebase and Extracting the Token

First, we need to initialize Firebase Admin SDK and extract the token from the request:

import firebase_admin
from firebase_admin import auth
import json

# Initialize Firebase Admin (only once)
if not firebase_admin._apps:
    firebase_admin.initialize_app()

def extract_token(request):
    """Extract Bearer token from Authorization header"""
    headers = request.headers
    auth_header = headers.get("Authorization") or headers.get("authorization")
    
    if not auth_header or not auth_header.startswith("Bearer "):
        return None
    
    return auth_header.split(" ")[1]
  • firebase_admin.initialize_app() uses Application Default Credentials automatically in Cloud Functions.
  • We check both uppercase and lowercase Authorization headers.
  • We extract the token after Bearer .

Example Output:

Extracted token: eyJhbGciOiJSUzI1NiIs...
2. Validating the Firebase ID Token

Now, let's validate the token with Firebase:

def verify_firebase_token(token):
    """Verify Firebase ID token and return user info"""
    try:
        decoded_token = auth.verify_id_token(token)
        print(f"✅ Token valid - User: {decoded_token['uid']}")
        return decoded_token
    except Exception as e:
        print(f"❌ Token validation failed: {str(e)}")
        return None
  • auth.verify_id_token() validates the token signature and expiration with Firebase.
  • Returns user information if valid, None otherwise.
  • Handles expired, invalid, and malformed tokens automatically.

Example Output:

✅ Token valid - User: abc123xyz

or

❌ Token validation failed: Token has expired
3. Returning the Authorization Result

Finally, we return the appropriate response based on validation:

def create_response(data, status_code=200):
    """Helper to create JSON responses"""
    return (
        json.dumps(data),
        status_code,
        {"Content-Type": "application/json"}
    )

# If not authorized
if not user_info:
    return create_response(
        {"error": "Unauthorized"},
        401
    )
  • If not authorized, we return a 401 Unauthorized response.
  • json.dumps(...) converts our Python dictionary to a JSON string.
  • If authorized, the function continues to process the request normally.

Example Output:

{"error": "Unauthorized"}
Protecting and Responding from Your Cloud Function

Once your authentication check is set up, you can protect your Cloud Function endpoint. The function should process requests only if the authentication check passes.

Here's a complete protected Cloud Function:

import firebase_admin
from firebase_admin import auth
import json

# Initialize Firebase Admin
if not firebase_admin._apps:
    firebase_admin.initialize_app()

def create_response(data, status_code=200):
    return (json.dumps(data), status_code, {"Content-Type": "application/json"})

def extract_token(request):
    headers = request.headers
    auth_header = headers.get("Authorization") or headers.get("authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        return None
    return auth_header.split(" ")[1]

def verify_firebase_token(token):
    try:
        return auth.verify_id_token(token)
    except Exception as e:
        print(f"Token validation failed: {str(e)}")
        return None

def protected_handler(request):
    # Extract and verify token
    token = extract_token(request)
    if not token:
        return create_response({"error": "Unauthorized"}, 401)
    
    user_info = verify_firebase_token(token)
    if not user_info:
        return create_response({"error": "Unauthorized"}, 401)
    
    # If authorized, process the request
    response = {
        "message": "🎉 You are authenticated!",
        "user_id": user_info['uid']
    }
    return create_response(response, 200)
  • If the request is authenticated, this function returns a success message with status code 200.
  • If the request is not authenticated, it returns a 401 Unauthorized response.

Example Output:

{
  "message": "🎉 You are authenticated!",
  "user_id": "abc123xyz"
}
Getting Test Tokens

To test your protected endpoint, you need a Firebase ID token. Here's how to get one using Python:

import requests

def get_firebase_token(email, password, api_key):
    """Sign in and get ID token"""
    url = f"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}"
    payload = {"email": email, "password": password, "returnSecureToken": True}
    
    response = requests.post(url, json=payload)
    data = response.json()
    
    if 'idToken' in data:
        print(f"✅ Got token: {data['idToken'][:50]}...")
        return data['idToken']
    else:
        print(f"❌ Error: {data.get('error', {}).get('message')}")
        return None

# Usage - find your Web API Key in Firebase Console → Project Settings
api_key = "YOUR_FIREBASE_WEB_API_KEY"
token = get_firebase_token("test@example.com", "testpass123", api_key)

Example Output:

✅ Got token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjY4MzY5NzFjZTU...

Note: In a real environment, you should never log a token. Logging tokens can expose sensitive user credentials in your logs, making them accessible to anyone with log access. This can lead to security breaches if tokens are stolen and used to impersonate users, so always treat tokens as secrets and avoid printing or storing them in logs.

Deploying and Testing Your Solution
Summary And What's Next

In this lesson, you learned how to secure your Cloud Functions endpoints using Firebase Authentication, Google Cloud's recommended authentication solution. You built a function to validate Firebase ID tokens, returned the correct response, and protected your endpoint so only authenticated requests get through. You also saw how to get test tokens and deploy your solution using the gcloud CLI.

This approach is production-ready and follows Google Cloud best practices, providing secure JWT validation, built-in token expiration, and integration with Google's identity platform.

Next, you'll get hands-on practice by completing the code and testing your own Firebase-authenticated Cloud Function. This will help you reinforce what you've learned and prepare you for building more secure and robust APIs in the future. Good luck!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal