Description
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:
- First set up authentication on the NextDrupal client
- Then wait until token expires
- 😢 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