OAuth2 authentication for a Google Cloud Functions
Tags: gcp, googlecloud, serverless, auth
Published: 2022-02-26 20:00:36

---
title: OAuth2 authentication for a Google Cloud Functions 
published: true
description: Cloud function setup with authentication
tags: gcp, googlecloud, serverless, auth
//cover_image: https://direct_url_to_image.jpg
---

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wx936fl02uvzt2ig0tnf.png)
Cloud Functions supports IAM authentication but can I authenticate the service using an OAuth2 provider?

## How does it work?
![Auth Flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwbhb6xas475inxockqm.png)
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](https://cloud.google.com/endpoints/docs/openapi/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:
* [Secure traffic to a service with the gcloud CLI](https://cloud.google.com/api-gateway/docs/secure-traffic-gcloud)
* [Using a custom method to authenticate users](https://cloud.google.com/endpoints/docs/openapi/authenticating-users-custom)
* [OpenAPI extensions ](https://cloud.google.com/endpoints/docs/openapi/openapi-extensions)
* [JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519)
* [OAuth 2.0](https://datatracker.ietf.org/doc/html/rfc6749)