Skip to content

Cached token #788

Open
Open
@MontiMarco92

Description

@MontiMarco92

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageA new issue that needs triage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions