Setting up user authentication can be easily achieved in apache by setting up .htaccess and .htpasswd However I am developing an new web application and I am using express.js or react.js to create a web server. I searched user authentication online but they are long projects for email style logins like here. All I need is a single universal username and password. When a user browse to my website a pop appear for the username and password. (Same like .htaccess style)
As per my understandings you need http server authentication in nodejs.
I know one npm module. link
And it's pretty easy to setup also.
Basic example
// Authentication module.
var auth = require('http-auth');
var basic = auth.basic({
realm: "Simon Area.",
file: __dirname + "/../data/users.htpasswd"
});
// Creating new HTTP server.
http.createServer(basic, (req, res) => {
res.end(`Welcome to private area - ${req.user}!`);
}).listen(1337);
If anyone is interested to do in ES2016 then follow the following code. Set the babel etc to make it work.
import express from 'express';
import auth from 'http-auth';
// Configure basic auth
const basic = auth.basic({
realm: 'SUPER SECRET STUFF'
}, (username, password, callback) => {
callback(username == 'admin' && password == 'password');
});
// Set up express app
const app = express();
// Create middleware that can be used to protect routes with basic auth
const authMiddleware = auth.connect(basic);
// Protected route
app.get('/', authMiddleware, (request, response) => {
response.send('you made it!');
});
app.listen(3000);
Related
I am using sessionStorage and also firebase authentication for email and password.
In my server.js I am wondering how can I make it so that if a user is not logged in they cannot access a route, or rather be redirected to the login route instead.
The firebase sdk I am using is only usable via the client side. Is there any documentation to help that I have been unable to find.
Please let me know if I need to clarify my question more and I will do my best to do so.
Here is my server.js:
const express = require('express');
const admin = require('firebase-admin');
const bcrypt = require('bcrypt');
const path = require('path');
let serviceAccount = require("./1234.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
let staticPath = path.join(__dirname,"public");
const app = express();
app.use(express.static(staticPath));
app.use(express.json());
app.get('/login', (req,res) => {
res.sendFile(path.join(staticPath, "form.html"));
})
app.get('/seller', (req,res) => {
// if(!user) res.redirect('/login');
res.sendFile(path.join(staticPath, "seller.html"));
})
Edit:
So far the only thing that helps me with this is by creating a seller.js
and inserting this code here, but I am unsure of if this method is safe or if there is a way to hide this from being manipulated:
body = document.getElementsByTagName('BODY')[0];
user = JSON.parse(sessionStorage.user);
if(user && user.seller){
console.log('Allow Access')
} else{
console.log('Deny')
body.innerHTML = `
<div class="sticky" id="nav"></div>
<div style="padding:300px">
<center>You do not have permission to view this page.</center>
</div>
<div id="footer"></div>
`;
}
You can use the firebase-admin package to verify the token on the server. If the verification passes, you can continue with route logic. To make things simple, you could wire up a middleware in Express that verifies the token, rather than repeating the calls for authenticated routes.
Relevant documentation: https://firebase.google.com/docs/auth/admin/verify-id-tokens#web
Also, you should not rely on client-side scripting to verify a user's authentication status if you're trying to restrict resources on a server. It would be trivial for someone to find the endpoints being used, and, assuming there's no logic on the server to verify the user, they could potentially retrieve sensitive information.
here is the thing. I have my app that has separated back-end and front-end into two different project (back-end with Node-JS, and front-end with Vue-JS). I got a home page with a button that should redirect the user to the authentication server to log in.
So i made an "on-click" function in my homePage.vue that will consume a function from my api.
I've read a lot of documentation and seen some examples. But to be honest, during those past weeks, even if I tried, I still do understand nothing about how authentication works.
You can see below the js file i wrote inspired by examples that i found:
auth.js
const express = require('express');
const { auth } = require('express-openid-connect');
const app = express();
app.use(
auth({
issuerBaseURL: 'myDomainUrl',
baseURL: 'http://localhost:8080',
clientID: 'myClient_ID',
secret: 'aaaaaaaaaaaaaaaaaaaa',
idpLogout: true,
authRequired: false,
})
);
module.exports = app;
There is also the route with the function that I try to implement:
auth.route.js
module.exports = app => {
var router = require("express").Router();
router.get('', location.replace('myDomainUrl'));
app.use('/api/login', router);
};
I don't know if it's important but my back-end runs on the port 4000 and my front-end runs on the port 8080.
If someone can explain me how I can do to make my authentication work and what I have to change, it would be great.
Thanks you in advance, I hope I was clear enough about my problem. If not, do not hesitate to ask me what was not clear.
I am using axios to call the firebase cloud functions I have created with express/firebase. I realized even without using and without importing the firebase and without initializeApp(firebaseConfig) in my frontend, I was able to call my cloud functions with the axios with the following way:
axios.get('https://us-central1...')
How I create cloud functions:
index.js
module.exports ={
...require('./controllers/foo')
}
foo.js
const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
const app = express();
//Middleware
app.use(express.json());
app.use(cors({ origin: true}));
app.get('/', async (req, res) => {
// Function goes in here
});
app.post('/', async (req, res) => {
// Function goes in here
});
app.put('/:id', async (req, res) => {
// Function goes in here
});
app.delete('/:id', async (req, res) => {
// Function goes in here
});
exports.foo = functions.https.onRequest(app);
Is this a normal behavior or is it due to way of working of express (cors as middleware) or axios somehow? Or do I just have to add auth and firestore rules into my database? Whatever the reason is then what is the meaning of the firebaseConfig information that firebase provides for us?
PS: Just to play around I have tried to add firestore rules. I have added the following rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
Even though in rules playground I was not able to retrieve anything, from my application I still got the query I wanted and I don't know why.
Yes that is absolutely normal. The HTTP Functions are made so you can integrate you Firebase Project with any (absolutely any) other language or platform by using HTTP requests as the trigger name shows. As you already do, you can even implement express apps behind those requests.
With those you gave full power and responsebility what goes trough them and with that comes also the need for you to know who is calling your HTTP requests. if you want to secure them you can use the link from the comment and check how to make Authorized HTTP Ednpoinds.
If you want to call those just from your App Frontend I would recommend to use Callable Firebse Functions because those will work only from your app and also provide the data of the user who called them in the context of your cloud function triggered.
I've been Googling for some time, but can't find any useful answers. I'm trying to get a subdomain for an api on my website api.example.com. However, all answers said that I needed to change my DNS to redirect api.example.com to example.com/api, which I don't want. Is it possible to just serve api. instead of redirect to /api? How would I go about doing that?
I'm using express.
I don't want to use any other packages that aren't built-in.
const path = require('path'),
http = require('http'),
https = require('https'),
helmet = require('helmet'),
express = require('express'),
app = express();
const mainRouter = require('./routers/mainRouter.js');
// security improvements
app.use(helmet());
// main pages
app.use('/', mainRouter);
// route the public directory
app.use(express.static('public'));
app.use(/* API subdomain router... */)
// 404s
app.use((req, res) => {
res.status(404).sendFile(path.join(__dirname, "views/404.html"));
})
I recommend You to use nginx and separate api service.
But because of some reasons You cannot avoid it (or You don't want it, cause You just want show prototype to customer ASAP).
You can write middleware that will catch host from header and forward to some custom router:
1) /middlewares/forwardForSubdomain.js:
module.exports =
(subdomainHosts, customRouter) => {
return (req, res, next) => {
let host = req.headers.host ? req.headers.host : ''; // requested hostname is provided in headers
host = host.split(':')[0]; // removing port part
// checks if requested host exist in array of custom hostnames
const isSubdomain = (host && subdomainHosts.includes(host));
if (isSubdomain) { // yes, requested host exists in provided host list
// call router and return to avoid calling next below
// yes, router is middleware and can be called
return customRouter(req, res, next);
}
// default behavior
next();
}
};
2) api router as an example /routers/apiRouter.js:
const express = require('express');
const router = express.Router();
router.get('/users', (req, res) => {
// some operations here
});
module.exports = router;
3) attach middleware before / handler:
const path = require('path'),
http = require('http'),
https = require('https'),
helmet = require('helmet'),
express = require('express'),
app = express();
const mainRouter = require('./routers/mainRouter');
// security improvements
app.use(helmet());
// ATTACH BEFORE ROUTING
const forwardForSubdomain = require('./middlewares/forwardForSubdomain');
const apiRouter = require('./routers/apiRouter');
app.use(
forwardForSubdomain(
[
'api.example.com',
'api.something.com'
],
apiRouter
)
);
// main pages
app.use('/', mainRouter);
// route the public directory
app.use(express.static('public'));
// 404s
app.use((req, res) => {
res.status(404).sendFile(path.join(__dirname, "views/404.html"));
})
P.S. It does the same that in express-vhost package, look at the code
There is an official vhost plugin for Express.js: https://github.com/expressjs/vhost
What you are describing, having multiple domains/hostnames hosted/handled by a single server, is typically referred to as a "vhost" (virtual host).
It will definitely be possible to point just the subdomain (api.example.com) to your api server.
DNS doesn't control subdirectories so a DNS entry of example.com/api would be invalid
If you have an IP address for your server you will need to add an A record with the value: api.example.com.
If you have a domain name for your server you will need to create a CNAME record.
I have an angular 5 app which is hosted on heroku. Currently the users can access the HTTP version of the app.
How can I force the users to be redirected to HTTPS version even if they access HTTP?
What I have tried:
app.use(function (req, res, next) {
let sslUrl;
if (process.env.NODE_ENV === 'production' &&
req.headers['x-forwarded-proto'] !== 'https') {
sslUrl = ['https://myapp.herokuapp.com', req.url].join('');
return res.redirect(sslUrl);
}
return next();
});
I have placed the above code in my node.js server but it has not worked.
The users are unable to use the app over HTTP as they get a 403 ERROR
I used the "force-ssl-heroku" package at https://github.com/rangle/force-ssl-heroku, works like magic and very easy integrated.
Just require inside your start entry point script:
var forceSsl = require('force-ssl-heroku');
and use it like that:
app.use(forceSsl);
Deploy and enjoy.