import type { BaseLogger } from 'pino';
import {
RegistrationResponseSchema,
type OpenIDConfiguration,
type RegistrationResponse,
} from '../../schemas/index.js';
import { escapeHtml } from '../../utils/htmlEscaping.js';
import {
getAGSScopes,
hasAGSSupport,
hasDeepLinkingSupport,
hasNRPSSupport,
} from '../../utils/ltiPlatformCapabilities.js';
import { ltiServiceFetch } from '../../utils/ltiServiceFetch.js';
/**
* Generates a generic dynamic registration form with service selection options.
* Creates a Bootstrap 5 form that detects available LTI Advantage services from the platform
* configuration and presents them as selectable checkboxes to the administrator.
*
* @param openIdConfiguration - Platform's OpenID Connect configuration containing supported services
* @param currentPath - Current request path used to build the form submission URL
* @param sessionToken - Security token for CSRF protection and session validation
* @returns Complete HTML page with Bootstrap form for service selection
*
* @example
* ```typescript
* const html = renderDynamicRegistrationForm(
* platformConfig,
* '/lti/register',
* 'uuid-session-token'
* );
* // Returns HTML form with AGS, NRPS, and Deep Linking options if supported
* ```
*/
// oxlint-disable-next-line max-lines-per-function
export function renderDynamicRegistrationForm(
openIdConfiguration: OpenIDConfiguration,
currentPath: string,
sessionToken: string,
): string {
const hasAGS = hasAGSSupport(openIdConfiguration);
const hasNRPS = hasNRPSSupport(openIdConfiguration);
const hasDeepLinking = hasDeepLinkingSupport(openIdConfiguration);
const agsScopes = getAGSScopes(openIdConfiguration);
// Build complete action from current path
const completeAction = `${currentPath}/complete`;
return `
Configure LTI Advantage Settings
`;
}
/**
* Submits tool registration payload to a platform's dynamic registration endpoint.
* Handles the HTTP communication with proper authentication, error handling, and response validation.
* Validates the registration response against the LTI 1.3 specification schema.
*
* @param registrationEndpoint - Platform's registration endpoint URL from OpenID configuration
* @param registrationPayload - Complete tool registration payload with OAuth and LTI configuration
* @param logger - Pino logger instance for request/response logging and error tracking
* @param registrationToken - Optional bearer token for authenticated registration requests
* @returns Validated registration response containing client credentials and deployment information
* @throws {Error} When registration request fails or response validation fails
*
* @example
* ```typescript
* const response = await postRegistrationToPlatform(
* 'https://platform.example/registration',
* registrationPayload,
* logger,
* 'optional-bearer-token'
* );
* console.log('Registered with client ID:', response.client_id);
* ```
*/
export async function postRegistrationToPlatform(
registrationEndpoint: string,
registrationPayload: unknown,
logger: BaseLogger,
registrationToken?: string,
): Promise {
const headers: Record = { 'Content-Type': 'application/json' };
if (registrationToken) {
headers['Authorization'] = `Bearer ${registrationToken}`;
}
const response = await ltiServiceFetch(registrationEndpoint, {
method: 'POST',
headers,
body: JSON.stringify(registrationPayload),
});
if (!response.ok) {
const errorText = await response.json();
logger.error({ errorText }, 'lti dynamic registration error');
throw new Error(JSON.stringify(errorText));
}
const data = await response.json();
logger.debug({ data }, 'Registration response');
const validated = RegistrationResponseSchema.parse(data);
logger.debug({ validated }, 'Registration response validated');
return validated;
}