jsnow
jsnow

Reputation: 1868

How to pass a dynamically generated `state` token to useGoogleLogin in react-oauth

Forgive me...I am a Python developer trying to integrate a Google OAuth flow into a react/TypeScript frontend. I have based this from examples from the react-oauth docs. What I want to do is dynamically generate the state token to be passed to useGoogleLogin when the login button is clicked.

const googleLogin = useGoogleLogin({
    ux_mode: "redirect",
    state: "token",  // <= I want to dynamically set this value when the login button is clicked
    redirect_uri: "http://localhost:3000/auth/google/callback/",
    flow: "auth-code",
    scope: "https://www.googleapis.com/auth/drive.readonly",
    onSuccess: (codeResponse) => console.log('Login OK so far'),
    onError: (error) => console.log('Login Failed:', error)
});

this is called in the page like so: <button onClick={() => googleLogin()}>Sign in with Googlefo 🚀 </button>

I can't figure out a way around the "rules of hooks" and and how to wrap this useGoogleLogin, but it seems like this should be an easy thing to accomplish. I have a method that I can call that requests a state token from the backend and can be called like this: const token = await api.user.getStateToken(). This token is temporarily cached on the back end so I would like to retrieve it synchronously when the login button is clicked.

This is what I do for LinkedIn login, which is hand-coded to initiate the OAuth redirect but simple enough and should give an idea of what I am looking for.

const onLoginLinkedIn = async () => {
    // need the request to the backend for a state token to resolve before moving on. 
    // the state token is used to verify that the later callback from LinkedIn is 
    // related to this call
    const token = await api.user.getStateToken()
    await router.push(`https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=${client_id}&redirect_uri=${encodeURIComponent(callback_uri)}&state=${token}&scope=r_basicprofile%20r_emailaddress%20w_member_social`)
}

this is called on the page like so: <img src="./linkedin/Sign-in-Large---Default.png" alt={"Login LinkedIn"} onClick={onLoginLinkedIn}/>

Is there a way to can create something equivalent to this, but that uses useGoogleLogin instead of the router.push?

Upvotes: 1

Views: 416

Answers (1)

Ori Drori
Ori Drori

Reputation: 193348

The useGoogleLogin returns a function that you can execute when needed. The returned function is updated when state changes. Your login should update the state, and when state is updated, a useEffect should trigger and perform the actual login call:

const [state, setState] = useState<string | undefined>();

const googleLogin = useGoogleLogin({
  ux_mode: "redirect",
  state,
  redirect_uri: "http://localhost:3000/auth/google/callback/",
  flow: "auth-code",
  scope: "https://www.googleapis.com/auth/drive.readonly",
  onSuccess: (codeResponse) => console.log('Login OK so far'),
  onError: (error) => console.log('Login Failed:', error)
});

useEffect(() => {
  if(state) googleLogin(state); // call googleLogin when state is defined
}, [googleLogin, state]);

const onLogin = async() => {
  const token = await api.user.getStateToken()
  setState(token)
};

Upvotes: 1

Related Questions