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 validate JWT tokens supplied by incoming requests #2057

Closed
yeager-j opened this issue Nov 19, 2018 · 10 comments
Closed

How to validate JWT tokens supplied by incoming requests #2057

yeager-j opened this issue Nov 19, 2018 · 10 comments

Comments

@yeager-j
Copy link

Description / Steps to reproduce / Feature proposal

I'm attempting to build an app with Auth0 and Loopback. Auth0 requires some Express middleware, such as express-jwt. I've searched through GitHub issues and the docs to find a way to use Express middleware in a Loopback app and I've found nothing. Perhaps I'm missing something glaringly obvious.

I need a way to do this - from the Auth0 Documentation:

// set dependencies - code omitted

// Enable CORS - code omitted

// Create middleware for checking the JWT
const checkJwt = jwt({
  // Dynamically provide a signing key based on the kid in the header and the singing keys provided by the JWKS endpoint
  secret: jwksRsa.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: `https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json`
  }),

  // Validate the audience and the issuer
  audience: '{YOUR_API_IDENTIFIER}', //replace with your API's audience, available at Dashboard > APIs
  issuer: 'https://YOUR_AUTH0_DOMAIN/',
  algorithms: [ 'RS256' ]
});

// Enable the use of request body parsing middleware - code omitted

// create timesheets API endpoint - code omitted
app.post('/timesheets', checkJwt, function(req, res){
  var timesheet = req.body;

  // Save the timesheet to the database...

  //send the response
  res.status(201).send(timesheet);
});
// launch the API Server at localhost:8080 - code omitted

I'm feeling a bit hopeless. I'm pretty new to Loopback and Auth0.

See Reporting Issues for more tips on writing good issues

@raymondfeng
Copy link
Contributor

There is an open issue - #2035

@yeager-j
Copy link
Author

There is an open issue - #2035

I saw that. I only need application-wide middleware. Is that not implemented yet as well?

@bajtos
Copy link
Member

bajtos commented Nov 22, 2018

@yeager-j In your example, you are showing a route-specific usage of checkJwt middleware.

In LoopBack 4, users should use Sequence actions for adding custom request (pre)processing steps. At least that was our original design idea. See Sequence chapter in our documentation to learn more.

Does jwt provide API with callback/promise style too? If it does, then you can implement your custom sequence action as follows:

export type CheckJwt = (request: Request): Promise<void>;

class CheckJwtActionProvider implements Provider<CheckJwt> {
  constructor(
    // does jwt provide a type definition we could use instead of generic object?
    @inject('jwt.config') private config: object 
  ) {}

  value() {
    // Use the lambda syntax to preserve the "this" scope for future calls!
    return (request: Request) => {
      this.action(request);
    };

  action(request: Request): Promise<void> {
    const config = // merge global config in `this.config` with request-specific config
    return jwt.check(request, config);
  }
}

If it does not, then you can invoke the middleware function too. The difficult part is how to handle the case when the middleware did not call next. We are already using this approach for serving static files, maybe we should export our executeRequestHandler helper as part of LoopBack's public API?

Using the above helper, the sequence action could be implemented as follows:

export type CheckJwt = (request: Request, response: Response): Promise<boolean>;

class CheckJwtActionProvider implements Provider<CheckJwt> {
  constructor(
    // does jwt provide a type definition we could use instead of generic object?
    @inject('jwt.config') private config: object 
  ) {
  }

  value() {
    // Use the lambda syntax to preserve the "this" scope for future calls!
    return (request: Request, response: Response) => {
      this.action(request);
    };

  action(request: Request, response: Response): Promise<void> {
    const config = // merge global config in `this.config` with request-specific config
    const checkJwt = jwt(config);
    return executeRequestHandler(checkJwt, request, response);
  }
}

Usage in your custom sequence:

class MySequence extends DefaultSequence {
  async handle(context: RequestContext) {
    const route = this.findRoute(context.request);

    const handled = await this.checkJwt();
    if (handled) {
      // ouch, jwt already sent back a response
      return;
    }

    const params = await this.parseParams(context.request, route);
    // etc.
  }
}

@bajtos bajtos changed the title Express middleware within LB4 How to validate JWT tokens supplied by incoming requests Nov 22, 2018
@dhmlau dhmlau added p3 and removed 2019Q2 labels Apr 12, 2019
@b4dnewz
Copy link

b4dnewz commented May 14, 2019

hi @bajtos in your sequence example posted above where does this.checkJwt() comes from?
How to inject the CheckJwtActionProvider into sequence handler?

Thanks in advance.

@dhmlau
Copy link
Member

dhmlau commented Jun 10, 2019

Discussion with @raymondfeng @jannyHou @emonddr:
This is similar to loopbackio/loopback4-example-shopping#36 (how to invalidate token). We can make extension points to allow users to plug-in their logic to validate/invalidate tokens themselves.

@dhmlau
Copy link
Member

dhmlau commented Jun 10, 2019

Acceptance Criteria

  • create an extension point so that users can implement the logics to validate/invalidate tokens using extensions.

@jannyHou
Copy link
Contributor

We now have the TokenService interface in @loopback/authentication that has a verify function. People can implement their token services and define the verify function, like https://github.com/strongloop/loopback4-example-shopping/blob/master/packages/shopping/src/services/jwt-service.ts#L16
@dhmlau I am afraid creating an extension point is not the right direction to go, the token service is already injectable, if user only needs one verify function, creating a token service provider is good enough.

@bajtos
Copy link
Member

bajtos commented Jun 14, 2019

@jannyHou do we have any documentation showing users how to write such custom token service provider? If not, then I am proposing to keep this issue open and set the acceptance criteria to write such documentation.

Thoughts?

@jannyHou
Copy link
Contributor

@bajtos Yep Dom wrote a tutorial for creating the token service in https://loopback.io/doc/en/lb4/Authentication-Tutorial.html, especially see the section https://loopback.io/doc/en/lb4/Authentication-Tutorial.html#creating-a-custom-sequence-and-adding-the-authentication-action

Let us know if you expect to see more details or have any questions. I can add it.

@jannyHou jannyHou self-assigned this Jun 24, 2019
@bajtos
Copy link
Member

bajtos commented Jun 25, 2019

Thank you @jannyHou for the pointers. I this the section Creating a Token Service is exactly what I was looking for.

Let's close this issue as resolved then.

@bajtos bajtos closed this as completed Jun 25, 2019
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