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

Custom Provider broken by V5 #10852

Open
Nik-Novak opened this issue May 8, 2024 · 6 comments
Open

Custom Provider broken by V5 #10852

Nik-Novak opened this issue May 8, 2024 · 6 comments
Labels
bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.

Comments

@Nik-Novak
Copy link

Nik-Novak commented May 8, 2024

Environment

System:
    OS: Linux 5.15 Ubuntu 20.04.6 LTS (Focal Fossa)
    CPU: (16) x64 AMD Ryzen 9 5950X 16-Core Processor
    Memory: 5.12 GB / 15.75 GB
    Container: Yes
    Shell: 5.0.17 - /bin/bash
  Binaries:
    Node: 20.12.0 - ~/.nvm/versions/node/v20.12.0/bin/node
    Yarn: 1.22.22 - ~/.nvm/versions/node/v20.12.0/bin/yarn
    npm: 10.5.0 - ~/.nvm/versions/node/v20.12.0/bin/npm
  npmPackages:
    @auth/prisma-adapter: ^1.5.1 => 1.5.1 
    next: ^14.2.3 => 14.2.3 
    next-auth: ^5.0.0-beta.17 => 5.0.0-beta.17 
    react: ^18.3.1 => 18.3.1

Reproduction URL

https://github.com/Nik-Novak/Mind-Knight

Describe the issue

I use a custom provider (https://github.com/Nekonyx/next-auth-steam) to enable steam sign in using steam's OpenId 2.0 support (not OpenID connect)

After requiring an upgrade to V5, the provider no longer works with the following:

[auth][error] InvalidEndpoints: Provider "steam" is missing both `issuer` and `token` endpoint config. At least one of them is required.. Read more at https://errors.authjs.dev#invalidendpoints
    at assertConfig (webpack-internal:///(rsc)/./node_modules/next-auth/node_modules/@auth/core/lib/utils/assert.js:92:24)
    at Auth (webpack-internal:///(rsc)/./node_modules/next-auth/node_modules/@auth/core/index.js:88:95)

I've rewritten the provider to match any new formats but to no avail:

import { randomUUID } from "crypto"
import { NextApiRequest } from "next"
import { AUTHORIZATION_URL, EMAIL_DOMAIN, LOGO_URL, PROVIDER_ID, PROVIDER_NAME, SteamProfile } from "next-auth-steam"
import { SteamProviderOptions } from "next-auth-steam/lib/steam"
import { OAuthConfig, OAuthUserConfig } from "next-auth/providers"
import { NextRequest } from "next/server"
import { RelyingParty } from 'openid'
import { TokenSet } from 'openid-client'

export function Steam(
  req: Request | NextRequest | NextApiRequest,
  options: SteamProviderOptions
): OAuthConfig<SteamProfile> {
  const callbackUrl = new URL(options.callbackUrl)

  // https://example.com
  // https://example.com/api/auth/callback/steam
  const realm = callbackUrl.origin
  const returnTo = `${callbackUrl.href}/${PROVIDER_ID}`

  if (!options.clientSecret || options.clientSecret.length < 1) {
    throw new Error(
      'You have forgot to set your Steam API Key in the `clientSecret` option. Please visit https://steamcommunity.com/dev/apikey to get one.'
    )
  }

  return {
    clientSecret: options.clientSecret,
    
    // options: options as OAuthUserConfig<SteamProfile>,
    id: PROVIDER_ID,
    name: PROVIDER_NAME,
    type: 'oauth',
    style: {
      logo: LOGO_URL,
      // logoDark: LOGO_URL,
      bg: '#000',
      text: '#fff',
      // bgDark: '#000',
      // textDark: '#fff'
    },
    // idToken: false,
    checks: ['none'],
    clientId: PROVIDER_ID,
    authorization: {
      url: AUTHORIZATION_URL,
      params: {
        'openid.mode': 'checkid_setup',
        'openid.ns': 'http://specs.openid.net/auth/2.0',
        'openid.identity': 'http://specs.openid.net/auth/2.0/identifier_select',
        'openid.claimed_id': 'http://specs.openid.net/auth/2.0/identifier_select',
        'openid.return_to': returnTo,
        'openid.realm': realm
      }
    },
    token: {
      async request() {
        console.log('RUN token');
        if (!req.url) {
          throw new Error('No URL found in request object')
        }

        const identifier = await verifyAssertion(req, realm, returnTo)

        if (!identifier) {
          throw new Error('Unauthenticated')
        }

        return {
          tokens: new TokenSet({
            id_token: randomUUID(),
            access_token: randomUUID(),
            steamId: identifier
          })
        }
      }
    },
    
    userinfo: {
      async request(ctx) {
        console.log('RUN userinfo.request');
        const url = new URL('https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002')

        url.searchParams.set('key', ctx.provider.clientSecret as string)
        url.searchParams.set('steamids', ctx.tokens.steamId as string)

        const response = await fetch(url)
        const data = await response.json()
        console.log('PROFILE',data.response.players[0] );
        return data.response.players[0]
      }
    },
    profile(profile: SteamProfile) {
      console.log('RUN PROFILE');
      // next.js can't serialize the session if email is missing or null, so I specify user ID
      return {
        id: profile.steamid,
        image: profile.avatarfull,
        email: `${profile.steamid}@${EMAIL_DOMAIN}`,
        name: profile.personaname
      }
    }
  }
}


/**
 * Verifies an assertion and returns the claimed identifier if authenticated, otherwise null.
 */
async function verifyAssertion(
  req: Request | NextRequest | NextApiRequest,
  realm: string,
  returnTo: string
): Promise<string | null> {
  // Here and from here on out, much of the validation will be related to this PR: https://github.com/liamcurry/passport-steam/pull/120.
  // And accordingly copy the logic from this library: https://github.com/liamcurry/passport-steam/blob/dcebba52d02ce2a12c7d27481490c4ee0bd1ae38/lib/passport-steam/strategy.js#L93
  const IDENTIFIER_PATTERN = /^https?:\/\/steamcommunity\.com\/openid\/id\/(\d+)$/
  const OPENID_CHECK = {
    ns: 'http://specs.openid.net/auth/2.0',
    claimed_id: 'https://steamcommunity.com/openid/id/',
    identity: 'https://steamcommunity.com/openid/id/'
  }

  // We need to create a new URL object to parse the query string
  // req.url in next@14 is an absolute url, but not in next@13, so example.com used as a base url
  const url = new URL(req.url!, 'https://example.com')
  const query = Object.fromEntries(url.searchParams.entries())

  if (query['openid.op_endpoint'] !== AUTHORIZATION_URL || query['openid.ns'] !== OPENID_CHECK.ns) {
    return null
  }

  if (!query['openid.claimed_id']?.startsWith(OPENID_CHECK.claimed_id)) {
    return null
  }

  if (!query['openid.identity']?.startsWith(OPENID_CHECK.identity)) {
    return null
  }

  const relyingParty = new RelyingParty(returnTo, realm, true, false, [])

  const assertion: {
    authenticated: boolean
    claimedIdentifier?: string | undefined
  } = await new Promise((resolve, reject) => {
    relyingParty.verifyAssertion(req, (error, result) => {
      if (error) {
        reject(error)
      }

      resolve(result!)
    })
  })

  if (!assertion.authenticated || !assertion.claimedIdentifier) {
    return null
  }

  const match = assertion.claimedIdentifier.match(IDENTIFIER_PATTERN)

  if (!match) {
    return null
  }

  return match[1]
}

How to reproduce

clone that repo, provide a .env file with the following:

NODE_ENV="development"
MINDNIGHT_WS="ws://thehardcoders.de:6543"
DATABASE_URL="ANY MONGO DATABASE URL"
NEXTAUTH_SECRET="5Esdlji1tsasdasgLGZMxqOkF32345rdfs1dxcKzkVtfY="
NEXTAUTH_DISCORD_CLIENTID="234234234234"
NEXTAUTH_DISCORD_SECRET="UcADFasfasF234GYCW4"
NEXTAUTH_STEAM_SECRET="1EDC0D204A7716E809F0B2DABE207BE7" # # # # # Used as API key from PHP sample steam
NEXTAUTH_URL="http://localhost:3000"
NEXT_PUBLIC_SERVEREVENTS_WS="ws://localhost:5347"
NEXT_PUBLIC_API_BASEPATH="http://localhost:3000/api"
WS_NO_BUFFER_UTIL=true # # # # # fix for bug when large text is sent
NEXT_PUBLIC_SUPPORT_URL="https://discord.gg/eEjA8ZNm3S"
ADMIN_STEAMIDS="76561175373023231"

Expected behavior

https://github.com/Nekonyx/next-auth-steam

Should work as it did before V5.

@Nik-Novak Nik-Novak added bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels May 8, 2024
@Nik-Novak
Copy link
Author

Can anyone assist? I've narrowed it down significantly

@bigbigbo
Copy link

bigbigbo commented May 9, 2024

I encountered the same issue, and upon reviewing the source code, I found that the token.request method was never actually called.

In version 4, the token.request method is called, but in version 5, I only found the userinfo.request method being called.

@Nik-Novak
Copy link
Author

I encountered the same issue, and upon reviewing the source code, I found that the token.request method was never actually called.

In version 4, the token.request method is called, but in version 5, I only found the userinfo.request method being called.

I've noticed the same thing.

@bigbigbo
Copy link

bigbigbo commented May 9, 2024

I encountered the same issue, and upon reviewing the source code, I found that the token.request method was never actually called.
In version 4, the token.request method is called, but in version 5, I only found the userinfo.request method being called.

I've noticed the same thing.

In this issue #10732 (comment), the author has responded to the question.

@Nik-Novak
Copy link
Author

I encountered the same issue, and upon reviewing the source code, I found that the token.request method was never actually called.
In version 4, the token.request method is called, but in version 5, I only found the userinfo.request method being called.

I've noticed the same thing.

In this issue #10732 (comment), the author has responded to the question.

Thanks for coming back to post, renewed my hope lol

@smo043
Copy link

smo043 commented May 15, 2024

I have the same issue open in Discussions.

#10829

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.
Projects
None yet
Development

No branches or pull requests

3 participants