Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...
Ed Lima, Solutions Architect

Want to create your own user directory that can scale to hundreds of millions of users?Amazon Cognito user pools are fully managed so that you don’t have to worry about the heavy lifting associated with building, securing, and scaling authentication to your apps.

The AWS Mobile blog post Integrating Amazon Cognito User Pools with API Gateway back in May explained how to integrate user pools withAmazon API Gateway using anAWS Lambda custom authorizer. Since then, we’ve released a new feature where you can directly configure a Cognito user pool authorizer to authenticate your API calls; more recently, we released a newproxy resource feature. In this post, I show how to use these new great features together to secure access to an API backed by a Lambda proxy resource.

Walkthrough

Start bycreating a user pool called “myApiUsers”, and enableverifications with optional MFA access for extra security:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

Now,create an app in your user pool, making sure to clear Generate client secret :


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

Using the client ID of your newly created app, add a user, “jdoe”, with theAWS CLI. The user needs a valid email address and phone number to receive MFA codes via SMS:

aws cognito-idp sign-up --client-id 12ioh8c17q3stmndpXXXXXXXX --username jdoe --password [email protected] --region us-east-1 --user-attributes '[{"Name":"given_name","Value":"John"},{"Name":"family_name","Value":"Doe"},{"Name":"email","Value":"[email protected]"},{"Name":"gender","Value":"Male"},{"Name":"phone_number","Value":"+61XXXXXXXXXX"}]'

In the Cognito User Pools console, under Users , select the new user and choose Confirm User and Enable MFA :


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

Your Cognito user is now ready and available to connect.

Next, create a Node.js Lambda function called LambdaForSimpleProxy. Here’s the code:

'use strict';
console.log('Loading CUP2APIGW2Lambda Function');
exports.handler = function(event, context) {
var responseCode = 200;
console.log("request: " + JSON.stringify(event));
var responseBody = {
message: "Hello, " + event.requestContext.authorizer.claims.given_name + " " + event.requestContext.authorizer.claims.family_name +"!" + " You are authenticated to your API using Cognito user pools!",
method: "This is an authorized "+ event.httpMethod + " to Lambda from your API using a proxy resource.",
body: event.body
};
//Response including CORS required header
var response = {
statusCode: responseCode,
headers: {"Access-Control-Allow-Origin" : "*"
},
body: JSON.stringify(responseBody)
};
console.log("response: " + JSON.stringify(response))
context.succeed(response);
};

For the last piece of the back-end puzzle, create a new API called CUP2Lambda. Under Authorizers , choose Create , Cognito User Pool Authorizer with the following settings:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

Create an ANY method under the root of the API as follows:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

After that, choose Save, OK to give API Gateway permissions to invoke the Lambda function. It’s time to configure the authorization settings for your ANY method. Under Method Request , enter the Cognito user pool as the authorization for your API:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

Finally, choose Actions , Enable CORS . This creates an OPTIONS method in your API:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

Now it’s time to deploy the API to a stage (such as prod) and generate a javascript SDK from the SDK Generation tab. Since we are using an ANY method the SDK does not have calls for specific methods other than the OPTIONS method created by Enable CORS , you have to add a couple of extra functions to the apigClient.js file so that your SDK can perform GET and POST operations to your API:

apigClient.rootGet = function (params, body, additionalParams) {
if(additionalParams === undefined) { additionalParams = {}; }
apiGateway.core.utils.assertParametersDefined(params, [], ['body']);
var rootGetRequest = {verb: 'get'.toUpperCase(),path: pathComponent + uritemplate('/').expand(apiGateway.core.utils.parseParametersToObject(params, [])),headers: apiGateway.core.utils.parseParametersToObject(params, []),queryParams: apiGateway.core.utils.parseParametersToObject(params, []),body: body
};
return apiGatewayClient.makeRequest(rootGetRequest, authType, additionalParams, config.apiKey);
};
apigClient.rootPost = function (params, body, additionalParams) {
if(additionalParams === undefined) { additionalParams = {}; }
apiGateway.core.utils.assertParametersDefined(params, ['body'], ['body']);
var rootPostRequest = {verb: 'post'.toUpperCase(),path: pathComponent + uritemplate('/').expand(apiGateway.core.utils.parseParametersToObject(params, [])),headers: apiGateway.core.utils.parseParametersToObject(params, []),queryParams: apiGateway.core.utils.parseParametersToObject(params, []),body: body
};
return apiGatewayClient.makeRequest(rootPostRequest, authType, additionalParams, config.apiKey);
};

You can now use a little front-end web page to authenticate users and test authorized calls to your API. In order for it to work, you need to add some external libraries forCognito, as well as theAPI Gateway SDK. Your code to load these libraries should look like the following:

<script src="https://sdk.amazonaws.com/js/aws-sdk-2.6.5.min.js">
</script>
<script src="http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js">
</script>
<script src="http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn2.js">
</script>
<script type="text/javascript" src="lib/aws-cognito-sdk.min.js">
</script>
<script type="text/javascript" src="lib/amazon-cognito-identity.min.js">
</script>
<script type="text/javascript" src="lib/sjcl-master/sjcl.js">
</script>
<script type="text/javascript" src="lib/axios/dist/axios.standalone.js">
</script>
<script type="text/javascript" src="lib/axios/dist/axios.standalone.js">
</script>
<script type="text/javascript" src="lib/CryptoJS/rollups/hmac-sha256.js">
</script>
<script type="text/javascript" src="lib/CryptoJS/rollups/sha256.js">
</script>
<script type="text/javascript" src="lib/CryptoJS/components/hmac.js">
</script>
<script type="text/javascript" src="lib/CryptoJS/components/enc-base64.js">
</script>
<script type="text/javascript" src="lib/moment/moment.js">
</script>
<script type="text/javascript" src="lib/url-template/url-template.js">
</script>
<script type="text/javascript" src="lib/apiGatewayCore/sigV4Client.js">
</script>
<script type="text/javascript" src="lib/apiGatewayCore/apiGatewayClient.js">
</script>
<script type="text/javascript" src="lib/apiGatewayCore/simpleHttpClient.js">
</script>
<script type="text/javascript" src="lib/apiGatewayCore/utils.js">
</script>
<script type="text/javascript" src="apigClient.js">
</script>

With the libraries in place, you can use the following JavaScript code to authenticate your Cognito user pool user and connect to your API in order to perform authorized calls (replace your own user pool Id and client ID details accordingly):

<script type="text/javascript">
//Configure the AWS client with the Cognito role and a blank identity pool to get initial credentials
AWS.config.update({
region: 'us-east-1',
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: ''
})
});
AWSCognito.config.region = 'us-east-1';
AWSCognito.config.update({accessKeyId: 'null', secretAccessKey: 'null'});
var token = "";
//Authenticate user with MFA
document.getElementById("buttonAuth").addEventListener("click", function(){
var authenticationData = {
Username : document.getElementById('user').value,
Password : document.getElementById('password').value,
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId : 'us-east-1_XXXXXXXXX',
ClientId : '12ioh8c17q3stmndpXXXXXXXX',
Paranoia : 7
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var userData = {
Username : document.getElementById('user').value,
Pool : userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
token = result.getIdToken().getJwtToken(); // CUP Authorizer = ID Token
console.log('ID Token: ' + result.getIdToken().getJwtToken());
var cognitoGetUser = userPool.getCurrentUser();
if (cognitoGetUser != null) {
cognitoGetUser.getSession(function(err, result) {if (result) { console.log ("Authenticated!"); }
});
}
},
onFailure: function(err) {
alert(err);
},
mfaRequired: function(codeDeliveryDetails) {var verificationCode = prompt('Please input a verification code.' ,'');cognitoUser.sendMFACode(verificationCode, this);
}
});
});
//Send a GET request to the API
document.getElementById("buttonGet").addEventListener("click", function(){
var apigClient = apigClientFactory.newClient();
var additionalParams = {
headers: {
Authorization: token
}
};
apigClient.rootGet({},{},additionalParams)
.then(function(response) {
console.log(JSON.stringify(response));
document.getElementById("output").innerHTML = ('<pre align="left"><code>Response: '+JSON.stringify(response.data, null, 2)+'</code></pre>');
}).catch(function (response) {
document.getElementById('output').innerHTML = ('<pre align="left"><code>Error: '+JSON.stringify(response, null, 2)+'</code></pre>');
console.log(response);
});
//}
});
//Send a POST request to the API
document.getElementById("buttonPost").addEventListener("click", function(){
var apigClient = apigClientFactory.newClient();
var additionalParams = {
headers: {
Authorization: token
}
};
var body = {
"message": "Sample POST payload"
};
apigClient.rootPost({},body,additionalParams)
.then(function(response) {
console.log(JSON.stringify(response));
document.getElementById("output").innerHTML = ('<pre align="left"><code>Response: '+JSON.stringify(response.data, null, 2)+'</code></pre>');
}).catch(function (response) {
document.getElementById('output').innerHTML = ('<pre align="left"><code>Error: '+JSON.stringify(response, null, 2)+'</code></pre>');
console.log(response);
});
});
</script>

After you add some extra CSS styling, your front end is ready. Enter the user name and password details for John Doe and choose Log In :


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

A MFA code is sent to the user’s mobile phone via SMS and can be validated accordingly:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

After authentication, you can see the ID token generated by Cognito for further access testing:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

If you go back to the API Gateway console and test your Cognito user pool authorizer with the same token, you get the authenticated user claims accordingly:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

In your front end, you can now perform authenticated GET calls to your API by choosing GET .


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

Or you can perform authenticated POST calls to your API by choosing POST .


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

The calls reach your Lambda proxy and return a valid response accordingly. You can also test from the command line using cURL, by sending the user pool ID token that you retrieved from the developer console earlier, in the “Authorization” header:


Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...

It’s possible to improve this solution by integrating an Amazon DynamoDB table, for instance. You could detect the method request on event.httpMethod in the Lambda function and issue a GetItem call to a table for a GET request or a PutItem call to a table for a POST request. There are lots of possibilities for this kind of proxy resource integration.

Summary

The Cognito user pools integration with API Gateway provides a new way to secure your API workloads, and the new proxy resource for Lambda allows you to perform any business logic or transformations to your API calls from Lambda itself instead of using body mapping templates. These new features provide very powerful options to secure and handle your API logic.

I hope this post helps with your API workloads. If you have questions or suggestions, please comment below.

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: HTMLJavaScriptJavaCSSNode.jsCUTIOPT
分页:12
转载请注明
本文标题:Authorizing Access Through a Proxy Resource to Amazon API Gateway and AWS Lambda ...
本站链接:http://www.codesec.net/view/520796.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(29)