0

How can I setup an API Gateway to authenticate services without using service account private key file?

Service A, for instance a cloud function, with service account A wants to make API calls to an API gateway without using service account private key file. I am wondering if it is possible that service A uses its credentials to make an API call where the API Gateway can authenticate the request?

Being able to do the above, the API gateway can be configured to allow various paths to different service accounts.

Updated

This is an example of what a client code could be

def make_jwt_request(signed_jwt, url):
    """Makes an authorized request to the endpoint"""
    headers = {
        'Authorization': 'Bearer {}'.format(signed_jwt),
        'content-type': 'application/json'
    }
    response = requests.get(url, headers=headers)

Also, the API Gateway definition would be something like

swagger: '2.0'
info:
  title: API_ID optional-string
  description: Sample API on API Gateway with a Google Cloud Functions backend
  version: 1.0.0
schemes:
  - https
produces:
  - application/json
securityDefinitions:
  google:
    authorizationUrl: ""
    flow: "implicit"
    type: "oauth2"
    x-google-issuer: "service-b@example-project.iam.gserviceaccount.com"
    x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/service-b@example-project.iam.gserviceaccount.com"
paths:
  /helloworld:
    get:
      summary: Hello World
      operationId: hello
      x-google-backend:
        address: https://us-central1-example-project.cloudfunctions.net/function-b
      security:
        - google: []
      responses:
        '200':
          description: A successful response
          schema:
            type: string
amir
  • 43
  • 2
  • 7
  • Just for clarification. You want to call an API Gateway which check the authorisation header (with a token generated by a service account key file) BUT you don't want to have a service account key file in the service A, because it's a secret and you have difficulties to handle it safely. correct? – guillaume blaquiere Feb 25 '21 at 08:21
  • @guillaumeblaquiere exactly. GCP allows something similar to what I am looking for in a cloud function to function call, https://cloud.google.com/functions/docs/securing/authenticating. – amir Feb 25 '21 at 14:19
  • Yes, it's possible to use the credential of the function A. Can you share the piece of code where you perform the call to API Gateway ? – guillaume blaquiere Feb 27 '21 at 14:35
  • @guillaumeblaquiere I updated the initial post to include a sample code – amir Feb 28 '21 at 19:09
  • How are you creating the Signed JWT? If you are trying to ask, How do I sign the JWT without a JSON private key, then you can use IAM SignBlob API. When you are using credentials from a service's metadata, the private key is not available. Google offers an API to sign data for you. At the bottom of this link are examples in most languages: https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts/signBlob – John Hanley Feb 28 '21 at 19:18
  • @JohnHanley Thanks John, that's what I was looking for – amir Mar 08 '21 at 15:33

1 Answers1

1

You are able to sign jwt's without a private key using the sign_jwt iamcredentials endpoint.

Now assuming the following security definition you can use the following python code to generate a signed jwt.

   {
        "securityDefinitions": {
            "SERVICE_ACCOUNT_NAME": {
                "authorizationUrl": "",
                "flow": "implicit",
                "type": "oauth2",
                "x-google-audiences": "SERVICE_ACCOUNT_EMAIL",
                "x-google-issuer": "SERVICE_ACCOUNT_EMAIL",
                "x-google-jwks_uri": "https://www.googleapis.com/service_accounts/v1/metadata/x509/SERVICE_ACCOUNT_EMAIL"
            }
        }
    }

python script

import time
import json 
import urllib.parse
import requests
from oauth2client.client import GoogleCredentials
from googleapiclient import discovery

def generate_jwt_payload(service_account_email):
    """Generates jwt payload"""
    now = int(time.time())
    payload = {
        'iat': now,
        "exp": now + 3600,
        'iss': service_account_email,
        'aud':  service_account_email,
        'sub': service_account_email,
        'email': service_account_email
    }
    return payload

def get_jwt(service_account_email):
    """Generate a signed JSON Web Token using a Google API Service Account."""

    credentials = GoogleCredentials.get_application_default()
    service = discovery.build('iamcredentials', 'v1', credentials=credentials)
    body = {
        "payload": json.dumps(generate_jwt_payload(service_account_email))
    }
    encoded_sa = urllib.parse.quote_plus(service_account_email)
    resp = service.projects().serviceAccounts().signJwt(name=f"projects/-/serviceAccounts/{encoded_sa}", body=body).execute()
    return resp["signedJwt"]


def make_jwt_request(service_account_email, url):
    """Makes an authorized request to the endpoint"""
    signed_jwt = get_jwt(service_account_email)
    headers = {
        'Authorization': 'Bearer {}'.format(signed_jwt),
        'content-type': 'application/json'
    }
    response = requests.get(url, headers=headers)
    return response


if __name__ == '__main__':
    print(make_jwt_request(SERVICE_ACCOUNT_EMAIL,GATEWAY_URL).text)
Austen Novis
  • 444
  • 1
  • 12
  • 30