Sapper: handle session.user check internally, suggestion to decorate routes somehow

Created on 20 Jun 2019  路  15Comments  路  Source: sveltejs/sapper

Hi,

I think I have a good grasp on how auth works with sapper now, I have a WIP based on #752 that is now working great (i ended up implementing my own wrapper to the auth API btw).

However, what I've come to notice is that enforcing a route to be protected by an authenticated user is cumbersome and repetive, for example:

<script context="module">
  export function preload({}, { user }) {
    if (!user) {
      return this.redirect(302, "login");
    }
    return { user };
  }
</script>

or checking the store directly, would have to be put in place on each route.

For example whilst reviewing the realworld example, i noticed that it is missing on the settings route, causing a 500 when navigating: https://svelte-realworld.now.sh/settings unauthenticated.

Would it be possible to somehow decorate the routes so that sapper would internally check for a session.user and redirect accordingly?

For example the login url could be set during the middleware bootstrapping:

sapper.middleware({
      loginRoute: '/login',
      session: (req, res) => ({
        user: req.user
      })
    })

I'm too new to svelte and sapper to be digging around the code here, but if you give a guidence I'll happily try to contribute.

Cheers,
P.

question

Most helpful comment

I was struggling with this problem with a passport JS OpenID integration.

I wanted my home page to redirect to a path. I wanted every page to require authentication. I was able to find a simple solution through the use of the _layout.svelte file.

<script context="module">
  export function preload(page, session) {
    if (!session.user) return this.redirect(308, 'login')
  }
</script>

By adding this to the top of my layout file I am able to enforce that there is a session for the user before ever rendering the page.

All 15 comments

One thing that is confusing me slightly about https://sapper.svelte.dev/docs#Seeding_session_data

I expected to be able to access this session store on the client, but that is not the case, if I do:

<script>
  import { stores } from '@sapper/app';
  const { session } = stores();
  console.log(session.user);
</script>

On a component or page, it is always undefined, am I expected to set this manually even though it has been seeded by the server? I've even tried on onMount, but it is the same.

My auth workflow involves going to a thirdparty address (oauth2) and it's working fine on the server side preload, see above. I just can't get to the user on the client side.

Thanks,
P.

mah bad regarding stores, forgot about $ so the above should read:

<script>
  import { stores } from '@sapper/app';
  const { session } = stores();
  console.log($session.user);
</script>

to handle a successfull redirect when a user is not logged in on both server and client side:

<script context="module">
  export function preload({}, { user }) {
    if (!user) {
      return this.redirect(302, "login");
    }
  }
</script>

<script>
  import { goto, stores } from "@sapper/app";
  const { session } = stores();

  const user = $session.user;
  if (!user) {
    goto("/login");
  }
</script>

If not to be handled internally by sapper, how best to optimize this process? I can forsee having quite a few routes that would require a logged in user.

Thanks,
P.

Hi! I'm new to Svelte and Sapper so i apologise if i've misunderstood your purpose. What i wonder is if making a svelte-component with just the for redirection and importing it to all pages where auth is required satisfies the problem of duplication of code.

Hi @OliverKarlsson that's actually not a bad idea, I'll have to test it - thanks bud.

Thx :) I recomend you to join the discord. I recently learned that the code for verification and redirection can be put in a _layout.svelte (see https://sapper.svelte.dev/docs#Layouts).

Yah I'm on it, thanks, but haven't been able to follow up much recently, with new work and city relocation :D

See yah around.

I was struggling with this problem with a passport JS OpenID integration.

I wanted my home page to redirect to a path. I wanted every page to require authentication. I was able to find a simple solution through the use of the _layout.svelte file.

<script context="module">
  export function preload(page, session) {
    if (!session.user) return this.redirect(308, 'login')
  }
</script>

By adding this to the top of my layout file I am able to enforce that there is a session for the user before ever rendering the page.

But when the login page is also loaded in _layout doesn't this lead to an endless redirect loop?

@thedeadkoiclub yes, but you should be able to condition it, so it would only redirect if it's not loading the login page.

Not sure if page.url is correct, but something like:

<script context="module">
  export function preload(page, session) {
    if (!session.user && page.url !== 'login') return this.redirect(308, 'login')
  }
</script>

Should definitely be feasible.

There is a working example of how you can handle an auth + route guarding scenario, for both client and server, as you're detailing above, implemented by https://github.com/beyonk-adventures/sapper-rbac.

I think route guarding is something we'd like to consider for the upcoming router implementation.

So what is now the best way to handle openid connect ? Express-openid connect extension or passport js or the sapper-rbac ? I have to create a website with openid connect and also with specific route authentication. Any suggestions ?

@mikart143 please ask in the Svelte Chat for usage questions - we can't have a discussion on github issues.

I do not speak English well. However, the solution can be updated.
Thank you.

index.svelte

export async function preload(page, { user }) {
    if (!user && !page.path.includes('login')) {
      return this.redirect(302, 'login');
    }
    return { user };
 };

I was struggling with this problem with a passport JS OpenID integration.

I wanted my home page to redirect to a path. I wanted every page to require authentication. I was able to find a simple solution through the use of the _layout.svelte file.

<script context="module">
  export function preload(page, session) {
    if (!session.user) return this.redirect(308, 'login')
  }
</script>

By adding this to the top of my layout file I am able to enforce that there is a session for the user before ever rendering the page.

OT: Just curious; why are you using 308 and not 302 (or 401)?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Rich-Harris picture Rich-Harris  路  4Comments

benmccann picture benmccann  路  3Comments

nolanlawson picture nolanlawson  路  4Comments

Rich-Harris picture Rich-Harris  路  3Comments

keyvan-m-sadeghi picture keyvan-m-sadeghi  路  4Comments