Google Plus auth for REST API - node.js

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.

Related

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

firebase.auth().currentUser returning null

In the html file that I have for the sign-in page, I perform the authentication using Firebase and on successful authentication, I redirect the given user to the homepage. When I call firebase.auth().currentUser in the express file, I use for rendering and routing pages, I get undefined or null for the current user.
Can anyone help me understand what the issue might be?
This is how I perform the authentication:
firebase
.auth()
.signInWithEmailAndPassword(temail, tpass)
.then(function(firebaseUser) {
window.location.href = "http://localhost:5000/homepage";
})
.catch(function(error) {
window.alert("incorrect pass");
});
This is the code that I have in my express file:
app.get("/homepage", (req, res) => {
var user = firebase.auth().currentUser;
console.log("USER IS " + user);
res.render("menu", { title: "Welcome" });
});
Backend code doesn't have a sense of "current user". When you sign in on the frontend, the current user is only known on that client. It isn't known on the backend. If you want the backend to know which user is signed in, the client will have to send an ID token to the backend for it to verify. The documentation for the Firebase Admin SDK is used for that on the backend. The client must send the ID token to in the request to your route, and the code handling that route must verify the token in order to know the user that made the request. From the documentation:
If your Firebase client app communicates with a custom backend server, you might need to identify the currently signed-in user on that server. To do so securely, after a successful sign-in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity and authenticity of the ID token and retrieve the uid from it. You can use the uid transmitted in this way to securely identify the currently signed-in user on your server.
When the user lands on a new page, Firebase automatically restores their previous authentication state. But to do so, it may have to contact the server, which means that it may take a few moments. While Firebase is restoring the state, auth().currentUser will be null.
To ensure you get the correct authentication state, you should use an authentication state listener, as shown in the documentation on getting the current user:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});

Is there a way to do Single Sign On for the Microsoft Bot framework webchat via the DirectLine API and an Azure Web App using an Oauth2.0 abstraction

The idea here is to do a SPA application with an implementation of a Microsoft Bot which will be embedded into an Azure Web App. The application will use an Oauth2 openId connect login that will take in other logins such as Google, GitHub, Facebook and Microsoft credentials.
I know that I can authenticate the conversation to include a userId in the tokenization of the bot. That documentation is here.
The question I have from that is what about if I have a login pathway to a dialog such as is explained here.
The issue I see is that having a site login won't activate the needed token for logging into the bot so it can call other api services. Or can I extract the token from the web browser effectively and use that for the token I need to obtain user info such as email or login and hash to access a service?
I want the same route protection and activation for inside of the bot if the user signs into the site outside of the bot without having them sign into the bot again.
What can I do to have the site token act upon the botframework itself?
But, if the user where using the bot in another channel per se Facebook they would still have to log into the bot to go down certain dialog pathways.
The solution that I think is best is to take the notes from the original blog post I posted in the original question.
So the plan is, with this code.
// Generates a Direct Line token
server.post('/directline/token', (req, res) => {
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
headers: {
'Authorization': `Bearer ${process.env.directLineSecret}`
},
json: {
User: {
Id: userId
}
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
res.send({
token: body.token
});
} else {
res.status(500).send('Call to retrieve token from DirectLine failed');
}
});
You would grab the id and user info from the localstorage and use that to pass the id into the conversation token creation as the controller above is illustrating. Once this has been passed into the bot the bot know is "aware" of the userId and you can now use this information as authentication for the bot then on out instead of querying it yourself. Also, this id would be used for the users other api calls downstream too.

Phonegap + Hello.js (server side authentication)

I have a Phonegap application that is communicating with Nodejs server.
Also for the Facebook\Twitter login I'm using Hello.js library (which is very easy to use by the way).
Unfortunately this library only makes client side login (authentication), so at server side I don't know if the user is valid or not (have been looged in with Facebook\Twitter).
Edit:
After the user is logged in (client side), Hello.js provides the user credentials, with a Facebook unique user ID, but I don't know how to pass it safely to the server, or generally if its a good idea to use it as a user id in my DB.
I'm looking for a simple example that will check the validity of the login at server side too.
Thanks.
If you are using https then sending the id to your server will be fine. What you can do is just check to see if that unique id already exists in your DB and return that users data (if needed) or create a new account.
I would also recommend creating a JWT (JSON Web Token) on the server side and sending that back to the app to be stored in local storage and used to validate all future requests to your node server. You can implement that method pretty easily if you use the jwt.verify method as middleware on all of your routes.
Here is a quick example:
var jwt = require('jsonwebtoken');
var jwtValidation = function(req, res, next) {
var token = req.body.jwt_token;
if (token) {
jwt.verify(token, 'yourSecretKeyHere', function(err, decoded) {
if (err) {
// Error when checking JWT - redirect to unauthorized
res.redirect('/unauthorized');
} else if (decoded.id) {
// Token that was passed in has been decoded
// Check your DB for the decoded.id
// Complete any other needed tasks then call next();
next();
} else {
// Something else went wrong - redirect to unauthorized
res.redirect('/unauthorized');
}
});
} else {
// No token present - redirect to unauthorized
res.redirect('/unauthorized');
}
};
module.exports = jwtValidation;
This is the main idea as I figured:
In the Phonegap application, after the user has logged in, this function will be called:
hello.on('auth.login', function(r){
var token = r.authResponse.access_token;
}
now, you can send only the token to the server, and the server will get the user credentials directly from Facebook.
For example, in Facebook, call this usr:
https://graph.facebook.com/me?access_token={token}

Caching Azure Mobile Service Token for Future Requests - Facebook and Error 400

Following the various examples in the documentation authentication using the azure mobile client services for javascript works fine. I am unable to persist the returned auth token so that it can be used to see if the user is still logged in on the next request.
Starting with the initial request to login all works fine:
client.login("facebook").then(function (results) {
console.log(results);
localStorageService.add('currentUser', client.currentUser);
}, function (error) {
console.log(error);
});
This returns an object that looks like this in the results and client.currentUser:
{
mobileServiceAuthenticationToken: "IHAVETRUNCATEDIT",
userId: "Facebook:1210971539"
}
I am storing this object into localstorage (or a cookie) so that the next time a login is required I can check this token exists and pass it back to the login client service (see the token part). According to various pages the format of this at least for the facebook provider should be in the form:
{"access_token": "THEACCESSTOKEN"}
Therefor this is what is being submitted when calling the login the second (persisted) time. The token being passed in is the same one that we placed in localstorage.
var currentUser = localStorageService.get('currentUser');
client.login("facebook",
{ "access_token": currentUser.mobileServiceAuthenticationToken })
.then(function (results) {
console.log(results);
}, function (error) {
console.log(error);
});
The error returned is:
Error: The Facebook Graph API access token authorization request failed with HTTP status code 400
I am not quite following how on the subsequent request (next day) to check to see if the user's token is still good.
It seems that the way to do this is the following:
client.currentUser = {
userId: currentUser.userId,
mobileServiceAuthenticationToken: currentUser.mobileServiceAuthenticationToken
};
There is no need to login again but just set the currentUser with the saved credentials.
The following blog post I used as a reference.
At least for Facebook, the access token returned from successful MobileServiceClient.login() request is not valid for further communication with Facebook API. Seems this is due the changes with FB Graph API done in March 2014 with their moving to version 2.2. What you can do is to perform manual login witn FB rather than use MobileServiceClient.login() and then set the obtained username and JWT to MobileServiceClient like you did:
client.currentUser = {
userId: "Facebook:xxx",
mobileServiceAuthenticationToken: "<your-users-JWT>"
};

Resources