{"record":{"id":"090ec425a386e3a8","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-090ec4","errorCode":"error-not-allowed","errorMessage":"Not allowed","messagePattern":"Not allowed","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/auth/deleteOAuthApp.ts","lineNumber":18,"sourceCode":"import type { IOAuthApps } from '@rocket.chat/core-typings';\nimport type { ServerMethods } from '@rocket.chat/ddp-client';\nimport { OAuthAccessTokens, OAuthApps, OAuthAuthCodes } from '@rocket.chat/models';\nimport { Meteor } from 'meteor/meteor';\n\nimport { hasPermissionAsync } from '../../lib/authorization/hasPermission';\nimport { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\tdeleteOAuthApp(applicationId: IOAuthApps['_id']): boolean;\n\t}\n}\n\nexport const deleteOAuthApp = async (userId: string, applicationId: IOAuthApps['_id']): Promise<boolean> => {\n\tif (!(await hasPermissionAsync(userId, 'manage-oauth-apps'))) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'deleteOAuthApp' });\n\t}\n\n\tconst application = await OAuthApps.findOneAndDeleteById(applicationId, { projection: { clientId: 1 } });\n\tif (!application) {\n\t\tthrow new Meteor.Error('error-application-not-found', 'Application not found', {\n\t\t\tmethod: 'deleteOAuthApp',\n\t\t});\n\t}\n\n\tawait OAuthAccessTokens.deleteMany({ clientId: application.clientId });\n\tawait OAuthAuthCodes.deleteMany({ clientId: application.clientId });\n\n\treturn true;\n};\n\nMeteor.methods<ServerMethods>({\n\tasync deleteOAuthApp(applicationId) {\n\t\tmethodDeprecationLogger.method('deleteOAuthApp', '9.0.0', '/v1/oauth-apps.delete');","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/auth/deleteOAuthApp.ts#L1-L36","documentation":"The deleteOAuthApp helper checks the 'manage-oauth-apps' permission for the calling userId and throws 'error-not-allowed' when absent, before any lookup happens. The Meteor method is deprecated since 9.0.0 in favor of DELETE /api/v1/oauth-apps.delete, which performs the same permission gate.","triggerScenarios":"Calling deleteOAuthApp as a user whose roles do not include 'manage-oauth-apps'; using a custom integration role that was never granted the permission.","commonSituations":"Delegating OAuth app management to a sub-admin role that lacks the grant; the permission was removed from the role during a permissions audit.","solutions":["Grant 'manage-oauth-apps' to the caller's role in the admin Permissions screen","Migrate to DELETE /api/v1/oauth-apps.delete with a token whose user holds the permission","Verify the effective permissions of the caller before retrying"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// server-side: check the permission before attempting the delete\nimport { hasPermissionAsync } from '../lib/authorization/hasPermission';\n\nif (!(await hasPermissionAsync(userId, 'manage-oauth-apps'))) {\n  // reject in the UI instead of triggering the server error\n}","typeGuard":null,"tryCatchPattern":"try {\n  await Meteor.callAsync('deleteOAuthApp', applicationId);\n} catch (err) {\n  if (err instanceof Meteor.Error && err.error === 'error-not-allowed') {\n    // caller lacks 'manage-oauth-apps': grant it or use an authorized token\n  }\n}","preventionTips":["Gate the delete action on hasPermission('manage-oauth-apps')","Prefer DELETE /api/v1/oauth-apps.delete with an authorized token over the deprecated method"],"tags":["authorization","permissions","oauth","meteor-methods","deprecated"],"backgroundTag":"permission-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}