test: add comprehensive token validation tests for access and room member tokens
This commit is contained in:
parent
993681395c
commit
cdbb30fc2a
@ -18,8 +18,7 @@ describe('API Keys API Tests', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await startTestServer();
|
||||
const { accessToken } = await loginRootAdmin();
|
||||
rootAdminAccessToken = accessToken;
|
||||
({ accessToken: rootAdminAccessToken } = await loginRootAdmin());
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -12,8 +12,7 @@ describe('Authentication API Tests', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await startTestServer();
|
||||
const { accessToken } = await loginRootAdmin();
|
||||
rootAdminAccessToken = accessToken;
|
||||
({ accessToken: rootAdminAccessToken } = await loginRootAdmin());
|
||||
});
|
||||
|
||||
describe('Logout Tests', () => {
|
||||
|
||||
@ -72,7 +72,7 @@ describe('Authentication API Tests', () => {
|
||||
expect(response.body.message).toContain('Invalid refresh token');
|
||||
});
|
||||
|
||||
it('should fail when using roomMemberToken token instead of refresh token', async () => {
|
||||
it('should fail when using room member token instead of refresh token', async () => {
|
||||
const { moderatorToken } = await setupSingleRoom();
|
||||
const response = await request(app)
|
||||
.post(`${AUTH_PATH}/refresh`)
|
||||
|
||||
@ -0,0 +1,357 @@
|
||||
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 {
|
||||
createUser,
|
||||
deleteAllRooms,
|
||||
deleteAllUsers,
|
||||
deleteUser,
|
||||
loginUser,
|
||||
resetUserPassword,
|
||||
sleep,
|
||||
startTestServer
|
||||
} from '../../../helpers/request-helpers.js';
|
||||
import { setupSingleRoom, setupTestUsers, setupUser } from '../../../helpers/test-scenarios.js';
|
||||
import { TestUsers, UserData } from '../../../interfaces/scenarios.js';
|
||||
|
||||
const USERS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`;
|
||||
const ROOMS_PATH = `${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`;
|
||||
|
||||
describe('Token Validation Tests', () => {
|
||||
let app: Express;
|
||||
let testUsers: TestUsers;
|
||||
|
||||
let roomId: string;
|
||||
let roomMemberToken: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await startTestServer();
|
||||
testUsers = await setupTestUsers();
|
||||
|
||||
// Setup a room with a member token
|
||||
const roomData = await setupSingleRoom();
|
||||
roomId = roomData.room.roomId;
|
||||
roomMemberToken = roomData.moderatorToken;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteAllRooms();
|
||||
await deleteAllUsers();
|
||||
});
|
||||
|
||||
describe('Access Token Tests', () => {
|
||||
it('should succeed when providing valid access token', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${USERS_PATH}/me`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when providing valid access token through query param without bearer prefix', async () => {
|
||||
const accessTokenQuery = testUsers.user.accessToken.replace('Bearer ', '');
|
||||
const response = await request(app).get(`${USERS_PATH}/me`).query({ accessToken: accessTokenQuery });
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when admin accesses admin-only endpoint', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when access token is missing', async () => {
|
||||
const response = await request(app).get(`${USERS_PATH}/me`);
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Unauthorized');
|
||||
});
|
||||
|
||||
it('should fail when access token is invalid', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${USERS_PATH}/me`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, 'Bearer invalidtoken');
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid token');
|
||||
});
|
||||
|
||||
it('should fail when access token is expired', async () => {
|
||||
// Set short access token expiration
|
||||
const initialTokenExpiration = INTERNAL_CONFIG.ACCESS_TOKEN_EXPIRATION;
|
||||
INTERNAL_CONFIG.ACCESS_TOKEN_EXPIRATION = '1s';
|
||||
|
||||
// Create a user and get their access token
|
||||
const userData = await setupUser({
|
||||
userId: `user_${Date.now()}`,
|
||||
name: 'User',
|
||||
password: 'password123',
|
||||
role: MeetUserRole.USER
|
||||
});
|
||||
|
||||
await sleep('2s'); // Ensure the token is expired
|
||||
|
||||
// Restore original expiration after setup
|
||||
INTERNAL_CONFIG.ACCESS_TOKEN_EXPIRATION = initialTokenExpiration;
|
||||
|
||||
const response = await request(app)
|
||||
.get(`${USERS_PATH}/me`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, userData.accessToken);
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid token');
|
||||
});
|
||||
|
||||
it('should fail when using room member token instead of access token', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${USERS_PATH}/me`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, roomMemberToken);
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid token');
|
||||
});
|
||||
|
||||
it('should fail when using refresh token instead of access token', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${USERS_PATH}/me`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.refreshToken);
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
});
|
||||
|
||||
it('should fail when token subject (user) does not exist', async () => {
|
||||
// Create a user and get their access token
|
||||
const userData = await setupUser({
|
||||
userId: `user_${Date.now()}`,
|
||||
name: 'User',
|
||||
password: 'password123',
|
||||
role: MeetUserRole.USER
|
||||
});
|
||||
|
||||
// Delete the user to invalidate the token subject
|
||||
await deleteUser(userData.user.userId);
|
||||
|
||||
// Attempt to use the token
|
||||
const response = await request(app)
|
||||
.get(`${USERS_PATH}/me`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, userData.accessToken);
|
||||
expect(response.status).toBe(403);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid token subject');
|
||||
});
|
||||
|
||||
it('should fail when USER tries to access ADMIN-only endpoint', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken);
|
||||
expect(response.status).toBe(403);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Insufficient permissions');
|
||||
});
|
||||
|
||||
it('should fail when ROOM_MEMBER tries to access ADMIN and USER endpoint', async () => {
|
||||
const response = await request(app)
|
||||
.get(USERS_PATH)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken);
|
||||
expect(response.status).toBe(403);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Insufficient permissions');
|
||||
});
|
||||
|
||||
describe('Password Change Required Tests', () => {
|
||||
const resetPassword = 'NewPassword123';
|
||||
let userData: UserData;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Create a user and get their access token
|
||||
userData = await setupUser({
|
||||
userId: `user_${Date.now()}`,
|
||||
name: 'User',
|
||||
password: 'password123',
|
||||
role: MeetUserRole.USER
|
||||
});
|
||||
|
||||
// Reset user password to force password change
|
||||
await resetUserPassword(userData.user.userId, resetPassword);
|
||||
});
|
||||
|
||||
it('should succeed when accessing /me endpoint with mustChangePassword', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${USERS_PATH}/me`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, userData.accessToken);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when accessing /change-password endpoint with mustChangePassword', async () => {
|
||||
const response = await request(app)
|
||||
.post(`${USERS_PATH}/change-password`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, userData.accessToken)
|
||||
.send({
|
||||
currentPassword: resetPassword,
|
||||
newPassword: userData.password
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
// Reset user password again for futher tests
|
||||
await resetUserPassword(userData.user.userId, resetPassword);
|
||||
});
|
||||
|
||||
it('should fail when accessing other endpoints with mustChangePassword', async () => {
|
||||
// Try to create a room (requires USER role which this user has, but password change blocks it)
|
||||
const response = await request(app)
|
||||
.post(ROOMS_PATH)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, userData.accessToken)
|
||||
.send({});
|
||||
expect(response.status).toBe(403);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Password change required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Temporary Access Token Tests', () => {
|
||||
let userId: string;
|
||||
let accessTokenTmp: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Create a user (when created, this user is set to require password change)
|
||||
const response = await createUser({
|
||||
userId: 'temp_user',
|
||||
name: 'Temp User',
|
||||
password: 'InitialPassword1!',
|
||||
role: MeetUserRole.USER
|
||||
});
|
||||
userId = response.body.userId;
|
||||
|
||||
// Login to get temporary access token
|
||||
({ accessToken: accessTokenTmp } = await loginUser(userId, 'InitialPassword1!'));
|
||||
});
|
||||
|
||||
it('should succeed when accessing /me endpoint with temporary token', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${USERS_PATH}/me`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessTokenTmp);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when accessing /change-password endpoint with temporary token', async () => {
|
||||
const response = await request(app)
|
||||
.post(`${USERS_PATH}/change-password`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessTokenTmp)
|
||||
.send({
|
||||
currentPassword: 'InitialPassword1!',
|
||||
newPassword: 'FinalPassword123!'
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when accessing other endpoints with temporary token', async () => {
|
||||
const response = await request(app)
|
||||
.post(ROOMS_PATH)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessTokenTmp)
|
||||
.send({});
|
||||
expect(response.status).toBe(403);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Password change required');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Room Member Token Tests', () => {
|
||||
it('should succeed when providing valid room member token', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomId}`)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMemberToken);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed when providing valid room member token through query param without bearer prefix', async () => {
|
||||
const roomMemberTokenQuery = roomMemberToken.replace('Bearer ', '');
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomId}`)
|
||||
.query({ roomMemberToken: roomMemberTokenQuery });
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when room member token is missing', async () => {
|
||||
const response = await request(app).get(`${ROOMS_PATH}/${roomId}`);
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Unauthorized');
|
||||
});
|
||||
|
||||
it('should fail when room member token is invalid', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomId}`)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, 'Bearer invalidtoken');
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid token');
|
||||
});
|
||||
|
||||
it('should fail when room member token is expired', async () => {
|
||||
// Set short room member token expiration
|
||||
const initialTokenExpiration = INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION;
|
||||
INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION = '1s';
|
||||
|
||||
const roomData = await setupSingleRoom();
|
||||
await sleep('2s'); // Ensure the token is expired
|
||||
|
||||
// Restore original expiration after setup
|
||||
INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION = initialTokenExpiration;
|
||||
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomData.room.roomId}`)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.moderatorToken);
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid token');
|
||||
});
|
||||
|
||||
it('should fail when using access token instead of room member token', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomId}`)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, testUsers.user.accessToken);
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid token');
|
||||
});
|
||||
|
||||
it('should succeed when both room member token and user access token are valid', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomId}`)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMemberToken)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken);
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should succeed with room member token even when access token is invalid', async () => {
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomId}`)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMemberToken)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, 'Bearer invalidtoken');
|
||||
expect(response.status).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when room member token is expired, even if user access token is valid', async () => {
|
||||
// Set short room member token expiration
|
||||
const initialTokenExpiration = INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION;
|
||||
INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION = '1s';
|
||||
|
||||
const roomData = await setupSingleRoom();
|
||||
await sleep('2s'); // Ensure the token is expired
|
||||
|
||||
// Restore original expiration after setup
|
||||
INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION = initialTokenExpiration;
|
||||
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${roomData.room.roomId}`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.moderatorToken);
|
||||
expect(response.status).toBe(401);
|
||||
expect(response.body).toHaveProperty('message');
|
||||
expect(response.body.message).toContain('Invalid token');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -30,8 +30,7 @@ describe('Meeting API Security Tests', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await startTestServer();
|
||||
const { accessToken } = await loginRootAdmin();
|
||||
rootAdminAccessToken = accessToken;
|
||||
({ accessToken: rootAdminAccessToken } = await loginRootAdmin());
|
||||
|
||||
roomData = await setupSingleRoom();
|
||||
roomId = roomData.room.roomId;
|
||||
|
||||
@ -3,7 +3,7 @@ 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 { deleteAllRooms, deleteAllUsers, sleep, startTestServer } from '../../../helpers/request-helpers.js';
|
||||
import { deleteAllRooms, deleteAllUsers, startTestServer } from '../../../helpers/request-helpers.js';
|
||||
import { setupSingleRoom, setupTestUsers, setupTestUsersForRoom } from '../../../helpers/test-scenarios.js';
|
||||
import { RoomData, RoomTestUsers, TestUsers } from '../../../interfaces/scenarios.js';
|
||||
|
||||
@ -325,24 +325,6 @@ describe('Room API Security Tests', () => {
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, newRoomData.moderatorToken);
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when room member token is expired, even if user access token is valid', async () => {
|
||||
// Set short room member token expiration
|
||||
const initialTokenExpiration = INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION;
|
||||
INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION = '1s';
|
||||
|
||||
const newRoomData = await setupSingleRoom();
|
||||
await sleep('2s'); // Ensure the token is expired
|
||||
|
||||
// Restore original expiration after setup
|
||||
INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_EXPIRATION = initialTokenExpiration;
|
||||
|
||||
const response = await request(app)
|
||||
.get(`${ROOMS_PATH}/${newRoomData.room.roomId}`)
|
||||
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken)
|
||||
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, newRoomData.moderatorToken);
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delete Room Tests', () => {
|
||||
|
||||
@ -23,8 +23,7 @@ describe('User API Security Tests', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await startTestServer();
|
||||
const { accessToken } = await loginRootAdmin();
|
||||
rootAdminAccessToken = accessToken;
|
||||
({ accessToken: rootAdminAccessToken } = await loginRootAdmin());
|
||||
testUsers = await setupTestUsers();
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user