Skip to main content
Glama
revoke.test.ts5.31 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { randomUUID } from 'crypto'; import express from 'express'; import request from 'supertest'; import { inviteUser } from '../admin/invite'; import { initApp, shutdownApp } from '../app'; import { loadTestConfig } from '../config/loader'; import { tryLogin } from '../oauth/utils'; import { withTestContext } from '../test.setup'; import { registerNew } from './register'; import { setPassword } from './setpassword'; const app = express(); describe('Revoke', () => { beforeAll(async () => { const config = await loadTestConfig(); await initApp(app, config); }); afterAll(async () => { await shutdownApp(); }); test('Unauthenticated', async () => { const res = await request(app).post('/auth/revoke').type('json').send({ loginId: randomUUID() }); expect(res.status).toBe(401); }); test('Revoke session', async () => { const email = `alex${randomUUID()}@example.com`; const password = randomUUID(); const { login, accessToken } = await withTestContext(() => registerNew({ firstName: 'Alexander', lastName: 'Hamilton', projectName: 'Hamilton Project', email, password, remoteAddress: '5.5.5.5', userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/107.0.0.0', }) ); // Get sessions 1st time // Should be 1 session const res1 = await request(app).get('/auth/me').set('Authorization', `Bearer ${accessToken}`); expect(res1.status).toBe(200); expect(res1.body).toBeDefined(); expect(res1.body.security.sessions).toHaveLength(1); expect(res1.body.security.sessions.find((s: any) => s.id === login.id)).toBeTruthy(); // Sign in again const login2 = await withTestContext(() => tryLogin({ authMethod: 'password', email, password, scope: 'openid', nonce: 'nonce', }) ); expect(login2).toBeDefined(); // Get sessions 2nd time // Should be 2 sessions const res2 = await request(app).get('/auth/me').set('Authorization', `Bearer ${accessToken}`); expect(res2.status).toBe(200); expect(res2.body).toBeDefined(); expect(res2.body.security.sessions).toHaveLength(2); expect(res2.body.security.sessions.find((s: any) => s.id === login.id)).toBeTruthy(); expect(res2.body.security.sessions.find((s: any) => s.id === login2.id)).toBeTruthy(); // Revoke the 2nd session const res3 = await request(app) .post('/auth/revoke') .set('Authorization', `Bearer ${accessToken}`) .type('json') .send({ loginId: login2.id }); expect(res3.status).toBe(200); // Get sessions 3rd time // Should be 1 session const res4 = await request(app).get('/auth/me').set('Authorization', `Bearer ${accessToken}`); expect(res4.status).toBe(200); expect(res4.body).toBeDefined(); expect(res4.body.security.sessions).toHaveLength(1); expect(res4.body.security.sessions.find((s: any) => s.id === login.id)).toBeTruthy(); expect(res4.body.security.sessions.find((s: any) => s.id === login2.id)).toBeUndefined(); // Try to revoke without a login // This should fail const res5 = await request(app) .post('/auth/revoke') .set('Authorization', `Bearer ${accessToken}`) .type('json') .send({}); expect(res5.status).toBe(400); // Try to revoke a random session // This should fail const res6 = await request(app) .post('/auth/revoke') .set('Authorization', `Bearer ${accessToken}`) .type('json') .send({ loginId: randomUUID() }); expect(res6.status).toBe(404); }); test('Different user', async () => { const aliceEmail = `alice${randomUUID()}@example.com`; const alicePassword = randomUUID(); const bobEmail = `bob${randomUUID()}@example.com`; const bobPassword = randomUUID(); const { bobLogin, accessToken } = await withTestContext(async () => { const { project, accessToken } = await registerNew({ firstName: 'Alice', lastName: 'Smith', projectName: 'Revoke Project', email: aliceEmail, password: alicePassword, remoteAddress: '5.5.5.5', userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/107.0.0.0', }); // Second, Alice invites Bob to the project const { user } = await inviteUser({ project, resourceType: 'Practitioner', firstName: 'Bob', lastName: 'Jones', email: bobEmail, }); // Set Bob password await setPassword(user, bobPassword); // Login as Bob const bobLogin = await tryLogin({ authMethod: 'password', email: bobEmail, password: bobPassword, scope: 'openid', nonce: 'nonce', }); expect(bobLogin).toBeDefined(); return { bobLogin, accessToken }; }); // Try to revoke Bob's session as Alice // This should fail const revokeResponse = await request(app) .post('/auth/revoke') .set('Authorization', `Bearer ${accessToken}`) .type('json') .send({ loginId: bobLogin.id }); expect(revokeResponse.status).toBe(404); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server