When I run this function, a user is created as exp...
# off-topic
l
When I run this function, a user is created as expected in the
users
table with a new UUID — but the returned value is
null.
Copy code
let { user, error } = await supabase.auth.signIn({
        email: formData.get('email')
      })
Is there anyway to return the newly created user's UUID / user object from this function? (I have turned off "Enable email confirmations" in the auth settings, to make it easier)
s
When I run this function, a user is created as expected in the
users
table with a new UUID — but the returned value is
null.
Copy code
let { user, error } = await supabase.auth.signIn({
        email: formData.get('email')
      })
Is there anyway to return the newly created user's UUID / user object from this function? (I have turned off "Enable email confirmations" in the auth settings, to make it easier)
I think the proper value is
data
not
user
, try that and see if you get the correct information back
l
I just swapped
data
for
user
, here's the full function — still returning null:
Copy code
javascript
     async function addEmail(e) {

      let formData = new FormData(e.target);

      console.log(formData.get('email'));

      let { data, error } = await supabase.auth.signIn({
        email: formData.get('email')
      })
      if (data) {
        console.log(data);
        let new_user = data;
        profile_form = true;
        return data;
      }
      else {
        console.log(error);
      }
     }
@User
s
Ah yes that's correct, because its using the magic link. It wouldn't have any information on the user at this point, its only sending an email
It would be an security issue if it was to expose information on the user at this point
The only time you would be able to see this information is after the user has clicked on the magic link in the email that was sent to them, because that's when authentication actually happens
l
Got it, thanks so much for the advice. To clarify my ask, I'm not actually interested in signing in the user. I'm just trying to return the newly created UUID, so I can store additional profile information for that user's UUID (e.g. name, website) in the same onboarding form. Is there anyway to return that newly created UUID?
s
Is it that you are trying to create some sort of profile for the user?
Also you wouldn't be continuing the onboarding form until the user has logged in right? otherwise this could become troublesome
l
Exactly right.
I'm trying to complete the onboarding form before dealing with authentication or the user leaving the page. I know this is do-able when including a password in the sign-up option, I'm just trying to think through how I could do it without needing to include a password (for UX reasons)
The current UX is as follows: 1. Submit email address form 2. Submit form with name, personal website, twitter handle And I just want to collect that information for now, associated with the new user UUID. No need to log-in or authenticate at the moment.
s
How are you currently storing the form information before sending it to the database? as in step 2
l
I'm not storing the form 2 data anywhere else in between — I'm trying store the profile form information in the database in a
profiles
table on capture, so I don't need to worry about a different form collection tool, and all of the profile information will be ready once the application is live and dealing with authentication
Does that make sense? Also huge thanks for your help on this!
s
Also what's stopping you from putting all the fields in the same form?
So having everything in Step 1?
My solution is keeping everything in 1 form and store the additional data that I need for the profiles table inside of the last parameter of the
signIn
function
Copy code
js
const { data, error } = await supabase.auth.signIn(
  { email },
  {
    data: {
      full_name,
      website,
      twitter
  }
})
Then you create a database trigger that will move that data into the
profiles
table
Scroll down to the using triggers section
In order to get the data from the
additional data
section inside of a trigger function, you would do something like the below
Copy code
sql
create function public.handle_new_profile()
returns trigger as $$
begin
    insert into public.profiles (user_id, full_name, website)
    values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'website');
    return new;
end;
$$ language plpgsql security definer;
So you wouldn't need to worry about the user's uuid externally, it would be handled internally by the database itself
l
Amazing, I didn't know this was possible (even having done the profiles examples before)! Exactly the solution I'm looking for!!!
And huge thank you for the code examples — it's like you read my mind! It's especially helpful, as I'm not very experienced with the SQL programming
s
If you want to keep your form 2 steps, just store the email into localstorage or session and don't call the
signIn
function until step 2 is completed
l
Yep exactly, that makes sense
s
Happy to be of help
l
So the function / trigger is successfully creating a new profile with the new UUID and user email, but none of the information in
{data}
is getting entered. Here's my function call:
Copy code
javascript
      const { data, error } = await supabase.auth.signIn(
        { email: localStorage.getItem('email') },
        {
          data: {
            full_name: formData.get('full_name'),
            introduction: formData.get('introduction'),
            website_url: formData.get('website_url'),
            linkedin_url: formData.get('linkedin_url'),
            twitter_handle: formData.get('twitter_handle'),
            contact_method: formData.get('contact_method')
        }
      })
And here is the function/trigger:
Copy code
sql
create function public.handle_new_profile()
returns trigger as $$
begin
    insert into public.profiles (user_id, user_email, full_name, introduction, website_url, linkedin_url, twitter_handle, contact_method )
    values (new.id, new.email, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'introduction', new.raw_user_meta_data->>'website_url', new.raw_user_meta_data->>'linkedin_url', new.raw_user_meta_data->>'twitter_handle', new.raw_user_meta_data->>'contact_method');
    return new;
end;
$$ language plpgsql security definer;

-- trigger the function every time a user is created
create trigger on_auth_user_created
  after insert on auth.users
  for each row execute procedure public.handle_new_profile();
I imagine it's just some syntax thing, that I'm not calling the submitted form data correctly in the Insert function. Any thoughts on that?
s
Can you just check that values are actually being set in the
formData.get('ful_name')
and so on
Also make sure that all required fields in the
profiles
table are being set
l
I just tried with all hardcoded data:
Copy code
javascript
      const { data, error } = await supabase.auth.signIn(
        { email: localStorage.getItem('email') },
        {
          data: {
            full_name: "test",
            introduction: "test",
            website_url: "test",
            linkedin_url: "test",
            twitter_handle: "test",
            contact_method: "test"
        }
      })
Same result — none of the information in
{ data: {} }
is appearing in the profiles table when the new profiles are created
s
Ok give me a few minutes, just in a meeting call
l
All good, thank you so much again!
Hey @User , just got off a call myself. If you have any other ideas for troubleshooting this, let me know.
Not sure if this thread or the other (setting metadata) is the right place to continue the conversation? Anyway, as @User pointed out, it appears the SignIn function doesn't have a data property. From the docs: https://github.com/supabase/gotrue-js/blob/master/src/GoTrueClient.ts#L115
Copy code
javascript
  async signIn(
    { email, phone, password, refreshToken, provider }: UserCredentials,
    options: {
      redirectTo?: string
      scopes?: string
    } = {}
compared to signUp which does seem to have data property:
Copy code
javascript
  async signUp(
    { email, password, phone }: UserCredentials,
    options: {
      redirectTo?: string
      data?: object
    } = {}
With that in mind, any other ideas as to how I can do what I'm trying to do? Create a user just with
email
, and then add form data to the user's
profile
, without authentication in the middle?
y
you won't nor should you attempt to store data in your profiles table until authentication is complete, the thing with magic links afaik is that you're not authenticated till you click on the link in your email and whatever auth handler processes the magic token to authenticate the user. Basically, what I'm trying to say is a second form is inevitable. Or you'll have to rely on a clunky workaround, idk exactly how supabase handles magic links but it must redirect the user to some callback route once the user clicks on the link, you could perhaps create a http cookie or if you prefer not to involve a server localStorage or a web cookie to store the additional user info on sign in and then get that data again on the callback route then just insert the data to the profiles table either by sending the data to the server or by making a request from the client sdk
s
Yeah don't do the clunky suggestion (I don't think @User want you to do the clunky suggestion either), it's better to ask for the rest of the information after the user has signed in
y
i would advise against it too, hence clunky because there's too many ways to mess the whole process up
l
Much appreciated @User @User . For context, there isn't really an application yet — this is equivalent to a waitlist/onboarding signup, for a potential future application. (Which is why passwords are a bit out of context.). With that in mind, two workarounds I can imagine? 1. Adding a password field in the form inputs, and using the
signUp
function. 2. Generating a random password for users in the background and running
signUp
with the randomly generated passwords, and then when there is a real application and sign-in flow, just forcing users to sign in with magic link or reset their passwords?
#2 sounds bad at first glance, but would it really be? The only issue would be that people don't have passwords — but there's nothing for them to sign into anyway.
y
i don't think #2 is possible, how would you get the passwords without signing in?
s
So 2 could work, but I fear if the API surface changes in the future this could easily break. Currently there is a bug (a feature to some) where if you don't provide an password on signIn it will send you a magic link anyway
This could be fixed in the future and would render your execution useless
So you could create the signup flow with a random password for each user and do all that you want, then when the app is ready you can sign them in with just their email (as magic link would be sent, even though there is a password on their account)
But as I stated before this could break if the Supabase team decides to fix this bug (feature)
l
This is what came to mind, as @User described:
Copy code
javascript
let { user, error } = await supabase.auth.signUp({
  email: 'new_user@email.com',
  password: '{RANDOMLY_GENERATED_PASSWORD}'
}, {          data: {
            full_name: "name",
            introduction: "hello I'm name",
            website_url: "example.com",
            linkedin_url: "linkedin.com",
            twitter_handle: "@name",
            contact_method: "reach me here "
        }
  }
})
This is exactly what I was thinking. This is all a rather short-term need, so it's okay if functionality breaks — good to know though and monitor the development!
Also, on this topic of conversation, I'm basically doing this to use supabase as my form handler — so it definitely seems like there could be a feature around this? I've seen quite a few issues mentioning it, here's a related one: https://github.com/supabase/supabase/discussions/3787
s
That issue is related to the developer using the wrong json accessor in Postgres
y
didn't know this was a thing lol
s
Yeah its a really bad bug, it's definitely not a feature
l
Regarding what could be a feature, what I'm trying to do is get rid of Typeform / Google Forms / Airtable / Mailchimp signups in the onboarding flow. I'm collecting emails and profile forms, which could be used a few different ways (e.g. newsletter, invite to a Slack space, users in an application). Instead of collecting that information via a separate form handler, and then having users (a) re-submit their form data in the application when it's released, or (b) manually taking data from the other form handler and merging it into profile rows in the application — I'm trying to store it in the application backend from the start, as it would be easier to export from Supabase out into other services, then import from an external service into Supabase and match everything correctly with the UUIDs. And then, when application is released, user profiles are already set-up. Does that make sense?
Aha @User , I just noticed the fix had to do with working with the data object. Here's another recent artifact that seems to be around the same topic (working with data object fields on signup): https://github.com/supabase/supabase/discussions/3782
s
What if I told you there is probably a better way to deal with this? you could just write these directly to a
invite
table without a
user_id
, then use the
inviteUserByEmail
functionality later to deal with the signUp process https://supabase.io/docs/reference/javascript/auth-api-inviteuserbyemail
Do note that this would need to run on the server side though as
inviteUserByEmail
requires the
service_role
key
l
This makes a ton of sense, and thank you very much for the recommendation
s
At the point of inviting you can also pass the additional data as a second parameter to the
inviteUserByEmail
function
Copy code
js
const { user, error } = await supabase.auth.api
  .inviteUserByEmail('email@example.com', { data: {
    full_name: "name",
    introduction: "hello I'm name",
    website_url: "example.com",
    linkedin_url: "linkedin.com",
    twitter_handle: "@name",
    contact_method: "reach me here "
  }})
l
You read my mind once again, this is exactly what I was going to ask about!
s
This thread is packed with so many gems
Let me go add this as a topic for my book 😁
l
I know. It makes me think about the unfortunate side, that none of it is discoverable because it's in Discord — there are some conversations picking up about that as well, all of the knowledge like this behind walled gardens in Discord/Slack instead of in public forums like Stack Overflow
Count me in for a pre-order!
s
This is why I always recommend that people should post on the GitHub Discussions
Everyone was asking for Discord, so the Supabase team listened to their users (this is what I love about the Supabase team)
l
Right right, I'm not faulting them for that — more just lamenting the fact that we do have so many developer/programmer in walled gardens nowadays, with a lot of knowledge kept in there. More reason for the book! By the way, for now, random generated password workflow works!
This is what it was all for by the way, powered by supabase and @User @User wisdom! https://rebrand.ly/community-savings
s
Nice one