Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to secure the backend api? #58

Open
RonAlmog opened this issue May 19, 2017 · 12 comments
Open

How to secure the backend api? #58

RonAlmog opened this issue May 19, 2017 · 12 comments

Comments

@RonAlmog
Copy link

What's the best way to secure the api? I mean to give separate permissions to each endpoint.
for example:
api/cats - open to anybody.
api/orders - available to admins only.

thanks.

@DavideViolante
Copy link
Owner

DavideViolante commented May 19, 2017

On the backend you need to add a new middleware to the endpoints you want to secure.
app.get('/api/orders', authMiddleware, function(req, res) {...} )
That middleware should verify that there is a header like this with a valid token:
Authorization Bearer: jwt_token_here

if(there is the Authorization Header)
  if(jwt.verify(token) is ok)
    next()

Of course you need to set that header somewhere once (I think at login). That header should be carried at each request.
It's not that easy to do, but probably I will implement it into this project when I know how to do it correctly.

For further info search on google:

  • jwt authentication
  • authorization bearer
  • express protected/secured routes

@RonAlmog
Copy link
Author

ok, great. i will work on that. thanks!

@simon-hardy
Copy link

On the client side:

You need to use the AuthHttp with class from angular2-jwt instead of the basic Http class.
The easiest way is in the shared.module.ts file:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpModule, Http, RequestOptions } from '@angular/http';
import { AuthHttp, AuthConfig } from 'angular2-jwt';

import { ToastComponent } from './toast/toast.component';

import {
            InputTextModule,
            ButtonModule,
            CalendarModule,
            DropdownModule,
            PasswordModule,
            ToggleButtonModule,
            CaptchaModule 
        } from 'primeng/primeng';

import { MustBeTrueValidator } from '../../validators/mustBeTrueValidator.directive';

export function authHttpServiceFactory(http: Http, options: RequestOptions) {
  return new AuthHttp(new AuthConfig({noJwtError: true}), http, options);
}

@NgModule({
   imports: [
       BrowserModule,
       BrowserAnimationsModule,
       FormsModule,
       ReactiveFormsModule,
       HttpModule
   ],
   exports: [
       // Shared Modules
       BrowserModule,
       FormsModule,
       ReactiveFormsModule,
       HttpModule,
       CalendarModule,
       DropdownModule,
       InputTextModule,
       ButtonModule,
       PasswordModule,
       ToggleButtonModule,
       CaptchaModule,

       // Shared Components
       ToastComponent

       // Shared Validators
       
   ],
   declarations: [ToastComponent, MustBeTrueValidator],
   providers: [ToastComponent, 
        {
            provide: AuthHttp,
            useFactory: authHttpServiceFactory,
            deps: [Http, RequestOptions]
        }],
})
export class SharedModule { }

Then pass the AuthHttp dependency to the services:

export class UserService {

  private headers = new Headers({ 'Content-Type': 'application/json', 'charset': 'UTF-8' });
  private options = new RequestOptions({ headers: this.headers });

  constructor(private http: AuthHttp) { }

On the server side:

Add the express-jwt package from npm:

npm install express-jwt --save

Add a catch all route to set the req.user property in app.ts:

  app.use(jwt({secret: config.get('sessionSecret'), credentialsRequired: false}));

  setRoutes(app);

Create a function to validate that req.user is set that you can call as middleware on your protected routes:

ensureLoggedIn(req, res, next) {
        if (req.user) {
            console.info(JSON.stringify(req.user));
            next();
        } else {
            console.info('here');
            res.status(401).json(JSON.stringify({'Message': 'Unauthorized.'}));
        }
    }

Hope that helps.

@zbagley
Copy link
Contributor

zbagley commented May 23, 2017

@simon-hardy Quick question before I check through this, is AuthHttp able to handle https? Thanks in advance.

@simon-hardy
Copy link

Yep.

@AmirGilboa
Copy link

that's sounds good, i'm trying to implement. just a few questions, because i'm new to this:

  1. where do i put, and how do i use the validation method, ensuerLoggedIn?
  2. what is the purpose of the shared module?
  3. is there a way to limit not only to logged in users, but to users who belong to a role, like admin?
    Thanks!

@simon-hardy
Copy link

simon-hardy commented Jun 9, 2017

@AmirGilboa

  1. I had to modify the routes to use a different syntax along the lines of:

app.get('/api/cart/:cartId', ensureLoggedIn, (req, res, next) => {
      // Logic...
      return res.status(200).json(cart);
});
  1. To load any components your app has a dependency on
  2. Yes, for instance:
ensureAdminLoggedIn(req, res, next) {
  if (req.user && req.user.role === 'admin') {
    next();
  } else {
    res.status(401).json(JSON.stringify({ 'Message': 'Unauthorized' }));
  }
}

@RonAlmog
Copy link
Author

@simon-hardy
I did as you said, and it worked. thank you!
One side note: when i implemented the 'ensureAdmin' validation, it did not work.
i digged a bit and found that inside req.user there is a user object with role in it.
means the path to test is
if( (req.user && req.user.user.role==='admin) ...

probably some error that i did somewhere, but i can't figure out. it works anyways ;)

@jjoshm
Copy link

jjoshm commented Jan 23, 2021

for the current version i have solved it like this

import * as express from 'express';
import * as jwt from 'jsonwebtoken';
import UserCtrl from './controllers/user';

function ensureAdminLoggedIn(req, res, next) {
  if (!req.headers.authorization.startsWith('Bearer ')) {
    return res.status(403).json({ error: { Message: 'Unauthorized' } });
  }
  const token = req.headers.authorization.substring(
    7,
    req.headers.authorization.length
  );
  const user = jwt.verify(token, process.env.SECRET_TOKEN);
  if (user.user.role != 'admin') {
    return res.status(403).json({ error: { Message: 'Unauthorized' } });
  }
  next();
}

function setRoutes(app): void {
  const router = express.Router();
  const userCtrl = new UserCtrl();

  // Users
  router.route('/login').post(userCtrl.login);
  router.route('/users').get(userCtrl.getAll);
  router.route('/users/count').get(userCtrl.count);
  router.route('/user').post(userCtrl.insert);
  router.route('/user/:id').get(userCtrl.get);
  router.route('/user/:id').put(userCtrl.update);
  router.route('/user/:id').delete(ensureAdminLoggedIn, userCtrl.delete);

  // Apply the routes to our application with the prefix /api
  app.use('/api', router);
}

export default setRoutes;

@DavideViolante
Copy link
Owner

DavideViolante commented Jan 24, 2021

You can also use express-jwt

isLogged = expressJwt({ secret: process.env.SECRET_TOKEN, algorithms: ['HS256']});
router.get('/something', isLogged, userCtrl.something);

I keep the issue open to let others see the solutions.
In future I think I will add it to the project.

@DavideViolante DavideViolante changed the title How to secure the api? How to secure the backend api? Jan 24, 2021
@jjoshm
Copy link

jjoshm commented Jan 27, 2021

I am thinking about how to test this. Do you have an idea? Maybe the test framework can intercept the "ensureAdminLoggedIn" function for this. I don't know about express-jwt

@DavideViolante
Copy link
Owner

I think express-jwt is tested itself so you don't need to but if you want more freedom I support the solution of @simon-hardy above and test just that function alone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants