Search code examples
react-nativetwitterexpotwitter-oauthexpo-auth-session

React Native Expo-Auth-Session Twitter: Value passed for the code verifier did not match the code challenge


I am currently trying to build a Twitter authorisation flow for a React Native (Expo) application using Twitter’s v2 API and expo-auth-session. I have been able to receive a code from Twitter’s API after authenticating the user, but I am running into a problem exchanging the code for an access token as per the documentation. I have built a React hook to handle the entire Twitter auth process, but after login, I always get this error

Value passed for the code verifier did not match the code challenge

Here is a snippet of my code

import React from "react"
import {
  useAuthRequest,
  CodeChallengeMethod,
  ResponseType,
  makeRedirectUri,
} from "expo-auth-session"
import { SocialLogins, SOCIAL_TOKENS, useGlobalStore } from "@libs/shared"
import { TWITTER_CLIENT_ID, TWITTER_CLIENT_SECRET } from "@env"
import pkceChallenge from "react-native-pkce-challenge"

const { codeChallenge, codeVerifier: code_verifier } = pkceChallenge()

export const useTwitterAuth = () => {
  const { socialAuth, handleSocialAuth, services } = useGlobalStore()

  const discovery = {
    authorizationEndpoint: "https://twitter.com/i/oauth2/authorize",
    tokenEndpoint: "https://twitter.com/i/oauth2/token",
    revocationEndpoint: "https://twitter.com/i/oauth2/revoke",
  }

  const state = "my-state"

  const [, , promptAsync] = useAuthRequest(
    {
      clientId: TWITTER_CLIENT_ID!,
      clientSecret: TWITTER_CLIENT_SECRET,
      // TODO: Refactor
      redirectUri: makeRedirectUri({ scheme: "scheme" }),
      usePKCE: true,
      state,
      scopes: ["tweet.read", "users.read", "offline.access"],
      responseType: ResponseType.Code,
      codeChallengeMethod: CodeChallengeMethod.S256,
      codeChallenge,
    },
    discovery
  )

  const handlePressAsync = async () => {
    const result = await promptAsync()
    if (result.type !== "success") {
      alert("Uh oh, something went wrong")
      return
    }

    const accessCode = result.params?.code as string

    const body = new URLSearchParams({
      code: accessCode,
      grant_type: "authorization_code",
      client_id: TWITTER_CLIENT_ID!,
      state,
      redirect_uri: makeRedirectUri({ scheme: "scheme" }),
      code_verifier,
    }).toString()

    fetch("https://api.twitter.com/2/oauth2/token", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body,
    })
      .then((resp) => resp.json())
      .then((data) => console.log("DATA =>", data))

    handleSocialAuth(SocialLogins.Twitter, true)
    await services.storage.setSocialToken(SOCIAL_TOKENS.Twitter, accessCode)
  }

  const handleAuth = async () => {
    if (socialAuth[SocialLogins.Twitter]) {
      handleSocialAuth(SocialLogins.Twitter, false)
      await services.storage.removeSocialToken(SOCIAL_TOKENS.Twitter)
    } else {
      handlePressAsync()
    }
  }

  React.useEffect(() => {
    const handleHasTwitterUser = async () => {
      const twitterToken = await services.storage.getSocialToken(
        SOCIAL_TOKENS.Twitter
      )

      if (!twitterToken) {
        return
      }
      handleSocialAuth(SocialLogins.Twitter, true)
    }

    handleHasTwitterUser()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  return {
    handleTwitterAuth: handleAuth,
  }
}

This STILL returns the following error:

Value passed for the code verifier did not match the code challenge I have checked that the react-native-pkce-challenge dependency I have used is working, by checking that the code challenge hasher creates the exact same code challenge as the dependency, and it seems to work as expected.

If anyone has experienced this issue, I am open to hearing from you. Thanks!

I tried to get the user to authorize my app to act on their behalf as a Twitter user. I have successfully received an access code, but I am not able to convert the access code to a token using PKCE, because even though it is correct, I still receive the same error.

Value passed for the code verifier did not match the code challenge


Solution

  • Above answer helped me in resolving below error

    OAuth "Facebook Platform" "invalid_code" "This authorization code has been used

    Instead of using codeVerifier from saved state in react native , I used it from request object returned by hook useAuthRequest()

    const [request, result, promptAsync, dismiss] = AuthSession.useAuthRequest(// rest of the code)

    Use it like this to print before trying to fetch access token console.log('code verifier is ', request.codeVerifier);

    hope this helps.