{"record":{"id":"d82bc7830cf53d0a","repo":"RocketChat/Rocket.Chat","slug":"customoauth-emailpath-is-required","errorCode":null,"errorMessage":"CustomOAuth: emailPath is required","messagePattern":"CustomOAuth: emailPath is required","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"warning","filePath":"apps/meteor/server/lib/auth-providers/custom-oauth/custom_oauth_server.js","lineNumber":278,"sourceCode":"\t\t\tidentity.email = await this.getEmailFromPath(accessToken);\n\t\t}\n\n\t\tif (this.avatarField) {\n\t\t\tidentity.avatarUrl = this.getAvatarUrl(identity);\n\t\t}\n\n\t\tif (this.nameField) {\n\t\t\tidentity.name = this.getCustomName(identity);\n\t\t} else {\n\t\t\tidentity.name = this.getName(identity);\n\t\t}\n\n\t\treturn renameInvalidProperties(identity);\n\t}\n\n\tasync getEmailFromPath(accessToken) {\n\t\tif (!this.emailPath) {\n\t\t\tthrow new Meteor.Error('CustomOAuth: emailPath is required');\n\t\t}\n\n\t\tconst params = {};\n\t\tconst headers = {\n\t\t\t'User-Agent': this.userAgent,\n\t\t\t'Accept': 'application/json',\n\t\t};\n\n\t\tif (this.identityTokenSentVia === 'header') {\n\t\t\theaders.Authorization = `Bearer ${accessToken}`;\n\t\t} else {\n\t\t\tparams[this.accessTokenParam] = accessToken;\n\t\t}\n\n\t\ttry {\n\t\t\t// SECURITY: URL can only be configured by users with enough privileges. It's ok to disable this check here.\n\t\t\tconst request = await fetch(`${this.emailPath}`, { method: 'GET', headers, params, ignoreSsrfValidation: true });\n","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/auth-providers/custom-oauth/custom_oauth_server.js#L260-L296","documentation":"getEmailFromPath() refuses to run when the strategy instance has no emailPath configured. In the current code normalizeIdentity() only invokes it under 'if (!identity.email && this.emailPath)', so a live hit means the method was called directly (subclass, fork, or an older unguarded call path) on an instance constructed without the emailPath option.","triggerScenarios":"Calling strategy.getEmailFromPath(accessToken) directly without having passed emailPath in the constructor options; running code from an older Rocket.Chat branch where normalizeIdentity called getEmailFromPath whenever identity.email was missing, regardless of emailPath.","commonSituations":"Custom forks or Apps that subclass the deprecated CustomOAuth class to fetch e-mails; upgrades where a private branch kept the old unguarded call; unit tests exercising getEmailFromPath in isolation.","solutions":["Pass emailPath in the CustomOAuth options (relative paths are auto-prefixed with serverURL), e.g. emailPath: '/api/v3/user/emails'","Prefer emailField mapping when the e-mail is already present in the identity payload - then emailPath is unnecessary","Do not call getEmailFromPath directly; go through the normal login flow so the guard applies"],"exampleFix":"// before\nnew CustomOAuth('gitea', { serverURL, tokenPath, identityPath });\n// later: await strategy.getEmailFromPath(token) -> throws 'CustomOAuth: emailPath is required'\n\n// after\nnew CustomOAuth('gitea', { serverURL, tokenPath, identityPath, emailPath: '/user/emails' });","handlingStrategy":"validation","validationCode":"const hasEmailPath = (strategy: CustomOAuth): boolean =>\n  typeof strategy.emailPath === 'string' && strategy.emailPath.length > 0;\n\nif (!hasEmailPath(strategy)) {\n  throw new Error('emailPath option is required before fetching e-mails from the provider');\n}","typeGuard":null,"tryCatchPattern":"try {\n  email = await strategy.getEmailFromPath(accessToken);\n} catch (error) {\n  if (/emailPath is required/.test(error.message)) {\n    email = identity.email; // fall back to whatever the identity payload already carries\n  } else {\n    throw error;\n  }\n}","preventionTips":["Configure emailPath whenever the provider omits e-mail from the identity endpoint","Prefer emailField over emailPath when possible - one less HTTP round trip and failure mode","Never call getEmailFromPath outside the normalized login flow"],"tags":["oauth","custom-oauth","configuration","email"],"backgroundTag":"missing-oauth-configuration","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}