1

I've been trying to write a simple server that accepts username/password credentials, validates them, then creates a JWT token for the client that they then use to gain access to certain routes. I am able to do everything up to the point of receiving and validating the client's JWT on the server side.

void main() {
//use Jwt based sessions and create the secret using a UUID
JwtSessionHandler sessionHandler = 
    new JwtSessionHandler('ffl_sales_server', new Uuid().v4(), 
    auth.lookupByUsername);

  //allow http for testing with curl, do not use in production
  bool allowHttp = true;

  final Map<String, String> headers = 
    {'Access-Control-Allow-Origin': 'http://192.168.100.83:3030',
     "Access-Control-Expose-Headers": "Authorization",
     "Access-Control-Allow-Credentials" : "true",
     "Access-Control-Allow-Headers" : "Authorization"};

  //fix the cors headers 
  Response options(Request request) => (request.method == 'OPTIONS') ?
      new Response.ok(null, headers: headers) : null;
  Response cors(Response response) => 
      response.change(headers: headers);
  Middleware fixCors = createMiddleware(requestHandler: options, 
      responseHandler: cors);

  // authentication middleware for a login handler (post)
  Middleware loginMiddleware = authenticate(
      [new UsernamePasswordAuthenticator(lookupByUsernamePassword)],
      sessionHandler: sessionHandler, allowHttp: allowHttp, 
      allowAnonymousAccess: false);

  // authentication middleware for routes other than login that 
  // require a logged in user
  // here we are relying solely on users with a session established  
  // via the login route
  Middleware defaultAuthMiddleware = authenticate(
      [],sessionHandler: sessionHandler, allowHttp: true, 
      allowAnonymousAccess: false);

  Router rootRouter = router(handlerAdapter: handlerAdapter()); 

  //the route where the login credentials are posted
  rootRouter.post('/login', (Request request) => new Response.ok('SUCCESS'), middleware: loginMiddleware);

  //the routes which require an authenticated user
  rootRouter.child('/authenticated', 
      middleware: defaultAuthMiddleware)
    ..add('/users', ALL_METHODS, new Users(_connection).handle)
    ..add('/stores', ALL_METHODS, new Stores(_connection).handle)
    ..add('/departments', ALL_METHODS, new Departments(_connection).handle)
    ..add('/invoices', ALL_METHODS, new Invoices(_connection).handle)
    ..add('/reports', ALL_METHODS, new Reports(_connection).handle)
    ..add('/imports', ALL_METHODS, new Imports(_connection).handle)
    ..add('/vendors', ALL_METHODS, new Vendors(_connection).handle)
    ..get('/foo', (Request request) => 
         new Response.ok("Doing foo as ${loggedInUsername(request)}\n"));

  printRoutes(rootRouter);

  Handler handler = const Pipeline()
      .addMiddleware(fixCors)
      .addMiddleware(logRequests())
      .addMiddleware(exceptionResponse())
      .addHandler(rootRouter.handler);

  shelf_io.serve(handler, '127.0.0.1', '8080').then((server){
    print('Serving as http://${server.address.host}:${server.port}');
  });
}

I know I'm probably missing something simple, but it seems like I should have some sort of handler in the first parameter of the authenticate() function that creates the defaultAuthMiddleware. What am I missing?

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Hey [Matthew Feeney](http://stackoverflow.com/users/4142285/matthew-feeney) can you give some more detail around what is going wrong. What is it that is not working? The above seems to be modelled closely off the example_with_login_and_jwt_session code. Did you have the same problem there? I've added some comments to that example. Try the curl commands https://bitbucket.org/andersmholmgren/shelf_auth/src/b54659668249526cfbd2af0532250c915bd53cb7/example/example_with_login_and_jwt_session.dart?at=master#cl-23 if you haven't already – Anders Feb 07 '15 at 00:32
  • @Anders, I tried the curl examples from your comments and get the same result. I'm successfully getting the token back from the server, but when I make the next request passing the content type as application/json and the authorization header as the received token, I get a 401 response back. – Matthew Feeney Feb 09 '15 at 16:27
  • I just took your code above and modified just enough for the missing code and it works for me - https://gist.github.com/Andersmholmgren/17d468c244a7848b72d7 Make sure you are on the latest versions of the shelf dependencies. Mine are in https://gist.github.com/Andersmholmgren/136427d800eb053d3a07 – Anders Feb 10 '15 at 00:51
  • My curl output https://gist.github.com/Andersmholmgren/c6f119f0b1fe560605a0 – Anders Feb 10 '15 at 00:59

2 Answers2

1

It looks like the issue was in my Auth class. The lookupByUsername function was simply not finding the authenticated user because the lookupByUsernamePassword function was storing it in function scope instead of class scope due to an oversight on my part.

Thanks for the help!

0

I think you need to pass the sessionMiddleware to the other routes like

 ..add('/invoices', ALL_METHODS, new Invoices(_connection).handle, 
     middleware: defaultAuthMiddleware)

Maybe there is a better way but this is the main difference I found to my setup which works fine.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • You definitely should NOT have to do this. If you can reproduce that on the latest version of shelf_route please file a bug against [shelf_route](https://bitbucket.org/andersmholmgren/shelf_route/issues/new). – Anders Feb 07 '15 at 23:19
  • Thanks for the info, did you find something why it's not working with the code in the question? – Günter Zöchbauer Feb 08 '15 at 11:09
  • I couldn't see anything obviously different from the `example_with_login_and_jwt_session` which I tried and it works fine in the latest version for me. Awaiting more info from Matthew – Anders Feb 08 '15 at 20:29
  • I tried this solution along with the curl commands @anders suggested and still ended up with a 401 response. It seems like it just isn't accepting the token for some reason.. – Matthew Feeney Feb 09 '15 at 16:30