Adonisjs - Add basic auth to Static server middleware? - node.js

Is there a way to protect statically served assets in adonis via basic auth?
It's not possible to add middleware to route that will hit statically served files from /public dir...
So, for example:
I have /public/docs/index.html
after serving adonis and hitting localhost:3333/docs I'll get content of index.html
I want to browser to prompt basic auth so I tried adding:
Route.get('/docs').middleware(['auth:basic'])
This will not work due: http://adonisjs.com/docs/4.0/http-context#_request_flow
Beacuase serve static is inside Server middlewares which happens before route hit.
Any ideas how to achieve this?

After writing this question I realized I just need to write my own server middleware that will run before static middleware... So I ended doing this:
app/Middleware/Server/StaticAuth.js
'use strict'
const auth = use('basic-auth')
const config = use('Adonis/Src/Config').get('auth.staticAuth')
const validConfig = config && config.protectedUrls.length
class StaticAuth {
async handle({request, response}, next) {
// if there is no valid config... skip this middleware
if(!validConfig) return await next();
// check if currently visited url is matching protectedUrls
if(!request.match(config.protectedUrls)) return await next()
// access native node request/response
const req = request.request
const res = response.response
// gather credentials
const credentials = auth(req)
if (!credentials || credentials.name !== config.username || credentials.pass !== config.password) {
res.statusCode = 401
// send Basic Auth header so browser prompts user for user/pass
res.setHeader('WWW-Authenticate', `Basic realm="${config.realm || 'Protected Area'}"`)
res.end('Access denied')
}
await next()
}
}
module.exports = StaticAuth
add this to list of server middlewares inside start/kernel.js
// ... contents of kernel.js file ...
const serverMiddleware = [
'App/Middleware/Server/StaticAuth', // add it BEFORE Static middleware!
'Adonis/Middleware/Static',
'Adonis/Middleware/Cors'
]
add configuration to config/auth.js
// ... contents of auth.js file ...
staticAuth: {
realm: 'Protected data',
username: 'admin',
password: 'somePassword',
protectedUrls: ['/', '/docs']
}

Related

How to set up CORS and JWT token validation with ExpressJs based on website

So basically, I have two websites with different subdomain. The first one doesn't need JWT Token validation but the second needs validation.
How to use the app.use(authorization) only when the origin is https://app.website.com ?
import cors from 'cors';
if (process.env.NODE_ENV === 'development') {
app.use(cors());
} else {
const whitelist = ['https://www.website.com', 'https://app.website.com'];
app.use(
cors({
origin: function (origin, callback) {
if (origin && whitelist.indexOf(origin) !== -1) {
return callback(null, true);
}
return callback(new Error('Forbidden'), false);
},
optionsSuccessStatus: 204
})
);
app.use(authorization);
}
JWT validation as a middleware, I tried to check req.get('origin') but it's undefined..
import admin from '../helpers/firebase.helper';
const authorization = async (
req,
res,
next
): Promise<void> => {
if (req.headers?.authorization?.startsWith('Bearer ')) {
const idToken = req.headers.authorization.split('Bearer ')[1];
try {
const decodeToken = await admin.auth().verifyIdToken(idToken);
req.current = decodeToken;
next();
} catch (err) {
res.status(StatusCodes.UNAUTHORIZED).send('UnAuthorized');
}
} else {
res.status(StatusCodes.UNAUTHORIZED).send('UnAuthorized');
}
};
The first site www.welcome.com is built with Nextjs (server side rendering) and the second one with React create app (client side rendering). So when I check req.get('origin') for react create app request it's working fine, but for the request which comes from the nextjs app it's undefined.
Diagram
You should be able to use req.hostname to get the host on which you received the request. You can check other properties of the request object in the docs: http://expressjs.com/en/api.html#req
Finally, I solved the problem. The problem was with NextJs, I was doing a server side request with getserversideprops (no CORS when server to server), so I move to a basic client request, then it's works fine. I have access to req.origin so I can check the origin.

Koa-Router : Skip the route if the request not in XHR

I have a Rest API made with Koa with some routes, but, at the same time, it will serve my Front (made with a JS framework and its own router).
The fact is, when I access from a browser "localhost/user" I want to display the front but when I reach the same url from fetch / ajax / XMLHttpRequest I want to display a JSON result (the one gave by the Koa-router).
So I would like to enable the /user route from the API only if it's called from XHR.
I did my isXMLHttpRequest middleware like this :
module.exports = async (ctx, next) => {
if(ctx.request.get('X-Requested-With') === 'XMLHttpRequest') {
return next()
}
}
Then, in my koa-router I did something like :
const Router = require('koa-router')
const isXMLHttpRequest = require("#middlewares/isXMLHttpRequest")
const router = new Router()
const user = require("#routes/user")
router.use('/user', isXMLHttpRequest, user.routes(), user.allowedMethods())
And then, it works when I do some XHR request, I have the JSON as planned, but if I try to access the /user from the browser, the API is giving me a Not Found Error and not my front...
I was looking on how to skip the router.use function if the request isn't made in XHR, but I can't find a solution...
I think it's in the middleware else condition, I have to return something, but what can I do to skip the koa-router from giving me 404 ...
Maybe you can help me ?
OK, so if you are using the SAME routes for static and XMLHttpRequests (which is probably not the best strategy), then this could work:
const Koa = require('koa')
const Router = require('koa-router')
const app = module.exports = new Koa();
isXmlRequest = (ctx) => {
// here you could also compare e.g. "accept" header
return (ctx.request.header && ctx.request.header['x-requested-with'] === 'XMLHttpRequest');
}
// static routes
const staticRouter = new Router()
staticRouter.get('/user', (ctx, next) => {
ctx.body = 'OK from static route';
next();
});
// XMLHttpRequest routes
const xmlRouter = new Router()
xmlRouter.get('/user', (ctx, next) => {
if (isXmlRequest(ctx)) {
// serve it
ctx.body = { ok: 'from JSON/XML' }
} else {
// downstream to next handler
next();
}
});
app.use(xmlRouter.routes());
app.use(staticRouter.routes());
const server = app.listen(3000)
This is not using middleware bwcause here you can only allow downstream with next but if there is no next, then this stops. There is no else ;-)
Just for reference
Not sure If I got your question right. So you have a backend that acts like a static web server AND a REST API, right?.
I would try to do it the other way round. Using e.g koa-static (https://www.npmjs.com/package/koa-static) would FIRST try to serve your files and if no matching files are found in your defines public directory, all other routes (so your REST API) are handled. Then you only have to make sure, that endpoint names do not overlap with files you are serving.

How to add middleware to shopify app routes?

Creating a shopify app with express and mongoose. The shop's domain and access tokens are saved to the database on the callback route of the install process. The index of the app is verified with the following function:
const verifyOAuth = query => {
if (!query.hmac) {
return false;
}
const hmac = query.hmac;
delete query.hmac;
const sortedQuery = Object.keys(query).map(key => `${key}=${Array(query[key]).join(',')}`).sort().join('&');
const calculatedSignature = crypto.createHmac('sha256', config.SHOPIFY_SHARED_SECRET).update(sortedQuery).digest('hex');
if (calculatedSignature === hmac) {
return true;
}
return false;
}
How can I create a middleware function for a request to access a shop's data from the mongo database.
EX:
router.get('/content', auth, (req, res) => {
const content = Content.findOne({shopifyDomain: 'shopify-domain-here'})
res.send(content);
});
var auth = (req, res, next) => {
// Get shop domain from authentication
next();
};
Would I have to add the shop domain and hmac as a query for every get request to '/content', or should I use res.setHeader to set them as headers when the index of the app is loaded, or is there a better solution?
You cannot add routes to Shopify. You will never have a request come to you from /Content. You obviously can make that a route in your own App though, and service that route.
If you want to sent content to Shopify, you should use the App Proxy. You receive a request for content, and then you fulfill that request with content formatted as Liquid or as JSON for example.

express-jwt middleware uses full paths instead of subpaths when used with express routers

I am using Node/Express.js to build an API. The API is present in the /api prefix if the prefix is not set, then Express server will return a Vue JS frontend. So basically I want express-jwt to get used as a middleware only when the /api prefix is set. For that reason I am using express router by doing app.use('/api, router).
And in the router file, I am using the express.jwt with unless syntax, to prevent any unauthorized access.
I also made the choice to implement express-jwt in the router file because I don't want to add /api/somepath in the express-jwt.unless, instead, I would like it to use /somepath and consider it as /api/somepath because I am using it with the router.
My router file:
const router = require('express').Router()
const routeController = require('./routeController')
const ejwt = require('express-jwt')
const config = require('../config/config')
// Protected Routes
// Protect Routes
router.use(
ejwt({
secret: config.APP_SECRET,
getToken: req => {
const { token } = req.body
if (token) return token
return null
}
}).unless({
path: ['/', '/auth/generateToken']
})
)
router.use(function(err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401).send({
status: false,
message: 'Access Forbidden'
})
} else {
next()
}
})
This didn't work for me. Whenever I try to access the frontend with /, the express-jwt middleware gets triggered and I can't even access my frontend. I can get this working If I also provide it all of my frontend routes as well. Which is not an all a good idea because the frontend has a lot of paths.
In short, I want the middleware to only check for the token if the path has /api prefix set. Thanks in Advance :)

how to send token jwt to verify before response with NodeJs using feathersjs?

I'm doing administration dashboard with use a token jwt. I would like to the user can obtain access to the middleware only if logged. I want to do that if he is logged, the server is shows home page if not, then he is redirect to login page. Below is my middleware index.js
const auth = require('feathers-authentication');
const pupils = require('./pupils');
const login = require('./login');
const home = require('./home');
module.exports = function () {
// Add your custom middleware here. Remember, that
// in Express the order matters
const app = this; // eslint-disable-line no-unused-vars
app.use('/pupils.html', pupils());
app.use('/login.html', login());
app.use('/', home());
app.post('/login', auth.express.authenticate('local', { successRedirect: '/', failureRedirect: '/login.html' }));
};
and pupils.js with use a handlebars
const lang = require('../../config/pupils.json');
module.exports = function (options = {}) {
return function login(req, res) {
res.render('home', lang);
};
};
If I open the main path '/' then it is redirecting to /login.html
I try authentication my user below code:
app.authenticate({
strategy: 'local',
email: 'username',
password: 'password'
}).then(function(result){
console.log('Authenticated!', result);
}).catch(function(error){
console.error('Error authenticating!', error);
});
When use app.authenticate method I receive a token object and it is storage in local storege but when I try again open main path, again I redirect to /login.html. What I do wrong?
How can I to do in this way:
1) I open this address http://127.0.0.1:3030:/
2) middleware on the server check if user is logged
3) if logged show home page, if not show login page

Resources