153 lines
4.7 KiB
TypeScript
153 lines
4.7 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { getServerSession } from 'next-auth/next';
|
|
import { authOptions } from '../options';
|
|
import { getServiceConfigByUrl, generateAuthenticatedUrl, serviceAuthConfigs } from '@/lib/services/iframe-auth';
|
|
|
|
/**
|
|
* API endpoint to generate authenticated URLs for iframe services
|
|
*
|
|
* This endpoint exchanges the user's Keycloak access token for a
|
|
* service-specific authentication URL that can be used in iframes.
|
|
*
|
|
* Usage:
|
|
* GET /api/auth/service-sso?service=nextcloud&path=/apps/files
|
|
* GET /api/auth/service-sso?url=https://espace.slm-lab.net/apps/mail
|
|
*/
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const session = await getServerSession(authOptions);
|
|
|
|
if (!session?.accessToken) {
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized', message: 'No active session' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
const searchParams = request.nextUrl.searchParams;
|
|
const serviceName = searchParams.get('service');
|
|
const targetUrl = searchParams.get('url');
|
|
const targetPath = searchParams.get('path') || '/';
|
|
|
|
const keycloakIssuer = process.env.KEYCLOAK_ISSUER;
|
|
if (!keycloakIssuer) {
|
|
return NextResponse.json(
|
|
{ error: 'Configuration error', message: 'Keycloak issuer not configured' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
let authenticatedUrl: string;
|
|
|
|
if (serviceName) {
|
|
// Get auth URL by service name
|
|
const config = serviceAuthConfigs[serviceName.toLowerCase()];
|
|
if (!config) {
|
|
return NextResponse.json(
|
|
{
|
|
error: 'Unknown service',
|
|
message: `Service '${serviceName}' not configured`,
|
|
availableServices: Object.keys(serviceAuthConfigs)
|
|
},
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
authenticatedUrl = generateAuthenticatedUrl(
|
|
serviceName,
|
|
session.accessToken,
|
|
targetPath,
|
|
keycloakIssuer
|
|
);
|
|
} else if (targetUrl) {
|
|
// Get auth URL by target URL
|
|
const config = getServiceConfigByUrl(targetUrl);
|
|
if (!config) {
|
|
// Service not configured, return original URL
|
|
return NextResponse.json({
|
|
success: true,
|
|
url: targetUrl,
|
|
authType: 'none',
|
|
message: 'Service not configured for SSO, using original URL'
|
|
});
|
|
}
|
|
|
|
// Extract path from target URL
|
|
const urlObj = new URL(targetUrl);
|
|
authenticatedUrl = generateAuthenticatedUrl(
|
|
config.name.toLowerCase(),
|
|
session.accessToken,
|
|
urlObj.pathname + urlObj.search,
|
|
keycloakIssuer
|
|
);
|
|
} else {
|
|
return NextResponse.json(
|
|
{
|
|
error: 'Missing parameter',
|
|
message: 'Provide either "service" or "url" parameter',
|
|
availableServices: Object.keys(serviceAuthConfigs)
|
|
},
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
url: authenticatedUrl,
|
|
expiresIn: 300, // URL is valid for 5 minutes (token expiry)
|
|
});
|
|
} catch (error) {
|
|
console.error('Error generating service SSO URL:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST endpoint for RocketChat iframe authentication
|
|
* This generates a login token for RocketChat's iframe auth
|
|
*/
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const session = await getServerSession(authOptions);
|
|
|
|
if (!session?.accessToken || !session?.user) {
|
|
return NextResponse.json(
|
|
{ error: 'Unauthorized', message: 'No active session' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
const body = await request.json();
|
|
const { service } = body;
|
|
|
|
if (service === 'rocketchat') {
|
|
// For RocketChat, we need to use their OAuth login or iframe auth
|
|
// This returns the token that can be used with postMessage
|
|
return NextResponse.json({
|
|
success: true,
|
|
authData: {
|
|
token: session.accessToken,
|
|
userId: session.user.id,
|
|
email: session.user.email,
|
|
username: session.user.username,
|
|
},
|
|
message: 'Use postMessage to send this to RocketChat iframe'
|
|
});
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{ error: 'Unsupported service for POST', message: 'Use GET for most services' },
|
|
{ status: 400 }
|
|
);
|
|
} catch (error) {
|
|
console.error('Error in service SSO POST:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Internal server error', message: error instanceof Error ? error.message : 'Unknown error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|