2

I am implementing the a Nextjs service with koa, koa-router and kow-jwt, but I'm confused with the routing setting with them.

My project have 2 pages, one is dashboard and the other is login. The dashboard need to pass the verification and the login not. If the auth failed, then redirect user to login page.

I've search on the Internet, and found some examples as following, none of them chain them together.

Please give me some advice to make them work well together.

const app = next({dev});
const handle = app.getRequestHandler();

app.prepare()
  .then(() => {
    const server = new koa();
    const router = new koaRouter();        

    router.get('/login', async ctx => {
        await app.render(ctx.req, ctx.res, '/login', ctx.query);
        ctx.respond = false;
    });

    router.get('/dashboard', 
        jwt({
            secret: config.graphqlSecret
        }),
        async ctx => {
            await app.render(ctx.req, ctx.res, '/dashboard', ctx.query);
            ctx.respond = false;
        }
    );

    // what is the purpose of this route?
    router.get('*', async ctx => {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
    });

    server.use(async (ctx, next) => {
        try {
            await next();
        } catch (err) {
            if (err.statusCode === 401) {
                ctx.redirect('/login');
            }
        }
    });

    server.use(router.routes());
    server.use(router.allowedMethods());
    server.listen(3000);
});

with the code above, the behavior is

  • If I link to dashboard with and without jwt token, it always redirect to login page.
  • If I link to dashboard from menu (implement with <Link> in Nextjs), it shows the content of dashboard.

Thank you for your help.

Jimmy Lin
  • 1,481
  • 6
  • 27
  • 44
  • maybe I am a bit late, but can help you if you are still facing the issue. I am quite sure you are doing the request wrongly. Could you document how you are doing the request? are you including the Authorization Bearer token in the header of the request? – Javier Aviles Jun 18 '18 at 08:40
  • I am now changing my auth from the path to component and it works well. I still want to know how to do the auth from path. Yes, I include the authorization token in the header of request. – Jimmy Lin Jun 19 '18 at 02:56
  • Have a look at the answer and see if it helps you :) – Javier Aviles Jun 19 '18 at 07:29
  • I've edited the answer, didn't see it was for nextjs sorry! didn't know how it worked before but just had a look at their doc and I think this one would do – Javier Aviles Jun 19 '18 at 09:18

1 Answers1

2

You need to include the jwt part in your server.use, not within the router. Make two different routers, one with the open routes and one with the protected ones. Then set open routes, set jwt middleware and then set protected routes:

const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare()
  .then(() => {
    const server = new Koa()
    const router = new Router()

    router.get('/login', async ctx => {
        await app.render(ctx.req, ctx.res, '/login', ctx.query);
        ctx.respond = false;
    });

    router.get('/dashboard', async ctx => {
        await app.render(ctx.req, ctx.res, '/dashboard', ctx.query);
        ctx.respond = false;
    });

    router.get('*', async ctx => {
      await handle(ctx.req, ctx.res)
      ctx.respond = false
    })

    // this will keep redirecting user to login until is logged in
    // if you remove it, will get an auth error unless you go manually
    // to the login path
    server.use(async (ctx, next) => {
        try {
            await next();
        } catch (err) {
            if (err.statusCode === 401) {
                ctx.redirect('/login');
            }
        }
    });

    // we need to do it this way because of the way nextjs works with '*' path    
    // Middleware below this line is only reached if JWT token is valid
    server.use(jwt({ secret: 'shared-secret' }).unless({ path: [/^\/b/] }));

    // specify in unless the unprotected path
    server.use(jwt({secret: config.graphqlSecret}).unless({ path: [/^\/login/] })).use(router.allowedMethods());

    // every route protected by default
    server.use(router.routes())

    server.listen(3000);
  })
Javier Aviles
  • 7,952
  • 2
  • 22
  • 28
  • Thank for your answer, however where did you put `router.get('*', async ctx...` this router. In protected or in opened? I've tried this method, but it doesn't work well with the * directory because the website is render from * route. – Jimmy Lin Jun 19 '18 at 07:48
  • have a look at the edition, as you said, because of the * nextjs path couldnt be done the other way, this one works :) – Javier Aviles Jun 19 '18 at 09:17