NextAuth.js Integration Guide
This guide provides instructions on how to integrate Optare SSO with a Next.js application using NextAuth.js (v4 or v5).
Configuration
Optare SSO is strictly OpenID Connect (OIDC) compliant and requires specific checks for security.
Prerequisites
- Client ID and Client Secret from the Optare Developer Console.
- Issuer URL:
https://id.optare.one(or your specific instance URL). - NextAuth.js installed in your project.
Complete Configuration Example
import NextAuth from "next-auth";
import { Provider } from "next-auth/providers";
export const authOptions = {
providers: [
{
id: "optare",
name: "Optare SSO",
type: "oauth", // Using 'oauth' type gives more control, but 'oidc' works too
wellKnown: "https://id.optare.one/.well-known/openid-configuration",
authorization: {
params: {
scope: "openid profile email offline_access",
// The 'nonce' parameter is automatically handled by NextAuth when checks includes 'nonce'
},
},
idToken: true,
checks: ["pkce", "state", "nonce"], // MANDATORY: Nonce is required for 'openid' scope
clientId: process.env.OPTARE_CLIENT_ID,
clientSecret: process.env.OPTARE_CLIENT_SECRET,
profile(profile) {
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture,
// Map custom claims
organizationId: profile.organizationId,
licenses: profile.licenses,
entitlements: profile.entitlements,
};
},
},
],
callbacks: {
async jwt({ token, account, profile }) {
// Persist custom claims to the token
if (profile) {
token.organizationId = profile.organizationId;
token.licenses = profile.licenses;
token.entitlements = profile.entitlements;
}
return token;
},
async session({ session, token }) {
// Make custom claims available in the session
if (session.user) {
session.user.organizationId = token.organizationId;
session.user.licenses = token.licenses;
session.user.entitlements = token.entitlements;
}
return session;
},
},
};
export default NextAuth(authOptions);Troubleshooting Common Issues
"nonce is required for openid scope"
This error occurs if the nonce parameter is missing from the authorization request.
Solution:
Ensure you have checks: ["pkce", "state", "nonce"] in your provider configuration. NextAuth.js will generate a random nonce, store it in a cookie, and send it as a query parameter &nonce=... to the authorization endpoint.
Note for type: "oidc":
If you use type: "oidc", NextAuth defaults to including these checks. However, if you override authorization params, ensure you don't accidentally remove default behaviors.
Custom Claims Not Appearing
If organizationId or licenses are missing:
- Ensure your requested scopes include the relevant product scopes (e.g.,
crm,analytics) if you need specific product licenses. - Check the
profilecallback in your NextAuth config to ensure you are mapping them from the provider's user info response to the NextAuth user object. - Check your
jwtandsessioncallbacks to pass the data through.
Type Definitions (TypeScript)
To properly type your session, add this to your types/next-auth.d.ts:
import NextAuth, { DefaultSession } from "next-auth"
declare module "next-auth" {
interface Session {
user: {
organizationId?: string;
licenses?: string[];
entitlements?: string[];
} & DefaultSession["user"]
}
interface Profile {
organizationId?: string;
licenses?: string[];
entitlements?: string[];
}
}