Guarding pages with token from serverless function or not

Hi,

I have read a lot about netlify identity and gotrue. But I do not know whats the best way to guard my static pages.

this is my function that will be called before every route change

const auth = new GoTrue({
  APIUrl: 'https://domain.netlify.app/.netlify/identity',
  setCookie: true,
})

app.router.beforeEach((to, from, next) => {
// check if page is inside privat folder
  const auth_required = to.path.split('/')[1] === 'privat'
  // no auth required: just procced 
  if(!auth_required) return next()
  // auth is required 
  // if user is logged in
  if(store.getters['auth/authenticated']) {
    // check if token is valid
    axios
      .get('/.netlify/functions/route-guard')
      .then(() => next())
      .catch(() => app.router.push('/login'))
  } else {
  //not logged in redirect
    app.router.push('/login')
  }
})

and my netlify function

Do I need to verify the token, in case someone might have tempered the token? Is this still possible if I store the token only inside a httpOnly cookie?

exports.handler = async function(event, context) {
  const { identity } = context.clientContext
  // do i need to verify the token somehow?
  if(identity.token) {
    return {
      statusCode: 200,
    }
  } else {
    return {
      statusCode: 401,
      body: JSON.stringify({message: '401 Unauthorized'})
    }
  }
}

Or is above completely too much and it’s save enough to check if a user exists in the auth object?

app.router.beforeEach((to, from, next) => {
  const auth_required = to.path.split('/')[1] === 'privat'
  if(!auth_required) return next()
  if(auth.currentUser()) {
    next()
  } else {
    app.router.push('/login')
  }
})

read so many tutorials/repos and everybody is doing it little bit different :wink: That’s why I’m a little unsure.

gregor

Hi @gregor

There is a example of protected routes using Vue.js and Netlify Identity Widget available that doesn’t use serverless functions

(note the demo site doesn’t work)

thanks for the link. Have seen this repo already.

Using identity without serverless function is not my problem. (see third code snippet)

I wanted to know if the third code snippet is enough for security for static rendered pages or if I should better go with a serverless function and somehow verify the token every time the user accesses a protected route.

I wonder if I can just set a user in ‘auth’ from the console so that the conditions become true.

Because first I checked my vuex store, but found out that I can easily set a user from the console

if(store.getters['auth/user']) {
  next()
document.getElementById('__nuxt').__vue__.$store.commit('auth/user', {user:'injected from console'})

so I changed to

if(auth.currentUser()) {
  next()

but still not sure if this can be easily manipulated

When a cookie is set using HttpOnly, there’s no way for JavaScript to manipulate it, so you can at least remove the tampering problem from the equation. But I believe auth.currentUser() is not enough too. The way it works is:

When you login, a value is stored in localstorage with the name gotrue.user and that value === the response you get from auth.login(email, password, remember). When you call auth.currentUser(), it simply takes that value from localstorage and gets done with it (is what I have understood by reading the code):

The way I have planned to do it (just started working on a project today with similar goals and tools stack :laughing:) is:

  1. Check if a user is logged in
  2. If yes, validate their token from the server
  3. Use Vuex store to save a property as user: true
  4. Check for that property in NavigationGuards.

From my understanding, that should suffice to secure the app for most needs.

ok then checking token serverside should be done…

this is my code so far in nuxtjs, if you like to take a look :wink: I’m a UI- Designer, with some frontend skills, but this authentication is absolutely new to me.

const auth = new GoTrue({
 APIUrl: 'https://domain.netlify.app/.netlify/identity',
 setCookie: true,
})

app.router.beforeEach((to, from, next) => {
  // get path (folder)
  const path = to.path.split('/')[1]

  // if no auth required: just proceed 
  if(path  !== 'admin') return next()
  
  // if I stay in the same directory skip server check, because it was already checked before
  const pathFrom = from.path.split('/')[1]
  if(path === pathFrom) return next()
  
  const user = auth.currentUser()

  if(user) {
    axios
      .get('/.netlify/functions/route-guard', {
        headers: {
          Authorization: `Bearer ${user.token.access_token}`
        }
      })
      .then(() =>  next())
      .catch(() => next({ path: '/login' }))
  } else {
    next({ path: '/login' })
  }
})

my server function

exports.handler = async function(event, context) {
  // the function is called with authorization header, so clientContext contains a valid certified user
  const { user, identity } = context.clientContext
  
  if(user) {
    return {
      statusCode: 200,
      body: JSON.stringify({user})
    }
  } else {
    return {
      statusCode: 401,
      body: JSON.stringify({message: '401 Unauthorized'})
    }
  }
}

gregor

That looks fine to me. The only thing I’d worry about is the function calls. How many times are users going to switch pages on your website? If the number is high, you might deplete your function calls pretty fast.

This is the only reason I suggested using Vuex as you can authenticate once in your website’s lifecycle and be done with it. As long as the user is not going to linger on your website for as long as the token remains valid, I think this way would help you save some function calls (and in turn, make navigation faster).

to reduce the functioncalls I have this code, because everything with authentication I have inside a admin folder

// new path
const path = to.path.split('/')[1]
// old path
const pathFrom = from.path.split('/')[1]
// if users are in the same directory skip function call
if(path === pathFrom) return next()

Yes, I saw that, but that’s what I was asking. How many times do you expect your users to go back and forth from that folder to elsewhere and return back to that folder. If that’s not a huge number, then you’re good. Or else, you might have to consider alternatives.

ah ok :wink: got it.

But how I can secure the vuex so that I can’t manipulate it from the outside? I can easily add a user from console, and get so access to protected routes

document.getElementById('__nuxt').__vue__.$store.commit('auth/user', {user:'injected from console'})

Or do I think to complicated? :wink:

No you’re not thinking too complicated, rather taking essential steps to stay secure. I gave some articles a good read and maybe you’d get some insights too:

In short, any client-side data as expected cannot be considered secure. So checking for access token on each route change is one way to go. Another way would be to store some token in external database for the first time and then checking that from client-side. The key used in the client-side should only have read rights.

In any case, backend calls are going to cost you, so as long as budget/number of calls is not an issue, that would be a good way to secure your stuff.

1 Like

oh wow thanks will take a read :slight_smile:

1 Like