Ways to obtain GitHub username using Firebase GitHub Authentication

I need assistance with retrieving the username of the user during authentication using Firebase Github Auth. I am able to access all user information except for the username. Below is the code snippet:

import React, { useState, useContext, createContext, useEffect } from "react"
import { auth, createUserProfileDocument } from "../config/fbConfig"

const AuthContext = createContext()

export const useAuth = () => {
  return useContext(AuthContext)
}

export const AuthProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null)

  const provider = new auth.GithubAuthProvider()
  provider.addScope("read:user")
  const githubSignIn = () => {
    return auth().signInWithRedirect(provider)
  }

  const githubSignOut = () => {
    return auth().signOut()
  }

  useEffect(() => {
    const unsubscribe = auth().onAuthStateChanged(async (userAuth) => {
      // setCurrentUser(user);
      if (userAuth) {
        const useRef = await createUserProfileDocument(userAuth)
        useRef.onSnapshot((snapShot) => {
          console.log(snapShot)
          setCurrentUser({
            id: snapShot.id,
            ...snapShot.data(),
          })
        })
      } else {
        setCurrentUser(userAuth)
      }
    })
    return unsubscribe
  }, [])

  const value = {
    currentUser,
    githubSignIn,
    githubSignOut,
  }

  return <AuthContext.Provider value={value}> {children} </AuthContext.Provider>
}

Code snippet from fbConfig.js file:

export const createUserProfileDocument = async (userAuth) => {
  if (!userAuth) return
  const userRef = firestore.doc(`users/${userAuth.uid}`)
  const snapShot = await userRef.get()

  if (!snapShot.exists) {
    const { email, photoURL, providerData } = userAuth
    const createdAt = new Date()
    getGitHubUserData(providerData[0].uid)
      .then(async (gitHubUserData) => {
        const username = gitHubUserData.login
        try {
          await userRef.set({
            email,
            photoURL,
            createdAt,
            displayName: providerData[0].displayName,
            username,
          })
        } catch (error) {
          console.log(error.message)
        }
      })
      .catch((err) => console.error("Don't forget error handling: ", err))
  }
  return userRef
}

Requesting assistance from anyone who can help me resolve this issue.

Answer №1

When verifying a GitHub user's authenticity, Firebase retains specific details in the authenticated user's ID token (auth.currentUser.providerData):

{
  // User's display name on GitHub
  displayName: "Display Name",
  // Public email address of the user, null if private
  email: null,
  // Phone number is not applicable, always null
  phoneNumber: null,
  // Link to the user's GitHub profile image, possibly hosted on Gravatar
  photoURL: "https://avatars.githubusercontent.com/u/GITHUB_ID?v=4",
  // Identifier for provider being "github.com"
  providerId: "github.com",
  // Numeric User ID on GitHub
  uid: "GITHUB_ID"
}

It is important to note that none of this data reveals the user's username on GitHub. This is due to the fact that a GitHub user has the ability to change their username without notification to connected apps or associated implications.

To map a GitHub user's ID to their username, known as the login on GitHub, the GitHub API can be queried using the user's ID:

https://api.github.com/user/{idOrLogin}

Fetching this data through a function results in:

async function getGitHubUserData(githubIdOrLogin) {
  return fetch(
    `https://api.github.com/user/${githubIdOrLogin}`,
    { headers: { 'Accept': 'application/json' } }
  )
    .then((response) => {
      if (!res.ok) {
        const err = new Error();
        err.response = res;
        if (res.status === 403 && res.headers.get('X-RateLimit-Remaining') == '0') {
          const resetsAtMS = Number(`${res.headers.get('X-RateLimit-Reset')}000`);
          err.message = `Rate limit exceeded, try again in ${Math.ceil((resetsAtMS-Date.now())/60000)}m`;
          err.code = "github/rate-limit-exceeded";
          err.resetsAt = resetsAtMS;
        } else if (res.status === 404) {
          err.message = `Could not find user data for github:${githubIdOrLogin}`);
          err.code = "github/not-found";
        } else {
          err.message = `Unexpected status code: ${res.status}`;
          err.code = "github/unknown";
        }
        return Promise.reject(err);
      }

      return res.json();
    });
}

This can be used as follows:

const githubProviderData = auth.currentUser
  .providerData
  .find((pd) => pd.providerId === 'github.com');

getGitHubUserData(githubProviderData.uid)
  .then((githubUserData) => {
    // The user's username will be available as githubUserData.login
    // Accessing githubUserData.html_url will lead to their profile link
  })
  .catch((err) => console.error('Do not forget error handling: ', err));

Additional Notes:

  • When utilizing this API anonymously (without authentication), there is currently a restriction of 60 public API calls per hour from that IP address. The rate-limit-exceeded response code by GitHub is 403 Forbidden. If the user's authentication token is integrated, the limit increases to 5000 calls/hour per user. By employing an application/server authentication token, the limit elevates to 12500 calls/hour. Refer to GitHub's documentation for further details.
  • Various Node packages are accessible for utilizing the GitHub API, such as the official @octokit/core, @octokit/request, and @octokit/rest packages if more extensive functionalities relating to the user's GitHub activities are required. For detailed information, review the GitHub request documentation. Utilizing @octokit/request would simplify the above code considerably (though error handling varies):
request('GET /users/{username}', { username: idOrLogin })

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Displaying genuine HTML content in a React application using Algolia Instantsearch

After setting up a demo app using React with an Algolia search feature, I uploaded some indices into Algolia. The content consists of raw HTML. Is there a way to display this content as real HTML using Algolia? ...

Can a unique class name generator be implemented for styled components in MaterialUI?

Currently, I am working on a create-react-app project that uses MaterialUI. In an attempt to switch from JSS to Styled Components, everything is functioning properly but the generated class names are not easily understandable. I came across information su ...

Unexpected behavior in Joi-Browser causing issues with React.js form validation

In my quest to create a multi-step form with React.js and material UI, I encountered an issue with Joi-Browser validation. The error message states: ValidationError: "value" must be an object. Since I am new to React.js, I would appreciate any guidance on ...

Can you explain the distinction between onKeyUp and onKeyUpCapture in React (as well as onKeyDown/Capture)?

I was unable to find any existing documentation or questions related to my query, which surprised me. Upon inspecting the React index.d.ts file, I came across the following: // Keyboard Events onKeyDown?: KeyboardEventHandler<T>; onKeyDownCapture?: ...

Utilize inherited autocomplete/suggestion prop types within the wrapping component

I am currently developing my own customized "wrapper" for a Chakra UI component. This wrapper allows me to include additional props, methods, and any other functionalities that I may require in the future. However, one drawback of this approach is that I ...

Error: The constructor for JsSHA is not valid for the TOTP generator

Attempting to create a TOTP generator similar to Google's timed-based authenticator using the React framework. I am utilizing the bellstrand module for TOTP generation, but I am encountering issues when trying to import it into my React code. Here is ...

There seems to be an issue with Material-UI and TypeScript where a parameter of type 'string' does not have an index signature in the type 'ClassNameMap<"container" | "navBar" | "section0">'

My current project is encountering a TS Error stating "(No index signature with a parameter of type 'string' was found on type 'ClassNameMap<"container" | "navBar" | "section0">'.)" The code below is used to assign styles to vari ...

Is there a way to customize the text color beneath a Doughnut chart using react-chartjs-2?

Currently, I am utilizing the Doughnut feature from the react-chartjs-2 library and I have a specific request. My goal is to change the color of the text beneath the graph to white because it is difficult to read against the current background (please refe ...

Utilize React to update the state of arrays in functional components

Need help with updating the cars array in my React app. When I click on the Add button, a new object is added to the updatedCars array but the state of cars does not get updated. Even after adding a new object to the array, the initial state remains uncha ...

Having trouble with a CardMedia component in React not displaying the image properly

I am facing an issue where the images in my project are not showing up. I have tried using an image with a HTTP link and also an image from the project source folder, but both are not loading. Everything else on the card is loading fine except for the ima ...

Updating the background image of a React app according to each component

After researching extensively and attempting various solutions, I am still struggling to make my app function correctly. My goal is to display a different background image depending on which component is being rendered on the screen. The app is built using ...

FirebaseError: The type 'Hc' was expected, but instead, a custom Yc object was provided

I've encountered an issue while attempting to perform a batch entry. The error I'm facing involves passing an array in a .doc file. Interestingly, this approach seems to work perfectly fine on another function where I pass an array into a .doc us ...

The counterpart to Ruby's `.select{ |x| condition }` in Javascript/ React.js would be to

This javascript function in React.js utilizes a for loop to determine the opponent team: getOpponentTeam: function(playerTeamId){ var matches = this.state.matches; var player_team = this.state.player.team.name for (i in matches){ if (matches[i]. ...

Having difficulty displaying JSON data in a react component

I am currently working on parsing JSON data retrieved from an Ajax call in order to display it in a table using the React DataTable component. However, I have encountered a problem while trying to store the data in a state variable using the setState metho ...

Is there a way to trigger a revalidation of a specific page from a client component in NextJS?

The issue Currently, I am in the process of developing a website that utilizes a supabase backend. The main feature of this website is the creation of guides by users. Each guide has a unique dynamic path /guides/[id] as well as an exclusive edit page /gu ...

Can the MUI autocomplete feature be connected to two different field values simultaneously?

Hello there! I am currently working on creating a unique custom search input that will provide users with filter categories to select from when clicked. Users will also have the option to enter a title keyword to refine their search based on both the title ...

Form-linked Progress Bar

This is a little project I created (for fun and learning purposes, even though it may not be the most optimized solution). If you're interested, here's the link to the code: https://codepen.io/paschos/pen/xxGXMQb I'm currently seeking assi ...

Revamping array elements in React

Once I added an element to the array, I needed to update this array by doubling all elements except for the one that was just added. Despite trying setArray([]) before retrieving the array from the database, it didn't seem to work... const [array, se ...

What is the reason lottieRef consistently returns null?

When using the library called "@ lottiefiles/react-lottie-player", it's important to obtain lottieRef in order to interact with the animation. However, I am encountering a problem where lottieRef is returning null. If you want to check out the code s ...

Explore various domains using the material-ui autocomplete feature

I'm currently working with material-ui and autoComplete in ReactJS to search within a single domain. How can I modify it to search across multiple domains? In addition, I would like to include the customerSurname field. Currently searching based on ...