Here is how to fix an Auth.js (formerly NextAuth.js) error that happens when your OAuth 2.0 provider does not follow OAuth 2.0 spec.
![Intro](/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fauth_js.048131a8.png&w=750&q=75)
TLDR
You can use my forked version of oauth4webapi which automatically converts `expires_in` field from string to number. You can check the forked version at @jacobkim/oauth4webapi.
Issue Description
Here is the error I faced when I tried to integrate Azure AD B2C and Naver provider.
[auth][cause]: OperationProcessingError: "response" body "expires_in" property must be a positive number
Apparently Naver and Azure AD B2C does not respect OAuth 2.0 spec and returns expires_in
parameter as a string not number.
Auth.js uses oauth4webapi internally to handle OAuth 2.0 authentication. However, oauth4webapi strictly follows the OAuth 2.0 spec. It does not automatically convert expires_in
from string to number. Other developers have tried to submit PRs to automatically convert expires_in
from string to number, but all of them were rejected.
- add convertStringExpiresInToNumber function for providers whose response's expires_in is a string instead of an integer(number) #112
- fix: Validate 'expires_in' as a convertible number #77
- allow expires_in string type #47
I respect the author of oauth4webapi's decision to absolutely respect the OAuth 2.0 spec. But I could not wait for Naver and Azure AD B2C to update their API so that expires_in
is returned as a number.
![λ€μ΄λ² ν¬λΌ μ€ν¬λ¦°μ·](/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnaver_forum.f2f6f863.png&w=2048&q=75)
Solution
Here are two solutions to this issue. You can choose the one that fits your needs the best.
Solution #1 (Recommended)
If you are okay with using my forked version of oauth4webapi, you can use my forked version of oauth4webapi which automatically converts `expires_in` field from string to number. You can check the forked repo at @jacobhjkim/oauth4webapi.
We can use NPM's overrides feature to easily override oauth4webapi package.
// package.json
...
"overrides": {
"oauth4webapi": "npm:@jacobkim/oauth4webapi@^2.10.4"
}
If you use pnpm as I do, you must use pnpm.overrides
instead of just overrides
, as shown bellow. (or else you might lose an hour pulling out your hair and searching through GitHub issues.)
// package.json
...
"pnpm": {
"overrides": {
"oauth4webapi": "npm:@jacobkim/oauth4webapi@^2.10.4"
}
}
npm might not honor overrides property
npm CLI
seems to have an issue with the overrides property. Here are some related GitHub issues:
- bug: overrides property only honored when running install the first time #5850
- [BUG] Overrides are not updating after running npm install #5443
If you are still using npm, I recommend switching to pnpm, which is better in many ways.
Solution #2
If you are against overriding your package's dependency or don't want to use my forked package, that's perfectly fine too.
Like @numman-ali suggests here you can write a custom interceptor that intercepts request just for specific OAuth provider.
// app/api/auth/[...nextauth]/route.ts
import { type NextRequest, NextResponse } from "next/server";
import {
auth,
GET as AuthGET,
instagramFetchInterceptor,
POST as AuthPOST,
} from "@/auth"
const originalFetch = fetch
export async function POST(req: NextRequest) {
return await AuthPOST(req)
}
export async function GET(req: NextRequest) {
const url = new URL(req.url)
if (url.pathname === "/api/auth/callback/naver") {
const session = await auth()
if (!session?.user) {
/* Prevent user creation for instagram access token */
const signInUrl = new URL("/?modal=sign-in", req.url)
return NextResponse.redirect(signInUrl)
}
/* Intercept the fetch request to patch access_token request to be oauth compliant */
global.fetch = naverFetchInterceptor(originalFetch) <- your custom interceptor
const response = await AuthGET(req)
global.fetch = originalFetch
return response
}
return await AuthGET(req)
}
Personally, I think utilizing overrides is easier and causes less hassle overall than the interceptor method.