mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
test: add sad paths for social interaction flow (#4300)
This commit is contained in:
parent
2660a1e132
commit
dfde117b6d
2 changed files with 367 additions and 0 deletions
|
@ -0,0 +1,367 @@
|
|||
import { ConnectorType } from '@logto/connector-kit';
|
||||
import { InteractionEvent } from '@logto/schemas';
|
||||
import { type Optional } from '@silverhand/essentials';
|
||||
|
||||
import {
|
||||
mockEmailConnectorId,
|
||||
mockSmsConnectorId,
|
||||
mockSocialConnectorId,
|
||||
} from '#src/__mocks__/connectors-mock.js';
|
||||
import { suspendUser } from '#src/api/admin-user.js';
|
||||
import {
|
||||
createSocialAuthorizationUri,
|
||||
patchInteractionIdentifiers,
|
||||
putInteraction,
|
||||
putInteractionEvent,
|
||||
putInteractionProfile,
|
||||
} from '#src/api/interaction.js';
|
||||
import { initClient, logoutClient, processSession } from '#src/helpers/client.js';
|
||||
import {
|
||||
clearConnectorsByTypes,
|
||||
setSocialConnector,
|
||||
setEmailConnector,
|
||||
setSmsConnector,
|
||||
} from '#src/helpers/connector.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
|
||||
import { generateEmail, generatePhone, generateUserId } from '#src/utils.js';
|
||||
|
||||
const state = 'foo_state';
|
||||
const redirectUri = 'http://foo.dev/callback';
|
||||
const code = 'auth_code_foo';
|
||||
|
||||
describe('Social identifier interaction sad path', () => {
|
||||
const connectorIdMap = new Map<string, string>();
|
||||
const userSocialId = generateUserId();
|
||||
// eslint-disable-next-line @silverhand/fp/no-let
|
||||
let createdUserId: Optional<string>;
|
||||
|
||||
beforeAll(async () => {
|
||||
await enableAllPasswordSignInMethods();
|
||||
await clearConnectorsByTypes([ConnectorType.Social, ConnectorType.Email, ConnectorType.Sms]);
|
||||
const { id: socialConnectorId } = await setSocialConnector();
|
||||
const { id: emailConnectorId } = await setEmailConnector();
|
||||
const { id: smsConnectorId } = await setSmsConnector();
|
||||
|
||||
connectorIdMap.set(mockSocialConnectorId, socialConnectorId);
|
||||
connectorIdMap.set(mockEmailConnectorId, emailConnectorId);
|
||||
connectorIdMap.set(mockSmsConnectorId, smsConnectorId);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await clearConnectorsByTypes([ConnectorType.Social, ConnectorType.Email, ConnectorType.Sms]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Note: As currently we can not prepare a social identities through admin api, register a user manually.
|
||||
*/
|
||||
it('register with social', async () => {
|
||||
const client = await initClient();
|
||||
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
|
||||
|
||||
await client.successSend(patchInteractionIdentifiers, {
|
||||
connectorId,
|
||||
connectorData: { state, redirectUri, code, userId: userSocialId },
|
||||
});
|
||||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
await client.successSend(putInteractionProfile, { connectorId });
|
||||
|
||||
const { redirectTo } = await client.submitInteraction();
|
||||
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||
createdUserId = await processSession(client, redirectTo);
|
||||
await logoutClient(client);
|
||||
});
|
||||
|
||||
it('Should fail to sign-in if state is an empty string', async () => {
|
||||
const client = await initClient();
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
client.send(createSocialAuthorizationUri, {
|
||||
state: '',
|
||||
redirectUri,
|
||||
connectorId,
|
||||
}),
|
||||
{
|
||||
code: 'session.insufficient_info',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should fail to sign-in if an invalid redirectUri is provided', async () => {
|
||||
const client = await initClient();
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
client.send(createSocialAuthorizationUri, {
|
||||
state,
|
||||
redirectUri: 'invalid_uri',
|
||||
connectorId,
|
||||
}),
|
||||
{
|
||||
code: 'guard.invalid_input',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should fail to sign-in if the related connector is not a social connector', async () => {
|
||||
const client = await initClient();
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
// Use email connector id
|
||||
await expectRejects(
|
||||
client.send(createSocialAuthorizationUri, {
|
||||
state,
|
||||
redirectUri,
|
||||
connectorId: connectorIdMap.get(mockEmailConnectorId) ?? '',
|
||||
}),
|
||||
{
|
||||
code: 'connector.unexpected_type',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
|
||||
// Use sms connector id
|
||||
await expectRejects(
|
||||
client.send(createSocialAuthorizationUri, {
|
||||
state,
|
||||
redirectUri,
|
||||
connectorId: connectorIdMap.get(mockSmsConnectorId) ?? '',
|
||||
}),
|
||||
{
|
||||
code: 'connector.unexpected_type',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should fail to sign-in if the related connector is not found', async () => {
|
||||
const client = await initClient();
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await expectRejects(
|
||||
client.send(createSocialAuthorizationUri, {
|
||||
state,
|
||||
redirectUri,
|
||||
connectorId: 'invalid_connector_id',
|
||||
}),
|
||||
{
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should fail to update interaction identifiers if related connector id is incorrect', async () => {
|
||||
const client = await initClient();
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
|
||||
|
||||
await expectRejects(
|
||||
client.send(patchInteractionIdentifiers, {
|
||||
connectorId: connectorIdMap.get(mockEmailConnectorId) ?? '',
|
||||
connectorData: { state, redirectUri, code, socialUserId: userSocialId },
|
||||
}),
|
||||
{
|
||||
code: 'session.invalid_connector_id',
|
||||
statusCode: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should fail to update interaction identifiers if related connector id is not exist', async () => {
|
||||
const client = await initClient();
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
|
||||
|
||||
await expectRejects(
|
||||
client.send(patchInteractionIdentifiers, {
|
||||
connectorId: 'non_exist_connector_id',
|
||||
connectorData: { state, redirectUri, code, socialUserId: userSocialId },
|
||||
}),
|
||||
{
|
||||
code: 'session.invalid_connector_id',
|
||||
statusCode: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should fail to update verified identifier if the social interaction is not verified', async () => {
|
||||
const client = await initClient();
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
|
||||
|
||||
// Email
|
||||
await expectRejects(
|
||||
client.send(patchInteractionIdentifiers, {
|
||||
connectorId,
|
||||
email: generateEmail(),
|
||||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
|
||||
// Phone
|
||||
await expectRejects(
|
||||
client.send(patchInteractionIdentifiers, {
|
||||
connectorId,
|
||||
phone: generatePhone(),
|
||||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should fail to update verified identifiers if related identifiers are not belong to the user', async () => {
|
||||
const client = await initClient();
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
|
||||
|
||||
// Verify social interaction
|
||||
await client.successSend(patchInteractionIdentifiers, {
|
||||
connectorId,
|
||||
connectorData: { state, redirectUri, code, userId: userSocialId },
|
||||
});
|
||||
|
||||
// Email
|
||||
await expectRejects(
|
||||
client.send(patchInteractionIdentifiers, {
|
||||
connectorId,
|
||||
email: generateEmail(),
|
||||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
|
||||
// Phone
|
||||
await expectRejects(
|
||||
client.send(patchInteractionIdentifiers, {
|
||||
connectorId,
|
||||
phone: generatePhone(),
|
||||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should fail to sign in with social if related user is suspended', async () => {
|
||||
const userId = createdUserId ?? '';
|
||||
await suspendUser(userId, true);
|
||||
|
||||
const client = await initClient();
|
||||
const connectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await client.successSend(createSocialAuthorizationUri, { state, redirectUri, connectorId });
|
||||
|
||||
await client.successSend(patchInteractionIdentifiers, {
|
||||
connectorId,
|
||||
connectorData: { state, redirectUri, code, userId: userSocialId },
|
||||
});
|
||||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.suspended',
|
||||
statusCode: 401,
|
||||
});
|
||||
|
||||
// Reset
|
||||
await suspendUser(userId, false);
|
||||
});
|
||||
|
||||
it('Should fail to sync profile if related connector id is incorrect on social user registration', async () => {
|
||||
const newUserSocialId = generateUserId();
|
||||
const client = await initClient();
|
||||
const socialEmail = generateEmail();
|
||||
|
||||
const socialConnectorId = connectorIdMap.get(mockSocialConnectorId) ?? '';
|
||||
const smsConnectorId = connectorIdMap.get(mockSmsConnectorId) ?? '';
|
||||
|
||||
await client.successSend(putInteraction, {
|
||||
event: InteractionEvent.SignIn,
|
||||
});
|
||||
|
||||
await client.successSend(createSocialAuthorizationUri, {
|
||||
state,
|
||||
redirectUri,
|
||||
connectorId: socialConnectorId,
|
||||
});
|
||||
|
||||
await client.successSend(patchInteractionIdentifiers, {
|
||||
connectorId: socialConnectorId,
|
||||
connectorData: { state, redirectUri, code, userId: newUserSocialId, email: socialEmail },
|
||||
});
|
||||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
||||
await client.successSend(putInteractionProfile, {
|
||||
connectorId: smsConnectorId,
|
||||
});
|
||||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 404,
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue