Plannorium Identity Integration Guide

This guide explains how to configure other Plannorium applications (Dashboards, Mobile Apps, etc.) to use Plannorium Identity as their authentication provider.

1. Prerequisites

Ensure you have a valid Client ID and Client Secret created in the Plannorium Identity database.

  • Client ID: e.g., plannorium-dashboard-v1
  • Client Secret: (secret)
  • Redirect URIs: e.g., http://localhost:3000/api/auth/callback/plannorium

2. Shared Configuration Package

It is recommended to create a shared configuration to enforce consistency across apps.

Installation

bash
npm install next-auth

Configuration (auth-config.ts)

auth-config.ts
import { NextAuthOptions } from "next-auth";

export const PLANNORIUM_IDENTITY_URL = process.env.PLANNORIUM_IDENTITY_URL || "https://id.plannorium.com";

export const plannoriumAuthProvider = {
  id: "plannorium",
  name: "Plannorium Identity",
  type: "oauth",
  issuer: PLANNORIUM_IDENTITY_URL,
  authorization: { 
    url: `${PLANNORIUM_IDENTITY_URL}/api/oauth/authorize`,
    params: { scope: "openid profile email" } 
  },
  token: `${PLANNORIUM_IDENTITY_URL}/api/oauth/token`,
  userinfo: `${PLANNORIUM_IDENTITY_URL}/api/oauth/userinfo`,
  idToken: true,
  checks: ["pkce", "state"],
  profile(profile: any) {
    return {
      id: profile.sub || profile.id,
      name: profile.name,
      email: profile.email,
      // Profile details: 'image' from userinfo, 'picture' from ID Token
      image: profile.image || profile.picture,
      role: profile.role,
      organizationId: profile.organizationId,
    };
  },
  clientId: process.env.PLANNORIUM_CLIENT_ID,
  clientSecret: process.env.PLANNORIUM_CLIENT_SECRET,
  // IMPORTANT: Since Plannorium Auth uses HS256 (Symmetric) for now, we must explicitly allow it.
  // Default is RS256 (Asymmetric).
  client: {
    id_token_signed_response_alg: "HS256",
  },
} as const;

export const authOptions: NextAuthOptions = {
  providers: [
    plannoriumAuthProvider,
  ],
  callbacks: {
    async jwt({ token, account }) {
      // Persist the Access Token to the JWT
      if (account) {
        token.accessToken = account.access_token;
      }
      return token;
    },
    async session({ session, token }) {
      // Pass the Access Token to the Client
      session.accessToken = token.accessToken as string;
      return session;
    },
  },
};

3. Application Usage

In your application's api/auth/[...nextauth]/route.ts:

app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import { authOptions } from "@plannorium/auth-config"; 

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

4. Environment Variables

.env
PLANNORIUM_IDENTITY_URL=https://id.plannorium.com 
PLANNORIUM_CLIENT_ID=your-client-id
PLANNORIUM_CLIENT_SECRET=your-client-secret
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-random-secret

5. Testing the Flow

  1. User clicks "Sign in with Plannorium" in your App.
  2. Redirects to id.plannorium.com/oauth/authorize.
  3. If not logged in, id.plannorium.com redirects to /login.
  4. User logs in at id.plannorium.com.
  5. User is redirected back to /authorize, which approves the request.
  6. User is redirected back to your App (/api/auth/callback/plannorium).
  7. NextAuth completes the exchange and creates the session.