Skip to main content
Glama
projectAccessKey.service.ts4.69 kB
import { randomBytes } from 'node:crypto'; import { ProjectModel } from '@models/project.model'; import { GenericError } from '@utils/errors'; import type { Types } from 'mongoose'; import type { AccessKeyData, OAuth2Access, OAuth2AccessData, Project, } from '@/types/project.types'; import type { User } from '@/types/user.types'; import { getProjectById } from './project.service'; /** * Generates cryptographically secure OAuth2 client credentials * * @returns Object containing clientId and clientSecret * * Security improvements: * - clientId: 32 characters (128 bits of entropy) * - clientSecret: 64 characters (256 bits of entropy) * - Uses crypto.randomBytes for cryptographically secure random generation * - Follows OAuth2 best practices for credential strength */ const generateClientCredentials = () => ({ clientId: randomBytes(16).toString('hex'), // 32 character hexadecimal string clientSecret: randomBytes(32).toString('hex'), // 64 character hexadecimal string }); /** * Adds a new access key to a project. * * @param accessKeyData - The access key data. * @param projectId - The ID of the project to add the access key to. * @param user - The user adding the access key. * @returns The new access key. * */ export const addNewAccessKey = async ( accessKeyData: AccessKeyData, projectId: string | Types.ObjectId, user: User ): Promise<OAuth2Access> => { const { clientId, clientSecret } = generateClientCredentials(); const newAccessKey: OAuth2AccessData = { ...accessKeyData, clientId, clientSecret, userId: user.id, accessToken: [], grants: accessKeyData.grants, }; const result = await ProjectModel.updateOne( { _id: projectId }, { $push: { oAuth2Access: newAccessKey } } ); if (result.modifiedCount === 0) { throw new GenericError('ACCESS_KEY_CREATION_FAILED', { accessKeyData, projectId, userId: user.id, }); } const updatedProject = await getProjectById(projectId); const newAccessKeyId = updatedProject.oAuth2Access.find( (access) => access.clientId === clientId ); if (!newAccessKeyId) { throw new GenericError('ACCESS_KEY_CREATION_FAILED', { accessKeyData, projectId, userId: user.id, }); } return newAccessKeyId; }; export const deleteAccessKey = async ( clientId: string | Types.ObjectId, project: Project, userId: string | Types.ObjectId ) => { const projectAccess = project.oAuth2Access.find( (access) => access.clientId === clientId && String(access.userId) === String(userId) ); if (!projectAccess) { throw new GenericError('ACCESS_KEY_NOT_FOUND', { clientId, projectId: project.id, }); } const result = await ProjectModel.updateOne( { 'oAuth2Access.clientId': clientId, 'oAuth2Access.userId': String(userId), }, { $pull: { oAuth2Access: { clientId } } } ); if (result.modifiedCount === 0) { throw new GenericError('ACCESS_KEY_DELETION_FAILED', { clientId, projectId: project.id, }); } return projectAccess; }; export const refreshAccessKey = async ( clientId: string | Types.ObjectId, projectId: string | Types.ObjectId, userId: string | Types.ObjectId ): Promise<OAuth2Access> => { const project = await ProjectModel.findOne({ _id: projectId, 'oAuth2Access.clientId': clientId, 'oAuth2Access.userId': String(userId), }); if (!project) { throw new GenericError('PROJECT_NOT_FOUND', { clientId, projectId, userId, }); } const projectAccess = project.oAuth2Access.find( (access) => access.clientId === clientId ); if (!projectAccess) { throw new GenericError('ACCESS_KEY_NOT_FOUND', { clientId, projectId: project.id, }); } const { clientSecret } = generateClientCredentials(); const result = await ProjectModel.updateOne( { 'oAuth2Access.clientId': clientId, 'oAuth2Access.userId': String(userId), }, { $set: { 'oAuth2Access.$.clientId': projectAccess.clientId, 'oAuth2Access.$.clientSecret': clientSecret, }, } ); if (result.modifiedCount === 0) { throw new GenericError('ACCESS_KEY_UPDATE_FAILED', { clientId, projectId, }); } const updatedProject = await getProjectById(projectId); const newAccessKeyId = updatedProject.oAuth2Access.find( (access) => access.clientId === projectAccess.clientId ); if (!newAccessKeyId) { throw new GenericError('ACCESS_KEY_CREATION_FAILED', { accessKeyData: updatedProject.oAuth2Access, projectId, userId, }); } return newAccessKeyId; };

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/aymericzip/intlayer'

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