RocketChat/Rocket.Chat · warning · Meteor.Error

CustomOAuth: emailPath is required

Error message

CustomOAuth: emailPath is required

What it means

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.

Source

Thrown at apps/meteor/server/lib/auth-providers/custom-oauth/custom_oauth_server.js:278

			identity.email = await this.getEmailFromPath(accessToken);
		}

		if (this.avatarField) {
			identity.avatarUrl = this.getAvatarUrl(identity);
		}

		if (this.nameField) {
			identity.name = this.getCustomName(identity);
		} else {
			identity.name = this.getName(identity);
		}

		return renameInvalidProperties(identity);
	}

	async getEmailFromPath(accessToken) {
		if (!this.emailPath) {
			throw new Meteor.Error('CustomOAuth: emailPath is required');
		}

		const params = {};
		const headers = {
			'User-Agent': this.userAgent,
			'Accept': 'application/json',
		};

		if (this.identityTokenSentVia === 'header') {
			headers.Authorization = `Bearer ${accessToken}`;
		} else {
			params[this.accessTokenParam] = accessToken;
		}

		try {
			// SECURITY: URL can only be configured by users with enough privileges. It's ok to disable this check here.
			const request = await fetch(`${this.emailPath}`, { method: 'GET', headers, params, ignoreSsrfValidation: true });

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Pass emailPath in the CustomOAuth options (relative paths are auto-prefixed with serverURL), e.g. emailPath: '/api/v3/user/emails'
  2. Prefer emailField mapping when the e-mail is already present in the identity payload - then emailPath is unnecessary
  3. Do not call getEmailFromPath directly; go through the normal login flow so the guard applies

Example fix

// before
new CustomOAuth('gitea', { serverURL, tokenPath, identityPath });
// later: await strategy.getEmailFromPath(token) -> throws 'CustomOAuth: emailPath is required'

// after
new CustomOAuth('gitea', { serverURL, tokenPath, identityPath, emailPath: '/user/emails' });
Defensive patterns

Strategy: validation

Validate before calling

const hasEmailPath = (strategy: CustomOAuth): boolean =>
  typeof strategy.emailPath === 'string' && strategy.emailPath.length > 0;

if (!hasEmailPath(strategy)) {
  throw new Error('emailPath option is required before fetching e-mails from the provider');
}

Try / catch

try {
  email = await strategy.getEmailFromPath(accessToken);
} catch (error) {
  if (/emailPath is required/.test(error.message)) {
    email = identity.email; // fall back to whatever the identity payload already carries
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/d82bc7830cf53d0a. Report an issue: GitHub.