API Gateway authentication with Cognito Federated Identities - javascript

I want to use Cognito Federated Entity (allowing signin through Google etc), to allow access to API Gateway for a web javascript application.
I managed to get the Cognito's sessionToken through signing-in with Google but I'm stuck on the API Gateway configuration for enabling the session token.
Is there a good tutorial for this entire Federated Entity authentication workflow?
Thanks!

Since you want to invoke APIs via authenticated Cognito identity, first
Amend the auth role of the identitypool to have api execute policy, you could just attach the managed policy "AmazonAPIGatewayInvokeFullAccess" to the respective role
In API gateway under respective method request, add Authorization as
"AWS_IAM"
You need to sign the request while using "IAM" auth, explained here https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html
Instead of #3, you could generate and download the SDK from the stage panel of your API gateway, and make a call to the api via sdk.
Once you obtain the cognito session, you could make a call using the sdk like below
var apigClient = apigClientFactory.newClient({
accessKey: AWSCognito.config.credentials.accessKeyId,
secretKey: AWSCognito.config.credentials.secretAccessKey,
sessionToken: AWSCognito.config.credentials.sessionToken
});
var params = {
// This is where any modeled request parameters should be added.
// The key is the parameter name, as it is defined in the API in API Gateway.
};
var body = {};
var additionalParams = {
// If there are any unmodeled query parameters or headers that must be
// sent with the request, add them here.
headers: {
'Content-Type': 'application/json'
},
queryParams: {}
};
apigClient.<resource><Method>(params, body, additionalParams)
.then(function(result) {
//
}).catch(function(err) {
//
});

Related

Postman AWS Rest API -- as javascript?

I made a simple IAM authenticated API that returns a random number. [GET only]
Postman call works ok:
https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-use-postman-to-call-api.html?shortFooter=true
What is a simple way of getting the postman call to plain javascript
(No npm or webpack)
Thanks heaps
You can use axios and aws4 library in javascript to make api calls and send a signed request respectively.You need to authenticate the user via Cognito and retrieve temporary credentials (access key, secret key, and session token)
let request = {
host: 'myapi.execute-api.us-west-2.amazonaws.com',
method: 'GET',
url: 'https://myapi.execute-api.us-west-2.amazonaws.com/foo/bar',
path: '/foo/bar'
}
let signedRequest = aws4.sign(request,
{
// assumes user has authenticated and we have called
// AWS.config.credentials.get to retrieve keys and
// session tokens
secretAccessKey: AWS.config.credentials.secretAccessKey,
accessKeyId: AWS.config.credentials.accessKeyId,
sessionToken: AWS.config.credentials.sessionToken
})
delete signedRequest.headers['Host']
delete signedRequest.headers['Content-Length']
let response = await axios(signedRequest)
This article might help you with the basic code to get temporary credentials from cognito and authenticate the user.

Can't authenticate to cognito from backend as administrator

I need to get all users from my backend (Node.js). But when I am trying to authenticate I've got:
error AccessDeniedException, User ... assumed-role/Cognito_XXXUnauth_Role/ CognitoIdentityCredentials is not authorized to perform: cognito-idp:AdminInitiateAuth ...
My current code:
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider()
var params = {
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId: process.env.AWS_CLIENT_ID,
UserPoolId: process.env.AWS_USER_POOL_ID,
AuthParameters: {
USERNAME: '...'
PASSWORD: '...'
}
}
cognitoidentityserviceprovider.adminInitiateAuth(params, (err, result) => { ... })
It looks like I didn't log in since Cognito_XXXUnath_Role is used. Anyone had similar problem?
You are trying to use a Cognito Federated Identity credentials providers to create the CognitoIdentityServiceProvider service client. This will make the AWS SDK to call the Federated Identity service to obtain temporary AWS credentials which will be used to call cognitoidentityserviceprovider.adminInitiateAuth(...).
In this case, there are two possible solutions:
Use a different credentials provider to create the CognitoIdentityServiceProvider client with credentials which have access to call adminInitiateAuth API. Since you care doing this from backend, it should be safe to do so. This guide can help you with this.
If you must use the Cognito Federated Unauth role to create the service client, allow cognito-idp:AdminInitiateAuth in the unauthenticated role of the identity pool you are using to do this.
In case you are using Serverless framework, then the following settings should fix an error:
provider:
# you can add statements to the Lambda function's IAM Role here
iamRoleStatements:
- Effect: "Allow"
Action:
- "cognito-idp:AdminInitiateAuth"
Resource:
- "arn:aws:cognito-idp:*"

setting js azure app service custom login authentication

I started implementing 2 years ago my custom authentication in azure mobile service with node.js to use it in my jquery/cordova appery.io app. I have a database with users, passwords, etc and a login api in node.js backend that generates a valid jwt like this:
http://www.thejoyofcode.com/Exploring_custom_identity_in_Mobile_Services_Day_12_.aspx
http://chrisrisner.com/Version-1-of-the-Mobile-Services-JWT-token-has-been-deprecated
But now i have been migrated to azure app service and don´t know how to implement custom authetication with javascript.
At the examples above:
"iss":"urn:microsoft:windows-azure:zumo",
"ver":2,
"aud":aud,
But i have read that "aud" and "iss" must be my azure website and "sub" my own userId, to generate a valid jwt in my custom auth to use it in azure. Is it right?
https://shellmonger.com/2016/04/08/30-days-of-zumo-v2-azure-mobile-apps-day-5-custom-authentication/
The important thing is the token. There are some requirements for using 3rd party tokens with an Azure Mobile App:
It must follow the Json Web Token format
The user Id must be in the subject (sub) field
The audience (aud) and issuer (iss) must be known and configured
I do a call to my login API with ajax post and obtain a valid JWT token (http://jwt.io), but i don´t know the correct process to login and use azure services because i don´t know the correct way to use it on my app. I have changed MobileServices.Web.min.js to azure-mobile-apps-client.js in the app.
https://github.com/Azure/azure-mobile-apps-js-client
Can i use the jwt token created in azure node.js or exchange it for a session token and use client.login() method by JS SDK?
EDIT:
I see the light again adding to ajax POST
headers: {'X-ZUMO-AUTH': usuarioToken},
So my "authenticated users only" api works again with the old MobileServices.Web.min.js! It also works perfect with invokeApi method:
var mobileClient = new WindowsAzure.MobileServiceClient(urlApp,keyApp);
mobileClient.currentUser = {
userId: myCustomUserId,
mobileServiceAuthenticationToken: myCustomToken
};
mobileClient
.invokeApi('data', {
method: 'POST',
headers: {'X-ZUMO-AUTH': myCustomToken},
body: JSON.stringify(theData)
}) //end invokeApi
.done(
function (response) {
alert(response);
},
function (error) {
alert("error"));
}
); //end done
The mobileClient generated contains this data:
mobileClient= {"applicationUrl":"https://xxx.azure-mobile.net/","applicationKey":"hGhzxxx","version":"ZUMO/1.2 (lang=Web; os=--; os_version=--; arch=--; version=1.2.21003.0)","currentUser":{"userId":"Custom:25F600BB-xxxx-xxxx-xxxx-8329BCCF31D2","mobileServiceAuthenticationToken":"ey__rest of jwt token"},"_serviceFilter":null,"_login":{"_loginState":{"inProcess":false,"cancelCallback":null},"ignoreFilters":true},"push":{"_apns":null,"_gcm":null,"_registrationManager":null}}
I will try again wiht the new azure-mobile-apps-client.js
Any comment will be welcome

Unable to query Google Search Console API using a Service Account

I need to retrieve some data from Google Search Console (Webmaster Tools) using a service account.
So far I've been able to retrieve an access_token for the service account which I need to append to the url of the request. The problem is that I can't find a way to do so, this is the code i'm using:
function retrieveSearchesByQuery(token)
{
gapi.client.webmasters.searchanalytics.query(
{
'access_token': token,
'siteUrl': 'http://www.WEBSITE.com',
'fields': 'responseAggregationType,rows',
'resource': {
'startDate': formatDate(cSDate),
'endDate': formatDate(cEDate),
'dimensions': [
'date'
]
}
})
.then(function(response) {
console.log(response);
})
.then(null, function(err) {
console.log(err);
});
}
This is the url called by the function:
https://content.googleapis.com/webmasters/v3/sites/http%3A%2F%2Fwww.WEBSITE.com/searchAnalytics/query?fields=responseAggregationType%2Crows&alt=json"
Instead it should be something like this:
https://content.googleapis.com/webmasters/v3/sites/http%3A%2F%2Fwww.WEBSITE.com/searchAnalytics/query?fields=responseAggregationType%2Crows&alt=json&access_token=XXX"
The gapi.client.webmasters.searchanalytics.query doesn't recognize 'access_token' as a valid key thus it doesn't append it to the url and that's why I get a 401 Unauthorized as response.
If I use 'key' instead of 'access_token' the parameter gets appended to the url but 'key' is used for OAuth2 authentication so the service account token I pass is not valid.
Does anyone have a solution or a workaround for this?
If your application requests private data, the request must be authorized by an authenticated user who has access to that data. As specified in the documentation of the Search Console API, your application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported.
If you application is correctly configured, when using the Google API, an authenticated request looks exactly like an unauthenticated request. As stated in the documentation, if the application has received an OAuth 2.0 token, the JavaScript client library includes it in the request automatically.
You're mentioning that you have retrieved an access_token, if correctly received, the API client will automatically send this token for you, you don't have to append it yourself.
A very basic workflow to authenticate and once authenticated, send a request would looks like the following code. The Search Console API can use the following scopes: https://www.googleapis.com/auth/webmasters and https://www.googleapis.com/auth/webmasters.readonly.
var clientId = 'YOUR CLIENT ID';
var apiKey = 'YOUR API KEY';
var scopes = 'https://www.googleapis.com/auth/webmasters';
function auth() {
// Set the API key.
gapi.client.setApiKey(apiKey);
// Start the auth process using our client ID & the required scopes.
gapi.auth2.init({
client_id: clientId,
scope: scopes
})
.then(function () {
// We're authenticated, let's go...
// Load the webmasters API, then query the API
gapi.client.load('webmasters', 'v3')
.then(retrieveSearchesByQuery);
});
}
// Load the API client and auth library
gapi.load('client:auth2', auth);
At this point, your retrieveSearchesByQuery function will need to be modified since it doesn't need to get a token by argument anymore in order to pass it in the query. The JavaScript client library should include it in the request automatically.
You can also use the API Explorer to check what parameters are supported for a specific query and check the associated request.
If you need to use an externally generated access token, which should be the case with a Service Account, you need to use the gapi.auth.setToken method to sets the OAuth 2.0 token object yourself for the application:
gapi.auth.setToken(token_Object);

Need example of calling AWS Lambda from JavaScript

Just need an example of how to call AWS Lambda from JavaScript running in a browser and display function result in JavaScript console. Incredibly, I cannot find any examples on Google or from AWS documentation.
My use case is that I have an HTML form. When the form is submitted, I want to use Lambda to process the form inputs. Assuming that the Lambda function finishes with no errors, I then want to take the user to a thank you page.
Please include a complete HTML example, not just a code snippet.
Since you need to run Lambda from the browser, you have two options you can achieve it.
Use AWS Javascript SDK, set it up with user via static configuration or Cognito with IAM Permissions to your Lambda. You can also consider subscribing your Lambda functions to SNS Topic and run the Lambda by sending a message to the topic. This SNS approach would also require you to store and retrieve the submission state via separate call.
Use AWS API Gateway to create RESTful endpoint with proper CORS configuration that you can ping from the browser using AJAX.
Both options have their pros and cons. More information about your use-case would be necessary to properly evaluate which one suits you best.
I see people have used AWS SDK for Javascript but it is not required specially since you need to create Amazon Cognito identity pool with access enabled for unauthenticated identities (Atleast for beginners like me). Below code works fine for me -
<html>
<head>
<script>
function callAwsLambdaFunction() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("myDiv").innerHTML = this.responseText;
}
};
xhttp.open("GET", "https://test123.ap-south-1.amazonaws.com/dev", true);
xhttp.send();
}
</script>
<title>Hello World!</title>
</head>
<body>
<h1>Hello world!</h1>
<h1>Click below button to call API gatway and display result below!</h1>
<h1><div id="myDiv"></div></h1>
<button onclick="callAwsLambdaFunction()">Click me!</button><br>
Regards,<br/>
Aniket
</body>
</html>
Above is sample index.html that I have added to my S3 bucket and made a static site. Couple of points to note -
Make your index.html open from outside if you are using S3 for static site hosting.
Make sure you turn on CORS for your API gateway if your website domain is not same as API gateway domain. Else you might get -
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://test123.ap-south-1.amazonaws.com/dev. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
For people who see this after 2017, you can check out AWS Amplify API class. The sample code is taken from Amplify API document.
Note that
1) You have to use POST method to invoke lambda functions.
2) Make sure to add policy to invoke lambda permission for your unauthenticated(if needed) and authenticated roles.
3) User doesn't need to sign in to invoke the lambda if a permission policy is granted.
import Amplify, { API } from 'aws-amplify';
Amplify.configure({
Auth: {
// REQUIRED - Amazon Cognito Identity Pool ID
identityPoolId: 'XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab',
// REQUIRED - Amazon Cognito Region
region: 'XX-XXXX-X',
// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: 'XX-XXXX-X_abcd1234',
// OPTIONAL - Amazon Cognito Web Client ID
userPoolWebClientId: 'XX-XXXX-X_abcd1234',
},
API: {
endpoints: [
{
name: "MyCustomLambdaApi",
endpoint: "https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/yourFuncName/invocations",
service: "lambda",
region: "us-east-1"
}
]
}
});
This is how you call your lambda function
let apiName = 'MyApiName'; // replace this with your api name.
let path = '/path'; //replace this with the path you have configured on your API
let myInit = {
body: {}, // replace this with attributes you need
headers: {} // OPTIONAL
}
API.post(apiName, path, myInit).then(response => {
// Add your code here
});
Invoke AWS Lambda Using No API Gateway || Using no AWS SDK
Assumption is that you've made your function url and made the necessary cross-origin settings. Both can be done on the configuration tab of the lambda function
Assumption is that the lambda is written in python, but called from javascript
1. Write the function
The payload is retrieved from the event variable
The format for the event variable is given by aws
def lambda_handler(event, context):
payload = event["rawQueryString"]
return {
"payload": payload
}
2. Use a simple fetch to your function URL
In the example, you want to send a name and password to the function
fetch('https://aws-lambda-function-url?name=jason&password=1234')
.then((response) => response.json())
.then((data) => console.log(data));
3. Result
{payload: 'name=jason&password=1234'}
I would use AWS SDK for Javascript, below the steps
Reference the js file
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.100.0.min.js"></script>
Initialize/Configure the SDK
AWS.config.update({region: 'REGION'});
AWS.config.credentials = new AWS.CognitoIdentityCredentials({IdentityPoolId: 'IdentityPool'});
Create the service lambda Object and so on...
you can see the next steps in this link
http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/browser-invoke-lambda-function-example.html

Categories