Hey @carvill,
Thank you for your patience. I’ve brought some good news! Turns out it’s possible. Definitely a hack, but it works (at least worked in my production tests , hope it does for you too).
So yeah, the solution is simple in concept and it’s actually great that you’re going to use the admin account thought Netlify Identity too. So what you’ve got to do is this:
If you’re using GoTrueJS to generate a custom sign-in/sign-up flow, you can do it like this:
import GoTrue from 'gotrue-js'
// call this when you're already logged in as yourself
const admin = new GoTrue({
APIUrl: 'https://<your-wesbsite>/.netlify/identity'
}).currentUser()
fetch('/.netlify/functions/updateRole/', {
method: 'patch',
body: JSON.stringify({
email: 'target email address', // dynamically fill these values as needed
role: 'role to apply'
}),
headers: {
'content-type': 'application/json',
Authorization: `Bearer ${admin.token.access_token}`
}
}).then(response => {
return response.json()
}).then(updatedUser => {
// updatedUser is the user with new role(s)
console.log(updatedUser)
})
Even if you use Netlify identity Widget, the process is going to be fairly similar, except instead you’d get your own admin account like this: const admin = netlifyIdentity.currentUser()
.
Finally, in your serverless function (updateRole.js
):
import fetch from 'node-fetch'
export async function handler(event, context) {
const payload = JSON.parse(event.body)
const identity = context.clientContext.identity
return fetch(`${identity.url}/admin/users`, {
headers: {
Authorization: `Bearer ${identity.token}`
}
}).then(response => {
return response.json()
}).then(data => {
const requiredUser = data.users.filter(user => {
return user.email === payload.email
})
return fetch(`${identity.url}/admin/users/${requiredUser[0].id}`, {
method: 'put',
body: JSON.stringify({
app_metadata: {
roles: [payload.role]
}
}),
headers: {
Authorization: `Bearer ${identity.token}`
}
}).then(response => {
return response.json()
}).then(updatedUser => {
return {
statusCode: 200,
body: JSON.stringify(updatedUser)
}
})
})
}
You can simply copy-paste it. To add an explanation on how it works:
Basically, you are sending your own auth token (something that’s required to use Identity stuff inside functions), instead of your users’. This won’t make any difference, only that you could login to your (admin) account. Our problem was that we need to get an auth token to send along with the fetch as a header. This was not possible as we needed to login as the user, but now we don’t. You can login as yourself and get the token and that would work too.
Once you get your identity instance initialised inside the function, there’s an endpoint you can hit to get all users: GitHub - netlify/gotrue-js: JavaScript client library for GoTrue. That’s what we do. We get a list of all users for your identity instance and then filter the one we need. We filter it based on the payload data you send from your frontend. The email you specify there would be matched against the list of users you’ve. Thus, you’d get the required user.
Once you get the required user, you can get their ID and that’s all needed to make another request to update their role. The role is the one you send as payload from your frontend.
Note that, you’d have to add error handling and test for edge cases. This is just a primitive demo to update the roles without logging in as the user. Also, you probably need to add another layer of security. For example, in the current state, anyone with a valid auth token (any user of your identity instance), can hit your function with a JSON payload and update themselves. So, you can do some checks internally to see if the user object carried in the context
is your account or not (you can check for email address, or check the role or other meta data). If you can verify it as yourself, only then proceed with the function, or return a 401 (unauthorised).
Hope this helps!