What is the process for retrieving the GitHub username in the GitHub OAuth Next.js sign-in callback in order to store it in a database?

1. Detail the issue

I am facing a challenge while developing a Full Stack Website using Next.js and Typescript. Specifically, I am having difficulty persisting the Github Username in the database when a user signs in via Github OAuth. Should I consider storing the user ID instead? My goal is to have URLs like "domain.com/[github username]". Currently, I am using the Github username as the primary key for storing user data in MongoDB. The user ID is added to the database during the sign-in callback in [...nextauth].ts.

Here is my [...nextauth].ts file:

/*
File: [..nextauth].ts
Description: This file will use NextAuth to handle requests, responses of any OAuth...
*/
import NextAuth from "next-auth/next";
import GitHubProvider from "next-auth/providers/github"
import type {CredentialsProvider} from "next-auth/providers";
import axios from "axios"
import clientPromise from "../../../lib/mongodb";
import {useSession} from "next-auth/react";

export default NextAuth({
    providers: [
        GitHubProvider({
            clientId: process.env.GITHUB_CLIENT_ID,
            clientSecret : process.env.GITHUB_CLIENT_SECRET,
            
        }),
    ],
    callbacks: {
        async jwt({ token, user, account, profile, isNewUser }) {
        // Persist the OAuth access_token to the token right after signin
        if(profile){
            token.login = profile.login
            // @ts-ignore
            user.login = profile.login
            console.log(user)
            // code up here is the user name in the jwt but user.login isn't being persisted in session nor signin
            token.id = profile.id
        }
        if (account) {
            token.accessToken = account.access_token
        }
        return token
        },
        async session({ session, token, user}) {
            // Send properties to the client, like an access_token from a provider.
            session.accessToken = token.accessToken
            session.login = token.login;
            session.id = token.id;
            // @ts-ignore
            console.log(user.name)
            return session
        },
        async signIn({ user: User, account:Account, profile: profile, email:Email }) {
            // define client
            const client = await clientPromise;

            // define database
            const db = client.db("userData");

            // define users
            const users = db.collection("users");

            console.log(User.login)


            try{
                // get user data
                const insertDocument = {"_id":User.id, "User":User}
                // @ts-ignore
                const dataUsers = await db.collection("users").insertOne(insertDocument);
                if(dataUsers){
                    console.log("Added " + String(User.id) + " to database!")
                    return true;
                }

                // if we are here user simply could not be added at all...

                return false;
            } catch (error) {
                console.log("User could not be added to database due to an error or either existing")
                return true;

            }
            return true;
        },
    },
    debug:true,
});

However, the main issue lies in not being able to retrieve the "login/username" within the sign-in callback function arguments.

       async signIn({ user: User, account:Account, profile: profile, email:Email }) {

2. Attempts made so far

I have observed that the Github username is available in the JWT function. However, despite declaring the variable correctly, the 'User' object does not have that property elsewhere.

async jwt({ token, user, account, profile, isNewUser }) {
        // Persist the OAuth access_token to the token right after signin
        if(profile){
            token.login = profile.login
            // @ts-ignore
            user.login = profile.login // code here is the user name in the jwt but user.login isn't being saved in the other functions for Arg User
            persisted in session nor signin
            token.id = profile.id
        }
        if (account) {
            token.accessToken = account.access_token
        }
        return token
        },

3. In-depth analysis of the code

Currently, I can only retrieve the user ID, which appears to be a numerical value used by Github. However, I specifically require the Github username for my project.

try{
                // get user data
                const insertDocument = {"_id":User.id, "User":User}
                // @ts-ignore
                const dataUsers = await db.collection("users").insertOne(insertDocument);
                if(dataUsers){
                    console.log("Added " + String(User.id) + " to database!")
                    return true;
                }

                // if we are here user simply could not be added at all...

                return false;
            } catch (error) {
                console.log("User could not be added to database due to an error or either existing")
                return true;

            }

Answer №1

I found a solution to my problem by customizing the default GitHub OAuth provider behavior and adding extra profile properties.

Here is the code snippet I used, which includes fetching the user's login:

    GitHubProvider({
      clientId: env.GITHUB_CLIENT_ID,
      clientSecret: env.GITHUB_CLIENT_SECRET,
      profile(profile: GithubProfile) {
        return {
          id: profile.id.toString(),
          name: profile.name,
          userName: profile.login,
          email: profile.email,
          image: profile.avatar_url,
        };
      },
    }),

Answer №2

Exploring the 2024 Firebase Integration with React Web Modular API

To access and display the current user object obtained from auth.onAuthStateChanged, you can use the following code snippet:

import { getAuth } from "firebase/auth";

  // Place this within a component that initiates login with signInWithRedirect or signInWithPopup

  React.useEffect(() => {
    // Listen for auth state changes
    auth.onAuthStateChanged((user) => {
      if (user) {
        console.log("onAuthStateChanged", "User is signed in:", user);
      } else {
        console.log("onAuthStateChanged", "User is signed out");
      }
    });
  }, []);

The firebase currentUser object in auth will provide the necessary github username along with the internal github user id details:

const auth = getAuth();
console.log(auth.currentUser.reloadUserInfo.screenName);
console.log(auth.currentUser.reloadUserInfo.providerUserInfo[0].rawId);
console.log(auth.currentUser.reloadUserInfo.providerUserInfo[0].screenName);

Upon examining the firebase reference, it appears that the screen name property is not officially documented. This indicates that it may be an internal attribute subject to change.

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

Metamorphosed Version.execute SourceText and TypeScript Writer

I'm currently working on transforming a TypeScript source file and I have successfully made the necessary changes to the code. Despite seeing my transformed node in the statements property of the transformed source file, the SourceFile.text property d ...

Error message: Nextjs encounters hydration issue only in the production environment

I've been facing this issue for hours now. I deployed my Next.js application on Vercel and encountered numerous hydration errors there. Interestingly, in my local development environment, I don't experience any errors at all. I came across sugge ...

The KeyValuePair<string, Date> type in Typescript cannot be assigned to the KeyValuePair<number, string> type

I encountered the following issue: An error occurred stating that Type 'KeyValuePair<string, Date>' is not assignable to type 'KeyValuePair<number, string>'. Also, it mentioned that Type 'string' is not assignab ...

Is it possible for RouteData in Angular 2 to transmit variables from a parent component to routed components?

How can I pass a variable from my AppComponent to CoursesComponent using RouteConfig? The "data" property in route config seems to only accept constant parameters and cannot recognize "this". Is there a workaround for this limitation? If not, what is the ...

When I delete the initial element from the array, the thumbnail image disappears

Using react-dropzone, I am attempting to implement image drag and drop functionality. The dropped image is stored in the React state within a files array. However, a problem arises when removing an image from the array causing the thumbnails of the remain ...

The selected image should change its border color, while clicking on another image within the same div should deselect the previous image

I could really use some assistance! I've been working on Angular8 and I came across an image that shows how all the div elements are being selected when clicking on an image. Instead of just removing the border effect from the previous image, it only ...

Tips for effectively typing a collection of React wrappers in TypeScript

I encountered a situation in my team's application where we need the ability to dynamically compose component wrappers (HOCs) without prior knowledge of all the wrapper interfaces. This is mostly needed for swapping out context providers when renderin ...

Guide on how to retrieve the information stored in an object

I am experiencing an issue with my function that retrieves data from Firebase. I am able to read the objects, but I cannot access the properties within them. Whenever I try to parse the content, an error occurs. Here is the function in question: this ...

"Warning: It is critical that each child in a list be assigned a distinct 'key' prop." This message is imperative to adhere to, despite its current absence

Despite following all the correct procedures, I can't seem to shake off this warning. The issue persists in both ssr and non-ssr setups (with a NoSsr wrapper from material-ui) I have extensive experience with React and am well aware of the key prop ...

What is the recommended approach for sending a null value to a mandatory property in a Vue.js component?

Setup: Vue.js (3.2) with Composition API, TypeScript, and Visual Studio Code File type.ts: export class GeographicCoordinate { latitude: number; longitude: number; altitude?: number; constructor(latitude: number, longitude: number, altitude?: num ...

Obtain the data from a promise in Angular

I have a function that returns a Promise, and within that Promise, I receive an object in the resolve. Below is the function from my service that is functioning correctly. buscarUsuario(email: string){ return new Promise((resolve, reject) => { this.ht ...

React Native is throwing an error message saying that the Component cannot be used as a JSX component. It mentions that the Type '{}' is not assignable to type 'ReactNode'

Recently, I initiated a new project and encountered errors while working with various packages such as React Native Reanimated and React Navigation Stack. Below is my package.json configuration: { "name": "foodmatch", "version ...

The Ins and Outs of Selecting the Correct Module to Attach a Controller in NestJS CLI

My experience with NestJS has been great so far, especially the Module system and how easy it is to parse requests. However, I have a question about the NestJS CLI. Let's say I have multiple modules. When I create a controller using the command "nes ...

Function Type Mapping

I am in the process of creating a function type that is based on an existing utility type defining a mapping between keys and types: type TypeMap = { a: A; b: B; } The goal is to construct a multi-signature function type where the key is used as a ...

What could be the reason for TypeScript throwing an error that 'product' does not exist in type '{...}' even though 'product' is present?

Structure of Prisma Models: model Product { id String @id @default(auto()) @map("_id") @db.ObjectId name String description String price Float image String createdAt DateTime @default(now()) updatedAt Da ...

What is the best approach to isolating tests for a CRUD App using Playwright?

In attempting to follow the recommendations in the Playwright documentation insisting on isolated tests, I am running into some practical difficulties. Initially, my approach involved truncating the table after each test to clear all records, with subseque ...

The call to Contentful's getAsset function resulted in an undefined value being

I am facing a challenge while trying to fetch an asset, specifically an image, from Contentful and display it in my Angular application. Despite seeing the images in the Network log, I keep encountering an issue where the console.log outputs undefined. Any ...

Using NextJS to Load Fonts from a Database

I've implemented a feature in my NextJS and Tailwind CSS app that allows users to select a theme with different color schemes and font options. The fonts available are all from Google Fonts. However, I'm facing an issue with loading the selected ...

Guide on converting enums in Angular 6

I have been working on translating enums in Angular, but I'm facing a challenge. While I can read and display them in a dropdown menu, translating them is proving to be difficult. Here is an example of my code: export enum test { test1 = '1 - ...

What is the best way to implement useRouter in Next.js version 14?

When I utilize router = useRouter() and then try to redirect with router.push('auth/new-password') from the path 'auth/login/approve', I unexpectedly end up being routed to 'auth/login/auth/new-password'. Can someone explain t ...