DEV Community

Pier
Pier

Posted on

OAuth2 authentication for a Google Cloud Functions

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!

Auth options
Cloud Functions supports IAM authentication but can I authenticate the service using an OAuth2 provider?

How does it work?

Auth Flow
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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Test!

Retrieve the gateway address

gcloud api-gateway gateways describe my-gateway \
  --location=us-central1 --project=my-project
Enter fullscreen mode Exit fullscreen mode

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 \
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

Deep Dive

If you want to learn more check out these links:

Top comments (0)