Custom node.js api's authorization with aws cognito permission rules - node.js

I'm able to register and authenticate users via amazon-cognito-identity-js
and aws-sdk and the process to implement it was pretty much straightforward and simple. Now I have few APIs built in node.js, how would I authorize these APIs using Cognito policies (i.e permissions) and wrap them around logged in user where I'm already able to get idToken, accessToken and refreshToken.
Other things I came across while reading the documentation and watching video tutorials are aws-lambda and aws-api-gateway. I understood that aws-lambda is a way to define methods that can communicate with no-sql database (i.e AWS dynamo) and api-gateways to access those lambda functions and interact with DB and that all is serverless.
My question is, what if I have my own APIs written in Node.js and I need to integrate Cognito user pools with it and to protect those resources using AWS Cognito policies, user groups and roles, is it possible? Please guide me in this regard.

You can use the same authentication token which you get when you authorize the user to authorize your node.js APIs too. It can be done in many ways. One of the ways can be calling a isAuthenticated() method in each constructor which will check if there is a valid token or not.
export interface LoggedInCallBack{
isLoggedIn(message:string, loggedIn:boolean):void;
}
isAuthenticated(callBack : LoggedInCallBack){
if(callBack==null){
throw("User login service call cannot be null");
}
console.log("Is authneticated");
var cognitoUser = this.cognitoUtil.getCurrentUser();
console.log("Is cognito user empty? " + cognitoUser);
if(cognitoUser!=null){
cognitoUser.getSession(function (err, session){
if(err){
console.log("Couldn't get session:" + err);
callBack.isLoggedIn(err, false);
}
else{
console.log("Session is: " +session);
callBack.isLoggedIn(err, true);
}
})
}
else{
callBack.isLoggedIn("Cannot retrieve the current user", false);
}
}

Related

Pub sub with REST API - the request is missing a valid API Key

I am using the following code to do a test publish to pubsub
var data = {
file: 'ciao',
content_type: 'image/png'
};
needle
.post('https://pubsub.googleapis.com/v1/projects/topic:publish', data, {
multipart: true
},
function(err, resp) {
if (err)
console.log('Error: ' + err.message);
else
console.log('OK.' + JSON.stringify(resp.body));
});
But I get the error
{"error":{"code":403,"message":"The request is missing a valid API key.","status":"PERMISSION_DENIED"}}
Do I need a service account authorized to PubSub? Any hint on how to solve this issue?
You will need to verify the credentials you are using and the account permissions that those credentials have.
One of the popular approach is to have a service-account.json file with the credential information and use it as an enviroment variable GOOGLE_APPLICATION_CREDENTIALS. You can get that file when creating a credential account for your pub/sub application. Examples on how to create that you can find it on this link under Setting up authentication for server to server production applications..
Now you also need to verify the permissions and roles you credential account have. For cloud pub/sub there are lot of roles, like roles/editor or roles/pubsub.editor for the scope of your test run. You can even use a sample called testing_permissions from the official documentation to test your access. For a full lists of permissions and roles please see this site.
For more details you can check the access and authentication page

Correct security implementation strategy for multi-provider authentication and api route protection with Nodejs

We have a small React Web application with a Fastify backend (but can very easily apply the same concepts to Express js) that feeds the db data to the frontend clients.
Some of the API routes are protected by means of middleware checking the validity of the access token.
Our auth strategy used to be only credentials based, ie using Username and Password.
We have recently implemented authentication by Google and Facebook as well and for the most part, they seem to work well.
We now want to replicate our web application to a mobile application.
I have never written a mobile app and am currently busy learning React-Native.
I managed to implement Google Auth on the IOS app.
Here is the problem. I am not sure how to implement token validation for mobile clients on protected routes using the Google access token. I am however able to successfully implement validation if I use the id token.
My understanding is that validation on protected routes should be done using the access token, otherwise, what is the point of the access token in the first place.
Anyway, my understanding of the entire authentication flow is clearly flawed.
I just need some advice, samples or references to articles that I can read that can clarify the concepts to me.
Just the summary of what I hope to achieve:
a single backend with various (but not all) protected routes
the backend to support credential-based, Google and Facebook authentication strategies
backend middleware to check the validity of the access token
backend should serve both native mobile and web applications.
Sample of a protected route:
fastify.get(
"/protectedroute",
{ preValidation: [fastify.googleverify] }, //<--- Middleware only checking Google validity currently. TODO: Multi-provider middleware to be created
async (request, reply) => {
reply.code(200).send({ message: "authenticated", user: request.user });
}
);
Sample of middleware to check google id_token validity:
fastify.decorate("googleverify", async function (request, reply, done) {
if (!request.raw.headers.authorization) {
return done(new Error("Missing token header"));
}
const bearer = request.raw.headers.authorization;
const token = bearer.split(" ");
try {
const userinfo = await verify(token[1]);
request.user = userinfo;
return done();
} catch (error) {
reply.code(401).send({ message: "Not Authorized", error: error });
}
});

signout user aws cognito

I have Integrated AWS SDK in Node.js. I'm following this documentation to do some actions using the SDK. However, I didn't find any method to sign out a user in that documentation. We have GlobalSignOut and AdminUserGlobalSignOut. But in my case, I want to sign out a user for a particular session only.
I understand that you would want a particular session of a user in your Cognito User Pool to terminate, instead of terminating the sessions in all devices.
To achieve this use-case, you can utilize the ForgetDevice[1] and AdminForgetDevice[2] API calls. These API calls make the refresh token associated with a device invalid, and as a result, the app will not be able to refresh new tokens and will force the user to sign-in successfully again.
A code snippet of Cognito's AdminForgetDevice API call in JavaScript is as follows:
var params = {
DeviceKey: 'STRING_VALUE', /* required */
UserPoolId: 'STRING_VALUE', /* required */
Username: 'STRING_VALUE' /* required */
};
cognitoidentityserviceprovider.adminForgetDevice(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
For a more detailed explanation on remembering devices in Amazon Cognito, kindly refer to the following AWS Mobile Blog post[3].
References
[1]. https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ForgetDevice.html
[2]. https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminForgetDevice.html
[3]. https://aws.amazon.com/blogs/mobile/tracking-and-remembering-devices-using-amazon-cognito-your-user-pools/

AWS lambda serverless website session maintaining

I developed a website using node.js as back-end.
Recently I am trying to make it serverless and deploy to lambda.
I will re-write most of my code but just haven't figured out how to maintain the session after user logged in. I was using "express-session" module and the session data is all recorded in the database.
To be honest I don't have a very deep understanding on sessions.
I searched on google and did not find what I need.
Does anyone have some sample code on maintaining sessions using lambda?
or any resources. Thanks a lot!
In the Amazon Cognito Identity SDK for Javascript, check in particular the use case 16, it shows how to retrieve the Cognito current user. You can use this function to pass from page to page the current user attributes.
var poolData = {
UserPoolId : '...', // Your user pool id here
ClientId : '...' // Your client id here
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function(err, session) {
if (err) {
alert(err);
return;
}
console.log('session validity: ' + session.isValid());
// other AWS actions ...
});
}
There are multiple mechanisms available in HTTP to maintain session
state within web applications, such as cookies (standard HTTP header),
URL parameters, URL arguments on GET requests, body arguments on POST
requests, such as hidden form fields (HTML forms), or proprietary HTTP
headers.
Source: Session Management Cheat Sheet
AWS Lambda has nothing to do with session management unless you want to re-invent the wheel and write Lambda functions that store/retrieve session variables from the database, in which case I'd recommend that you use Amazon Cognito for session management. See Amazon Cognito Identity SDK for JavaScript.
Either you use Cognito or your own way of session management, beware that lambda calls share their runtime and share static state between them. Make sure that your design considers this fact and architect your session sharing accordingly.
https://www.linkedin.com/pulse/does-lambda-call-share-any-commonstate-santhosh-gandhe/

Google Plus auth for REST API

I'm trying to create a rest api for a service I'm working on.
The service has two parts to it - the website and the mobile client. Basically, the mobile device keeps its location up to date via the api, the website displays the data via the api.
Seeing as my application only targets Android, I'm hoping to use 'Sign in with Google' as the authentication mechanism for both the mobile and website clients.
The API is using Node.js and Express.js. I'm running into trouble when generating new user accounts though. Seeing as I don't want to trust data from the client, my expected sign up process was something like this:
Through the website:
User visits website, hits 'Sign up with Google'.
User accepts the app request to see their Google details.
Website gets a google auth token back, which it sends to the api.
API contacts google with that auth token to get the user details.
API creates a new user in the database along with my own form of access token.
API returns my own access token to the client for future request signing.
Through the Android app:
User downloads the app, runs and hits 'Sign up with Google'.
User accepts authorisation step presented by google.
App gets a token, which it sends to the API.
API contacts google with that auth token to get the user details.
API realises the user exists and registers this new device with that user.
API returns my own access token to the app for future request signing.
I'm running into a lot of trouble here as soon as the token gets to the server though. Every time I use the token generated, I just get an error 'Invalid Credentials'.
Initially I started to use Passport.js, but what I found was this. In the documentation it states setup happens like so:
passport.use(new GoogleStrategy({
returnURL: 'http://www.example.com/auth/google/return',
realm: 'http://www.example.com/'
},
function(identifier, profile, done) {
User.findOrCreate({ openId: identifier }, function(err, user) {
done(err, user);
});
}));
But when I log the contents of 'identifier' it is actually something like
https://www.google.com/accounts/o8/id?id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
I assume the ID is something unique to me but I can't seem to discover exactly what it is. Furthermore I don't know if it is time-limited or will last forever. As a final problem, I don't even know if I can get that same value when signing up via Android because I don't know where the value comes from. It's not the kind of API access token that I was expecting. When I output the contents of profile, it's just my name and email address - nothing that I can use for contacting the Google API with to verify the user.
The above solution I don't like anyway because it means the server hosting the client site has to make an api request in order to pass the id to the api. Or it sends the id details to the client so that it can pass them on to the api server. Or, the website server puts it into the api database, which is also a bad solution.
So next I figured I would use the javascript library from the Google sign in docs. I have something like this:
Website Client:
<script type="text/javascript">
function createUser(token)
{
$.ajax({
url:"http://api.example.com/user",
dataType: 'jsonp',
data: 'token='+token,
success:function(json){
alert("Success: "+json);
},
error:function(jqXHR, textStatus, errorThrown){
alert("Error "+textStatus+" "+errorThrown);
}
});
}
function signinCallback(authResult)
{
if(authResult['access_token'])
{
document.getElementById('signinButton').setAttribute('style', 'display: none');
alert('RES: '+JSON.stringify(authResult));
createUser(authResult['access_token']);
}
else if(authResult['error'])
{
alert('There was an error: ' + authResult['error']);
}
}
</script>
Node API user handling function:
function(req, res)
{
var callback = req.query.callback;
if(callback == null)
{
res.send("{valid:false}");
}
else
{
var token = req.query.token;
if(token == null)
{
res.send("{valid:false}");
}
else
{
var oauth2Client = new OAuth2Client('xxxxxx', 'xxxxxx', '');
oauth2Client.credentials = {
access_token: token
};
googleapis
.discover('plus', 'v1')
.execute(function(err, client){
if(client == null)
{
console.log("Client is null");
}
else
{
var request1 = client.plus.people.get({ userId: 'me' })
.withApiKey('xxxxxx');
request1.execute(function(err, result){
console.log("Result: " + (err ? err.message : result.displayName));
});
}
});
res.set('Content-Type', 'text/javascript');
res.send(callback+"({ret:'User Test'});");
}
}
}
This works fine on the client side - I see the alert box with my access token and some other details. The trouble is that when I call the google api functions on my api server for getting the user details, I get 'Invalid Credentials' returned. I assume this is because I generated the access token in javascript for a website and I'm using it from somewhere else.
So, that pretty much leaves me out of ideas. Is there an easy way to achieve this that I'm missing? I just want to be able to generate a token from a website and from an Android app that I can use on the server for validating the user's details. The generated token doesn't even need to work on the website or the Android app, just from the api server. The API server can't do the process of directing the user to Google for authorisation though because the user doesn't directly interact with it.

Resources