Skip to main content
Glama
auth-routes.test.ts9.1 kB
import t from 'tap'; import { expect } from 'chai'; import _ from 'lodash'; import { InstanceEnvType } from '@prisma/client'; import { request } from './helpers/supertest-agents'; import { testSuiteAfter, testSuiteBefore } from './helpers/test-suite-hooks'; import { mockAuth0TokenExchange } from './helpers/auth0-mocks'; import { decodeAuthToken, createSdfAuthToken } from '../src/services/auth.service'; import { createWorkspace } from "../src/services/workspaces.service"; import { createInvitedUser, getUserById } from "../src/services/users.service"; import { setManagementApiTokenForTesting } from '../src/services/auth0.service'; t.before(async () => { await testSuiteBefore(); await setManagementApiTokenForTesting(); }); t.teardown(testSuiteAfter); t.test('Auth routes', async () => { t.test('GET /auth/login - begin login flow', async (t) => { t.test('redirects to auth0', async () => { await request.get('/auth/login') .expect(302) .expect((res) => { // example redirect url // https://systeminit.auth0.com/authorize?response_type=code&client_id=XXX&redirect_uri=http%3A%2F%2Flocalhost%3A9001%2Fauth%2Flogin-callback&state=ZZZ&scope=openid+profile+email' const redirectUrl = res.headers.location; expect(redirectUrl.startsWith(`https://${process.env.AUTH0_DOMAIN}/authorize?`)).to.eq(true); }); }); }); t.test('GET /auth/login-callback - auth0 login callback', async (t) => { let validState: string; let validToken: string; const testEmail = `test-${+new Date()}@example.com`; t.test('(initiate login to get valid state)', async () => { await request.get('/auth/login') .expect(302) .expect((res) => { const redirectUrl = res.headers.location; // record the state value from our redirect url validState = redirectUrl.match(/state=([^&]+)/)[1]; }); }); t.test(`works with a valid state`, async () => { mockAuth0TokenExchange({ profileOverrides: { email: testEmail }, }); await request.get('/auth/login-callback') .query({ code: 'mockedbutvalidcode', state: validState, }) .expect(302) .expect(async (res) => { const setCookie = res.headers['set-cookie'][0]; const [,authToken] = setCookie.match(/si-auth=([^;]+); path=\/; httponly/); validToken = authToken; const authData = await decodeAuthToken(authToken); expect(authData.userId).to.be.ok; expect(res.headers.location).to.eq(`${process.env.AUTH_PORTAL_URL}/login-success`); }); }); t.test('verify cookie works to make authenticated request', async () => { await request.get('/whoami') .set('cookie', `si-auth=${validToken};`) .expectOk() .expectBody({ user: { email: testEmail, }, }); }); t.test('verify bad cookie fails for authenticated request', async () => { await request.get('/whoami') .set('cookie', `si-auth=${validToken}X;`) .expectError('Unauthorized'); }); t.test('verify sdf auth token succeeds for whoami', async () => { const { userId } = await decodeAuthToken(validToken); const user = await getUserById(userId); if (!user) { t.bailout("User Fetch has failed"); } const workspace = await createWorkspace( user!, InstanceEnvType.SI, "https://app.systeminit.com", `${user!.nickname}'s Testing Workspace`, false, "", ); const sdfToken = createSdfAuthToken({ userId, workspaceId: workspace.id, role: "web", }); await request.get('/whoami') .set('cookie', `si-auth=${sdfToken}`) .expectOk() .expectBody({ user: { email: testEmail, }, }); }); t.test(`fails if state is reused`, async () => { await request.get('/auth/login-callback') .query({ code: 'mockedbutvalidcode', state: validState, }) .expectError('Conflict'); }); _.each({ 'missing code': { code: undefined }, 'missing state': { state: undefined }, // non-string values are treated as strings since they come in querystring // currently we do no other validation of if the values look like they are in the right format }, (queryOverride, description) => { t.test(`bad params - ${description}`, async () => { await request.get('/auth/login-callback') .query({ code: 'somecode', state: 'somestate', ...queryOverride, }) .expectError('BadRequest'); }); }); }); t.test('signup/login behaviour', async (t) => { // helper used to run a few tests about signup vs login behaviour and conflicting id/email async function runAuthTest(options: { mockOptions?: Parameters<typeof mockAuth0TokenExchange>[0], expectUserData?: any }) { // begin flow to get state const loginRes = await request.get('/auth/login'); const validState = loginRes.headers.location.match(/state=([^&]+)/)[1]; // trigger auth0 callback and mock token/profile requests mockAuth0TokenExchange(options.mockOptions); const loginCallbackRes = await request.get('/auth/login-callback') .query({ code: 'mockedbutvalidcode', state: validState, }) .expect(302); const setCookie = loginCallbackRes.headers['set-cookie'][0]; const [,validToken] = setCookie.match(/si-auth=([^;]+); path=\/; httponly/); // use token to call whoami and get info about user const whoRes = await request.get('/whoami') .set('cookie', `si-auth=${validToken}`) .expectOk() .expectBody({ user: options?.expectUserData }); return { userId: whoRes.body.user.id, }; } const AUTH0_ID = 'google-oauth|123456'; const EMAIL_1 = 'new-user@systeminit.dev'; let originalUserId: string; t.test('can sign up a new account', async () => { const { userId } = await runAuthTest({ mockOptions: { profileOverrides: { user_id: AUTH0_ID, email: EMAIL_1, email_verified: false }, }, expectUserData: { auth0Id: AUTH0_ID, email: EMAIL_1 }, }); originalUserId = userId; }); t.test('logging in again with dupe email but different auth0 id will create new account', async () => { const { userId } = await runAuthTest({ mockOptions: { profileOverrides: { user_id: `${AUTH0_ID}9`, email: EMAIL_1 }, }, expectUserData: { email: EMAIL_1 }, }); expect(userId).not.to.eq(originalUserId); }); t.test('logging in again with existing auth0 id will not create a new account, but will update other EMPTY data', async () => { await runAuthTest({ mockOptions: { profileOverrides: { user_id: AUTH0_ID, email_verified: true }, }, expectUserData: { id: originalUserId, emailVerified: true }, }); }); t.test('logging in with a partially signed up user will not create a new account, but will update other data', async () => { const invitedUser = await createInvitedUser("partially+signedup@systeminit.com"); await runAuthTest({ mockOptions: { profileOverrides: { user_id: `${AUTH0_ID}999`, email: invitedUser.email }, }, expectUserData: { id: invitedUser.id, email: invitedUser.email }, }); }); t.test('GET /auth/logout - begin logout flow', async (t) => { t.test('redirects to auth0', async () => { await request.get('/auth/logout') .expect(302) .expect((res) => { // check redirects to auth0 logout, which will clear the cookie used for user <> auth0 requests const redirectUrl = res.headers.location; expect(redirectUrl.startsWith(`https://${process.env.AUTH0_DOMAIN}/v2/logout?`)).to.eq(true); // // check we cleared our cookie, which is used for user <> auth-api requests const setCookie = res.headers['set-cookie'][0]; expect(setCookie).to.eq('si-auth=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; httponly'); }); }); }); t.test('GET /auth/logout-callback - auth0 logout callback', async (t) => { t.test('redirects to auth portal', async () => { await request.get('/auth/logout-callback') .expect(302) .expect((res) => { const redirectUrl = res.headers.location; expect(redirectUrl).to.eq(`${process.env.AUTH_PORTAL_URL}/logout-success`); }); }); }); t.test('signup', async () => { /* - duplicate emails are allowed and do not merge - first login will sign up, second will just log in - check default workspace is automatically created */ }); }); });

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/systeminit/si'

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