OAuth2 authentication for a Google Cloud Functions
Pier
Posted on February 26, 2022
What is Google Cloud Functions?
Cloud Functions is the go-to tool for FaaS (functions as a service) on Google Cloud. It has a very intuitive interface for creating functions in few minutes!
Cloud Functions supports IAM authentication but can I authenticate the service using an OAuth2 provider?
How does it work?
In the first step we retrieve the token from an OAuth2 provider (I used Keycloak but you could use another one), then we call the function through the Google API Gateway. The API Gateway supports OpenAPI2 configuration, where we need to setup the authentication. In the final step the API Gateway calls, using a service account, the Cloud Function.
Create the Cloud Function
Here we can see how to create a cloud function using gcloud command line, but the same can be achieved using the Google Cloud web interface.
git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
cd nodejs-docs-samples/functions/helloworld/
# CREATE FUNCTION
gcloud functions deploy helloGET --runtime nodejs16 --trigger-http
The last command asks if we want to allow unauthenticated invocations for the new function, which we don’t, so we say no.
This creates a cloud function with IAM authentication and with an http trigger.
We need to create a GCP Service Account with the Cloud Function Invoker role, we use it later.
# CREATE SERVICE ACCOUNT
gcloud iam service-accounts create function-invoker --display-name="function-invoker"
# BINDING ROLES
gcloud projects add-iam-policy-binding my-project --member="serviceAccount:function-invoker@my-project.iam.gserviceaccount.com" --role="roles/cloudfunctions.invoker"
API Gateway setup
Enable APIs
To use API Gateway we need to enable services.
gcloud services enable apigateway.googleapis.com
gcloud services enable servicemanagement.googleapis.com
gcloud services enable servicecontrol.googleapis.com
Create the API Config
API Gateway supports the OpenAPI spec. Here we need to define the right security configuration! In order to fill the correct information in this file we need to have the cloud function http trigger (x-google-backend
) and the right security information (openapi-extensions).
An example below:
# openapi2-functions.yaml
swagger: '2.0'
info:
title: hello
description: Sample API on API Gateway with a Google Cloud Functions backend
version: 1.0.0
schemes:
- https
produces:
- application/json
paths:
/hello:
get:
summary: Greet a user
operationId: hello
x-google-backend:
address: -> cloud function address!
security:
- keycloak: []
responses:
'200':
description: A successful response
schema:
type: string
securityDefinitions:
# keycloak auth
keycloak:
type: oauth2
flow: implicit
authorizationUrl: https://KEYCLOA_INSTANCE/auth/realms/test/protocol/openid-connect/auth
x-google-issuer: https://KEYCLOA_INSTANCE/auth/realms/test
x-google-jwks_uri: https://KEYCLOA_INSTANCE/auth/realms/test/protocol/openid-connect/certs
x-google-audiences: test-auth-function-gcp
Create your Gateway
Now we can create our API and Gateway.
# CREATE API
gcloud api-gateway apis create my-api --project=my-project
# CREATE CONFIG
gcloud api-gateway api-configs create my-config \
--api=my-api --openapi-spec=openapi2-functions.yaml \
--project=my-project --backend-auth-service-account=0000000000000-compute@developer.gserviceaccount.com
# CREATE GW
gcloud api-gateway gateways create my-gateway \
--api=my-api --api-config=my-config \
--location=us-central1 --project=my-project
Test!
Retrieve the gateway address
gcloud api-gateway gateways describe my-gateway \
--location=us-central1 --project=my-project
We get the address from the response of the above command. Check for the defaultHostname
field.
Get the JWT Token
I used Keycloak for this demo, so we need to insert our token url and a valid client and user credentials.
curl --request POST \
--url https://KEYCLOAK/auth/realms/test/protocol/openid-connect/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data client_id=myclient \
--data username=myuser \
--data password=mypassword \
--data grant_type=password \
You will get a response like this:
{
"access_token": "XXXXXX",
"expires_in": 36000,
"refresh_expires_in": 1800,
"refresh_token": "XXXXXX",
"token_type": "Bearer",
"not-before-policy": 0,
"scope": "email profile"
}
We save the access token, it is needed for the next call.
Call the API
curl --request GET \
--url https://hello-gw-xxxxxxx.gateway.dev/hello \
--header 'Authorization: Bearer XXXXXX'
We get the "Hello World!" response :)
If we try to make the same call without the authentication bearer token we receive this message:
{
"message": "Jwt is missing",
"code": 401
}
Deep Dive
If you want to learn more check out these links:
Posted on February 26, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.