Skip to main content
Glama
prisma

Prisma MCP Server

Official
by prisma
login.ts8.35 kB
import { select } from '@inquirer/prompts' import type { PrismaConfigInternal } from '@prisma/config' import Debug from '@prisma/debug' import { arg, Command, getCommandWithExecutor, isError, link } from '@prisma/internals' import listen from 'async-listen' import http from 'http' import { green } from 'kleur/colors' import open from 'open' import { credentialsFile } from '../_lib/credentials' import { successMessage } from '../_lib/messages' import { consoleUrl } from '../_lib/pdp' import { unknownToError } from '../_lib/prelude' import { getUserAgent } from '../_lib/userAgent' const packageJson = require('../../../package.json') interface CallbackData { token: string user: { id: string displayName: string email: string } } const debug = Debug('prisma:cli:platform:login') export class Login implements Command { public static new() { return new Login() } public async parse(argv: string[], _config: PrismaConfigInternal): Promise<string | Error> { const args = arg(argv, { // internal optimize flag to track signup attribution '--optimize': Boolean, }) if (isError(args)) return args if (args['--optimize']) { console.warn("The '--optimize' flag is deprecated. Use API keys instead.") } const credentials = await credentialsFile.load() if (isError(credentials)) throw credentials if (credentials) return `Already authenticated. Run ${green(getCommandWithExecutor("prisma platform auth show --early-access"))} to see the current user.`; // prettier-ignore console.info('Authenticating to Prisma Platform CLI via browser.\n') const server = http.createServer() /** * When passing 0 as a port to listen, the OS will assign a random available port */ const randomPort = 0 const redirectUrl = await listen(server, randomPort, '127.0.0.1') const loginUrl = await createLoginUrl({ connection: 'github', redirectTo: redirectUrl.href }) console.info('Visit the following URL in your browser to authenticate:') console.info(link(loginUrl.href)) const callbackResult = await Promise.all([ new Promise<CallbackData>((resolve, reject) => { server.once('request', (req, res) => { server.close() res.setHeader('connection', 'close') const searchParams = new URL(req.url || '/', 'http://localhost').searchParams const token = searchParams.get('token') ?? '' const error = searchParams.get('error') const location = getBaseAuthUrl() if (error) { location.pathname += '/error' location.searchParams.set('error', error) reject(new Error(error)) } else { // TODO: Consider getting the user via Console API instead of passing it via query params const user = decodeUser(searchParams.get('user') ?? '') if (user) { searchParams.delete('token') searchParams.delete('user') location.pathname += '/success' const nextSearchParams = new URLSearchParams({ ...Object.fromEntries(searchParams.entries()), email: user.email, }) location.search = nextSearchParams.toString() resolve({ token, user }) } else { location.pathname += '/error' location.searchParams.set('error', 'Invalid user') reject(new Error('Invalid user')) } } res.statusCode = 302 res.setHeader('location', location.href) res.end() }) server.once('error', reject) }), open(loginUrl.href), ]) .then((results) => results[0]) .catch(unknownToError) if (isError(callbackResult)) throw new Error(`Authentication failed: ${callbackResult.message}`); // prettier-ignore { const writeResult = await credentialsFile.save({ token: callbackResult.token }) if (isError(writeResult)) throw new Error('Writing credentials to disk failed', { cause: writeResult }) } return successMessage(`Authentication successful for ${callbackResult.user.email}`) } } const getBaseAuthUrl = () => new URL('/auth/cli', consoleUrl) const createLoginUrl = async (params: { connection: string; redirectTo: string }) => { const userAgent = await getUserAgent() const state: State = { client: userAgent, ...params, } const stateEncoded = encodeState(state) const url = getBaseAuthUrl() url.searchParams.set('state', stateEncoded) url.searchParams.set('utm_source', 'cli') url.searchParams.set('utm_medium', 'command-platform-login') url.searchParams.set('utm_campaign', packageJson.version as string) return url } interface State { client: string connection: string redirectTo: string } const encodeState = (state: State) => Buffer.from(JSON.stringify(state), 'utf-8').toString('base64') const decodeUser = (stringifiedUser: string) => { try { const maybeUser = JSON.parse(Buffer.from(stringifiedUser, `base64`).toString(`utf-8`)) if (typeof maybeUser !== 'object' || maybeUser === null) return false const isUser = typeof maybeUser.id === 'string' && typeof maybeUser.displayName === 'string' && typeof maybeUser.email === 'string' return isUser ? maybeUser : null } catch (e) { debug(`parseUser() failed silently with ${e}`) return null } } export const loginOrSignup = async () => { const providerAnswer = await select({ message: 'Select an authentication method', default: 'google', choices: [ { name: 'Google', value: 'google' }, { name: 'GitHub', value: 'github' }, ], }) console.info('Authenticating to Prisma Platform via browser.\n') const server = http.createServer() /** * When passing 0 as a port to listen, the OS will assign a random available port */ const randomPort = 0 const redirectUrl = await listen(server, randomPort, '127.0.0.1') const loginUrl = await createLoginUrl({ connection: providerAnswer, redirectTo: redirectUrl.href }) console.info('Visit the following URL in your browser to authenticate:') console.info(link(loginUrl.href)) const callbackResult = await Promise.all([ new Promise<CallbackData>((resolve, reject) => { server.once('request', (req, res) => { server.close() res.setHeader('connection', 'close') const searchParams = new URL(req.url || '/', 'http://localhost').searchParams const token = searchParams.get('token') ?? '' const error = searchParams.get('error') const location = getBaseAuthUrl() if (error) { location.pathname += '/error' location.searchParams.set('error', error) reject(new Error(error)) } else { // TODO: Consider getting the user via Console API instead of passing it via query params const user = decodeUser(searchParams.get('user') ?? '') if (user) { searchParams.delete('token') searchParams.delete('user') location.pathname += '/success' const nextSearchParams = new URLSearchParams({ ...Object.fromEntries(searchParams.entries()), email: user.email, }) location.search = nextSearchParams.toString() resolve({ token, user }) } else { location.pathname += '/error' location.searchParams.set('error', 'Invalid user') reject(new Error('Invalid user')) } } res.statusCode = 302 res.setHeader('location', location.href) res.end() }) server.once('error', reject) }), open(loginUrl.href), ]) .then((results) => results[0]) .catch(unknownToError) if (isError(callbackResult)) throw new Error(`Authentication failed: ${callbackResult.message}`); // prettier-ignore { const writeResult = await credentialsFile.save({ token: callbackResult.token }) if (isError(writeResult)) throw new Error('Writing credentials to disk failed', { cause: writeResult }) } return { message: successMessage(`Authentication successful for ${callbackResult.user.email}`), email: callbackResult.user.email, token: callbackResult.token, } }

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/prisma/prisma'

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