I am using Google ESP and on an endpoint I have authorisation with Auth0 like so:
securityDefinitions:
auth0_jwk:
authorizationUrl: "AUTH_URL"
flow: "implicit"
type: "oauth2"
x-google-issuer: "AUTHO_URL"
x-google-jwks_uri: "AUTH0_JWKS_URL"
x-google-audiences: "AUTH0_AUDIENCE"
paths:
/pluginviewservice/v1:
post:
summary: Submits a page to be analysed with auth.
operationId: pluginviewservicev1
x-google-backend:
address: APP_ENGINE_URL
path_translation: CONSTANT_ADDRESS
responses:
'200':
description: A successful response
schema:
type: string
x-security:
- {}
- auth0_jwk:
audiences:
- "AUTH0_AUDIENCE"
However what I want is, when the auth0 access token is available it populates "X-Endpoint-API-UserInfo", and when it isn't it lets it through anyway. The endpoint is designed to be accessible both to authorised and unauthorised users. I have tried adding a blank "x-security" as seen above, but that does not seem to work.
Related
I am trying to set up a GraqphQL Server, on Cloud Functions, and I want to set up an API Gateway to handle authentication with auth0 and jwt....
I have it working from the tutorial, the problem is it always requires a JWT token, where as I want some GraphQL queries to be available publicly, and if the user signs in they get more access...
From my understanding the way you do this is by using two authentications in the security settings, where one is the JWT and one is empty, however the API Gateway seems to always want the JWT token... Here is my open API spec, maybe someone has an idea?
swagger: '2.0'
info:
title: <redacted>-graphql-api
description: Basic GraphQL Open APISchema
version: 1.0.0
schemes:
- https
produces:
- application/json
securityDefinitions:
auth0_jwk:
authorizationUrl: "<redacted>"
flow: "implicit"
type: "oauth2"
# Replace YOUR-ACCOUNT-NAME with your Auth0 account name.
x-google-issuer: "<redacted>"
x-google-jwks_uri: "<redacted>"
# Optional. Replace YOUR-CLIENT-ID with your client ID
x-google-audiences: "<redacted>"
paths:
/:
post:
summary: GraphQL endpoint
operationId: gql
x-google-backend:
address: <redacted> # App URL/endpoint
responses:
'200':
description: A successful response
schema:
type: object
security:
- {}
- auth0_jwk: []
get:
summary: GraphQL Playground
operationId: playground
x-google-backend:
address: <redacted> # App URL/endpoint
responses:
'200':
description: A successful response
schema:
type: string
I'm using gcp api gateway for JWT authentication. after generating a token from my auth service and then putting it in postman I always receive this response no matter what I put in the 'aud' part of the token:
Here is my open api file:
# openapi2-run.yaml
swagger: '2.0'
info:
title: my-gateway-id
description: Sample API on API Gateway with a Cloud Run backend
version: 1.0.0
schemes:
- https
produces:
- application/json
x-google-backend:
address: https://my-cloud-run.a.run.app
jwt_audience: https://my-cloud-run.a.run.app
securityDefinitions:
jwt_auth:
authorizationUrl: ''
flow: 'implicit'
type: 'oauth2'
x-google-issuer: 'id-admin#my-project.iam.gserviceaccount.com'
x-google-jwks_uri: 'https://www.googleapis.com/service_accounts/v1/metadata/x509/id-admin#my-project.iam.gserviceaccount.com'
paths:
/:
post:
security:
- jwt_auth: []
summary: GraphQL endpoint
operationId: gql
responses:
'200':
description: A successful response
schema:
type: object
I've looked over and over the docs and can't see what's going on? thanks in advance.
You get 403 because the aud on the JWT token you've generated is not found on securityDefinitions of your API config.
To allow additional client IDs to access the backend service, you can specify the allowed client IDs in the x-google-audiences field by using comma-separated values. API Gateway then accepts the JWTs with any of the specified client IDs in the aud claim.
Go here and paste your token to see your JWT "aud" claim. If you generated the ID token using gcloud auth, the aud will most likely be a Client ID like 1234567890.apps.googleusercontent.com. But if you generated the token using your own service, then it would depend on what you've specified as a target audience.
To solve the problem, add x-google-audiences field on the securityDefinitions section and the value should match with your JWT "aud" claim.
Assuming that the aud on your JWT token is a Cloud Run service endpoint, then your API config should look like this. Feel free to check the documentation as additional reference:
x-google-backend:
address: https://my-cloud-run.a.run.app
securityDefinitions:
jwt_auth:
authorizationUrl: ''
flow: 'implicit'
type: 'oauth2'
x-google-issuer: 'id-admin#my-project.iam.gserviceaccount.com'
x-google-jwks_uri: 'https://www.googleapis.com/service_accounts/v1/metadata/x509/id-admin#my-project.iam.gserviceaccount.com'
x-google-audiences: 'https://my-cloud-run.a.run.app'
If you have multiple audiences, then it should be a single string separated by a comma. Spaces aren't allowed between the audiences.
I'm trying to setup Google API Gateway to use an API key that callers send in the header.
My api config yaml looks like this:
...
securityDefinitions:
api_key_header:
type: apiKey
name: key
in: header
api_key_query:
type: apiKey
name: key
in: query
paths:
/foo-header:
get:
summary: Test foo endpoint
operationId: testGet-header
x-google-backend:
address: "<backend address>"
protocol: h2
path_translation: APPEND_PATH_TO_ADDRESS
security:
- api_key_header: []
responses:
204:
description: A successful response
/foo-query:
get:
summary: Test foo endpoint
operationId: testGet-header
x-google-backend:
address: "<backend address>"
protocol: h2
path_translation: APPEND_PATH_TO_ADDRESS
security:
- api_key_query: []
responses:
204:
description: A successful response
I expect both calls, /foo-header and /foo-query to fail with 401 status if a valid API key is not provided via header or query parameter.
But in a fact only /foo-query behaves as expected.
Requests to /foo-header pass to the backend even when the API key is not provided in request header.
Do I have issue with the config, or is it the Google API Gateway that doesn't work properly when API key is provided in request header?
When in is header, the name should be x-api-key.
https://cloud.google.com/endpoints/docs/openapi/openapi-limitations#api_key_definition_limitations
It seems that the Google API Gateway should work fine when the API key is provided in request header since the Google API Gateway documentation states:
A developer generates an API key in a project in the Cloud Console and embeds that key in every call to your API as a query parameter or in a request header.
However, I was able to reproduce the behavior you reported, thus I don't think that there is something wrong in your configuration.
For that I'd been following the GCP quickstart for the Google API Gateway, modifying it slightly so that my OpenAPI spec would also have 2 paths: one is looking for a key in query parameters, while another in the request header.
paths:
/foo-header:
get:
summary: Test security
operationId: headerkey
x-google-backend:
address: [MY_CLOUD_FUNCTION_1]
security:
- api_key_header: []
responses:
'200':
description: A successful response
schema:
type: string
/foo-query:
get:
summary: Test security
operationId: querykey
x-google-backend:
address: [MY_CLOUD_FUNCTION_2]
security:
- api_key_query: []
responses:
'200':
description: A successful response
schema:
type: string
securityDefinitions:
# This section configures basic authentication with an API key.
api_key_header:
type: "apiKey"
name: "key"
in: "header"
api_key_query:
type: "apiKey"
name: "key"
in: "query"
Just like you, I could see the requests to the /foo-header pass to the backend even when there was no API key provided.
I would suggest you to report this issue on the Public Issue Tracker, so that it would be reviewed by an appropriate GCP engineering team.
I was building authenticated Cloud functions usingCloud functions with ESPV2 and Firebase authentication and API Management. Once I got the JWT token from firebase after authentication, I tried curl to the link with the token in Authorization as Bearer. I got 'JWT verification fails' when I tried in postman. I got 'Bad Request' when I tried it from my client application. Other than the setup mentioned in the links, do I need to do anything extra before I make the request?
Update with more details as requested
swagger: "2.0"
info:
title: My API Endpoints
description: My API Endpoints
version: 1.0.0
host: myapi-abcdefg.a.run.app
schemes:
- https
produces:
- application/json
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/fan-demand"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "my-google-project-id"
paths:
/getevents:
get:
summary: Get Events
operationId: getevents
x-google-backend:
address: https://us-central1-my-google-project-id.cloudfunctions.net/getevents
protocol: h2
security:
- firebase: []
responses:
"200":
description: A successful response
schema:
type: string
"403":
description: Failed to authenticate
After deploying this service, I get the id token from Firebase using the getIdToken() method in the Firebase Dart SDK. The JWT token is in the Header.payload.tail format. Then I added the token in the Authorization header with Bearer + id token and I get the following response.
Update:
I tried the new API Gateway product using https://cloud.google.com/api-gateway/docs/authenticating-users-firebase instead of ESP.
My configuration:
swagger: "2.0"
info:
title: My API Endpoints
description: My API Endpoints
version: 1.0.0
schemes:
- https
produces:
- application/json
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/my-project"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "my-project"
paths:
/getevents:
get:
summary: Get Events
operationId: getevents
x-google-backend:
address: https://us-central1-my-project.cloudfunctions.net/getevents
security:
- firebase: []
responses:
"200":
description: A successful response
schema:
type: string
"403":
description: Failed to authenticate
Client Side Code:
Client side is developed in dart and user here is a firebase auth object from https://pub.dev/documentation/firebase_auth/latest/firebase_auth/User/getIdToken.html
user.getIdToken().then((token) async {
final response = await http.get(
Uri.parse(
'https://mygateway/getevents'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
print('Token : ${token}');
print(response.body);
});
I got the response
403 Forbidden - Your client does not have permission to get URL
Without ESP
Cloud functions need to be deployed publicly (with allUsers) to be able to use firebase authentication.
Be careful:
Unlike Google Sign-in above, your function is doing the authentication;
therefore, you will be billed for unauthenticated requests since the function must do work to validate the token.
Link to relevant documentation
With ESP
If you want to use cloud functions with ESPv2 in front of it, you need to create a specific IAM for your ESP to be able to trigger privately your cloud functions.
To provide API management for Cloud Functions, you deploy the prebuilt ESPv2 container to Cloud Run.
You then secure your functions by using Cloud Functions IAM so that ESPv2 can invoke them.
Link to relevant documentation
My setup contains google-endpoints with google-cloud-functions as my backend.
Google endpoints is defined with the following swagger v2 yaml:
swagger: "2.0"
info:
description: "yada..."
version: "0.0.1"
title: "yadada.."
termsOfService: "http://swagger.io/terms/"
contact:
name: "blah"
email: "email#mail.com"
url: "https://example.com"
host: "(generated service url by google when endpoints is deployed, i.e. 'api-gateway-xyz123123-ew.a.run.app')"
tags:
- name: "Documents"
description: "blah"
schemes:
- "https"
paths:
/api/documents:
post:
tags:
- "Documents"
summary: "Add a new document"
description: ""
security:
- firebase: []
operationId: "addDocument"
x-google-backend:
address: "(cloud functions http url)/documents"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "body"
description: "Document supplied"
required: true
schema:
$ref: "#/definitions/Document"
responses:
201:
description: "The document was successfully created."
schema:
$ref: "#/definitions/Document"
400:
description: "Invalid input. See response for details"
schema:
items:
$ref: "#/definitions/Error"
/api/documents/{document_id}:
get:
tags:
- "Documents"
summary: "Get a document with the given ID"
description: ""
security:
- firebase: []
operationId: "getDocument"
x-google-backend:
address: "(cloud function http url)/documents/"
path_translation: APPEND_PATH_TO_ADDRESS
produces:
- "application/json"
parameters:
- in: "path"
name: "document_id"
description: "ID of the document to modify"
required: true
type: "string"
responses:
200:
description: "success."
schema:
type: "array"
items:
$ref: "#/definitions/Document"
404:
description: "Document not found"
schema:
items:
$ref: "#/definitions/Error"
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/%%GOOGLE_PROJECT_ID%%"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "%%GOOGLE_PROJECT_ID%%"
definitions:
(a lot of type definitions)
This works with the POST endpoint without any problems.
The problem is with the GET REST endpoint where the path variable is not passed correctly to the backend.
As in https://cloud.google.com/endpoints/docs/openapi/openapi-extensions I tried to add the x-google-backend parameter as in the swagger api above. (path_translation: APPEND_PATH_TO_ADDRESS).
However this does not work.
I get an Unauthorized Error (403) as the cloud function is not hit by the endpoints frontend.
Currently I use an ugly workaround without the path_translation parameter which translates the google endpoints path variable to a query parameter in the cloud function backend with the same name. I.e. in the backend the url /documents?document_id=xyz is called.
(What I try to achieve is to pass the call with the backend url /documents/{document_id})
Does anyone know how to configure path based parameters correctly so that they are passed correctly to the cloud function backend?
Thank you in advance.
Regards,
Sebastian
TL;DR:
I assume that your 403 error isn't the correct error. It should be a 404, but because the endpoint is unknown, I guess that 403 is answered.
Cloud Endpoint is frustrating about this behavior. With the path_translation: APPEND_PATH_TO_ADDRESS, you think that your final called address will be /documents/{document_id}, but NO. The full openAPI path is append to your backend address, in your case: /documents/api/documents/{document_id}
That's why the endpoint doesn't exist and you should have a 404 (and not a 403).
For more details, you can have a look to this page.
Note: I'm in relation with Google team on this topic, and it will take time before having an update on this behavior.