netlify-labs / react-netlify-identity

a tiny (4kb) React hook for using Netlify Identity, no UI. SEEKING MAINTAINERS
https://netlify-gotrue-in-react.netlify.com/
156 stars 25 forks source link

How to confirm email? #5

Closed YChebotaev closed 4 years ago

YChebotaev commented 5 years ago

I invited user (myself) from netlify Identity console, and receive email titled "You've been invited to join ..." with "Accept the invite" link within it.

I follow this link and turned up at my-app.com/login page. When I enter my email and empty password I get error: invalid_grant: Email not confirmed

So i'm wondering how do I suppose to confirm email before using?

YChebotaev commented 5 years ago

Should I invoke recoverAccount handle?

swyxio commented 5 years ago

good question. i probably should document this better but we rely on a hash: https://github.com/sw-yx/react-netlify-identity/blob/48726cb891917b085b942027959a6c67d19d2fa0/src/index.tsx#L78

and the confirmation is automatically done through a React useEffect hook.

dont forget you can see open source working examples in https://netlify-gotrue-in-react.netlify.com/

YChebotaev commented 5 years ago

I'm make change to use <NetlifyIdentity> as a component, but it does not help.

Same error: invalid_grant: Email not confirmed when trying to login after registration.

YChebotaev commented 5 years ago

See into console: Failed to load resource: the server responded with a status of 422 () Invited users must specify a password at ...

It should definetely have confirmUser(password: String) handle.

YChebotaev commented 5 years ago

As a workaround, I create this code:

const confirmUser = async password => {
    try {
      const { token } = match.params
      _goTrueInstance._setRememberHeaders(remember)
      const response = await _goTrueInstance._request('/verify', {
        method: 'POST',
        body: JSON.stringify({
          token,
          password,
          type: 'signup'
        })
      })
      const user = await _goTrueInstance.createUser(response, remember)
      setUser(user)
      return user
    } catch (error) {
      console.error(error)
      throw error
    }
  }

Will be happy if this gets merged into react-netlify-identity as well.

swyxio commented 5 years ago

hmm thanks for commenting. i’ll look into this in a couple days

ljosberinn commented 4 years ago

@YChebotaev sorry for pinging you. v0.2.0 introduced a TokenParam that you can destructure from useNetlifyIdentity().

const {
    param: { token, type }, // type being 'invite' | 'recovery' | 'email_change' | undefined
} = useIdentityContext();

It's not quite what you proposed and I'll see whether I can create a wrapper for your logic. I haven't tried inviting users yet, from the looks of it you chose their initial password?

What would be the preferred flow?

YChebotaev commented 4 years ago

No. The problem with accepting invite. The lib (at times I leave workaround) simply don't support accepting invites at all — it's just not working.

Just try to use it, and You probably will face the problem, as I were.

ljosberinn commented 4 years ago

This should work with #32:

// App.js
const { replace } = useHistory();
const { param: { token, type } } = useNetlifyIdentity();

if(token && pathname === '/' && type === 'invite') {
  replace('/invite', { token }); // history.location.state.token
}

// Invite route: show password field
const { verifyToken, user, signupUser } = useNetlifyIdentity();
const { location } = useHistory();
const [token] = useState(location.state?.token);
const [password, setPassword] = useState(undefined);

function handleSubmit(event) {
  event.preventDefault();

  verifyToken()
    .then(user => signupUser(user.email, password, {}, true));
}
YChebotaev commented 4 years ago

@ljosberinn Cool, thanks!

But I think that pass token via location.state is not good idea. I want more explicit way of getting token.

Well, if my app may rely on history api, it would strange if one library change it. I definetely don't want to use such a lib.

ljosberinn commented 4 years ago

Sorry, I should've been clearer - useHistory comes from react-router!

import { useHistory, Route, Switch } from 'react-router-dom';
import { useNetlifyIdentity } from 'react-netlify-identity';

function App() {
  const { replace } = useHistory();
  const { param: { token, type } } = useNetlifyIdentity();

  if(token && pathname === '/' && type === 'invite') {
    replace('/invite', { token }); // or just replace(`/invite/${token}`) if you dont mind the token being in the url
  }

  return (
    <Switch>
      <Route exact path="/" component={...} />
      <Route exact path="/invite/:token" component={...} />
      <Route exact path="/reset-password/:token?" component={...} />
    </Switch>
  )
}
YChebotaev commented 4 years ago

@ljosberinn I mean, here: https://github.com/sw-yx/react-netlify-identity/pull/32/commits/0033b604f441e54b65037dfac8c733c9918eee42 You pushState to history.

And in Your comment this line:

const [token] = useState(location.state?.token);
ljosberinn commented 4 years ago

Oh sorry now I got you. It should probably be replaceState too.

We don't really have another way of getting the token, do we? It's a given by Netlify, so all this lib can do is parse it, cleaning the URL and deliver the token to the dev.

Both replace(path, { token }) or replace(path + token) together with useState(location.state?.token) are implementation detail; it's just an example.

YChebotaev commented 4 years ago

@ljosberinn But why? If I put something useful for My app in state, Your code will replace it. I don't like lib which doing so.

ljosberinn commented 4 years ago

Like, what for example? The only code getting replaced is the document.location.hash.