Skip to main content
Glama
rotatesecret.ts3.07 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { allOk, badRequest, forbidden, OperationOutcomeError } from '@medplum/core'; import type { FhirRequest, FhirResponse } from '@medplum/fhir-router'; import type { ClientApplication, OperationDefinition } from '@medplum/fhirtypes'; import { getAuthenticatedContext } from '../../context'; import { generateSecret } from '../../oauth/keys'; import { getSystemRepo } from '../repo'; import { buildOutputParameters, parseInputParameters } from './utils/parameters'; const operation: OperationDefinition = { resourceType: 'OperationDefinition', name: 'clientapplication-rotate-secret', status: 'active', kind: 'operation', code: 'rotate-secret', experimental: true, resource: ['ClientApplication'], system: false, type: false, instance: true, parameter: [ { use: 'in', name: 'secret', type: 'string', min: 0, max: '1' }, { use: 'in', name: 'retiringSecret', type: 'string', min: 0, max: '1' }, { use: 'out', name: 'return', type: 'ClientApplication', min: 1, max: '1' }, ], }; type RotateSecretParameters = { secret?: string; retiringSecret?: string; }; /** * Handles a request to import codes and their properties into a CodeSystem. * * Endpoint - ClientApplication resource type * [fhir base]/ClientApplication/:id/$rotate-secret * * @param req - The FHIR request. * @returns The FHIR response. */ export async function rotateSecretHandler(req: FhirRequest): Promise<FhirResponse> { const repo = getAuthenticatedContext().repo; if (!repo.isSuperAdmin() && !repo.isProjectAdmin()) { return [forbidden]; } const params = parseInputParameters<RotateSecretParameters>(operation, req); if (!params.secret && !params.retiringSecret) { return [badRequest('Secret to rotate must be provided')]; } if (params.secret && params.retiringSecret) { return [badRequest('Only one secret can be rotated at a time')]; } // Patch using system repo since the secret fields should not generally be user-writeable const systemRepo = getSystemRepo(); const clientApp = await systemRepo.withTransaction(async () => { let clientApp = await systemRepo.readResource<ClientApplication>('ClientApplication', req.params.id); if (params.secret && params.secret === clientApp.secret) { clientApp = await systemRepo.updateResource({ ...clientApp, secret: generateSecret(32), // Generate new secret retiringSecret: clientApp.secret, // Rotate existing secret to "retiring" slot }); } else if (params.retiringSecret && params.retiringSecret === clientApp.retiringSecret) { clientApp = await systemRepo.updateResource({ ...clientApp, retiringSecret: undefined, // Remove rotated secret after it's been retired }); } else { throw new OperationOutcomeError(badRequest('Provided secret does not match client')); } return clientApp; }); return [allOk, buildOutputParameters(operation, clientApp)]; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server