mirror of
https://github.com/logto-io/logto.git
synced 2025-04-07 23:01:25 -05:00
feat(core,console): add trust unverified email settings to Azure SSO (#6800)
add trust unverified email settings to Azure OIDC SSO connector
This commit is contained in:
parent
64e4b08b25
commit
4e826deabe
5 changed files with 72 additions and 8 deletions
|
@ -2,9 +2,11 @@ import { SsoProviderName } from '@logto/schemas';
|
|||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { isDevFeaturesEnabled } from '@/consts/env';
|
||||
import CopyToClipboard from '@/ds-components/CopyToClipboard';
|
||||
import FormField from '@/ds-components/FormField';
|
||||
import InlineNotification from '@/ds-components/InlineNotification';
|
||||
import Switch from '@/ds-components/Switch';
|
||||
import TextInput from '@/ds-components/TextInput';
|
||||
import { uriValidator } from '@/utils/validator';
|
||||
|
||||
|
@ -83,6 +85,17 @@ function OidcMetadataForm({ providerConfig, config, providerName }: Props) {
|
|||
<FormField title="enterprise_sso.metadata.oidc.scope_field_name">
|
||||
<TextInput {...register('scope')} error={Boolean(errors.scope)} />
|
||||
</FormField>
|
||||
{isDevFeaturesEnabled && providerName === SsoProviderName.AZURE_AD_OIDC && (
|
||||
<FormField
|
||||
title="enterprise_sso_details.trust_unverified_email"
|
||||
tip={t('enterprise_sso_details.trust_unverified_email_tip')}
|
||||
>
|
||||
<Switch
|
||||
label={t('enterprise_sso_details.trust_unverified_email_label')}
|
||||
{...register('trustUnverifiedEmail')}
|
||||
/>
|
||||
</FormField>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ export const oidcConnectorConfigGuard = z
|
|||
clientSecret: z.string(),
|
||||
issuer: z.string(),
|
||||
scope: z.string().optional(),
|
||||
// The following fields are only available for EntraID (OIDC) connector
|
||||
trustUnverifiedEmail: z.boolean().optional(),
|
||||
})
|
||||
.partial();
|
||||
|
||||
|
|
|
@ -2,18 +2,56 @@ import { SsoProviderName, SsoProviderType } from '@logto/schemas';
|
|||
import { conditional } from '@silverhand/essentials';
|
||||
import camelcaseKeys from 'camelcase-keys';
|
||||
import { decodeJwt } from 'jose';
|
||||
import { z } from 'zod';
|
||||
|
||||
import assertThat from '#src/utils/assert-that.js';
|
||||
|
||||
import OidcConnector from '../OidcConnector/index.js';
|
||||
import { fetchToken, getIdTokenClaims, getUserInfo } from '../OidcConnector/utils.js';
|
||||
import { OidcSsoConnector } from '../OidcSsoConnector/index.js';
|
||||
import { type SingleSignOnFactory } from '../index.js';
|
||||
import { SsoConnectorError, SsoConnectorErrorCodes } from '../types/error.js';
|
||||
import { type SingleSignOnConnectorData, type SingleSignOn } from '../types/connector.js';
|
||||
import {
|
||||
SsoConnectorConfigErrorCodes,
|
||||
SsoConnectorError,
|
||||
SsoConnectorErrorCodes,
|
||||
} from '../types/error.js';
|
||||
import { basicOidcConnectorConfigGuard } from '../types/oidc.js';
|
||||
import { type ExtendedSocialUserInfo } from '../types/saml.js';
|
||||
import { type SingleSignOnConnectorSession } from '../types/session.js';
|
||||
|
||||
export class AzureOidcSsoConnector extends OidcSsoConnector {
|
||||
export const azureOidcConnectorConfigGuard = basicOidcConnectorConfigGuard.extend({
|
||||
trustUnverifiedEmail: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export class AzureOidcSsoConnector extends OidcConnector implements SingleSignOn {
|
||||
private readonly trustUnverifiedEmail: boolean;
|
||||
|
||||
constructor(readonly data: SingleSignOnConnectorData) {
|
||||
const parseConfigResult = azureOidcConnectorConfigGuard.safeParse(data.config);
|
||||
|
||||
if (!parseConfigResult.success) {
|
||||
throw new SsoConnectorError(SsoConnectorErrorCodes.InvalidConfig, {
|
||||
config: data.config,
|
||||
message: SsoConnectorConfigErrorCodes.InvalidConnectorConfig,
|
||||
error: parseConfigResult.error.flatten(),
|
||||
});
|
||||
}
|
||||
|
||||
const { trustUnverifiedEmail, ...oidcConfig } = parseConfigResult.data;
|
||||
|
||||
super(oidcConfig);
|
||||
|
||||
this.trustUnverifiedEmail = trustUnverifiedEmail ?? false;
|
||||
}
|
||||
|
||||
async getConfig() {
|
||||
return this.getOidcConfig();
|
||||
}
|
||||
|
||||
async getIssuer() {
|
||||
return this.issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the sign-in callback from the OIDC provider and return the user info
|
||||
*
|
||||
|
@ -67,10 +105,13 @@ export class AzureOidcSsoConnector extends OidcSsoConnector {
|
|||
id,
|
||||
...conditional(name && { name }),
|
||||
...conditional(picture && { avatar: picture }),
|
||||
...conditional(email && email_verified && { email }),
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
...conditional(email && (email_verified || this.trustUnverifiedEmail) && { email }),
|
||||
...conditional(phone && phone_verified && { phone }),
|
||||
...camelcaseKeys(rest),
|
||||
...conditional(email && !email_verified && { unverifiedEmail: email }),
|
||||
...conditional(
|
||||
email && !email_verified && !this.trustUnverifiedEmail && { unverifiedEmail: email }
|
||||
),
|
||||
...conditional(phone && !phone_verified && { unverifiedPhone: phone }),
|
||||
};
|
||||
}
|
||||
|
@ -106,6 +147,6 @@ export const azureOidcSsoConnectorFactory: SingleSignOnFactory<SsoProviderName.A
|
|||
name: {
|
||||
en: 'Microsoft Entra ID (OIDC)',
|
||||
},
|
||||
configGuard: basicOidcConnectorConfigGuard,
|
||||
configGuard: azureOidcConnectorConfigGuard,
|
||||
constructor: AzureOidcSsoConnector,
|
||||
};
|
||||
|
|
|
@ -2,7 +2,10 @@ import { type I18nPhrases } from '@logto/connector-kit';
|
|||
import { type SsoProviderType, type SsoProviderName } from '@logto/schemas';
|
||||
|
||||
import { type AzureAdSsoConnector } from '../AzureAdSsoConnector/index.js';
|
||||
import { type AzureOidcSsoConnector } from '../AzureOidcSsoConnector/index.js';
|
||||
import {
|
||||
type azureOidcConnectorConfigGuard,
|
||||
type AzureOidcSsoConnector,
|
||||
} from '../AzureOidcSsoConnector/index.js';
|
||||
import {
|
||||
type googleWorkspaceSsoConnectorConfigGuard,
|
||||
type GoogleWorkspaceSsoConnector,
|
||||
|
@ -29,7 +32,7 @@ export type SingleSignOnConnectorConfig = {
|
|||
[SsoProviderName.AZURE_AD]: typeof samlConnectorConfigGuard;
|
||||
[SsoProviderName.GOOGLE_WORKSPACE]: typeof googleWorkspaceSsoConnectorConfigGuard;
|
||||
[SsoProviderName.OKTA]: typeof basicOidcConnectorConfigGuard;
|
||||
[SsoProviderName.AZURE_AD_OIDC]: typeof basicOidcConnectorConfigGuard;
|
||||
[SsoProviderName.AZURE_AD_OIDC]: typeof azureOidcConnectorConfigGuard;
|
||||
};
|
||||
|
||||
export type SingleSignOnFactory<T extends SsoProviderName> = {
|
||||
|
|
|
@ -109,6 +109,11 @@ const enterprise_sso_details = {
|
|||
auth_params_tooltip:
|
||||
'Additional parameters to be passed in the authorization request. By default only (openid profile) scopes will be requested, you can specify additional scopes or a exclusive state value here. (e.g., { "scope": "organizations email", "state": "secret_state" }).',
|
||||
},
|
||||
trust_unverified_email: 'Trust unverified email',
|
||||
trust_unverified_email_label:
|
||||
'Always trust the unverified email addresses returned from the identity provider',
|
||||
trust_unverified_email_tip:
|
||||
'The Entra ID (OIDC) connector does not return the `email_verified` claim, meaning that email addresses from Azure are not guaranteed to be verified. By default, Logto will not sync unverified email addresses to the user profile. Enable this option only if you trust all the email addresses from the Entra ID directory.',
|
||||
};
|
||||
|
||||
export default Object.freeze(enterprise_sso_details);
|
||||
|
|
Loading…
Add table
Reference in a new issue