feat: Force Microsoft account selection on OAuth for all Microsoft credentials (#32015)

This commit is contained in:
Csaba Tuncsik
2026-06-15 13:30:46 +02:00
committed by GitHub
parent be1bedcec3
commit 939307a4d5
3 changed files with 114 additions and 1 deletions
@@ -1877,6 +1877,88 @@ describe('OauthService', () => {
expect(mockGetUri).toHaveBeenCalled();
});
it('should merge authQueryParameters into the authorize URL query', async () => {
const { ClientOAuth2 } = await import('@n8n/client-oauth2');
// Capture the options passed into the ClientOAuth2 constructor to prove the service
// parses authQueryParameters and sets oAuthOptions.query from it.
let capturedOptions: { query?: Record<string, string> } | undefined;
jest.mocked(ClientOAuth2).mockImplementation((options) => {
capturedOptions = options as { query?: Record<string, string> };
return {
code: {
getUri: () => ({
toString: () => 'https://example.domain/oauth2/auth?response_type=code',
}),
},
} as any;
});
const credential = mock<CredentialsEntity>({ id: '1', type: 'microsoftOutlookOAuth2Api' });
const oauthCredentials: OAuth2CredentialData = {
clientId: 'client_id',
clientSecret: 'client_secret',
authUrl: 'https://example.domain/oauth2/auth',
accessTokenUrl: 'https://example.domain/oauth2/token',
scope: 'openid',
grantType: 'authorizationCode',
authentication: 'header',
authQueryParameters: 'response_mode=query&prompt=select_account',
};
jest.spyOn(service, 'getOAuthCredentials').mockResolvedValue(oauthCredentials);
jest.spyOn(service, 'encryptAndSaveData').mockResolvedValue(undefined);
await service.generateAOauth2AuthUri(credential, {
cid: credential.id,
origin: 'static-credential',
userId: 'user-id',
});
expect(capturedOptions?.query).toEqual({
response_mode: 'query',
prompt: 'select_account',
});
});
it('should not set query when credentials have no authQueryParameters', async () => {
const { ClientOAuth2 } = await import('@n8n/client-oauth2');
let capturedOptions: { query?: Record<string, string> } | undefined;
jest.mocked(ClientOAuth2).mockImplementation((options) => {
capturedOptions = options as { query?: Record<string, string> };
return {
code: {
getUri: () => ({
toString: () => 'https://example.domain/oauth2/auth?response_type=code',
}),
},
} as any;
});
const credential = mock<CredentialsEntity>({ id: '1', type: 'microsoftOutlookOAuth2Api' });
const oauthCredentials: OAuth2CredentialData = {
clientId: 'client_id',
clientSecret: 'client_secret',
authUrl: 'https://example.domain/oauth2/auth',
accessTokenUrl: 'https://example.domain/oauth2/token',
scope: 'openid',
grantType: 'authorizationCode',
authentication: 'header',
};
jest.spyOn(service, 'getOAuthCredentials').mockResolvedValue(oauthCredentials);
jest.spyOn(service, 'encryptAndSaveData').mockResolvedValue(undefined);
await service.generateAOauth2AuthUri(credential, {
cid: credential.id,
origin: 'static-credential',
userId: 'user-id',
});
expect(capturedOptions?.query).toBeUndefined();
});
it('should handle dynamic client registration with root-level server URL', async () => {
const axios = require('axios');
const { ClientOAuth2 } = await import('@n8n/client-oauth2');
@@ -37,7 +37,7 @@ export class MicrosoftOAuth2Api implements ICredentialType {
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: 'response_mode=query',
default: 'response_mode=query&prompt=select_account',
},
{
displayName: 'Authentication',
@@ -0,0 +1,31 @@
import { MicrosoftOAuth2Api } from '../MicrosoftOAuth2Api.credentials';
describe('MicrosoftOAuth2Api Credential', () => {
const microsoftOAuth2Api = new MicrosoftOAuth2Api();
it('should have correct credential metadata', () => {
expect(microsoftOAuth2Api.name).toBe('microsoftOAuth2Api');
expect(microsoftOAuth2Api.displayName).toBe('Microsoft OAuth2 API');
expect(microsoftOAuth2Api.extends).toEqual(['oAuth2Api']);
const authUrlProperty = microsoftOAuth2Api.properties.find((p) => p.name === 'authUrl');
expect(authUrlProperty?.default).toBe(
'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
);
const accessTokenUrlProperty = microsoftOAuth2Api.properties.find(
(p) => p.name === 'accessTokenUrl',
);
expect(accessTokenUrlProperty?.default).toBe(
'https://login.microsoftonline.com/common/oauth2/v2.0/token',
);
});
it('should request the Microsoft account chooser via authQueryParameters', () => {
const authQueryParamsProperty = microsoftOAuth2Api.properties.find(
(p) => p.name === 'authQueryParameters',
);
expect(authQueryParamsProperty?.type).toBe('hidden');
expect(authQueryParamsProperty?.default).toBe('response_mode=query&prompt=select_account');
});
});