I am trying to make a serverless application (ReactJS, API Gateway, AWS Lambda, DynamoDB) with federated authentication. Below is the kind of architectire that I am envisioning ( Have not added STS for brevity. Also I don not think that I understand the flow fully):
I have created API Gateway endpoints that trigger lambda functions. I want to authenticate my users with google first and if they are successful then they should be able to use API endpoints.
First step is to authenticate the user with google using standard Outh2. I have created 2 unauthenticated endpoints /signin/google and /callback/google for the purpose . Once I get the successful authentication response from google in callback lambda function I have id_token (among others) that I can use.
At this point in time I have 2 approaches that I can use for authenticating APIs.
Build a custom authorizer that I can use for API endpoints. Here is the code (https://github.com/prabhatsharma/api-gateway-custom-authorizer/). This is pretty straightforward. I can use the same id_token provided by google to authenticate API endpoints. Custom authorizer will validate that id_token is good and grant access to the endpoint. It will also cache the result so that this verification is not needed everytime. (Is this a good approaach to reuse the id_token of google in this way?) You can use the authorizer with this (https://github.com/prabhatsharma/lambda-custom-auth)
I can use AWS cognito for authentication. For this I have created a federated identity pool and have set the google app client Id to cognito console. In my /callback/google lambda function I am using AWS SDK to get the identityId, sessionToken and accessKeyId. (Source code https://github.com/prabhatsharma/lambda-cognito-auth)
// Add the Google access token to the Cognito credentials login map. AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: config.COGNITO_IDENTITY_POOL_ID, //dauth Logins: { 'accounts.google.com': JSON.parse(body).id_token } });
Now I can use the credentials to get Token using following code
// Obtain AWS credentials
AWS.config.credentials.get(function () {
// Access AWS resources here.
// Credentials will be available when this function is called.
var identityId = AWS.config.credentials.identityId;
var cognitoidentity = new AWS.CognitoIdentity();
var params = {
IdentityId: identityId, /* required */
Logins: {
'accounts.google.com': JSON.parse(body).id_token
}
};
cognitoidentity.getOpenIdToken(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else {
console.log(data); // successful response
res.headers = {
location: config.APPLICAION_URL + '/auth/google?' + querystring.stringify(data)
}
callback(null, res); //redirect to front end application
}
});
I can now pass the identityId and Token to my front end reactJS application.
This is where I need little help understanding the concept. I can now use the AWS SDK in browser to access AWS resources. But hold on!!! Wasn't the purpose of creating standard RESTful APIs via Gateway was to use standard javascript without reliance on any specific library? Using AWS SDK directly looks like a more apt use case for android/ios/unity apps. I would like developers in my team to be able to use the standard javascript libraries of front end that they use for this situation too. Also I do not want to use the exported API SDK for my API endpoints and really want to keep the front end app clean of AWS SDK specifics. Signing every request with v4 signature manually is redundant work. Can't we have standard token based API endpoint access using Cognito?
What is the best practice for this kind of authentication? Am I thinking in right direction?
Disclaimer - Please do not use the code in the repos as yet. It is work in progress and not yet production ready.