signout user aws cognito - node.js

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/

Related

Cognito Login MFA SMS Verification Problem

I’m new to AWS Cognito and currently having 2 problems with it:
in making the login using MFA SMS to work.
in setting the SMS MFA flag to enabled/disabled
I’m using Node.js and Amazon Cognito Identity SDK for JavaScript [1][2]
My app login flow:
User submits a mobile number (then is redirected to TOTP verification form)
At this time, the app hits my API endpoint
and in turn, invokes the function to send TOTP if the number is registered
User receives the TOTP code via SMS
User input TOTP code
The app hits my API endpoint
and then invokes the function to verify the TOTP
If TOTP verified, then my API returns Cognito Token to the app
I have managed to send the TOTP via SMS but I can't find a way to verify the TOTP it in order to get the Cognito Token. The sample[3] shown in the Github page uses a prompt, so everything is contained within that function. However, I cannot do that in my case since the 2 calls need to happen on 2 different screens. I've tried separating the sendMFACode() function, but instead of verifying the TOTP, it sends another SMS.
I'm hitting the wall and there is limited docs/resources I've found so far.
Reference:
https://github.com/aws-amplify/amplify-js/tree/master/packages/amazon-cognito-identity-js
https://github.com/amazon-archives/amazon-cognito-identity-js/blob/master/src/CognitoUser.js
https://stackoverflow.com/a/51394500
My user pool settings is 'MFA optional' so I can accommodate login by email method (doesn’t send SMS for login by email. I know that’s odd.) So far, I've managed to set the MFA to required in the code using enableMFA() function. However, the Github page says that function enableMFA() is now deprecated, instead, use setUserMfaPreference() instead. I tried that and got a "SUCCESS" but when I open it in AWS web console, it still states SMS MFA disabled. Is there something I'm missing here?
Here’s my code:
cognitoUser.sendMFACode(VerificationCode, {
onSuccess: (result) => {
console.log(result)
},
onFailure: (error) => {
console.log(error)
}
})
debug result:
{
"code": "InvalidParameterException",
"Name": "InvalidParameterException",
"Message" "Invalid Parameter Required Session"
}
Note: I received the invalid params exception although successfully getting the SMS.
I'm struggling with the MFA process myself at the moment, but for the enabling/disabling of MFA via the SDK I've found a way to get that to work with the following code.
setUserSettings: (req, res) => {
const { access_token, enable } = req.body;
/* ISOLATE START HERE */
const params = {
AccessToken: access_token,
MFAOptions: [
{
AttributeName: "phone_number",
DeliveryMedium: enable == "true" ? "SMS" : null
}
]
};
cognitoIdentityServiceProvider.setUserSettings(params, (err, data) => {
if (err) {
console.log(err);
res.status(400).json({ message: err.message });
} else {
console.log(data);
res.status(200).json(data);
}
});
/* ISOLATE END HERE */
},
This is using Node and Express. You could isolate the main functionality and get it to work in the context you like, I've marked the place to cut the code for isolation. The documentation for this call is located here https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#setUserSettings-property.
I'll circle back if I get the MFA working appropriately.

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

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);
}
}

CRON Node.js Gmail API script

How do I properly setup Gmail API script that sends emails?
I am about to use this method and I started building my script from this quickstart guide.
Is there alternative way to do this without using OAuth 2 validation? Or a way to validate once for all?
Well, in using Gmail APi with your app, you need to use OAuth 2.0 because all request to the Gmail API must be authorized by an authenticated user. And if you notice the quickstart, there is a step here that you need to create a credentials/Outh client ID to make this API work.
For more information, there is another way to authorize your app with Gmail. You can do it with the help of Google+ Sign-in that provide a "sign-in with Google" authentication method for your app.
While asking for authorization from GMail, OAuth 2.0 gives one access token and one refresh token. To avoid validation every time, store the access token. Use the refresh token to get the new access token after it is expired (access token expires every one hour).
Read about this process here: https://developers.google.com/identity/protocols/OAuth2
I found solution using JWT to authorize OAuth2.
You need to have admin account to create Domain wide delegation service account. Then in Developer console you need to download service key JSON file which you load as credentials.
First fetch all users like this: (here you need to use account with admin directory rights)
const google = require('googleapis');
const gmail = google.gmail('v1');
const directory = google.admin('directory_v1');
const scopes = [
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/admin.directory.user.readonly'
];
const key = require('./service_key.json');
var authClient = new google.auth.JWT(
key.client_email,
key,
key.private_key,
scopes,
"authorized#mail.com"
);
authClient.authorize(function(err, tokens){
if (err) {
console.log(err);
return;
}
directory.users.list(
{
auth: authClient,
customer: 'my_customer',
maxResults: 250,
orderBy: 'email'
}, (err, resp) => {
if (err) {
console.log(err);
return;
}
console.log(resp);
});
});
Then you need to fetch Thread lists (100 per request (page)). And for each thread object you need to call get method for full thread. When using Gmail API authorize as user you want to fetch emails from. In request as userId use value 'me'.

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.

Using Resource Owner Password in Oauth2orize module

I am developing an app with an mobile client for which I want to deploy Oauth2orize as Oauth server an use authenticate with Resource Owner Password way. But I am not able to understand how the flow should be. I searched for lots of examples but could not find one where this use.
What should the flow be to give a token to the client?
This came a little late but I think this post can help someone else. I just spent a week trying to implement this because oauth2orize mix all the oauth flows in one file in the samples so is difficult to figure out which one to use to obtain the desired result.
To start answering your question you ask about a resource owner password grant as described here. This should give you a head start on the steps defined by oauth2 to exchange a username(or email) and password for a token and optionally a refresh token.
Step 1: The client requests a token using username and password to the authorization server
Step 2: The authorization server issues a token to the client if the client has valid credentials
So you start sending a request to an authentication resource in application/x-www-form-urlencoded format containing a username, password and grant_type params, optionally you can also use scopes. Oauth2orize provides the server.token() function which generates a middleware to parse this request.
app.post('/token', server.token(), server.errorHandler());
But before this stage you should have the server created and configured. I usually use a different file and use module.exports to pass the middleware back to the app.
authorization.js file
// Create the server
var server = oauth2orize.createServer();
// Setup the server to exchange a password for a token
server.exchange(oauth2orize.exchange.password(function (client, username, password, scope, done) {
// Find the user in the database with the requested username or email
db.users.find({ username: username }).then(function (user) {
// If there is a match and the passwords are equal
if (user && cryptolib.compare(password, user.password)) {
// Generate a token
var token = util.generatetoken();
// Save it to whatever persistency you are using
tokens.save(token, user.id);
// Return the token
return done(null, /* No error*/
token, /* The generated token*/
null, /* The generated refresh token, none in this case */
null /* Additional properties to be merged with the token and send in the response */
);
} else {
// Call `done` callback with false to signal that authentication failed
return done(null, false);
}
}).catch(function (err) {
// Signal that there was an error processing the request
return done(err, null);
})
};
// Middlewares to export
module.exports.token = [
server.token(),
server.errorHandler()
];
Later in your app you write something like this
var auth = require('./authorization');
app.post('/token', auth.token);
This is a basic example of how you do it. Moreover you should enable some sort of protection on this endpoint. You could use client credential validation with the passport-oauth2-client-password module. This way the client variable in the oauth2orize.exchange.password function will contain information about the client that is trying to access the resource enabling an extra security check for your authorization server.

Resources