How to delete user programatically?

Hi

I’m trying to delete Netlify user programatically but can’t seem to find any docs about it.

How can I achieve this?

I’ve created a function based on https://github.com/netlify/gotrue-js#delete-a-user

It doesn’t succeed as the context I get is just an empty object: {"clientContext":{}}.

I see the context gets identity and user. How do I pass them using context from the client?

EDIT: I’ve passed the data using request body instead. However, one issue is that function failed because the token was expired. Second issue is that I’m getting code: 401, msg: 'User not allowed' when trying to delete the user. How can I resolve these issues?

The tokens we issue last only an hour, so you’d need to request one with every invocation before using it, I think - you couldn’t hardcode anything. Are you saying it never works, or that it only works sometimes?

Unless deleting gotrue user from the browser storage and logging back in isn’t enough to get a new token, I can never get to delete the user. It always returns a 401:

Deleted a user!
{ data: { code: 401, msg: 'User not allowed' } }
Response with status 204 in 414 ms.

On a related note, how do I request a new token?

Hi @abtx, you can’t delete a user from the browser because the Identity token users get doesn’t have admin access to your Identity instance. In Netlify functions there is a short lived Identity token that you can use to perform admin actions like deleting a user. If you already wrote a function, can you share the source code for it so we can take a look?

Hi @futuregerald, maybe I should made myself clearer. I didn’t try to delete the user from the browser per se, obviously that cannot work. What I meant is that I was looking for a way to make sure the go true token I use is not expired (with new login). Here’s my function:

import fetch from “node-fetch”;
const chalk = require(‘chalk’)

exports.handler = async (event, context) => {
// const { identity, user } = context.clientContext;

let body = JSON.parse(event.body)
const identity = body.identity
const user = body.user

console.log(chalk.red(JSON.stringify(body)))

console.log(chalk.blue(JSON.stringify(identity)))
console.log(chalk.yellow(JSON.stringify(user)))

const userID = user.id;
// let userID = JSON.parse(event.body)
const userUrl = ${identity.url}/admin/users/{${userID}};
const adminAuthHeader = "Bearer " + identity.token.access_token;
console.log(chalk.red(JSON.stringify(adminAuthHeader)))
console.log(chalk.red(JSON.stringify(userUrl)))

try {
return fetch(userUrl, {
method: “DELETE”,
headers: { Authorization: adminAuthHeader }
})
.then(response => {
console.log(“Deleted a user!”);
return response.json();
})
.then(data => {
console.log({ data });
// return { statusCode: 204 };

    return {      
      statusCode: 204,
      body: JSON.stringify({ data }),    
    } 
  })
  .catch(e => {
    return {
      statusCode: 500,
      body: "Internal Server Error: " + e
    };
  });

} catch (e) {
return e;
}
};

@abtx, if you pass the user’s gotrue token in the authorization header to your function, we’ll verify and decode it. So you can check for the existence of the user in the clientContext, if it’s there then that means the token passed in to the function is valid.

@futuregerald, I get the token from netlifyIdentity.gotrue.currentUser for the logged in user, object returned from Netlify so I assume the token must be valid.

I tried this in production as well to check if _sameOrigin: true was blocking this, but I’ve got the same result.

What am I missing?

  1. I log in using Netlify Identity Widget
  2. I get the netlifyIdentity object where I can find the token innetlifyIdentity.gotrue.currentUser()
  3. I pass a bearer token in headers: { Authorization: "Bearer <the token>"}
  4. I call ${identity.url}/admin/users/{${userID}} with the header above, checked the identity url and it’s correct, tried triggering in prod, the user ID is from netlifyIdentity.currentUser().id
  5. I get the 401.

I’m really not sure why I’m still getting the 401 ‘User not allowed’.

PS: I’ve verified the token with:

const user = auth.currentUser();
const jwt = user.jwt();
jwt
  .then(response => console.log("This is a JWT token", response))
  .catch(error => {
    console.log("Error fetching JWT token", error);
    throw error;
  });

and I’m getting a valid token, still same 401.

PS2: one thing to notice is that I pass all the data in the body to my delete function, as opposed to using this:

const {identity, user} = context.clientContext

If I log context.clientContext it’s an empty object in my function.

The docs say

If an Identity service is active for a site, serverless functions running on that site have access to an identity and a user object in the clientContext .

I’ve got netlifyIdentity.init() that runs before everything else loads.

currentUser returns the currentUser but not necessarily a valid token, that depends on the last time you got a new token and how long that session has been open. Tokens are only good for 1 hour. I see you used jwt() function to verify your token, I recommend running it before every request you make to a function that requires a token.

I deployed the following simple function:
image
and I made a request to it passing a valid JWT token in an authorization header:


and it’s logging the user properly:

  email: 'gerald@netlify.com',
  exp: 1576517864,
  sub: '16asdf8f8-7db8-4650-bd3c-bd0db9970c68',
  user_metadata:
   { avatar_url:
      'https://lh3.googleusercontent.com/a-/AAuE7mAu69dksfsfadfhKoaG-Q',
     full_name: 'Gerald Onyango' } }

Cool, thanks for this. I was generating the header in the actual Netlify function which for some reason didn’t work. Passing the context worked, and on top of that logging out the variables generated with the context I noticed it wouldn’t work in the localhost anyway. For anyone having similar issue, pass your bearer on the front end to your function (and then again in your function).

1 Like