mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(core): add social returned email sso guard (#4889)
add social returned email sso guard
This commit is contained in:
parent
e68637f12d
commit
6c8bd20fe0
3 changed files with 33 additions and 2 deletions
|
@ -1,3 +1,4 @@
|
|||
import { type SocialUserInfo } from '@logto/connector-kit';
|
||||
import { type IdentifierPayload } from '@logto/schemas';
|
||||
import { type Context } from 'koa';
|
||||
import type Provider from 'oidc-provider';
|
||||
|
@ -15,10 +16,14 @@ import assertThat from '#src/utils/assert-that.js';
|
|||
// Guard the SSO only email identifier
|
||||
export const verifySsoOnlyEmailIdentifier = async (
|
||||
{ getAvailableSsoConnectors }: SsoConnectorLibrary,
|
||||
identifier: IdentifierPayload
|
||||
identifier: IdentifierPayload | SocialUserInfo
|
||||
) => {
|
||||
// TODO: @simeng-li remove the dev features check when the SSO feature is released
|
||||
if (!('email' in identifier) || !EnvSet.values.isDevFeaturesEnabled) {
|
||||
if (!EnvSet.values.isDevFeaturesEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!('email' in identifier) || !identifier.email) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@ await mockEsmWithActual('../utils/interaction.js', () => ({
|
|||
storeInteractionResult: jest.fn(),
|
||||
}));
|
||||
|
||||
const { verifySsoOnlyEmailIdentifier } = mockEsm('../utils/single-sign-on-guard.js', () => ({
|
||||
verifySsoOnlyEmailIdentifier: jest.fn(),
|
||||
}));
|
||||
|
||||
const { verifyIdentifierByVerificationCode } = mockEsm(
|
||||
'../utils/verification-code-validation.js',
|
||||
() => ({
|
||||
|
@ -178,6 +182,22 @@ describe('identifier verification', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should throw if social email is SSO only', async () => {
|
||||
const identifier = { connectorId: 'logto', connectorData: {} };
|
||||
const useInfo = { id: 'foo', email: 'foo@example.com' };
|
||||
|
||||
verifySsoOnlyEmailIdentifier.mockRejectedValueOnce(new RequestError('session.sso_enabled'));
|
||||
verifySocialIdentity.mockResolvedValueOnce(useInfo);
|
||||
|
||||
await expect(async () =>
|
||||
identifierPayloadVerification(baseCtx, tenant, identifier, interactionStorage)
|
||||
).rejects.toMatchError(new RequestError('session.sso_enabled'));
|
||||
|
||||
expect(verifySocialIdentity).toBeCalledWith(identifier, baseCtx, tenant);
|
||||
expect(verifySsoOnlyEmailIdentifier).toBeCalledWith(tenant.libraries.ssoConnectors, useInfo);
|
||||
expect(findUserByIdentifier).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('verified social email', async () => {
|
||||
const interactionRecord: AnonymousInteractionResult = {
|
||||
event: InteractionEvent.SignIn,
|
||||
|
|
|
@ -34,6 +34,7 @@ import {
|
|||
isPasswordIdentifier,
|
||||
isSocialIdentifier,
|
||||
} from '../utils/index.js';
|
||||
import { verifySsoOnlyEmailIdentifier } from '../utils/single-sign-on-guard.js';
|
||||
import { verifySocialIdentity } from '../utils/social-verification.js';
|
||||
import { verifyIdentifierByVerificationCode } from '../utils/verification-code-validation.js';
|
||||
|
||||
|
@ -90,6 +91,11 @@ const verifySocialIdentifier = async (
|
|||
): Promise<SocialIdentifier> => {
|
||||
const userInfo = await verifySocialIdentity(identifier, ctx, tenant);
|
||||
|
||||
const {
|
||||
libraries: { ssoConnectors },
|
||||
} = tenant;
|
||||
await verifySsoOnlyEmailIdentifier(ssoConnectors, userInfo);
|
||||
|
||||
return { key: 'social', connectorId: identifier.connectorId, userInfo };
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue