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

JWT security and scopes - my working solution #481

Open
ric79 opened this issue Mar 16, 2017 · 6 comments

Comments

@ric79
Copy link

@ric79 ric79 commented Mar 16, 2017

Hello,
here there is my solution, hope I can help other developers!

Token

Lets us imagine that the APP will receive in the header a JWT token (TOKENVALUE). The token has been sign with a private key. Payload can be for example:

{ user:
   { system_name: 'SVJWTDEV',
     user_uid: 'root',
     name: 'Supervisor',
     surname: 'Supervisor',
     email: 'riccardo.russo@xxx.it' },
  scopes: [ 'admin' ],
  custom: {},
  exp: 1489696621,
  iat: 1489675021 }

In my case, the scope is just an array of roles

Swagger.yaml

...
securityDefinitions:
  APIKey:
    description: "Accesso tramite JWT"
    type: "apiKey"
    name: "Authorization"
    in: "header"
...
paths:
  /protected_calls:
    get:
      security:
        - APIKey: []
      x-security-scopes: 
      - admin           

I have extended swagger adding x-security-scopes. This is the key point of the solution.

Client authorization

Every calls to server should contains

Authorization: Bearer TOKENVALUE

Middleware

In the swaggerSecurity function it is now easy to verify the token using the public key and check if there is an intersection between scopes from token and x-security-scopes
I'm a newbie to nodejs so just get the idea and not the specific implementation

  app.use(middleware.swaggerSecurity({
    APIKey: function(req, def, JWTAuth, callback) {
      var current_req_scopes = req.swagger.operation["x-security-scopes"]
      if (!!JWTAuth && JWTAuth.indexOf("Bearer ") == 0) {
          var JWTToken = JWTAuth.split("Bearer ")[1]
          jwt.verify(JWTToken, appl_config.jwt.pubKey, function(err, payload) {
               if (err) {
                 var err = new Error('Invalid token');
                 err['statusCode'] = 400;
                 callback(err);  
                 return
               }
               if (_.intersection(payload.scopes, current_req_scopes).length == 0) {
                 console.log("Not Authorized!")
                 var err = new Error('Not Authorized');
                 err['statusCode'] = 401;
                 callback(err);  
                 return
               }
               else {
                   console.log("Authorized!")
                   req.swagger.params.auth_payload = payload;    //example
                   callback()
               }
          });   
      } else {
         var err = new Error('Failed to authenticate using bearer token');
         err['statusCode'] = 403; // custom error code
         callback(err);
      }
    },
  }));

Riccardo

@miguelduarte42

This comment has been minimized.

Copy link

@miguelduarte42 miguelduarte42 commented Apr 7, 2017

Great solution, thanks for sharing Riccardo!

@muhmud

This comment has been minimized.

Copy link

@muhmud muhmud commented Oct 30, 2017

Only problem is it can't be documented through swagger-ui...

@markohaapala

This comment has been minimized.

Copy link

@markohaapala markohaapala commented Dec 4, 2017

We also came up with a similar solution. On top of that, we have added a version check to expire any existing tokens, when needed, to force a re-authentication.

if(token.version !== config.jwt.version) { 
  // return 403
}
@gjblanco-clutch

This comment has been minimized.

Copy link

@gjblanco-clutch gjblanco-clutch commented Jan 11, 2018

@ric79 Thanks for the great post.

What if you have a multiple security definition?

securityDefinitions:
  APIKey1:
    description: "Accesso tramite JWT"
    type: "apiKey"
    name: "Authorization"
    in: "header"
APIKey2:
   description: "another key"
   type: "apiKey"
   name: "another key"
   in: header
...
paths:
  /protected_calls:
    get:
      security:
        - APIKey: []
          APIKey2: [] # Here, the endpoint uses both keys
      x-security-scopes: 
      - admin           
...

And your authentication mw uses both keys? What do you use instead of ??? ?

app.use(middleware.swaggerSecurity({
   ???: function(req, def, JWTAuth, callback) {
....
@biscaldis

This comment has been minimized.

Copy link

@biscaldis biscaldis commented Jan 15, 2019

Did someone use this solution using Python and the Connexion module?
I don't know how to access req.swagger.operation["x-security-scopes"]

@k1rked

This comment has been minimized.

Copy link

@k1rked k1rked commented Jul 10, 2019

@ric79 Thanks for the great post.

What if you have a multiple security definition?

securityDefinitions:
  APIKey1:
    description: "Accesso tramite JWT"
    type: "apiKey"
    name: "Authorization"
    in: "header"
APIKey2:
   description: "another key"
   type: "apiKey"
   name: "another key"
   in: header
...
paths:
  /protected_calls:
    get:
      security:
        - APIKey: []
          APIKey2: [] # Here, the endpoint uses both keys
      x-security-scopes: 
      - admin           
...

And your authentication mw uses both keys? What do you use instead of ??? ?

app.use(middleware.swaggerSecurity({
   ???: function(req, def, JWTAuth, callback) {
....

Something like this?

securityDefinitions:
  adminKey:
    type: apiKey
    in: header
    name: Authorization
  userKey:
    type: apiKey
    in: header
    name: Authorization
const { authorize, authorizeAdmin } = require('./api/helpers/auth');
    app.use(middleware.swaggerSecurity({
        userKey: authorize,
        adminKey: authorizeAdmin,
    }));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.