Skip to main content
Glama
method.ts3.24 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { OperationOutcomeError, Operator, badRequest } from '@medplum/core'; import type { DomainConfiguration } from '@medplum/fhirtypes'; import type { Request, Response } from 'express'; import { body } from 'express-validator'; import { getConfig } from '../config/loader'; import { getSystemRepo } from '../fhir/repo'; import { globalLogger } from '../logger'; import { makeValidationMiddleware } from '../util/validator'; /* * The method handler is used to determine available login methods. * This is an unauthorized endpoint that does not require a login. * We do not leak the existence of a user account, but we do leak the existince of domain configurations. * For example, an unauthenticated user could determine if "foo.com" has a domain configuration. */ export const methodValidator = makeValidationMiddleware([ body('email').isEmail().withMessage('Valid email address is required'), ]); export async function methodHandler(req: Request, res: Response): Promise<void> { const externalAuth = await isExternalAuth(req.body.email); if (externalAuth) { // Return the authorization URL // This indicates the client should redirect to the authorization URL res.status(200).json(externalAuth); return; } // Send empty response indication no information available // This indicates the client should proceed with the default login flow res.status(200).json({}); } /** * Checks if the given email address is configured for external authentication. * @param email - The user email address. * @returns External auth url if available. Otherwise undefined. */ export async function isExternalAuth(email: string): Promise<{ domain: string; authorizeUrl: string } | undefined> { const domain = email.split('@')[1]; const domainConfig = await getDomainConfiguration(domain); if (!domainConfig) { return undefined; } const idp = domainConfig.identityProvider; if (!idp) { return undefined; } try { const url = new URL(idp.authorizeUrl as string); url.searchParams.set('client_id', idp.clientId as string); url.searchParams.set('redirect_uri', getConfig().baseUrl + 'auth/external'); url.searchParams.set('response_type', 'code'); url.searchParams.set('scope', 'openid profile email'); return { domain, authorizeUrl: url.toString() }; } catch (_err) { globalLogger.error(`Error constructing URL for domain ${domain}:`); throw new OperationOutcomeError(badRequest('Failed to construct URL for the domain')); } } /** * Returns the domain configuration for the given domain name. * @param domain - The domain name. * @returns The domain configuration for the domain name if available. */ export async function getDomainConfiguration(domain: string): Promise<DomainConfiguration | undefined> { const systemRepo = getSystemRepo(); const results = await systemRepo.search<DomainConfiguration>({ resourceType: 'DomainConfiguration', filters: [ { code: 'domain', operator: Operator.EQUALS, value: domain.toLowerCase(), }, ], }); return results.entry?.[0]?.resource; }

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