Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bungie Provider and Typescript / Does this provider even work? #6930

Open
BaileyMillerSSI opened this issue Mar 12, 2023 · 13 comments
Open

Bungie Provider and Typescript / Does this provider even work? #6930

BaileyMillerSSI opened this issue Mar 12, 2023 · 13 comments
Labels
bug Something isn't working good first issue Good issue to take for first time contributors help-needed The maintainer needs help due to time constraint/missing knowledge providers

Comments

@BaileyMillerSSI
Copy link

BaileyMillerSSI commented Mar 12, 2023

Question 💬

I followed the example for adding the Bungie provider here. After doing so I found that the default implementation isn't happy with Typescript.

Argument of type '{ clientId: string; clientSecret: string; headers: { "X-API-Key": string; }; }' is not assignable to parameter of type 'Partial<OAuthConfig<any>>'. Object literal may only specify known properties, and 'headers' does not exist in type 'Partial<OAuthConfig<any>>'.

What is the intended setup when using Typescript with this provider?

How to reproduce ☕️

  1. Create a typescript application using create t3 app
  2. Select nextAuth and Typescript at least
  3. Go to the src/server/auth.ts file
  4. Locate the provider section
  5. Copy/Paste default provider implementation from provider docs

Contributing 🙌🏽

No, I am afraid I cannot help regarding this.

Unless it is as simple as changing the default to this:

BungieProvider({
      clientId: env.BUNGIE_CLIENT_ID,
      clientSecret: env.BUNGIE_SECRET,
      httpOptions: {
        headers: {
          "X-API-Key": env.BUNGIE_API_KEY
        }
      }
    }),
@BaileyMillerSSI BaileyMillerSSI added the question Ask how to do something or how something works label Mar 12, 2023
@BaileyMillerSSI
Copy link
Author

BaileyMillerSSI commented Mar 12, 2023

Follow up question, does this provider even work?

Upon further investigation my short answer is no. Something is wrong deeper into the NextAuth logic then I can follow or configure. I've written a custom OAuth provider that gets through the token retrieval and then afterwards NextAuth tanks and I have no clue as to why. The same issue happens when using the default Bungie provider.

[next-auth][error][OAUTH_CALLBACK_ERROR] 
https://next-auth.js.org/errors#oauth_callback_error expected 200 OK, got: 500 Internal Server Error {
  error: OPError: expected 200 OK, got: 500 Internal Server Error
      at processResponse ({ProjectPath}node_modules\openid-client\lib\helpers\process_response.js:41:11)
      at Client.userinfo ({ProjectPath}node_modules\openid-client\lib\client.js:1237:18)
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async oAuthCallback ({ProjectPath}node_modules\next-auth\core\lib\oauth\callback.js:131:17)
      at async Object.callback ({ProjectPath}node_modules\next-auth\core\routes\callback.js:52:11)
      at async AuthHandler ({ProjectPath}node_modules\next-auth\core\index.js:201:28)
      at async NextAuthHandler ({ProjectPath}node_modules\next-auth\next\index.js:24:19)
      at async {ProjectPath}node_modules\next-auth\next\index.js:60:32
      at async Object.apiResolver ({ProjectPath}node_modules\next\dist\server\api-utils\node.js:372:9)
      at async DevServer.runApi ({ProjectPath}node_modules\next\dist\server\next-server.js:514:9)
      at async Object.fn ({ProjectPath}node_modules\next\dist\server\next-server.js:828:35)
      at async Router.execute ({ProjectPath}node_modules\next\dist\server\router.js:243:32)
      at async DevServer.runImpl ({ProjectPath}node_modules\next\dist\server\base-server.js:432:29)
      at async DevServer.run ({ProjectPath}node_modules\next\dist\server\dev\next-dev-server.js:831:20)
      at async DevServer.handleRequestImpl ({ProjectPath}node_modules\next\dist\server\base-server.js:375:20)
      at async {ProjectPath}node_modules\next\dist\server\base-server.js:157:99 {
    name: 'OAuthCallbackError',
    code: undefined
  },
  providerId: 'bungie',
  message: 'expected 200 OK, got: 500 Internal Server Error'

@BaileyMillerSSI BaileyMillerSSI changed the title Bungie Provider and Typescript Bungie Provider and Typescript / Does this provider even work? Mar 13, 2023
@benedfit
Copy link

I have it working with v3.29.10, but have not got it working with v4

@BaileyMillerSSI
Copy link
Author

Ah good so I'm not crazy it just doesn't work. I'll see about bumping my version down for the project I'm working on.

@benedfit
Copy link

I plan to put this in a PR at some point when I have some down time, but I managed to get it working in v4 with some additional config:

BungieProvider({
  clientId: process.env.BUNGIE_CLIENT_ID,
  clientSecret: process.env.BUNGIE_CLIENT_SECRET,
  // The Bungie API doesn't like scope being set
  authorization: { params: { scope: '' } },
  httpOptions: { headers: { 'X-API-Key': process.env.BUNGIE_API_KEY } },
  // Correctly gets the current user info so that the existing `profile` definition works
  userinfo: {
    async request({ tokens, provider }) {
      return await fetch(
        'https://www.bungie.net/platform/User/GetMembershipsForCurrentUser',
        {
          headers: {
            ...provider.httpOptions.headers,
            authorization: `Bearer ${tokens.access_token}`
          }
        }
      ).then(async response => await response.json())
    }
  }
})

@BaileyMillerSSI
Copy link
Author

Awesome, I'll give that a try maybe this week. I am not familiar with this library but a better error message would have been very helpful too. It would be nice if the error said something along the lines of "Error loading user info from provider Bungie".

@balazsorban44 balazsorban44 added help-needed The maintainer needs help due to time constraint/missing knowledge providers good first issue Good issue to take for first time contributors bug Something isn't working and removed question Ask how to do something or how something works labels Mar 16, 2023
@rrohans
Copy link

rrohans commented Mar 18, 2023

Hey all, also using the BungieProvider with a new T3 application with the exact same setup as above. I'm running into the following issue:

[next-auth][error][OAUTH_CALLBACK_ERROR]
https://next-auth.js.org/errors#oauth_callback_error State cookie was missing. {
  error: TypeError: State cookie was missing.
      at Object.use (/Users/rhs_air/dev/d2builds/node_modules/next-auth/core/lib/oauth/checks.js:103:23)
      at oAuthCallback (/Users/rhs_air/dev/d2builds/node_modules/next-auth/core/lib/oauth/callback.js:89:25)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)
      at async Object.callback (/Users/rhs_air/dev/d2builds/node_modules/next-auth/core/routes/callback.js:52:11)
      at async AuthHandler (/Users/rhs_air/dev/d2builds/node_modules/next-auth/core/index.js:201:28)
      at async NextAuthHandler (/Users/rhs_air/dev/d2builds/node_modules/next-auth/next/index.js:24:19)
      at async /Users/rhs_air/dev/d2builds/node_modules/next-auth/next/index.js:60:32
      at async Object.apiResolver (/Users/rhs_air/dev/d2builds/node_modules/next/dist/server/api-utils/node.js:372:9)
      at async DevServer.runApi (/Users/rhs_air/dev/d2builds/node_modules/next/dist/server/next-server.js:514:9)
      at async Object.fn (/Users/rhs_air/dev/d2builds/node_modules/next/dist/server/next-server.js:828:35)
      at async Router.execute (/Users/rhs_air/dev/d2builds/node_modules/next/dist/server/router.js:243:32)
      at async DevServer.runImpl (/Users/rhs_air/dev/d2builds/node_modules/next/dist/server/base-server.js:432:29)
      at async DevServer.run (/Users/rhs_air/dev/d2builds/node_modules/next/dist/server/dev/next-dev-server.js:831:20)
      at async DevServer.handleRequestImpl (/Users/rhs_air/dev/d2builds/node_modules/next/dist/server/base-server.js:375:20)
      at async /Users/rhs_air/dev/d2builds/node_modules/next/dist/server/base-server.js:157:99 {
    name: 'OAuthCallbackError',
    code: undefined
  },
  providerId: 'bungie',
  message: 'State cookie was missing.'
}

I check my browser's cookies and it seems that the state cookie is defined. Any idea what could be causing the issue?

@BaileyMillerSSI
Copy link
Author

BaileyMillerSSI commented Mar 18, 2023

This is working for me:

interface IBungieNetUser {
  membershipId: number;
  uniqueName: string;
  displayName: string;
  profilePicture: number;
  profileTheme: number;
  userTitle: string;
  successMessageFlags: string | number;
  isDeleted: boolean;
  about: string;
  firstAccess: string;
  lastUpdate: string;
  context: {
    isFollowing: boolean;
    ignoreStatus: {
      isIgnored: boolean;
      ignoreFlags: boolean;
    };
  };
  xboxDisplayName: string;
  showActivity: boolean;
  locale: string;
  localeInheritDefault: boolean;
  showGroupMessaging: boolean;
  profilePicturePath: string;
  profileThemeName: string;
  userTitleDisplay: string;
  statusText: string;
  statusDate: string;
  blizzardDisplayName: string;
  steamDisplayName: string;
  twitchDisplayName: string;
  cachedBungieGlobalDisplayName: string;
  cachedBungieGlobalDisplayNameCode: number;
  egsDisplayName: string;
}

declare module "next-auth" {
  interface Session extends DefaultSession {
    user: {} & DefaultSession["user"] & IBungieNetUser;
  }
}

declare module "next-auth" {
  interface Profile {
    Response: {
      bungieNetUser: IBungieNetUser;
    };
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    bungieUser: IBungieNetUser;
  }
}

export const authOptions: NextAuthOptions = {
  callbacks: {
    async jwt({ token, profile }) {
      if (profile) {
        token.bungieUser = profile.Response.bungieNetUser;
      }
      return token;
    },
    session({ session, token }) {
      if (token && session.user) {
        session.user = {
          ...session.user,
          ...token.bungieUser,
        };
      }
      return session;
    },
  },
  providers: [
    BungieProvider({
      clientId: env.BUNGIE_CLIENT_ID,
      clientSecret: env.BUNGIE_SECRET,
      // The Bungie API doesn't like scope being set
      authorization: { params: { scope: "" } },
      httpOptions: { headers: { "X-API-Key": env.BUNGIE_API_KEY } },
      // Correctly gets the current user info so that the existing `profile` definition works
      userinfo: {
        request: async ({ tokens, provider }) => {
          const fetchResult = await fetch(
            `${env.BUNGIE_API_ROOT}/Platform/User/GetMembershipsForCurrentUser/`,
            {
              headers: {
                ...provider?.httpOptions?.headers,
                authorization: `Bearer ${tokens.access_token}`,
              },
            }
          );

          return await fetchResult.json();
        },
      },
    }),
  ],
};

Index page:

const Home: NextPage = () => {
  const { data: sessionData } = useSession();

  return (
    <>
      <Head>
        <title>Create T3 App</title>
        <meta name="description" content="Generated by create-t3-app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c]">
        {sessionData && (
          <div className="pb-5">
            <div className="flex flex-col items-center text-center">
              {sessionData?.user.image && (
                <Image
                  src={sessionData?.user.image}
                  unoptimized
                  width={320}
                  height={320}
                  alt="Profile picture"
                />
              )}
              <p className="text-xl text-white">{sessionData?.user.name}</p>
            </div>

            <pre className="text-white">
              {JSON.stringify(sessionData, null, "\t")}
            </pre>
          </div>
        )}
        <button
          className="rounded-full bg-white/10 px-10 py-3 font-semibold text-white no-underline transition hover:bg-white/20"
          onClick={
            sessionData ? () => void signOut() : () => void signIn("bungie")
          }
        >
          {sessionData ? "Sign out" : "Sign in"}
        </button>
      </main>
    </>
  );
};

@rrohans
Copy link

rrohans commented Mar 18, 2023

Nope, I still get the same error. FWIW, here is part of my package.json file for package versions.

"dependencies": {
    "@next-auth/prisma-adapter": "^1.0.5",
    "@prisma/client": "^4.9.0",
    "@tanstack/react-query": "^4.20.2",
    "@trpc/client": "^10.9.0",
    "@trpc/next": "^10.9.0",
    "@trpc/react-query": "^10.9.0",
    "@trpc/server": "^10.9.0",
    "next": "^13.2.1",
    "next-auth": "^4.19.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "superjson": "1.9.1",
    "zod": "^3.20.6"
  },
  "devDependencies": {
    "@types/eslint": "^8.21.1",
    "@types/node": "^18.14.0",
    "@types/prettier": "^2.7.2",
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@typescript-eslint/eslint-plugin": "^5.53.0",
    "@typescript-eslint/parser": "^5.53.0",
    "autoprefixer": "^10.4.7",
    "eslint": "^8.34.0",
    "eslint-config-next": "^13.2.1",
    "postcss": "^8.4.14",
    "prettier": "^2.8.1",
    "prettier-plugin-tailwindcss": "^0.2.1",
    "prisma": "^4.9.0",
    "tailwindcss": "^3.2.0",
    "typescript": "^4.9.5"
  },
  "ct3aMetadata": {
    "initVersion": "7.7.0"
  }

@BaileyMillerSSI
Copy link
Author

Question for you, when you click sign in and get redirected to the Bungie screen do you see state in the url?
Here is mine:

"dependencies": {
    "next": "^13.2.1",
    "next-auth": "^4.19.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "zod": "^3.20.6"
  },
  "devDependencies": {
    "@types/eslint": "^8.21.1",
    "@types/node": "^18.14.0",
    "@types/prettier": "^2.7.2",
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@typescript-eslint/eslint-plugin": "^5.53.0",
    "@typescript-eslint/parser": "^5.53.0",
    "autoprefixer": "^10.4.7",
    "eslint": "^8.34.0",
    "eslint-config-next": "^13.2.1",
    "postcss": "^8.4.14",
    "prettier": "^2.8.1",
    "prettier-plugin-tailwindcss": "^0.2.1",
    "tailwindcss": "^3.2.0",
    "typescript": "^4.9.5"
  },
  "ct3aMetadata": {
    "initVersion": "7.7.0"
  }

@rrohans
Copy link

rrohans commented Mar 18, 2023

Yes, I do, I think the URL is well-formed as per the docs. Seems like we're on the same versions for almost all of our dependencies.

@BaileyMillerSSI
Copy link
Author

Here is my complete auth setup:

.env:

# Next Auth
# You can generate a new secret on the command line with:
# openssl rand -base64 32
# https://next-auth.js.org/configuration/options#secret
# NEXTAUTH_SECRET=""
NEXTAUTH_URL=https://localhost:3001 <--- This was important because of the proxy stuff. I am using a C# application to proxy my local development because of the Bungie API requiring even local host to be https.

# Next Auth Bungie Provider
BUNGIE_CLIENT_ID=
BUNGIE_SECRET=
BUNGIE_API_KEY=

# Links
BUNGIE_API_ROOT=https://www.bungie.net
import { type GetServerSidePropsContext } from "next";
import {
  getServerSession,
  type NextAuthOptions,
  type DefaultSession,
} from "next-auth";
import BungieProvider from "next-auth/providers/bungie";
import { env } from "~/env.mjs";

interface IBungieNetUser {
  membershipId: number;
  uniqueName: string;
  displayName: string;
  profilePicture: number;
  profileTheme: number;
  userTitle: string;
  successMessageFlags: string | number;
  isDeleted: boolean;
  about: string;
  firstAccess: string;
  lastUpdate: string;
  context: {
    isFollowing: boolean;
    ignoreStatus: {
      isIgnored: boolean;
      ignoreFlags: boolean;
    };
  };
  xboxDisplayName: string;
  showActivity: boolean;
  locale: string;
  localeInheritDefault: boolean;
  showGroupMessaging: boolean;
  profilePicturePath: string;
  profileThemeName: string;
  userTitleDisplay: string;
  statusText: string;
  statusDate: string;
  blizzardDisplayName: string;
  steamDisplayName: string;
  twitchDisplayName: string;
  cachedBungieGlobalDisplayName: string;
  cachedBungieGlobalDisplayNameCode: number;
  egsDisplayName: string;
}

declare module "next-auth" {
  interface Session extends DefaultSession {
    user: {} & DefaultSession["user"] & IBungieNetUser;
  }
}

declare module "next-auth" {
  interface Profile {
    Response: {
      bungieNetUser: IBungieNetUser;
    };
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    bungieUser: IBungieNetUser;
  }
}

/**
 * Options for NextAuth.js used to configure adapters, providers, callbacks, etc.
 *
 * @see https://next-auth.js.org/configuration/options
 */
export const authOptions: NextAuthOptions = {
  callbacks: {
    async jwt({ token, profile }) {
      if (profile) {
        token.bungieUser = profile.Response.bungieNetUser;
      }
      return token;
    },
    session({ session, token }) {
      if (token && session.user) {
        session.user = {
          ...session.user,
          ...token.bungieUser,
        };
      }
      return session;
    },
  },
  providers: [
    BungieProvider({
      clientId: env.BUNGIE_CLIENT_ID,
      clientSecret: env.BUNGIE_SECRET,
      // The Bungie API doesn't like scope being set
      authorization: { params: { scope: "" } },
      httpOptions: { headers: { "X-API-Key": env.BUNGIE_API_KEY } },
      // Correctly gets the current user info so that the existing `profile` definition works
      userinfo: {
        request: async ({ tokens, provider }) => {
          const fetchResult = await fetch(
            `${env.BUNGIE_API_ROOT}/Platform/User/GetMembershipsForCurrentUser/`,
            {
              headers: {
                ...provider?.httpOptions?.headers,
                authorization: `Bearer ${tokens.access_token}`,
              },
            }
          );

          return await fetchResult.json();
        },
      },
    }),
  ],
};

/**
 * Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file.
 *
 * @see https://next-auth.js.org/configuration/nextjs
 */
export const getServerAuthSession = (ctx: {
  req: GetServerSidePropsContext["req"];
  res: GetServerSidePropsContext["res"];
}) => {
  return getServerSession(ctx.req, ctx.res, authOptions);
};

@rrohans
Copy link

rrohans commented Mar 18, 2023

Alright, found my bug. My .env file had my NEXTAUTH_URL still using localhost rather than127.0.0.1. This would cause a redirect which would erase the cookie from the link between logins. Thanks for all your help!

@BaileyMillerSSI
Copy link
Author

Alright, found my bug. My .env file had my NEXTAUTH_URL still using localhost rather than127.0.0.1. This would cause a redirect which would erase the cookie from the link between logins. Thanks for all your help!

I'm personally really surprised Bungie requires localhost to be secure which causes us to need to setup a cert and changing defaults. I'm so used to platforms letting localhost be open because it's used for development

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working good first issue Good issue to take for first time contributors help-needed The maintainer needs help due to time constraint/missing knowledge providers
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants