0
Fork 0
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:
simeng-li 2023-11-17 15:58:31 +08:00 committed by GitHub
parent e68637f12d
commit 6c8bd20fe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 2 deletions

View file

@ -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;
}

View file

@ -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,

View file

@ -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 };
};