diff --git a/meet-ce/backend/tests/integration/api/auth/login.test.ts b/meet-ce/backend/tests/integration/api/auth/login.test.ts index 9a634430..6dbceed0 100644 --- a/meet-ce/backend/tests/integration/api/auth/login.test.ts +++ b/meet-ce/backend/tests/integration/api/auth/login.test.ts @@ -1,93 +1,166 @@ -import { beforeAll, describe, expect, it } from '@jest/globals'; +import { afterAll, beforeAll, describe, expect, it } from '@jest/globals'; +import { MeetUserRole } from '@openvidu-meet/typings'; import { Express } from 'express'; import request from 'supertest'; import { INTERNAL_CONFIG } from '../../../../src/config/internal-config.js'; +import { MEET_ENV } from '../../../../src/environment.js'; import { expectValidationError } from '../../../helpers/assertion-helpers.js'; -import { startTestServer } from '../../../helpers/request-helpers.js'; +import { createUser, deleteAllUsers, startTestServer } from '../../../helpers/request-helpers.js'; +import { setupTestUsers } from '../../../helpers/test-scenarios.js'; +import { TestUsers } from '../../../interfaces/scenarios.js'; const AUTH_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`; describe('Authentication API Tests', () => { let app: Express; + let testUsers: TestUsers; beforeAll(async () => { app = await startTestServer(); + testUsers = await setupTestUsers(); + }); + + afterAll(async () => { + await deleteAllUsers(); }); describe('Login Tests', () => { - it('should successfully login with valid root admin credentials', async () => { - const response = await request(app) - .post(`${AUTH_PATH}/login`) - .send({ - username: 'admin', - password: 'admin' - }) - .expect(200); - + it('should succeed with valid root admin credentials', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: MEET_ENV.INITIAL_ADMIN_USER, + password: MEET_ENV.INITIAL_ADMIN_PASSWORD + }); + expect(response.status).toBe(200); expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('logged in successfully'); // Check for access and refresh tokens expect(response.body).toHaveProperty('accessToken'); expect(response.body).toHaveProperty('refreshToken'); + expect(response.body.mustChangePassword).toBeUndefined(); }); - it('should return 404 for invalid credentials', async () => { - const response = await request(app) - .post(`${AUTH_PATH}/login`) - .send({ - username: 'admin', - password: 'invalidpassword' - }) - .expect(404); - + it('should succeed with valid ADMIN user credentials', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: testUsers.admin.user.userId, + password: testUsers.admin.password + }); + expect(response.status).toBe(200); expect(response.body).toHaveProperty('message'); - expect(response.body.message).toContain('Invalid username or password'); + expect(response.body.message).toContain('logged in successfully'); + + // Check for access and refresh tokens + expect(response.body).toHaveProperty('accessToken'); + expect(response.body).toHaveProperty('refreshToken'); + expect(response.body.mustChangePassword).toBeUndefined(); }); - it('should return 422 when username is missing', async () => { - const response = await request(app) - .post(`${AUTH_PATH}/login`) - .send({ - password: 'user' - }) - .expect(422); + it('should succeed with valid USER user credentials', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: testUsers.user.user.userId, + password: testUsers.user.password + }); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('logged in successfully'); - expectValidationError(response, 'username', 'Required'); + // Check for access and refresh tokens + expect(response.body).toHaveProperty('accessToken'); + expect(response.body).toHaveProperty('refreshToken'); + expect(response.body.mustChangePassword).toBeUndefined(); }); - it('should return 422 when password is missing', async () => { - const response = await request(app) - .post(`${AUTH_PATH}/login`) - .send({ - username: 'user' - }) - .expect(422); + it('should succeed with valid ROOM_MEMBER user credentials', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: testUsers.roomMember.user.userId, + password: testUsers.roomMember.password + }); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('logged in successfully'); + // Check for access and refresh tokens + expect(response.body).toHaveProperty('accessToken'); + expect(response.body).toHaveProperty('refreshToken'); + expect(response.body.mustChangePassword).toBeUndefined(); + }); + + it('should succeed with user requiring password change but return temporary token', async () => { + // Create a user (when created, this user is set to require password change) + const userId = 'user_12345'; + const password = 'initialPass123'; + await createUser({ + userId, + name: 'User', + password, + role: MeetUserRole.USER + }); + + // Attempt login + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId, + password + }); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('password change is required'); + + // Check for access token but NOT refresh token + expect(response.body).toHaveProperty('accessToken'); + expect(response.body).not.toHaveProperty('refreshToken'); + expect(response.body.mustChangePassword).toBe(true); + }); + + it('should fail with 404 when user ID does not exist', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: 'nonexistent_user', + password: testUsers.user.password + }); + expect(response.status).toBe(404); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('Invalid user ID or password'); + }); + + it('should fail with 404 when password is incorrect', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: testUsers.user.user.userId, + password: 'wrongpassword' + }); + expect(response.status).toBe(404); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('Invalid user ID or password'); + }); + }); + + describe('Login Validation Tests', () => { + it('should fail when userId is missing', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + password: 'adminpass' + }); + expectValidationError(response, 'userId', 'Required'); + }); + + it('should fail when password is missing', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: 'admin' + }); expectValidationError(response, 'password', 'Required'); }); - it('should return 422 when username is too short', async () => { - const response = await request(app) - .post(`${AUTH_PATH}/login`) - .send({ - username: 'usr', - password: 'user' - }) - .expect(422); - - expectValidationError(response, 'username', 'Username must be at least 4 characters long'); + it('should fail when userId is too short', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: 'admi', + password: 'adminpass' + }); + expectValidationError(response, 'userId', 'userId must be at least 5 characters long'); }); - it('should return 422 when password is too short', async () => { - const response = await request(app) - .post(`${AUTH_PATH}/login`) - .send({ - username: 'user', - password: 'usr' - }) - .expect(422); - - expectValidationError(response, 'password', 'Password must be at least 4 characters long'); + it('should fail when password is too short', async () => { + const response = await request(app).post(`${AUTH_PATH}/login`).send({ + userId: 'admin', + password: 'pass' + }); + expectValidationError(response, 'password', 'password must be at least 5 characters long'); }); }); }); diff --git a/meet-ce/backend/tests/integration/api/auth/logout.test.ts b/meet-ce/backend/tests/integration/api/auth/logout.test.ts index bdfca976..0f2049ea 100644 --- a/meet-ce/backend/tests/integration/api/auth/logout.test.ts +++ b/meet-ce/backend/tests/integration/api/auth/logout.test.ts @@ -2,21 +2,33 @@ import { beforeAll, describe, expect, it } from '@jest/globals'; import { Express } from 'express'; import request from 'supertest'; import { INTERNAL_CONFIG } from '../../../../src/config/internal-config.js'; -import { startTestServer } from '../../../helpers/request-helpers.js'; +import { loginRootAdmin, startTestServer } from '../../../helpers/request-helpers.js'; const AUTH_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`; describe('Authentication API Tests', () => { let app: Express; + let rootAdminAccessToken: string; beforeAll(async () => { app = await startTestServer(); + const { accessToken } = await loginRootAdmin(); + rootAdminAccessToken = accessToken; }); describe('Logout Tests', () => { - it('should successfully logout', async () => { - const response = await request(app).post(`${AUTH_PATH}/logout`).expect(200); + it('should succeed with authentication', async () => { + const response = await request(app) + .post(`${AUTH_PATH}/logout`) + .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, rootAdminAccessToken); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('Logout successful'); + }); + it('should succeed without authentication', async () => { + const response = await request(app).post(`${AUTH_PATH}/logout`); + expect(response.status).toBe(200); expect(response.body).toHaveProperty('message'); expect(response.body.message).toBe('Logout successful'); }); diff --git a/meet-ce/backend/tests/integration/api/auth/refresh-token.test.ts b/meet-ce/backend/tests/integration/api/auth/refresh-token.test.ts index 6ac678c6..ded4dfaf 100644 --- a/meet-ce/backend/tests/integration/api/auth/refresh-token.test.ts +++ b/meet-ce/backend/tests/integration/api/auth/refresh-token.test.ts @@ -1,8 +1,17 @@ import { beforeAll, describe, expect, it } from '@jest/globals'; +import { MeetUserRole } from '@openvidu-meet/typings'; import { Express } from 'express'; import request from 'supertest'; import { INTERNAL_CONFIG } from '../../../../src/config/internal-config.js'; -import { startTestServer } from '../../../helpers/request-helpers.js'; +import { + deleteAllRooms, + deleteAllUsers, + deleteUser, + loginRootAdmin, + resetUserPassword, + startTestServer +} from '../../../helpers/request-helpers.js'; +import { setupSingleRoom, setupUser } from '../../../helpers/test-scenarios.js'; const AUTH_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`; @@ -13,44 +22,106 @@ describe('Authentication API Tests', () => { app = await startTestServer(); }); + afterAll(async () => { + await deleteAllRooms(); + await deleteAllUsers(); + }); + describe('Refresh Token Tests', () => { - it('should successfully refresh access token with valid refresh token', async () => { - // First, login to get a valid refresh token - const loginResponse = await request(app) - .post(`${AUTH_PATH}/login`) - .send({ - username: 'admin', - password: 'admin' - }) - .expect(200); + let accessToken: string; + let refreshToken: string; - expect(loginResponse.body).toHaveProperty('refreshToken'); - const refreshToken = loginResponse.body.refreshToken; - - const response = await request(app) - .post(`${AUTH_PATH}/refresh`) - .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, `Bearer ${refreshToken}`) - .expect(200); - - expect(response.body).toHaveProperty('message'); - expect(response.body).toHaveProperty('accessToken'); + beforeAll(async () => { + // Login to get a valid refresh token + ({ accessToken, refreshToken } = await loginRootAdmin()); }); - it('should return 400 when no refresh token is provided', async () => { - const response = await request(app).post(`${AUTH_PATH}/refresh`).expect(400); + it('should succeed when providing valid refresh token', async () => { + const response = await request(app) + .post(`${AUTH_PATH}/refresh`) + .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, refreshToken); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('successfully refreshed'); + expect(response.body).toHaveProperty('accessToken'); + expect(response.body).not.toHaveProperty('refreshToken'); + }); + it('should fail when refresh token is missing', async () => { + const response = await request(app).post(`${AUTH_PATH}/refresh`); + expect(response.status).toBe(400); expect(response.body).toHaveProperty('message'); expect(response.body.message).toContain('No refresh token provided'); }); - it('should return 400 when refresh token is invalid', async () => { + it('should fail when refresh token is invalid', async () => { const response = await request(app) .post(`${AUTH_PATH}/refresh`) - .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, 'Bearer invalidtoken') - .expect(400); - + .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, 'Bearer invalidtoken'); + expect(response.status).toBe(400); expect(response.body).toHaveProperty('message'); expect(response.body.message).toContain('Invalid refresh token'); }); + + it('should fail when using access token instead of refresh token', async () => { + const response = await request(app) + .post(`${AUTH_PATH}/refresh`) + .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, accessToken); + expect(response.status).toBe(400); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('Invalid refresh token'); + }); + + it('should fail when using roomMemberToken token instead of refresh token', async () => { + const { moderatorToken } = await setupSingleRoom(); + const response = await request(app) + .post(`${AUTH_PATH}/refresh`) + .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, moderatorToken); + expect(response.status).toBe(400); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('Invalid refresh token'); + }); + + it('should fail when refresh token has invalid subject', async () => { + // Create a user and get their refresh token + const userData = await setupUser({ + userId: 'tempuser', + name: 'Temporary User', + password: 'TempPassword1!', + role: MeetUserRole.USER + }); + + // Delete the user to invalidate the refresh token subject + await deleteUser(userData.user.userId); + + // Attempt to refresh token + const response = await request(app) + .post(`${AUTH_PATH}/refresh`) + .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, userData.refreshToken); + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('Invalid token subject'); + }); + + it('should fail when user must change password', async () => { + // Create a user and get their refresh token + const userData = await setupUser({ + userId: 'testuser', + name: 'Test User', + password: 'TestPassword1!', + role: MeetUserRole.USER + }); + + // Reset user password to force password change + await resetUserPassword(userData.user.userId, 'NewPassword1!'); + + // Attempt to refresh token + const response = await request(app) + .post(`${AUTH_PATH}/refresh`) + .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, userData.refreshToken); + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message'); + expect(response.body.message).toContain('Password change required'); + }); }); });