I'm going to do my best to give a fairly generic answer, using a JWT for Authorization, but I'm going to have to make some assumptions since I'm not super familiar with Google OAuth & related Google system.
First, and most importantly, it's important to clarify the difference between Authentication, or "who you are" and Authorization, or "what you can do."
The best idea I've got so far is some sort of token management that mixes NextAuth.js and my back end. That doesn't seem to be the way NextAuth.js is designed though so I'm rethinking the whole architecture and looking for suggestions.
NextAuth is an Authentication library, and doesn't support external validation of the NextAuth-created JWTs, so you're right to not want to mix NextAuth with your backend. When someone logs in NextAuth creates a NextAuth-specific JWT (an ID Token) that will be passed between the Client and the Next Server. It tells you who the user is, and proves that they've logged in. Unless you're using database sessions, which I haven't used so can't speak to.
Extra work is required to implement Authorization so that you have a JWT that also describes what access rights the user has, that you can pass to your backend.
Ideally you will be able to leverage Google OAuth for this, and assuming that is the case, this is what I would do:
For the architecture
- I'm not very familiar with GraphQL/Apollo, but separation there seems fine. Important to note, though, that not separating is also probably fine. Any approach will have tradeoffs, so you'll have to evaluate what works best for your situation.
- Whenever you make a call to the backend, you pass your JWT with the call as an
Authorization
header, in the form of Bearer <token>
.
- On the backend, then, you validate the token with each incoming call, and allow the call to proceed (or not) as appropriate.
This is the relevant info I found in the Apollo docs.
For the token
In your NextAuth provider configuration, in the jwt callback you can add information to the NextAuth JWT.
Ideally, your Google OAuth also provides you a JWT (an Access Token). This should be something that should be relatively easy to validate on the backend, and it is what you'd want to use for Authorization.
You can store this Google OAuth JWT (access token) within the NextAuth JWT (id token) when the user first logs in, and then retrieve it on the Next.js server before you make your calls to the GraphQL backend. That would look something like this:
// [...nextauth].js
const options = {
// ... other configuration
callbacks: {
jwt: async (token, user, account, profile, isNewUser) => {
const isSignIn = !!user
if (isSignIn) {
token.b2c = {
accessToken: account.accessToken,
refreshToken: account.refreshToken,
iat: profile.iat,
exp: profile.exp,
}
}
return Promise.resolve(token)
}
}
}
This is a simplified example from the configuration I use. My config is for Azure AD B2C but it's the same general flow you're looking for. You can see my full config here which shows some additional code I use to handle refreshing the access token as needed.
If you don't have something usable from the Google OAuth flow, this gets more complicated and you'd have to build something custom.