Login user from backend (firebase + node.js) - node.js

I'm looking for a way to send user's email and password from the client to my Firebase Backend Functions and make the login from the backend, I find out info about Id tokens and stuff like that, but I need just simple function that receives email and password and make the request to Firebase Auth.
On firebase late versions you might have something like "signInWithEmailAndPassword(email, password)", So I'm looking the exact operation just for the SDK for node.js, and I dont seems to find one.
Thank you.

Firebase Auth only allows you to use signInWithEmailAndPassword on client side.

You should not authenticate users on backend (although it's possible), because it might have unintended consequences, but you can authenticate user with signInWithEmailAndPassword on the browser and then verify "ID Token" to your backend.
Frontend (using firebase):
const payload = {
uid: user.id,
idToken: await user.getIdToken()
}
//send payload to server
Backend (using firebase-admin):
const decodedToken = await getAuth(app).verifyIdToken(body.idToken)
//check decodedToken.uid equals body.uid
You can read more into "ID token verification" here:
https://firebase.google.com/docs/auth/admin/verify-id-tokens#web

Related

Can an open id connect id token be used to authenticate to an api

I am building a mern application.
the backend built using express exposes an api which users can create data and access the data they have created.
I want to allow users to sign in with google and get authorization to create and access the resources on this api which i control (not on google apis).
I keep coming across oauth 2 / open id connect articles stating that an Id token is for use by a client and a access token provided by a resource server should be used to get access to an api.
e.g. https://auth0.com/blog/why-should-use-accesstokens-to-secure-an-api/
the reason stated for this is that the aud property on the id token wont be correct if used on the api.
I realise that some sources say: that if the spa and api are served from same server and have same client id and therefore audience I can use and id token to authenticate to the api, but I am looking to understand what I can do when this is not the case?
I feel using oauth2 for authorization is overkill for my app and I cant find any information about how to use open id connect to authenticate to my api.
Surely when you sign in to Auth0 authourization server using google it is just requesting an open id connect id token from google?
I am wondering if using Authorization Code Grant flow to receive an id token on the api server would allow me to authenticate a user to my api?
in this case would the api server be the client as far as open id connect is concerned and therefore the aud value would be correct?
I can generate an url to visit the google oauth server using the node googleapis library like so:
const { google } = require("googleapis");
const oauth2Client = new google.auth.OAuth2(
'clientid','clientsecret',
"http://localhost:3000/oauthcallback",//this is where the react app is served from
);
const calendar = google.calendar({ version: "v3", auth: oauth2Client });
const scopes = ["openid"];
const url = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: "offline",
// If you only need one scope you can pass it as a string
scope: scopes,
});
async function getUrl(req, res) {
console.log(url)
res.status(200).json({
url,
});
}
and use the following flow.
You are not supposed to access any API's using the ID-Token. First of all the life-time of the ID-token is very short, typically like 5 minutes.
You should always use the access-token to access API's and you can using the refresh token get new access-tokens. The ID-token you can only get one time and you use that to create the local user and local cookie session.
If you are using a SPA application, you should also consider using the BFF pattern, to avoid using any tokens in the SPA-Application
see The BFF Pattern (Backend for Frontend): An Introduction
I agree with one of the commenters that you should follow the principle of separation of concern and keep the authorization server as a separate service. Otherwise it will be a pin to debug and troubleshoot when it does not work.

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

node js JWT get current user

I work on app with an authentication using Node JS, JWT and Sequelize for the API and I'm using React JS / redux for the front part. I'm successfully implemented the login/logout/register parts for the application, but now I need to access to the current_user logged in.
I put a JWT in the localStorage, but I want to have access to the user ID, user email, user name and more informations about my user currently logged in.
Should I use Cookies ? LocalStorage ? Or should I create a currentUser method in my API ?
I'm a bit lost with this, if someone could help me find some usefull resources or advices !
Thank !
If you put that information in the payload of the JWT, then you can get it without decoding on the server or needing the secret, and can therefore put the token in LocalStorage for use whenever. By the spec, a JWT is <headerINfo>.<payloadInfo>.<signature>. So on the client, you can just do:
// given a payload object of { username: 'bob', userid: 1, email: 'bob#example.com' }
const tokenParts = token.split('.');
const encodedPayload = tokenParts[1];
const rawPayload = atob(encodedPayload);
const user = JSON.parse(rawPayload);
console.log(user.username); // outputs 'bob'
Obviously, this info is available to any client that has access to the Token, so you only want to put stuff in the payload that that's OK for.
Storing the token in LocalStorage is fine. If you need to fetch the user details, create an endpoint in your API such as getUser. You can then use jwt.decode(accessToken, JWT SECRET HERE) and return the decoded value (which will be your user) assuming the accessToken is valid.
You can make a middleware if you haven't already that will ensure that user info is always available to those routes that require it:
const auth = jwt({
secret: JWT_SECRET,
userProperty: 'payload',
algorithms: ['HS256']
});
module.exports = auth;
Then you should have req.payload with user details. Alternatively you can check the Authorization property in your headers depending on how you set up your app.
When logging in, server should send token and user data, you can store that data in Redux store. Then simply request data from there. When user reloads page, send API request with JWT token and server should return user data, that you will again put in Redux store.

RESTful Authentication using Ember and Node/Express and json web tokens, how can I verify users' email addresses?

Here's my workflow:
Ember action on new user signup is to send Express the user data.
Express then creates a web token, encrypts the contents, and puts a link in an email that it sends with Nodemailer.
The email is sent successfully.
User goes to their email and clicks on the link.
On clicking the link, Express gets the token from the query params decrypts and decodes the token, and creates a New User.
All of the above works ok, but here is where I'm stuck. I'd like for the user to be redirected back to the Ember frontend, and automatically logged in. This is the bit I'm stuck on. Here is the Server code:
<!-- language: lang-js -->
signUpUser.save().then(function(model) {
res.set('location', 'http://localhost:4200/login');
res.status(302).json({user:model})
});
I'm able to successfully redirect back but I'm not able to capture the json data in my ember code, and I'm not sure where or how in Ember I can call a login action in the given scenario.
I have a feeling my approach may be wrong? Because email verification is a common thing. Also, I'd rather not have to make users input their form information more than once.
Here's how I'm doing this:
In Express, add query params to the response url after saving user:
signUpUser.save().then(function(model) {
res.set('location', 'http://localhost:4200/login?token=' + token + 'id=' + id);
res.status(302).json({user:model})
});
In Ember, in the /login route beforeModel hook, grab the query params:
beforeModel: function(transition) {
console.log(transition.queryParams.token);
if (transition.queryParams.token) {
this.controllerFor('login').send('assignTokenToUser', transition.queryParams.token, transition.queryParams.id);
};
if (!Ember.isEmpty(this.controllerFor('login').get('token'))) {
return this.transitionTo('courses');
}
}
I'm not sure this is the Ember Way, but the key here is being able to grab queryParams of the transition object.
Can you provide some more information about the authentication system you are using? It sounds like you are using a JWT to convey some information about email verification, but how do you authenticate API requests? Do you use another JWT that is stored in a cookie? If so you want to create this cookie when they arrive with their verification JWT.
Disclaimer: I work at Stormpath and we have a fully-featured email verification workflow in our service. While we don’t have an integration for Ember.js, we do have a good overview of JWTs and Single Page Applications, it may be useful at a high level: Token Based Authentication for Single Page Apps
We do have an Angular integration, if you have the option to switch frameworks: Stormpath AngularJS SDK

Oauth2 flow without redirect_uri

I am creating an Android/iOS app which communicates with a Node.js server and would like to identify them securely on my server using Google (and/or Facebook) and OAuth2. I've looked at the following documentation: https://developers.google.com/+/web/signin/server-side-flow
I do not need authorization, I only need authentication (I only want to make sure that the person calling my Node.js service is the person they say they are). To achieve this, if I understand properly, I have to let the user log in using Google on the client side, this will give them an authorization_code which they can then give to my server. My server can then exchange that code for an access_token, and therefore retrieve information about the user. I am then guaranteed that the user is the person they say they are.
The Google documentations (link above) says: "In the Authorized redirect URI field, delete the default value. It is not used for this case.", however, for my server to exchange the authorization_code for an access_token, it needs to provide a redirect_uri, am I missing something?
The redirect_uri is useless for Unity games, for instance (since logging in with Google simply opens a new "window", which is closed when logged in, no redirection involved).
TL;DR
How do you use OAuth2 to authenticate users between my client and my server without redirection?
TL;DR How do you use OAuth2 to authenticate users between my client and my server without redirection?
You can't. OAuth requires that the user is directed to an authorization (and possibly login) screen, and then redirected back to your app.
EDIT 20/12/22. See comment below regarding latest status
Have you looked at this documentation? https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi
Choosing a redirect URI
When you create a client ID in the Google Developers Console, two
redirect_uris are created for you: urn:ietf:wg:oauth:2.0:oob and
http://localhost. The value your application uses determines how the
authorization code is returned to your application.
http://localhost
This value signals to the Google Authorization Server that the
authorization code should be returned as a query string parameter to
the web server on the client. You may specify a port number without
changing the Google Developers Console configuration. To receive the
authorization code using this URL, your application must be listening
on the local web server. This is possible on many, but not all,
platforms. If your platform supports it, this is the recommended
mechanism for obtaining the authorization code.
I had this problem and it took me ages to find the "postmessage" solution that Nepoxx mentions in the comments of the accepted answer here.
For clarification, here's what worked for me.
Follow steps 1-6 here: https://developers.google.com/identity/sign-in/web/server-side-flow
Install googleapis library npm install --save googleapis
For the server-side token exchange do this:
var googleapis = require('googleapis');
var OAuth2 = googleapis.auth.OAuth2;
var oauth2Client = new OAuth2(
GOOGLE_SSO_CLIENT_ID,
GOOGLE_SSO_CLIENT_SECRET,
'postmessage' // this is where you might otherwise specifiy a redirect_uri
);
oauth2Client.getToken(CODE_FROM_STEP_5_OF_INSTRUCTIONS, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
});
The redirect_uri can be a URL with a custom URL scheme for which the client registered a handler. This is described here: What's a redirect URI? how does it apply to iOS app for OAuth2.0?. It is not so much about "redirecting" it is about a callback endpoint to your app.
And it become really easy if you use VueJS with https://github.com/guruahn/vue-google-oauth2
Client side
import GAuth from 'vue-google-oauth2'
Vue.use(GAuth, {
clientId: 'xxxxxxx.apps.googleusercontent.com',
scope: 'profile',
})
async signWithGoogle() {
const code = await this.$gAuth.getAuthCode() //
console.log(code ) // { code: 'x/xxxxxxxxxx' }
// send the code to your auth server
// and retrieve a JWT or something to keep in localstorage
// to send on every request and compare with database
}
Server side
import { google } from 'googleapis'
const oauth2Client = new google.auth.OAuth2(GOOGLE_ID, GOOGLE_SECRET, 'postmessage')
google.options({ auth: oauth2Client })
async function getAccount(code) {
// the code you sent with the client
const { tokens } = await oauth2Client.getToken(code)
oauth2Client.setCredentials(tokens)
const oauth2 = google.oauth2({ version: 'v2' })
const { data: { id } } = await oauth2.userinfo.get()
// there you have the id of the user to store it in the database
// and send it back in a JWT
}

Resources