I am trying to get username of the user while authenticating using Firebase Github Auth. but I am not able to get it. I get all the user info except an username Here is my code:
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>
}
fbConfig.js file code
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
}
Anyone please help me with this.
CodePudding user response:
When authenticating a GitHub user, Firebase will store the following information in the authenticated user's ID token (auth.currentUser.providerData
):
{
// User's GitHub display name
displayName: "Display Name",
// User's public email address, null when private/out-of-scope
email: null,
// Phone numbers aren't applicable. Always null.
phoneNumber: null,
// Link to user's GitHub profile image, may be hosted on Gravatar
photoURL: "https://avatars.githubusercontent.com/u/GITHUB_ID?v=4",
// Always "github.com" - used to identify this ProviderData entry
providerId: "github.com",
// Numeric Github User ID
uid: "GITHUB_ID"
}
Note here, that none of this data provides the user's username on GitHub. This is mainly because a GitHub user can change their username at will and GitHub will not send out "new username" notifications to any connected apps along with other side effects.
With this in mind, to resolve a GitHub user ID to their username, known on GitHub as their login
, you can query the GitHub API with the user's ID:
https://api.github.com/user/{idOrLogin}
Accessing this data with a function gives:
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 { // add other cases if you want to handle them
err.message = `Unexpected status code: ${res.status}`;
err.code = "github/unknown";
}
return Promise.reject(err);
}
return res.json();
});
}
Then you can use it like so:
const githubProviderData = auth.currentUser
.providerData
.find((pd) => pd.providerId === 'github.com');
getGitHubUserData(githubProviderData.uid)
.then((githubUserData) => {
// githubUserData.login will be their username
// githubUserData.html_url will be a link to their profile
})
.catch((err) => console.error('Don\'t forget error handling: ', err));
Notes:
- When using this API anonymously (i.e. without authentication) you are subject to (at the time of writing) a limit of 60 public API calls per hour for that IP address. GitHub's rate-limit-exceeded response code is
403 Forbidden
. If you tie in the user's authentication token, this limit bumps up to 5000 calls/hour for that user. If you use a application/server authentication token, this limit bumps up to 12500 calls/hour. See GitHub's documentation for more details. - There are node packages available that consume the GitHub API like the official
@octokit/core
,@octokit/request
and@octokit/rest
packages if you are doing more with the user's GitHub than just getting their username. See the GitHub request documentation for more info. BVy using@octokit/request
, the above code could be simplified to just (but errors are handled differently):
request('GET /users/{username}', { username: idOrLogin })