How to unprotect one route in firebase functions - javascript

I used this code sample here, in which a snippet is attached:
const validateFirebaseIdToken = (req, res, next) => {
cors(req, res, () => {});
// console.log("Check if request is authorized with Firebase ID token");
if (
(!req.headers.authorization ||
!req.headers.authorization.startsWith("Bearer ")) &&
!(req.cookies && req.cookies.__session)
) {
console.error(
"No Firebase ID token was passed as a Bearer token in the Authorization header.",
"Make sure you authorize your request by providing the following HTTP header:",
"Authorization: Bearer <Firebase ID Token>",
'or by passing a "__session" cookie.'
);
res.status(403).send("Unauthorized");
return;
}
let idToken;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer ")
) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split("Bearer ")[1];
} else if (req.cookies) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.status(403).send("Unauthorized");
return;
}
My question is, after including this in my index.js file for my firebase functions, all of my routes are protected. I have a GET route that I do not need protected, and in fact, makes it more complicated when I protect the route. How can I avoid protecting this route:
app.post("/getArt", (req, res) => {
admin
.firestore()
.collection("art")
.where("date", "==", req.body.day)
.limit(1)
.get()
.then((data) => {
let arr = [];
data.forEach((doc) => {
arr.push(doc.data());
});
return res.json(arr);
})
.catch((err) => console.error(err));
});
I have several other endpoints in the file that I want to stay protected via Authorization tokens, but this one route does not need to be protected.

The code you're showing here is only showing the definition of validateFirebaseIdToken, but not where it's being used. In the linked example, you can see it's being applied to the entire app:
app.use(validateFirebaseIdToken);
If you do this, it will apply to all of your routes - that's the way middleware works at the application level.
If you want this middleware to apply to only certain routes, you should pass it as an argument to app.post() as shown in the documentation for router-level middleware. You will want to pass the middleware function to app.post() so that it describes all of the processing to be done for that route.
app.post("/routeYouWantProtected", validateFirebaseIdToken, (req, res) => { ... })
Alternatively, you could rewrite the middleware to only decode the token if it was provided. If you do that, you can check inside your handler function if it was, and decide what to do with that outcome.

Related

How do I make node.js using express find a JSON Web Token (JWT) inside of a response header cookie in user's browser?

I have a question on how to search for my JWT token inside of a user's browser cookies.
Below I have some code that searches the user's browser for cookies in the response header, but I am not sure how to make the code more specific and search for the JWT token within the cookie and verify that it is an actual JWT token that was a assigned to that user.
const jwt = require('jsonwebtoken');
const router = require('express')();
const cookieParser = require('cookie-parser');
router.use(cookieParser());
module.exports = function(req,res,next){
const token = req.header('Cookie');
if (!token) {
return res.status(403).send('Access Denied');
}
try{
const verified = req.header('Cookie');
req.user = verified;
// const verified = jwt.verify(token, process.env.TOKEN_SECRET);
// req.user = verified;
next();
} catch (err) {
res.clearHeader;
res.status(403).send('Invalid Token');
}
};
I hope I didn't misunderstand your question and waste a bunch time.
Short Answer: How to retrieve information
Use req.body or req.headers. If something will contain the token or authentication details, then it's one of these two.
Full Auth Walkthrough:
To get the JSON Web Tokens you first have to generate them. Wouldn't recommend implementing your own token authentication though. I'll show how to create a whole authentication system here step by step.
For simplicity, let's say we have an exported route in a file auth.js, this route will be a sub-route domain.com/auth, an array of all active refreshTokens and the jwt:
const express = require("express")
const jwt = require("jsonwebtoken")
let route = (exports.route = express())
let refreshTokens = []
What we will do is generate a long-lasting refresh token, which users will be able to use to generate a smaller 15-minute access token. Afterwards, you generate a new access token with the refresh token and so on. But to get the refresh token you need to login or register. Users can also logout killing the refresh token.
route.post("/token", async (req, res) => {
// Input: Refresh Token
// Output: Access Token Generation
})
route.post("/login", async (req, res) => {
// Input: User, Password
// Output: Refresh Token
})
route.delete("/logout", async (req, res) => {
// Input: Token to Remove
})
Let's start with the end. You have a refresh token, you won't to destroy it. Simply filter the array against this token and submit a status. The token becomes unusable after it's cleared from the array, that's the goal here.
route.delete("/logout", async (req, res) => {
refreshTokens = refreshTokens.filter((token) => token != req.body.token)
res.sendStatus(204)
})
With me so far? Now let's jump back to the start. If you log in with an email and password, if they're wrong respond with an error message, if they're correct receive the tokens.
route.post("/login", async (req, res) => {
const username = req.body.username
const password = req.body.password
// This is just a quick demonstration,
// you would have to use the bcrypt hash
// or other hash/salt methods. DO NOT
// STORE passwords plaintext
// Not existent user = Unauthorized
if (username != 'admin') return res.sendStatus(401)
// Wrong Password = Forbidden
if (password != 'abc123') return res.sendStatus(403)
const user = {
id: 0,
username: username,
password: password
}
const accessToken = generateAccessToken(user)
const refreshToken = generateRefreshToken(user)
let result = {
success: true,
accessToken: accessToken,
refreshToken: refreshToken,
}
res.send(result)
})
Now how do we sign the JSON web tokens? Let's take a look at the two methods used here:
function generateAccessToken(content) {
return jwt.sign(content, process.env.ACCESS_TOKEN_SECRET, {
expiresIn: "15m",
})
}
function generateRefreshToken(content) {
const token = jwt.sign(content, process.env.REFRESH_TOKEN_SECRET)
refreshTokens.push(token)
return token
}
Both use some sort of environment tokens, but why? That's the token you will have to generate once for the back end. It will be used as a public key. We simply generate the access tokens for 15 minutes and push the refresh tokens to the array.
route.post("/token", async (req, res) => {
const refreshToken = req.body.token
if (refreshToken == null) return res.sendStatus(401)
if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403)
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403)
res.json({ accessToken:
generateAccessToken({
id: 0,
username: user.name,
password: user.password
})
})
})
})
We verify the refresh token, if it exists and it is valid, return a new access token for 15 minutes. That's it for the token part, you can login (create refresh token), retrieve an access token and logout (kill refresh token)
How to Use: Authenticate and Authorize
Admin pages should return 403 while the forum board should be different whether you're logging as a guest or an actual user. The first one is authentication, the second authorization.
Let's create two functions for each. Express is quite handy with the next() function
exports.authenticate = function (req, res, next) {
const authHeader = req.headers["authorization"]
const token = authHeader?.split(" ")[1]
jwt.verify(token || "", process.env.ACCESS_TOKEN_SECRET, (err, user) => {
req.user = err ? {} : user
next()
});
};
exports.authorize = function (req, res, next) {
const authHeader = req.headers["authorization"]
const token = authHeader?.split(" ")[1]
if (token == null)
return res.sendStatus(401)
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403)
req.user = user
next()
})
}
Now you're done with the whole authentication system (aside some cleanup's) and probably the registration system. Let's make use of it.
Client side you can create a REST api like so:
POST http://localhost:8081/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "abc123"
}
# Returns refresh and access token.
###
DELETE http://localhost:8081/auth/logout
Content-Type: application/json
{
"token": "REFRESH_TOKEN"
}
# Logs out a user.
###
POST http://localhost:8081/auth/token
Content-Type: application/json
{
"token": "REFRESH_TOKEN"
}
#
# This is how you can provide the access token
# when making a request to say a forum api
#
GET http://localhost:8081/forum/api/board/0
Authorization: Bearer ACCESS_TOKEN
Usage:
route.get("forum/board/:id", authenticate, async (req, res) => {
res.send(req.user)
})
Expected Output when going to localhost:8081/forum/board/7 authenticated:
{id:0,username:"admin",password:"abc123"}
Otherwise:
{}
Nevertheless, do not try implementing your own authentication. Really, you shouldn't.
Source
https://www.youtube.com/watch?v=mbsmsi7l3r4

Starting to use JWT in node.js

I am trying to work with JWT in a node.js application.
I followed this tutorial.
But I am facing problems at the level of the middleware:
function authenticateToken(req, res, next)
which is at the timing 10:30 in the video.
When I run the code I always have:
authHeader == null
I have tried various things to find other possible forms for req.headers['authorization'] like req.headers.authorization but to no avail.
I need to say that I have a route in my app that allows me to login and that I use right before using the route hitting the middleware. But in any case the authHeader keeps being null.
What should I do to make sure I get a valid authHeader as expected for the app to work as in the tutorial and move forward?
In case this may be useful, here is the complete code for the middleware function:
function authenticateToken(req, res, next) {
// Get the jwt access token from the request header.
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if (token == null) {
console.log('authenticateToken-401')
return res.sendStatus(401)
}
jwt.verify(token, 'myBigSecret', (err, user) => {
console.log(err)
if (err) {
console.log('authenticateToken-403')
return res.sendStatus(403)
}
req.user = user
next()
})
}
And also the code that is run when a user logs in:
app.post('/login', async function(req, res) {
Parse.initialize(process.env.APP_ID);
Parse.serverURL = process.env.SERVER_URL;
Parse.User.logIn(req.body.usrname, req.body.password, {
success: user => {
// Let us handle this using JWT.
const jwtUser = {name: req.body.usrname}
const accessToken = jwt.sign(jwtUser, 'myBigSecret')
res.setHeader("Authorization", 'Bearer '+accessToken);
res.json({accessToken: accessToken})
},
error: (user, error) => {
console.log('Error: '+error);
res.render('pages/login.ejs', {});
},
});
});
You need to set headers in your request, looks like you didn't set headers in your request
On the login code above you have Signed using jwt so that it can be used when client app want to access some restrictable routes on your backend
Now whenever clients want to access route from frontend you need to set authorization token in ajax as header Something like this
$.ajax({
url: "/apiyouwanttoaccess",
method: "GET",
headers: { Authorization: "Bearer " + Cookies.get("token") }
})
The above ajax code should be somewhere around js code where you want to access secure routes.
You have already signed token on your login(2nd code snippet) so it can be used within specific user by setting the cookies

How to get request headers in express?

I writing code for authorization. After click on the button for login I create a new header with
res.header('Authorization', token)
admin_login router:
router.post('/admin_login', async (req, res) => {
const adminDB = data.admins;
const admin = adminDB.find(admin => req.body.email === admin.email)
if (!admin) return res.status(400).send('Email in not found!')
if (admin.password !== req.body.password) return res.status(400).send('Invalid password')
const token = jwt.sign({ admin }, 'the_secret_key')
res.header('Authorization', token)
res.redirect('/admin')
})
I wont to recieve Authorization header after login in admin router, but I wont recieve it. I see Authorization header in
Code for verification:
const jwt = require('jsonwebtoken')
module.exports = (req, res, next) => {
const token = req.header('Authorization')
console.log(token)
if (!token) return res.status(401).send('Access Denied')
try {
const verified = jwt.verify(token, 'the_secret_key')
req.admin = verified
next()
} catch (e) {
res.status(400).send('Invalid token')
}
}
first img: headers in admin router
second img: headers in admin_login router after click on the button for login
Please, help me
As far as I can tell from the documentation, there is no header method on the Express Request object or the Node.js IncomingMessage it extends.
The Express documentation says that to retrieve a header, you use get:
req.get(field)
Returns the specified HTTP request header field (case-insensitive match).
So:
const token = req.get("Authorization");
Or you could use the headers object from IncomingMessage:
const token = req.headers["authorization"];
(Note the lower case, Node.js makes the header names lower case when building the object.)
You should use the req.get(headerName) method

How to check user authentication in GET method?

My frontend is Reactjs and backend Nodejs and expressjs with Postgresql database.
I have a simple signin page which checks user authentication from database.
In my Reactjs app, after signing in, user uploads files and then there is a GET method on my nodejs which send files (res.sendFile) when user wants to get the file from server. It is just a simple
<img alt='none' src=`http://example.com/source/${filename}` />
in my Reactjs app which does request for file.
Problem: if I am not logged in to my app, I can paste the URL in my browser and the file is displayed which is NOT what I want.
I want the GET method on nodejs should check for authentication of user either if the user is signed in or not, and then only fulfill the request of sending file.
How can I do it?
Should I use some kind of POST method in my Reactjs app before it makes any GET request to the same location of GET method then parse the information then handle it to app.get etc...
This is my nodejs + expressjs.
server.js
app.post('/signin', (req, res) => { signin.handleSignin(req, res, db, bcrypt)})
app.get('/source/:fileid', (req, res) => {
const { fileid } = req.params;
res.sendFile(__dirname + /data/ + fileid);
});
./controllers/signin.js
const handleSignin = (req, res, db, bcrypt) => {
const { email, password } = req.body;
if (!email || !password ) {
return res.status(400).json('Incorrect form submission');
}
db.select('email', 'hash').from('login')
.where('email', '=', email)
.then(data => {
const isValid = bcrypt.compareSync(password, data[0].hash);
if (isValid) {
return db.select('*').from('users')
.where('email', '=', email)
.then(user => {
res.json(user[0])
})
.catch(err => res.status(400).json('unable to get user'))
} else {
res.status(400).json('wrong credentials' )
}
})
.catch(err => res.status(400).json('wrong credentials'))
}
module.exports = {
handleSignin: handleSignin
}
You have to implement authentication mechanism via cookie or session. After successful login you will set a cookie in the browser and on each HTTP req you will have access to cookie data.
Create a middleware function which will check for valid cookie data in req object for each API requests.
If a user is not logged in and trying to access the URL you won't receive data in the cookie and you can unauthorized (401) the access to that particular resource.
// On valid credentials, you can set the cookie like this
res.cookie(cookieName, cookieData, cookieOptions);
and middleware function can go like this
function checkSession(req, res, next) {
if(!req.cookies || !Object.keys(req.cookies).length){
res.sendStatus(401)
}
else next();
}
You can find more details on how to use cookie-parser here.

Returning HTML or JSON based on header type Nodejs

My nodeJS Api needs to return HTML or Json based on the header. If the header is:
Accept = application/json
The Api needs to return Json else my Api needs to return a HTML file.
this is the code I use on my routes:
var app = express();
app.use('/auth', require('./routes/Authenticate'));
In the Authenticate file I catch /login, and do the login stuff. if it succeeds I redirect to /users. In the /users I check for the Accept with an if statement:
router.get('/users', function(req,res){
if(req.get('Accept') === 'application/json'){
res.json({ success: true, user: req.user });
} else {
res.render("account/profile") //redirect to the file
}
});
This works(from this solution) but is there a better way? Because there are like 20 endpoints and the application is growing and this will be a mess for every endpoint.
you can split these actions into 2 functions. One to verify the content type and an other to doing your actions.
router.get('/users', checkIfJson, action);
function checkIfJson(req, res, next) {
if(!(req.get('Content-Type') === 'application/json')) {
res.render("account/profile");
return;
}
next();
}
function action(req, res) {
res.json({ success: true, user: req.user });
return;
}
If you write your code like that you can reuse your checkIfJson into other routes.
You can wrap router.get function with a custom function
router.wrappedGet = function (path, callback) {
router.get(path, function (req, res) {
if (req.get('Content-Type') === 'application/json') {
res.render = res.json;
}
callback(req, res);
})
};
Here's what I've doneā€”seems pretty straightforward.
router.get("/foo", HTML_ACCEPTED, (req, res) => res.send("<html><h1>baz</h1><p>qux</p></html>"))
router.get("/foo", JSON_ACCEPTED, (req, res) => res.json({foo: "bar"}))
Here's how those middlewares work.
function HTML_ACCEPTED (req, res, next) { return req.accepts("html") ? next() : next("route") }
function JSON_ACCEPTED (req, res, next) { return req.accepts("json") ? next() : next("route") }
Personally I think this is quite readable (and therefore maintainable).
$ curl localhost:5000/foo --header "Accept: text/html"
<html><h1>baz</h1><p>qux</p></html>
$ curl localhost:5000/foo --header "Accept: application/json"
{"foo":"bar"}
Notes:
I recommend putting the HTML routes before the JSON routes because some browsers will accept HTML or JSON, so they'll get whichever route is listed first. I'd expect API users to be capable of understanding and setting the Accept header, but I wouldn't expect that of browser users, so browsers get preference.
The last paragraph in ExpressJS Guide talks about next('route'). In short, next() skips to the next middleware in the same route while next('route') bails out of this route and tries the next one.
Here's the reference on req.accepts.

Categories

Resources