With Serverless I have this config:
functions:
my-account:
handler: src/index.handler
events:
- http:
path: /my-account
method: post
cors: true
authorizer:
name: authorizer
arn: arn:aws:cognito-idp:${self:provider.region}:XXXXXX:userpool/${self:custom.YYYY.cognito.userpool}
I'm trying to test with Postman but I got an:
{
"message": "Unauthorized"
}
I don't get what kind of Authorization it needs. I don't have API KEYs, I have a token in the app that consume that graphql, I put it as a "Bearer Token" but it still fails. Any advice?
You'll need to make a call first to Cognito to get a JWT token, then pass this in the request Authorization header as Bearer. I haven't used this route, but there's an API that does just that: InitiateAuth
Related
I have configured my api to authorized against cognito user pool following is a example of the configuration.
getRecords:
handler: abc.def.GetRecords
events:
- http:
path: /getRecords
method: get
authorizer:
- {arn of coginito userPool}
When i pass access_token while calling the API i don't get response, but when i pass id_token it let me though with the authorisation, but i want it to work with access_token but not with id_token, Please help me to solve this usecase
I was trying GCP API Gateway using firebase authentication. I can see my request has been processed from the logs and completed with response code 200. However, I am not getting the response back to my client. I am getting the response when I call the function directly. Am I missing something ?
API Config
swagger: "2.0"
info:
title: API Endpoints
description: API Endpoints
version: 1.0.1
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:
/hello:
get:
summary: Test link
operationId: hello
x-google-backend:
address: https://us-central1-my-project.cloudfunctions.net/hello
security:
- firebase: []
responses:
"200":
description: A successful response
schema:
type: string
"403":
description: Failed to authenticate
Function
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = (req, res) => {
let message = req.query.message || req.body.message || 'Hello World!';
res.status(200).send(message);
};
Logs
Additional Query
Initially, I had my functions private and was getting 403. It gave me 200 once I added allUsers with Cloud Functions Invoker to the permissions to the function I am trying to invoke. So I am a bit confused here. Part of the reason I am using API gateway with firebase auth is to protect it against unauthorised calls. And for firebase auth to work, I had to add allUsers, making it public. My understanding was that the API gateway alone would be public while all the backend services that it invokes would be private. In this case, the function can be directly invoked by anyone, rendering API Gateway useless. How can I setup the backend to private and only respond to authenticated calls through API gateway ?
Additional Logs
{
insertId: "8c13b49c-2752-4216-8188-d445f4724ef14850908905639612439#a1"
jsonPayload: {
api_key_state: "NOT CHECKED"
api_method: "1.myapi.GenerateRTCToken"
api_name: "1.myapi"
api_version: "1.0.1"
client_ip: "999.999.999.999"
http_method: "GET"
http_response_code: 200
location: "us-central1"
log_message: "1.myapi.GenerateRTCToken is called"
producer_project_id: "myproject"
request_latency_in_ms: 161
request_size_in_bytes: 4020
response_size_in_bytes: 579
service_agent: "ESPv2/2.17.0"
service_config_id: "myapi-config"
timestamp: 1606313914.9804168
url: "/generateRTCToken"
}
logName: "projects/myproject/logs/myapi%2Fendpoints_log"
receiveTimestamp: "2020-11-25T14:18:36.521292489Z"
resource: {
labels: {
location: "us-central1"
method: "1.myapi.GenerateRTCToken"
project_id: "myproject"
service: "myapi"
version: "1.0.1"
}
type: "api"
}
severity: "INFO"
timestamp: "2020-11-25T14:18:34.980416865Z"
}
To call the Google Cloud Function from the Api-Gateway without making it public you can grant the service account used by the API-Gateway the role CloudFunctions Invoker
On Creating an API config (4) : you set a service Account, take note of this service account.
Once you have identified the service account you can grant it the Cloud Funtions Invoker role to the service account. For these you can follow these steps:
Go to IAM&Admin
Look for the service account, and click on the pencil next to it( If you don't see the service account click on Add and type the service account email)
For role Select Cloud Functions Invoker
Click on Save
This will grant the service account the permission to call the functions without having them public.
I have an similar issue with API Gateway throwing the same response validating a token against keycloak.
The issue was with the JWT, it was too long, we remove unused roles from the user and work perfectly.
Hope it helps.
Take a look at this document where it is explained in detail how to use Firebase to authenticate in API Gateway.
In general there are two conditions that we need to keep on mind in order to configure these services:
When your client application sends an HTTP request, the authorization header in the request must contain the following JWT claims:
iss (issuer)
sub (subject)
aud (audience)
iat (issued at)
exp (expiration time)
To support Firebase authentication, add the following to the security definition in your API config:
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
# Replace YOUR-PROJECT-ID with your project ID
x-google-issuer: "https://securetoken.google.com/YOUR-PROJECT-ID"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "YOUR-PROJECT-ID"
Im using Serverless framework to deploy a set of API's running on API Gateway using cognito as authorizer. Everything seemed to work but i found an issue that when lambda crashes for some reason (maybe time out or some unhandled exception), API Gateway returns 401 Unauthorized.
I added some gateway responses and I can handle some of the errors but even if I get the initial error, I keep receiving the 401 Unauthorized in Frontend.
This is part of my serverless.yml file:
getSimulationStatus:
handler: getSimulationStatus.handler
events:
- http:
path: /simulation/status
method: post
cors: true
authorizer:
arn: arn:aws:cognito-idp:us-east-1:${self:custom.settings.COGNITO_ARN}
resources:
Resources:
GatewayResponseDefault5XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
gatewayresponse.header.Access-Control-Allow-Methods: "'*'"
ResponseType: DEFAULT_5XX
RestApiId:
Ref: 'ApiGatewayRestApi'
GatewayResponseDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
gatewayresponse.header.Access-Control-Allow-Methods: "'*'"
ResponseType: DEFAULT_4XX
RestApiId:
Ref: 'ApiGatewayRestApi'
For the frontend im using Angular and I have an error interceptor that captures all this events. Currently im forcing a 504 Request time out and Im able to see that, but also the 401 Unauthorized appears.
This is the code for the interceptor:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(err => {
console.log("error captured", err);
return throwError(err.error.message);
}));
}
The idea is to sign out if I receive 401 from backend but currently if any of my lambdas fails, Im immediately logged out.
Any Idea on what could be the problem?
Edit:
This is a capture from web console:
I have a better solution.Why you don't verify if the token it's valid before send the request to api gateway?
you can install this:
npm i jwt-decode
In my case, I always have a fuction that return me the headers and that allows me to validate in that function the token on every call to the API.
Here a function that validate the token (you only need to pass the token of cognito if you are ussing amplify or something like that).
import jwt_decode from "jwt-decode";
....
some code
....
tokenValidator(){
if(localStorage.getItem('access_token') == null){
//call signout cognito and go to login
}
const decoded = jwt_decode(localStorage.getItem('access_token'));
if (decoded.exp === undefined) return null;
const date = new Date(0);
date.setUTCSeconds(decoded.exp);
if(new Date() > date){
//call signout cognito and go to login
}
}
This solution prevents your problem.
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
I am trying to do an http request from an angularjs app to a lambda function that I had setup using serverless.
Here is my serverless.yaml function
functions:
createcustomer:
handler: handler.createcustomer
events:
- http: post /createcustomer
cors: true
Create Customer Function
module.exports.createcustomer = (event, context, callback) => {
let customer = JSON.parse(event.body).customer;
service.create(customer, function(result) {
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Credentials": true,
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
},
body: JSON.stringify({
result: 'Created Customer Successfully',
message: 'The account has been created!',
type: 'success',
customer: result
})
};
callback(null, response);
});
};
From my AngularJS app I call it like this
app.factory('MyFactory', ['$http', function($http) {
return {
CreateCustomer: function(customer) {$http.post('<apipath>/createcustomer', {customer:customer})}
}
}]);
However, I keep getting this error:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:5000' is therefore not allowed access. The response had HTTP status code 403.
I have tried to enable CORS in the API Gateway on the POST method, but that did not change the outcome.
I've also tried setting CORS in the yaml file explicitly
functions:
createcustomer:
handler: handler.createcustomer
events:
- http: post /createcustomer
cors:
origin: '*'
Still no luck.
Does anyone know what I'm doing wrong here?
One weird thing is that I could get the post to work just fine through PostMan, but if I try it through my app it breaks.
Thanks
UPDATE
When I do serverless deploy it shows up in AWS like the picture above and the method looks like this
As I said before, I tried to enable CORS directly from the API Gateway console but there was no difference when I tried to call the method.
Your update with screenshots shows that the OPTIONS method is not set up for any of these resources. When you enable CORS for an API Gateway resource in the console, AWS sets this up automatically.
You can see this happen in the AWS console when you enable CORS for a resource, but, of course, your serverless deploy is overwriting this configuration.
To have the /createcustomer resource properly configured in AWS by the serverless deploy, you can rewrite this part of your serverless.yaml:
events:
- http: post /createcustomer
cors: true
To look like this:
events:
- http:
path: /createcustomer
method: post
cors: true
I'm not an expert in the framework's .yml syntax, so I can't explain exactly why this is.
Nonetheless, I've confirmed that a file like this:
functions:
deletecustomer:
handler: handler.deletecustomer
events:
- http:
path: /deletecustomer
method: post
cors: true
createcustomer:
handler: handler.createcustomer
events:
- http: post /createcustomer
cors: true
will create two resources in AWS API Gateway, one correctly configured for CORS, and one missing the OPTIONS method:
Here is the configuration that could help. Please note that it's always safe to be specific in allowing the CORS origins. So better to lock the origin down to localhost:{port-number}. In addition, you can also enable credentials on CORS settings. Please see the following serverless config as an example:
cors:
origin: 'http://localhost:4200'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: true
The cors configuration got moved up into the provider config:
provider:
httpApi:
cors: true