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

Cached token #788

Open
MontiMarco92 opened this issue Jul 16, 2024 · 2 comments
Open

Cached token #788

MontiMarco92 opened this issue Jul 16, 2024 · 2 comments
Labels
bug Something isn't working triage A new issue that needs triage

Comments

@MontiMarco92
Copy link
Contributor

MontiMarco92 commented Jul 16, 2024

Package containing the bug

next-drupal (NPM package)

Describe the bug

A clear and concise description of what the bug is.

When having authentication enabled, fetch requests work at first, but then when the token expires, the getAccessToken function is triggered to refresh the token. However, it returns the same previous cached token causing subsequent requests to drupal to fail due to an Unauthorized 401 Error. From what I've seen, this is caused by the fact that Next caches everything by default and the fetch function for this token request does not have any cache or next: { revalidate } prop set.

With the revalidation features proposed on this PR to support NextJs revalidation options, I tried setting a cache: no-store or even a next: {revalidate: 0} prop to the fetch on the getAccessToken However, on production build I've faced a DYNAMIC_SERVER_USAGE error.
The only way I worked around this is by actually setting a revalidate time to this. I've set a revalidate: 5. and now the token issue seems to solve, although I'm not really sure if this is the way to go.

Has someone else experienced this ?

  async getAccessToken(
    clientIdSecret?: NextDrupalAuthClientIdSecret
  ): Promise<AccessToken> {
    if (this.accessToken) {
      return this.accessToken
    }

    let auth: NextDrupalAuthClientIdSecret
    if (isClientIdSecretAuth(clientIdSecret)) {
      auth = {
        url: DEFAULT_AUTH_URL,
        ...clientIdSecret,
      }
    } else if (isClientIdSecretAuth(this.auth)) {
      auth = { ...this.auth }
    } else if (typeof this.auth === "undefined") {
      throw new Error(
        "auth is not configured. See https://next-drupal.org/docs/client/auth"
      )
    } else {
      throw new Error(
        `'clientId' and 'clientSecret' required. See https://next-drupal.org/docs/client/auth`
      )
    }

    const url = this.buildUrl(auth.url)

    // Ensure that the unexpired token was using the same scope and client
    // credentials as the current request before re-using it.
    if (
      this.token &&
      Date.now() < this._tokenExpiresOn &&
      this._tokenRequestDetails?.clientId === auth?.clientId &&
      this._tokenRequestDetails?.clientSecret === auth?.clientSecret &&
      this._tokenRequestDetails?.scope === auth?.scope
    ) {
      this.debug(`Using existing access token.`)
      return this.token
    }

    this.debug(`Fetching new access token.`)

    // Use BasicAuth to retrieve the access token.
    const clientCredentials: NextDrupalAuthUsernamePassword = {
      username: auth.clientId,
      password: auth.clientSecret,
    }
    const body = new URLSearchParams({ grant_type: "client_credentials" })

    if (auth?.scope) {
      body.set("scope", auth.scope)

      this.debug(`Using scope: ${auth.scope}`)
    }

    const response = await this.fetch(url.toString(), {
      method: "POST",
      headers: {
        Authorization: await this.getAuthorizationHeader(clientCredentials),
        Accept: "application/json",
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body,
      next: { revalidate: 5 },
    })

    await this.throwIfJsonErrors(
      response,
      "Error while fetching new access token: "
    )

    const result: AccessToken = await response.json()

    this.token = result

    this._tokenRequestDetails = auth

    return result
  }

Expected behavior

Once the token expires, getAccessToken should actually refetch a fresh one, and not return the original cached one.

Steps to reproduce:

  1. First set up authentication on the NextDrupal client
  2. Then wait until token expires
  3. 😢 you get Unauthorized - 401 error, and If you log the used token, you can see is the old one.

Additional context

  • The path where I'm testing this is /[lang]/page.tsx with a generateStaticParams
  • I'm using ClientId + ClientSecret auth.
  • next-drupal npm package 2.0 beta version
  • Nextjs App router v 14.2.3
@MontiMarco92 MontiMarco92 added bug Something isn't working triage A new issue that needs triage labels Jul 16, 2024
@yobottehg
Copy link
Contributor

I also see this problem: If you do multiple concurrent authenticated requests with the DrupalClient you do a lot of requests against the oauth token endpoint:

Bildschirmfoto 2024-10-14 um 10 49 23

@tomzidani
Copy link

Hello,

Does anyone know if this fix will be available soon ?
If not, how can I properly fix this by knowing that I need to update the node_modules ?

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 A new issue that needs triage
Projects
None yet
Development

No branches or pull requests

3 participants