saget-auth-middleware
Version:
SSO Middleware dengan dukungan localStorage untuk validasi authentifikasi domain malinau.go.id dan semua subdomain pada aplikasi Next.js 14 & 15
240 lines (205 loc) ⢠7.63 kB
JavaScript
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const envTemplate = `# ADD Config For SSO Intergrations To SAGET MALINAU
# SSO Configuration
SSO_LOGIN_URL=https://sso.malinau.go.id/login
SSO_APP_KEY=r9k2m5x8v4n7q3w6t1y0z8h5j2l4c9p1
SSO_JWT_SECRET=your-actual-jwt-secret-here
SSO_JWT_REFRESH_SECRET=your-actual-refresh-secret-here
SSO_API_URL=https://sso.malinau.go.id/api
# Cookie Configuration
COOKIE_ACCESS_TOKEN_NAME=sso_access_token
COOKIE_REFRESH_TOKEN_NAME=sso_refresh_token
COOKIE_DOMAIN=.malinau.go.id
# Redirect Configuration
NEXT_REDIRECT_URL=http://localhost:3000
NEXT_PUBLIC_REDIRECT_URL=http://localhost:3000
# Public Environment Variables
NEXT_PUBLIC_COOKIE_ACCESS_TOKEN_NAME=sso_access_token
NEXT_PUBLIC_COOKIE_REFRESH_TOKEN_NAME=sso_refresh_token
NEXT_PUBLIC_COOKIE_DOMAIN=.malinau.go.id
NEXT_PUBLIC_APP_KEY=r9k2m5x8v4n7q3w6t1y0z8h5j2l4c9p1
NEXT_PUBLIC_API_URL_CLIENT_SIDE=https://sso.malinau.go.id/api
# LocalStorage Keys
LOCALSTORAGE_ACCESS_TOKEN_KEY=x-accessToken:sso.malinau.go.id
LOCALSTORAGE_REFRESH_TOKEN_KEY=x-refreshToken:sso.malinau.go.id
`;
const middlewareJSTemplate = `// middleware.js
import SSOMiddleware from 'saget-auth-middleware';
export function middleware(request) {
// Skip authentication for public routes
if (request.nextUrl.pathname.startsWith('/api/auth') ||
request.nextUrl.pathname.startsWith('/login') ||
request.nextUrl.pathname === '/') {
return;
}
return SSOMiddleware.withSSOValidation((req) => {
// Middleware berhasil, lanjutkan ke route
console.log('User authenticated:', req.user?.email);
console.log('User role:', req.role);
console.log('User subrole:', req.subrole);
})(request);
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api/auth (auth routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api/auth|_next/static|_next/image|favicon.ico).*)',
],
};
`;
const middlewareTSTemplate = `// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import SSOMiddleware from 'saget-auth-middleware';
// Type definitions for SSO payload
interface UserProfile {
id: string;
userId: string;
name: string;
externalId: string | null;
address: string;
village: string | null;
district: string | null;
city: string;
province: string;
}
interface UserApplication {
id: string;
applicationName: string;
applicationKey: string;
organization: string;
organizationAddress: string | null;
organizationContact: string | null;
url: string;
callbackUrl: string;
isPublic: boolean;
status: string;
createdAt: string;
updatedAt: string;
role: string;
subrole: string;
}
interface SSOPayload {
user: UserProfile;
application: UserApplication;
role: string;
subrole: string;
iat: number;
exp: number;
}
// Extend NextRequest to include SSO payload
declare module 'next/server' {
interface NextRequest {
user?: UserProfile;
application?: UserApplication;
role?: string;
subrole?: string;
ssoPayload?: SSOPayload;
}
}
export function middleware(request: NextRequest): NextResponse | void {
// Skip authentication for public routes
if (request.nextUrl.pathname.startsWith('/api/auth') ||
request.nextUrl.pathname.startsWith('/login') ||
request.nextUrl.pathname === '/') {
return;
}
return SSOMiddleware.withSSOValidation((req: NextRequest) => {
// Middleware berhasil, lanjutkan ke route
console.log('User authenticated:', req.user?.name);
console.log('User email:', req.user?.userId);
console.log('User role:', req.role);
console.log('User subrole:', req.subrole);
console.log('Application:', req.application?.applicationName);
return NextResponse.next();
})(request);
}
export const config = {
matcher: [
'/((?!api/auth|_next/static|_next/image|favicon.ico|public).*)',
],
};
`;
function detectProjectType(projectRoot) {
const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
const packageJsonPath = path.join(projectRoot, 'package.json');
// Check if tsconfig.json exists
if (fs.existsSync(tsconfigPath)) {
return 'typescript';
}
// Check package.json for TypeScript dependencies
if (fs.existsSync(packageJsonPath)) {
try {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
if (deps.typescript || deps['@types/node'] || deps['@types/react']) {
return 'typescript';
}
} catch (error) {
console.warn('Could not parse package.json, defaulting to JavaScript');
}
}
return 'javascript';
}
function setupEnvironment() {
try {
// Get the project root (where package.json is located)
const projectRoot = process.cwd();
const envPath = path.join(projectRoot, '.env.local');
const envExamplePath = path.join(projectRoot, '.env.example');
// Detect project type
const projectType = detectProjectType(projectRoot);
const isTypeScript = projectType === 'typescript';
const middlewareFileName = isTypeScript ? 'middleware.ts' : 'middleware.js';
const middlewarePath = path.join(projectRoot, middlewareFileName);
const middlewareTemplate = isTypeScript ? middlewareTSTemplate : middlewareJSTemplate;
console.log('š Setting up SAGET SSO Middleware...');
console.log(`š Detected project type: ${projectType.toUpperCase()}`);
// Check if .env.local already exists
if (!fs.existsSync(envPath)) {
fs.writeFileSync(envPath, envTemplate);
console.log('ā
Created .env.local with SSO configuration');
} else {
console.log('ā¹ļø .env.local already exists, skipping creation');
console.log('š Please add the following variables to your .env.local:');
console.log(envTemplate);
}
// Always create/update .env.example
fs.writeFileSync(envExamplePath, envTemplate);
console.log('ā
Created/updated .env.example with SSO configuration');
// Create middleware file if it doesn't exist
if (!fs.existsSync(middlewarePath)) {
fs.writeFileSync(middlewarePath, middlewareTemplate);
console.log(`ā
Created ${middlewareFileName} with SSO configuration`);
} else {
console.log(`ā¹ļø ${middlewareFileName} already exists, skipping creation`);
}
console.log('\nš Next steps:');
console.log('1. āļø Update the values in .env.local with your actual SSO credentials');
console.log(`2. š§ Customize ${middlewareFileName} according to your needs`);
if (isTypeScript) {
console.log('3. š¦ Make sure you have @types/node installed for TypeScript support');
console.log('4. š Start your Next.js development server');
} else {
console.log('3. š Start your Next.js development server');
}
console.log('\nš For detailed setup instructions, visit: https://github.com/your-repo/saget-auth-midleware');
console.log('\nš SAGET SSO Middleware setup completed!');
} catch (error) {
console.error('ā Error setting up environment:', error.message);
process.exit(1);
}
}
// Only run setup if this script is executed directly (not imported)
if (import.meta.url === `file://\${process.argv[1]}`) {
setupEnvironment();
}
export { setupEnvironment };