0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-23 20:33:16 -05:00
logto/packages/integration-tests/tests/api/session.test.ts
2022-11-10 16:37:12 +08:00

332 lines
8.9 KiB
TypeScript

import { SignInIdentifier, SignUpIdentifier } from '@logto/schemas';
import { adminConsoleApplicationId } from '@logto/schemas/lib/seeds';
import { assert } from '@silverhand/essentials';
import {
mockEmailConnectorId,
mockEmailConnectorConfig,
mockSmsConnectorId,
mockSmsConnectorConfig,
} from '@/__mocks__/connectors-mock';
import {
sendRegisterUserWithEmailPasscode,
verifyRegisterUserWithEmailPasscode,
sendSignInUserWithEmailPasscode,
verifySignInUserWithEmailPasscode,
sendRegisterUserWithSmsPasscode,
verifyRegisterUserWithSmsPasscode,
sendSignInUserWithSmsPasscode,
verifySignInUserWithSmsPasscode,
disableConnector,
signInWithPassword,
createUser,
} from '@/api';
import MockClient from '@/client';
import {
registerNewUser,
signIn,
setUpConnector,
readPasscode,
createUserByAdmin,
setSignUpIdentifier,
setSignInMethod,
} from '@/helpers';
import { generateUsername, generatePassword, generateEmail, generatePhone } from '@/utils';
describe('username and password flow', () => {
const username = generateUsername();
const password = generatePassword();
beforeAll(async () => {
await setSignUpIdentifier(SignUpIdentifier.Username, true);
await setSignInMethod([
{
identifier: SignInIdentifier.Username,
password: true,
verificationCode: false,
isPasswordPrimary: false,
},
]);
});
it('register and sign in with username & password', async () => {
await expect(registerNewUser(username, password)).resolves.not.toThrow();
await expect(signIn({ username, password })).resolves.not.toThrow();
});
});
describe('email and password flow', () => {
const email = generateEmail();
const [localPart, domain] = email.split('@');
const password = generatePassword();
assert(localPart && domain, new Error('Email address local part or domain is empty'));
beforeAll(async () => {
await setUpConnector(mockEmailConnectorId, mockEmailConnectorConfig);
await setSignUpIdentifier(SignUpIdentifier.Email, true);
await setSignInMethod([
{
identifier: SignInIdentifier.Email,
password: true,
verificationCode: false,
isPasswordPrimary: false,
},
]);
});
it('can sign in with email & password', async () => {
await createUser({ password, primaryEmail: email, username: generateUsername(), name: 'John' });
await expect(
Promise.all([
signIn({ email, password }),
signIn({ email: localPart.toUpperCase() + '@' + domain, password }),
signIn({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
email: localPart[0]! + localPart.toUpperCase().slice(1) + '@' + domain,
password,
}),
])
).resolves.not.toThrow();
});
});
describe('email passwordless flow', () => {
beforeAll(async () => {
await setUpConnector(mockEmailConnectorId, mockEmailConnectorConfig);
await setSignUpIdentifier(SignUpIdentifier.Email, false);
await setSignInMethod([
{
identifier: SignInIdentifier.Username,
password: true,
verificationCode: false,
isPasswordPrimary: true,
},
{
identifier: SignInIdentifier.Email,
password: false,
verificationCode: true,
isPasswordPrimary: false,
},
]);
});
// Since we can not create a email register user throw admin. Have to run the register then sign-in concurrently.
const email = generateEmail();
it('register with email', async () => {
const client = new MockClient();
await client.initSession();
assert(client.interactionCookie, new Error('Session not found'));
await expect(
sendRegisterUserWithEmailPasscode(email, client.interactionCookie)
).resolves.not.toThrow();
const passcodeRecord = await readPasscode();
expect(passcodeRecord).toMatchObject({
address: email,
type: 'Register',
});
const { code } = passcodeRecord;
const { redirectTo } = await verifyRegisterUserWithEmailPasscode(
email,
code,
client.interactionCookie
);
await client.processSession(redirectTo);
await expect(client.isAuthenticated()).resolves.toBe(true);
});
it('sign-in with email', async () => {
const client = new MockClient();
await client.initSession();
assert(client.interactionCookie, new Error('Session not found'));
await expect(
sendSignInUserWithEmailPasscode(email, client.interactionCookie)
).resolves.not.toThrow();
const passcodeRecord = await readPasscode();
expect(passcodeRecord).toMatchObject({
address: email,
type: 'SignIn',
});
const { code } = passcodeRecord;
const { redirectTo } = await verifySignInUserWithEmailPasscode(
email,
code,
client.interactionCookie
);
await client.processSession(redirectTo);
await expect(client.isAuthenticated()).resolves.toBe(true);
});
afterAll(async () => {
void disableConnector(mockEmailConnectorId);
});
});
describe('sms passwordless flow', () => {
beforeAll(async () => {
await setUpConnector(mockSmsConnectorId, mockSmsConnectorConfig);
await setSignUpIdentifier(SignUpIdentifier.Sms, false);
await setSignInMethod([
{
identifier: SignInIdentifier.Username,
password: true,
verificationCode: false,
isPasswordPrimary: true,
},
{
identifier: SignInIdentifier.Sms,
password: false,
verificationCode: true,
isPasswordPrimary: false,
},
]);
});
// Since we can not create a sms register user throw admin. Have to run the register then sign-in concurrently.
const phone = generatePhone();
it('register with sms', async () => {
const client = new MockClient();
await client.initSession();
assert(client.interactionCookie, new Error('Session not found'));
await expect(
sendRegisterUserWithSmsPasscode(phone, client.interactionCookie)
).resolves.not.toThrow();
const passcodeRecord = await readPasscode();
expect(passcodeRecord).toMatchObject({
phone,
type: 'Register',
});
const { code } = passcodeRecord;
const { redirectTo } = await verifyRegisterUserWithSmsPasscode(
phone,
code,
client.interactionCookie
);
await client.processSession(redirectTo);
await expect(client.isAuthenticated()).resolves.toBe(true);
});
it('sign-in with sms', async () => {
const client = new MockClient();
await client.initSession();
assert(client.interactionCookie, new Error('Session not found'));
await expect(
sendSignInUserWithSmsPasscode(phone, client.interactionCookie)
).resolves.not.toThrow();
const passcodeRecord = await readPasscode();
expect(passcodeRecord).toMatchObject({
phone,
type: 'SignIn',
});
const { code } = passcodeRecord;
const { redirectTo } = await verifySignInUserWithSmsPasscode(
phone,
code,
client.interactionCookie
);
await client.processSession(redirectTo);
await expect(client.isAuthenticated()).resolves.toBe(true);
});
afterAll(async () => {
void disableConnector(mockSmsConnectorId);
});
});
describe('sign-in and sign-out', () => {
const username = generateUsername();
const password = generatePassword();
beforeAll(async () => {
await createUserByAdmin(username, password);
await setSignUpIdentifier(SignUpIdentifier.Username);
});
it('verify sign-in and then sign-out', async () => {
const client = new MockClient();
await client.initSession();
assert(client.interactionCookie, new Error('Session not found'));
const { redirectTo } = await signInWithPassword({
username,
password,
interactionCookie: client.interactionCookie,
});
await client.processSession(redirectTo);
await expect(client.isAuthenticated()).resolves.toBe(true);
await client.signOut();
await expect(client.isAuthenticated()).resolves.toBe(false);
});
});
describe('sign-in to demo app and revisit Admin Console', () => {
const username = generateUsername();
const password = generatePassword();
beforeAll(async () => {
await createUserByAdmin(username, password);
});
it('should throw in Admin Console consent step if a logged in user does not have admin role', async () => {
const client = new MockClient();
await client.initSession();
assert(client.interactionCookie, new Error('Session not found'));
const { redirectTo } = await signInWithPassword({
username,
password,
interactionCookie: client.interactionCookie,
});
await client.processSession(redirectTo);
await expect(client.isAuthenticated()).resolves.toBe(true);
const { interactionCookie } = client;
const acClient = new MockClient({ appId: adminConsoleApplicationId });
acClient.assignCookie(interactionCookie);
await expect(acClient.initSession()).rejects.toThrow();
});
});