Best practice to associate Identity user with db data?

Hi folks, netlify newbie here, so it’s probable this is answered elsewhere, but I’m trying to grok the best way to associate a Netlify identity user with their persisted data in e.g. FaunaDB. My main two questions at the moment regard 1) unique user ids, and 2) best way to associate db data with a given user.

For example, in other contexts each user might have an immutable id created at registration time that wouldn’t change if the user e.g. changes their email address. (Again, I assume this exists I’m just missing it)

Question 1) Is there an immutable id assigned to Identity users that I can store with their data? If so, how do I access this id within a function? What about client side? (I’ve seen reference to user metadata elsewhere but I’m having a hard time tracking down the right docs on this)

Question 1b) Is this userID already embedded in the JWT that Identity returns to the client side upon login? e.g. There is a sub field in the Identity JWT. But seeing as this is returned client-side perhaps this is not in fact the unique user id under the hood in Identity? Or is it?

Question 2) In the Netlify + Identity + FaunaDB paradigm, what is the best practice for associating persisted user data with a given user? Do you simply store unique ID with the various db entries and fetch against that ID for a given user, or is it something different?

Feel free to point me to the docs if this is answered elsewhere, but after several hours of research and experimentation I’m coming up short!

Thank you.

1 Like

Hi @Malcolm,

  1. User IDs are generated when a user signs-up or is invited and stay persistent through any kind of change except user deletion. It’s also unique to each user (at least in each Identity instance), so you can safely use it to identify each user individually. To access the user ID in a function, well it’s kinda tricky. I say tricky because the way Netlify Identity works with Netlify Functions is that, the function needs an Authorisation header with a Bearer token sent with the fetch request. The Bearer token is basically the JWT token generated for the user which, if you’re using Netlify Identity Widget, can be accessed using netlifyIdentity.currentUser().token.access_token. So the overall fetch call could look something like:
fetch('/.netify/functions/<function-name>/', {
  headers: {
    Authorization: `Bearer ${netlifyIdentity.currentUser().token.access_token}`
  }
})

Once the function is called this way, you get the user in the function, like:

exports.handler = async (event, context) => {
  console.log(context.clientContext.user.sub)
}

The downside is, as you might have guessed, you can only get this ID when you’re logged in as that particular user as the ID will depend on what Bearer token is being sent. In most cases, this is what you need. A user should only be able to access/edit their data when they’re logged in as themselves, but you can’t run any admin actions (example thread here). If you want to run some admin-side actions, there’s a workaround mentioned in that thread towards the end, but here’s it in short: When a user signs-up, it’s recommended to get the user ID then and there and save it to an external database or something using the identity-signup function. So, in future, you could find the user by ID.

1b) Yes, the ID is encoded within the token. Basically, all of the data that you’d find in the user object in the function, is just the decoded JWT. Yes, the ID can be accessed on the client side, actually the entire Identity data of the logged in user can be accessed by anyone on the client side, but they can’t do much with it if you’ve set your security in the function correctly. For example, if you are not using any JS bundler and directly importing the Identity Widget from the CDN, it allows anyone to access netlifyIdentity in the global scope. So, once logged in, they can do something like netlifyIdentity.currentUser() to get their own data, but they definitely can’t get someone else’s data with this. However, if they get their Bearer token, then can call your serverless function with it and act maliciously. So, for any kind of secure (admin-only) functions, I’d advise you to add a condition that will allow access to the function only when you call it. For example, something like this:

exports.handler = async (event, context) => {
  const user = context.clientContext.user
  if (user && user.sub === 'your-admin-user-id') {
    // process the function
  } else {
    return: {
      statusCode: 401
      body: JSON.stringify('Unauthorised')
    }
  }
}
  1. I think, the answer for this question is up to you. You’d have to figure out the most optimal way fo your use case and keeping in mind the limitations of your frontend and backend systems. Yes, you can keep a database entry unique to each user by their ID, but you’d have to see if it scales correctly if you need it.
3 Likes

Thanks @hrishikesh this is exactly the help I needed. Appreciate it!

1 Like