Using AWS Cognito in a Lambda function with npm - node.js

I'm trying to use AWS Cognito in a Lambda function to authorize a user.
I have some sample code from a Udemy Course (no longer available): https://www.udemy.com/minimum-viable-aws-cognito-user-auth-in-javascript
The code uses the script files:
aws-cognito-sdk.min.js
amazon-cognito-identity.min.js
The second seems to available by npm as: amazon-cognito-identity-js
The first file is supposed to be a cut down version of the aws-sdk with just the Cognito api components. The full aws-sdk is available from npm as: aws-sdk but I cannot find the cutdown version in npm.
Is the cutdown file: aws-cognito-sdk.min.js available in npm?
EDIT:
According to Russell I should use the aws-sdk package.
So if I have code:
const AWS = require('aws-sdk');
var authenticationDetails = new AWS.AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
I get the error:
Cannot read property 'CognitoIdentityServiceProvider' of undefined
What is the correct path to AuthenticationDetails?

For Lambdas use the aws-sdk module as such:
const { CognitoIdentityServiceProvider } = require('aws-sdk')
//or
const CognitoIdentityServiceProvider = require('aws-sdk/clients/cognitoidentityserviceprovider') // Much smaller size
For authentication use the AdminInitiateAuth method.
const cognitoProvider = new CognitoIdentityServiceProvider({
apiVersion: '2016-04-18',
accessKeyId:...
secretAccessKey: ...
region:...
})
await cognitoProvider.adminInitiateAuth(...)
The amazon-cognito-identity-js package is meant for frontend clients (React, React Native, etc). It contains only the functionality necessary to connect to Cognito. It does not require the aws-sdk module (unless you need extra features).
While you may be able to use the amazon-cognito-identity-js for your use case it's far from ideal as you are just pretending to be an unauthenticated user with limited functionality compared to loading the admin method using your api key thereby providing you with much more functionality.

Got this working.
package.json needs dependencies:
"amazon-cognito-identity-js": "^1.31.0",
"aws-sdk": "^2.182.0",
AWS Lambda does not use Javascript ES6 and so you can't use the 'import' keyword.
const AWS = require('aws-sdk');
var AmazonCognitoIdentity = require('amazon-cognito-identity-js');
var CognitoUserPool = AmazonCognitoIdentity.CognitoUserPool;
var AuthenticationDetails = AmazonCognitoIdentity.AuthenticationDetails;
var CognitoUser = AmazonCognitoIdentity.CognitoUser;
var poolData = {
UserPoolId: 'THE USER POOL ID',
ClientId: 'THE CLIENT ID'
};
var userPool = new CognitoUserPool(poolData);
AWS.config.region = 'AWS_REGION';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'THE USERPOOL ID',
});
var email = "someone#somewhere.com";
var password = "password";
var authenticationData = {
Username: email,
Password: password
};
var authenticationDetails = new AuthenticationDetails(authenticationData);
var userData = {
Username: email,
Pool: userPool
};
var cognitoUser = new CognitoUser(userData);
console.log(result);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
callback(null, result);
},
onFailure: function (err) {
console.log('Login error: ' + err);
callback(null, result);
}
});

I believe you are referring to the amazon-cognito-identity-js npm package here:
https://www.npmjs.com/package/amazon-cognito-identity-js
The NPM package includes both files.
The package includes the cognito SDK calls (aws-cognito-sdk). It also depends on the core AWS SDK.

Related

NodeJS Googleapis Service Account authentication

I'm trying to perform authentication on GoogleAPIs using a Service Account. I have a service account set up, with its credentials located at credentials.json. I try to access a private sheet, to which I added the E-Mail address of the service account with editing rights.
Here the code I am using:
const {
google
} = require('googleapis');
const fs = require('fs');
let scopes = ['https://www.googleapis.com/auth/spreadsheets'];
let credentials = require("./credentials.json");
const authClient = new google.auth.JWT(
credentials.client_email,
null,
credentials.private_key,
scopes);
authClient.authorize(function(err, tokens) {
if (err) {
console.log(err);
return;
} else {
authClient.setCredentials(tokens);
}
});
const sheets = google.sheets({
version: 'v4',
authClient
});
let spreadsheetId = //...
let range = //...
const request = {
spreadsheetId: spreadsheetId,
range: range
};
sheets.spreadsheets.values.get(request, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
} else {
console.log('Result: ' + response);
}
});
I guess the API changed over time, since many guides showed different approaches, and in the end none worked for me.
The error is as follows:
The API returned an error: Error: The request is missing a valid API key.
To my understanding, a simple API key should only be necessary for unauthenticated access on public sheets, so I don't get why it is even requiring that. If I add such an API key I get the error
The API returned an error: Error: The caller does not have permission
Using
$ npm list googleapis
`-- googleapis#52.1.0
Any help would be greatly appreciated.
For who still facing googleapis problems within NodeJS Runtime in 2022.
Firstly, redirect into Google-IAM-Admin/ServiceAccount to pick the current working project.
Secondly, click to jump into Service Account that has the following format project#sub-name-id.iam.gserviceaccount.com.
Thirdly, between [Details, Permissions, Keys, Metrics, Logs]. Jump into Keys then Add Key -> Create new Key -> Key type::JSON and save JSON file to your computer.
Here within NodeJS Runtime, I use the following Semantic Version
googleapis#100.0.0
You can create JWT Client and inject into google default auth at google.options({auth: client}); or provide auth-client to specific Service as google.chat({version: 'v1', auth: client});
However, in the following example. I create a GoogleAuth instance and then make an AuthClient after. Which resulted the same behaviour to the JWT Method.
/** Import Node Native Dependencies !*/
import * as path from "path";
/** Import ES6 Default Dependencies !*/
import {google} from "googleapis";
const {client_email, private_key} = require('$/keys/credentials.json');
/**
** #description - Google [[Service Account]] Authenticator.
**/
const auth = new google.auth.GoogleAuth({
keyFile: path.resolve('keys/credentials.json'),
/** Scopes can be specified either as an array or as a single, space-delimited string; ~!*/
scopes: [
"https://www.googleapis.com/auth/chat.bot",
],
});
const client = new google.auth.JWT({
email: client_email,
key: private_key,
/** Scopes can be specified either as an array or as a single, space-delimited string; ~!*/
scopes: [
"https://www.googleapis.com/auth/chat.bot",
],
});
(async () => {
/** #description - Either [[Get Client]] from [Google Auth] or Use directly from [JWT Client] ~!*/
const client = await auth.getClient();
/** #description - Use this Authorized Client as Default Authenticated to fallback from [Non-Authenticated Services] ~!*/
google.options({auth: client});
const chat = google.chat({
version: 'v1',
/** #description - Provide [Authenticated Services] to [Google Chat Service] Instance ~!*/
auth: client,
});
const response = await chat.spaces.members.get({
// Required. Resource name of the attachment, in the form "spaces/x/messages/x/attachments/x".
name: 'spaces',
});
console.log('response', response.data);
return void 0;
})();

Authenticate googleapis library in Google Cloud Function

For developing locally, most google cloud client libraries are configured to use the GOOGLE_APPLICATION_CREDENTIALS environment variable to locate the credentials for the service account in use, and then authenticate that library. When deployed to GCP, they similarly don't require any manual authentication in the code, and instead use they environment to authenticate behind the scenes. This means that most client libraries, for example BigQuery, Cloud Storage, etc, just work in Cloud Functions, without needing any code for authentication. However, the googleapis Nodejs client library doesn't use GOOGLE_APPLICATION_CREDENTIALS and seems to require manual authentication in the code. Below is a minimal example of how I am doing this locally. How could I run this code in a Google Cloud Function without needing to upload the service account credentials to the cloud function?
const { google } = require("googleapis");
const key = require("service_account_credentials.json");
const client = new google.auth.JWT(key.client_email, null, key.private_key, [
"https://www.googleapis.com/auth/spreadsheets",
]);
client.authorize(function (err, tokens) {
const gsapi = google.sheets({ version: "v4", auth: client });
const opt = {
spreadsheetId: "spreadsheetId",
range: "Sheet1!A:T",
};
gsapi.spreadsheets.values.get(opt).then(res => console.log(res));
});
I managed to find a solution to this in the readme.md of the googleapis nodejs github repo. To solve my problem I used:
async function main() {
const auth = new google.auth.GoogleAuth({
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
});
const authClient = await auth.getClient();
const project = await auth.getProjectId();
const gsapi = google.sheets({ version: "v4", auth: authClient });
const opt = {
spreadsheetId: "spreadsheetID",
range: "Sheet1!A:T",
};
gsapi.spreadsheets.values.get(opt).then(res => console.log(res.data.values));
}

Can I run Cognito in a Lambda function?

I want to sign up users with Cognito in a Lambda function. However I am receiving "TypeError: fetch is not a function"
My code is basically step 3 in this. However I keep getting the above-mentioned error, even though I have node-fetch installed. From what I understand, the Cognito SDK makes use of fetch. So is this simply not possible? Will I need to spin up a Node server?
const AWS = require("aws-sdk");
const AmazonCognitoIdentity = require("amazon-cognito-identity-js");
//Configuring pool data of Cognito Identity Pool
const poolData = {
UserPoolId: "us-east-2_aCvZbFzeS",
ClientId: "4nv2krchr77pbrq3cpk0q0kknu"
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
AWS.config.region = "us-east-2";
const attributeList = [];
attributeList.push(
new AmazonCognitoIdentity.CognitoUserAttribute({
Name: "email",
Value: "sampleEmail#gmail.com"
})
);
userPool.signUp(
"sampleEmail#gmail.com",
"SamplePassword123",
attributeList,
null,
function(err, result) {
if (err) {
console.log(err);
return;
}
const cognitoUser = result.user;
console.log("user name is " + cognitoUser.getUsername());
}
);
const data = JSON.parse(event.body);
const headers = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
};
const response = {
statusCode: 200,
headers: headers,
"Content-Type": "application/json",
body: JSON.stringify(data.age)
};
callback(null, response);
};
//I keep receiving this error when attempting to hit the endpoint with Postman:
"errorMessage": "Uncaught error in your 'hello' handler",
"errorType": "TypeError",
"stackTrace": [
"TypeError: fetch is not a function"
You can definitely use Cognito from Lambda! Source: have done it.
You may not be able to use the AWS Cognito JS SDK from Lambda nicely, though.
The AWS Cognito JS SDK appears to be designed for client-side applications, where fetch is a built-in. You have installed node-fetch, but the SDK is not loading it because it doesn't think it needs to, because it is expecting it to be built-in.
I see two options:
If you aren't particularly attached to JS, you could use another language where you are confident that the library is designed and tested for server-side applications.
If you are attached to JS or have a large sunk cost, you could hack up the AWS Cognito JS SDK locally before deploying the code to Lambda to make it require node-fetch or otherwise make it functional server-side.
This thread has a good description of the same issue and some workarounds; probably the best one for you is:
global.fetch = require('node-fetch')
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
in your script, which should make it appear as a built-in to the SDK's code without hacking up the internals.

Trying to call cognito token from postman script

Working within Postman I'm trying to call a jwtToken from Cognito using node's aws-sdk but I'm getting the following error:
There was an error in evaluating the Pre-request Script: Error: Cannot find module 'aws-sdk'
This is my scrypt:
const AWS = require('aws-sdk');
var authenticationData = {
Username: 'username',
Password: 'password',
};
var authenticationDetails = new
AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId: 'us-east-1_xxxxxx',
ClientId: 'xxxxx'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username: 'username',
Pool: userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
var accessToken = result.getAccessToken().getJwtToken();
var idToken = result.idToken.jwtToken;
},
});
pm.globals.set("token", idToken);
So far the only commands I ran where: brew install node
 and npm install aws-sdk
I'm sure I'm missing some sort of configuration to call the module, Any help will do! Thnx!
Seems like Postman Sandbox cannot obtain a script's external dependencies.
You can leverage eval() Javascript function to import additional Javascript code by having the code stored within a postman global or environment variable.
Example: eval(postman.getGlobalVariable('aws-sdk-code'))
See TIP #5: http://blog.getpostman.com/2017/07/28/api-testing-tips-from-a-postman-professional/
Also, you might encounter a later issue once the code executes as it seems you are requesting for AmazonCognitoIdentity class in the amazon-cognito-identity-js package.
https://www.npmjs.com/package/amazon-cognito-identity-js

How to verify AWS S3 bucket details is valid or not using Nodejs?

In my application am having a form, in that user should enter their AWS S3 details. Before saving the details I have to check whether the account is correct or not. I am building this application using Node js,mongodb,Angular js.
UPDATE
aws s3 details are
{
"aws": {
"key": "",
"secretkey": "",
"region": "",
"bucket_name": ""
}
Using the AWS Javascript SDK you should make a headBucket request using the supplied credentials in the constructor.
const AWS = require('aws-sdk');
const s3 = new AWS.S3({ credentials, region });
s3.headBucket({
Bucket: "examplebucket"
}, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
})
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#headBucket-property
If the request is successful you have access.
You can try making a s3 client using aws s3 sdk and then try calling method that your app needs to. For example if you need to create an object in the provided bucket, Then simply try making test object and see if it works. You can pass stored credentials like in below links:
https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-json-file.html
https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html
const AWS = require('aws-sdk');
const fs = require('fs');
AWS.config.update({ accessKeyId: '...', secretAccessKey: '...' });
//make sure you pass proper credentials
const s3 = new AWS.S3();
fs.readFile('test-file.txt', function (err, data) {
if (err) { throw err; }
var base64data = new Buffer(data, 'binary');
const params = {
Bucket: '...bucket-name-here...',
Key: 'test-object.txt',
Body: base64data
};
s3.putObject(params, function(err, data) {
//check error or data to see if you have proper permissions.
});
});
Also see these examples for putObject method:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
https://gist.github.com/homam/8646090
If you have the user credentials then you can use aws-sdk which is available as node module.
set the credentials in sdk config and use S3 API to verify the given buckets.
npm install aws-sdk
var AWS = require('aws-sdk');
AWS.config = new AWS.Config();
AWS.config.accessKeyId = "accessKey";
AWS.config.secretAccessKey = "secretKey";
AWS.config.region = "region";
var s3 = new AWS.S3();
You can refer the site for better understanding

Resources