0

I have hosted a React app in firebase. And created Google Cloud Functions and deployed directly to GCP Cloud Functions.

API Gateway is setup to do APIKey/JWT validations before calling the functions. I added the Cloud Functions Invoker role to allAuthenticatedUsers. But when I make request via curl it fails (401: Unauthorized) with below message.

Message: Your client does not have permission to the requested URL

I am using the token from user.getIdToken() and making call with below curl command

curl --request GET --header "Authorization: Bearer ${TOKEN}" $AUTH_GATEWAY_URL

I get the valid api response, if I add the Cloud Functions Invoker role to allUsers .

I referred couple of SO questions below

SO Q1: Issue was using access token, instead of IdToken.

SO Q2: In my case, the JWT issuer is matching.

I am not sure what I am missing. Any suggestions would be greatly appreciated.

new_programmer
  • 840
  • 3
  • 12
  • 22
  • I think the issue you are having is that the Identity Token does not have the correct audience `aud` set. This needs to be the Cloud Function URL (including the function name). – John Hanley Mar 10 '21 at 05:58
  • Thanks @John Hanley. I checked the JWT token it has the `aud` same as `x-google-audiences` in swagger (same as app name). `iss` matching to the `x-google-issuer` provided in the swagger. – new_programmer Mar 10 '21 at 06:03

1 Answers1

1

If you deploy your Cloud Functions without the help of firebase (in this case you can take advantage of the callable function to reduce boilerplate, mentioned in SO Q2).

But it force you to use NodeJS.

When using GCP Cloud Functions it does not know which service to authenticate JWT to so we have to do it our self.

package middleware

import (
    "context"
    "net/http"
    "strings"

    "firebase.google.com/go/auth"
    "github.com/gin-gonic/gin"
)

const (
    UID       = "uid"
    UserEmail = "user-email"
)

type header struct {
    Authorization string `header:"Authorization"`
}

func ValidateFirebaseAuthToken(auth *auth.Client) gin.HandlerFunc {
    return func(c *gin.Context) {
        defer c.Next()

        var h header
        c.ShouldBindHeader(&h)
        splitToken := strings.Split(h.Authorization, "Bearer ")
        if h.Authorization == "" || len(splitToken) < 2 {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "must provide Authorization Bearer",
            })
            return
        }
        token := splitToken[1]
        tk, err := auth.VerifyIDToken(context.Background(), token)
        if err != nil {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
                "error": "cannot verify token",
            })
            return
        }
        c.Set(UID, tk.UID)
        if email, ok := tk.Claims["email"].(string); ok {
            c.Set(UserEmail, email)
        }
    }
}

I normally use the above in my Go function. Notice the line:

        tk, err := auth.VerifyIDToken(context.Background(), token)

Which will verify the Bearer <token> with firebase auth.

So I have the Cloud Functions access to allUsers and do this firebase Authentication layer in code.

For other languages you can have a look at this link https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_the_firebase_admin_sdk

Chop TRAN
  • 487
  • 4
  • 9
  • Thank you @Chop TRAN for looking into this. I am doing the JWT validation in APIGateway, as mentioned here https://cloud.google.com/endpoints/docs/openapi/authenticating-users-firebase (openapi v2). Isn't the above code to validate/get claims from the token in application/function? – new_programmer Mar 10 '21 at 05:07
  • If you doing the JWT at the API Gateway (GCP Endpoints in this case) It will validate and the request will be forward to your service (your Cloud Functions). Here the important part is to config the Endpoints correctly in your project. And modify your code to decode the header `X-Endpoint-API-UserInfo` using base64 like mentioned in the document. – Chop TRAN Mar 10 '21 at 11:13