Firebase Authentication Microservice - node.js

I am trying to write a REST microservice in Node.js that would deal with user authentication (among some other things) requests coming from different platforms.
What I would like is for the device to remember which user is signed in and keep the session for itself only. What currently happening is that I am able to login only one user at a time; if another user logs in from another device, the new user is returned as the currentUser. It's my first time using Firebase Authentication so I am very confused.
Here's the code for the login endpoint:
async signInUser( req, res, next ) {
var user = firebase.auth().currentUser;
if ( !user) {
var email = req.body.email;
var password = req.body.password;
// sign user in: if login fails, send error message as response
user = await firebase.auth().signInWithEmailAndPassword( email, password)
.catch( function( error) {
res.send( error.message);
});
}
// login successful: send user object as response
res.send( user);
}

Firebase have custom authentication you can pass your user unique id to firebase and genrate token for perticular user so you can able create unique session for different user and you can refer below link
https://firebase.google.com/docs/auth/admin/create-custom-tokens

Related

Appwrite authentication - "createJWT is not a function" error

I am trying to build a Node.js server-side signup function for user authentication. The data for the user is being sent via "req.body" and the authentication database is provided by Appwrite.
The signup function should:
Create a user with the credentials provided in the request body.
Return the user details, such as the username and email.
Generate and return a token (cookie/JWT)
I am encountering issues with the Appwrite documentation and would appreciate guidance on building this function.
When trying to POST a new user using the Users API, an error of
createJWT is not a function
is produced, and when using the Account API, an error of
User (role: guests) missing scope (account)
is produced.
Here's the code I have:
const sdk = require('node-appwrite')
const client = sdk.Client()
client
.setEndpoint(endpoint)
.setProject(projectId)
.setKey('...')
const users = sdk.Users(client)
async function signup(req, res) {
try {
const { email, username } = req.body
let { password } = req.body
password = await bcrypt.hash(password, SALT_ROUNDS)
const result = await users.createBcryptUser("unique()", email, password, username)
// Create a token
// Combine data
res.send(userWithToken)
} catch (err) {
error('Failed to signup', err)
throw new Error(err)
}
}
The Users API is intended to be used in an admin perspective rather than as a user. You can use the Account API to execute things on behalf of a user, but the JWT token is typically generated client side and passed to the server, where you can call client.setJWT().

What's the best way to add social login (Sign in with Google) to existing email/password app and database?

I want to integrate sign in by google to an app that already has account signup and login.
I followed some youtube tutorial and I now have a working sign in by google working on my frontend. It returns a JWT upon successful log in. Decoding it gives me an object that contains an email, name, family name, pfp, and some other properties that I don't know what they are for.
What do I do with that ?
In my express server I have a register route
router.post("/register", async (req, res, next) => {
try {
// expects {email, password} in req.body
const { password, email } = req.body;
const user = await User.create(req.body);
const token = jwt.sign(
{ id: user.dataValues.id },
process.env.SESSION_SECRET,
{ expiresIn: 86400 }
);
res.json({
...user.dataValues,
token,
});
} catch (error) {
if (error.name === "SequelizeUniqueConstraintError") {
return res.status(401).json({ error: "Email already exists" });
} else if (error.name === "SequelizeValidationError") {
return res.status(401).json({ error: "Validation error" });
} else next(error);
}
});
Login route is similar.
The database I used was postgres with sequelize ORM, User.create(...) basically just creates a user and stores the hashed password to verify later.
As you could see, if they use google auth it wont have a password while regular signup would. Do I just allow User.create to also create a user if password not given? Would that be secure? What is the correct way to go about this?
A user who logs on to your app must
either type their email address and password into your login form, then your app checks the password hash and creates the token = jwt.sign(...). I would recommend that your JWT also contains the email address, preferably in the same format that Google's JWT uses.
or start a Google logon flow, then Google sends your app a JWT. During this flow, no password hash is looked up from your user database, but if the Google email address is not already in your database, it is a new user for whom you must insert a record into your database (only email address, no password).
The JWT should have an iss claim that tells you whether it was issued by your app or by Google. In Google's case, the JWT is signed by Google, and in your /register and /login routes you must verify the signature with jwt.verify using Google's public key (presumably this). (Actually, registration and login don't differ much if you use a third party authentication service like Google's.)
I assume that in both cases you store the JWT in a session cookie
res.cookie("jwt", token, {httpOnly: true});
and every subsequent request must repeat the signature verification of the JWT
try {
var jwt = jwt.verify(req.cookies.jwt, publicKey);
if (jwt.exp <= new Date().getTime() / 1000)
throw "expired";
// Token verification succeeded
} catch(e) {
// Token verification failed
}
(either with your own app's public key or with Google's, depending on the iss). Only after successful verification does the request count as authenticated, and you can then retrieve the user record from your user store based on the email address.
The password (hash) in your use database is thus optional, but even users with a password could use Google for logon.

Recognizing first user login (Node.js/JWT)

I've developed an authentication system who consists on Node.js/Express and JWT.
I need to recognize the first time a user logs on and show him a message relevant only on that one time.
How can I do it?
This is the auth middleware i'm using to verify the user:
const auth = (req, res, next) => {
const token = req.header("x-auth-token")
if (!token) return res.status(401).json({ msg : "No token, authorization failed" })
try {
const decoded = jwt.verify(token, config.get("jwtSecret"))
req.user = decoded
next()
}
catch {
res.status(400).json({ msg : "Token is not valid" })
}
}
For this approach, you should let User to login with Basic Authentication for the first time (Email/Username + Password). After you validate that the user is the valid user on the backend, then you should store his _id in JWT and send him that JWT. On each next request, that user should send that JWT, and you can use your JWT authentication (but for initial login, you should go with Basic Authentication).

How to get an idToken that is valid for development from firebase without having to spin up my frontend?

I am working on some firebase functions. This one will check if an user is logged in in firebase first. However this is a bit of a hassle in development. Now I need to login on the frontend first to get the id_token, pass it to my function url, then see the result.
The process I am following is described in the official docs: https://firebase.google.com/docs/auth/admin/verify-id-tokens
node.js
const admin = require('firebase-admin');
admin.initializeApp();
module.exports = function( request, response ) {
if( !request.query.id_token )
response.status(400).json({message: 'id token has not been provided'});
admin.auth()
.verifyIdToken( request.query.id_token )
.then( token => {
// TODO: redirect to payment portal
return response.status(200).json({message: 'Success'});
})
.catch( error => {
return response.status(401).json({message: 'You are currently not logged in as an authorised user'});
})
}
Is there a way to get an id_token that is valid from firebase without having to spin up my frontend? Good and simple alternatives solutions are welcome too.
NOTE: I am using the firebase emulators during development.
Since you're using the Firebase emulators you may create a fake user and retrieve an id token programmatically. The code below creates and logs in a user and returns an id_token that will be accepted by your function.
var firebase = require("firebase/app");
require("firebase/auth");
// Initialize Firebase and connect to the Authentication emulator
var firebaseConfig = {
// Insert Firebase config here
};
firebase.initializeApp(firebaseConfig);
firebase.auth().useEmulator('http://localhost:9099/');
// Create a fake user and get the token
firebase.auth().createUserWithEmailAndPassword("example#example.com", "password")
.then((userCredential) => {
console.log("User created")
});
firebase.auth().signInWithEmailAndPassword("example#example.com", "password")
.then((userCredential) => {
console.log("User logged in")
userCredential.user.getIdToken().then((idToken) => {
console.log(idToken)
});
});

Web and mobile, where to store token/cookies

I'm developing a program which consists of a back-end server, a mobile application, and a web application. I have added JWT token to my mobile application and I'm storing in async storage. However, I cannot find an appropriate way to do in web server.
I have created a middleware file for creating and checking token's validity. In my API route's I'm doing the following
router.post('/:url', middleware.checkToken, (req, res, next)=>
{
...
}
So, every time I call this API, middleware file checks for the token. In my mobile application, I'm storing the token in the async storage and pass it to the web server.
However, in the browser side, I want to store the token inside a cookie rather than storing in local storage. How can I do this without changing my code?
This is mobile login API.
router.post('/login', (req,res,next) => {
let username = req.body.username;
let password = req.body.password;
User.findOne({'username' : username})
.exec()
.then(doc =>{
if(doc.validPassword(password))
{
let token = jwt.sign({
id: doc.id,
email: doc.email,
},
config.secret,
{ expiresIn: '24h' // expires in 24 hours
}
);
res.status(200).json({
success: true,
message: 'Authentication successful!',
token: token
});
}
else{
// invalid credentials
res.send(403).json({
success: false,
message: 'Incorrect username or password'
});
}
})
})
I don't want a separate file for web login. I just want to use the same code, without copying to another file.
Should I write another different code for both mobile and web but one send a cookie, other sends plain token? Is there any way to achieve this with simple solution?
In short:
Mobile users send credentials to the mobile login page and they receive token.
Web users send credentials to the web page and they receive a cookie (a token resides inside the cookie). I don't want to have separate code for login.
You can easily add a new cookie in the front-end with document.cookie = ... (MDN document cookie)
In your middleware, you just have to parse for cookie instead of some Bearer token or whatever.

Resources