Skip to content

Commit

Permalink
Fix auth flow tests to use Playwright properly, use JWT tokens correc…
Browse files Browse the repository at this point in the history
…tly with Users
  • Loading branch information
humphd committed Apr 13, 2021
1 parent b06165a commit 4be2c9c
Showing 1 changed file with 117 additions and 126 deletions.
243 changes: 117 additions & 126 deletions src/api/auth/test/e2e/auth-flows.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// NOTE: you need to run the auth and login services in docker for these to work
const { chromium } = require('playwright');
const { decode } = require('jsonwebtoken');
const { createServiceToken, hash } = require('@senecacdot/satellite');
const fetch = require('node-fetch');
Expand All @@ -8,10 +7,6 @@ const fetch = require('node-fetch');
// of allowed origins, to compare with assumptions in the tests below.
const { AUTH_URL, ALLOWED_APP_ORIGINS } = process.env;

let browser;
let context;
let page;

// We have 3 SSO user accounts in the login service (see config/simplesamlphp-users.php):
//
// | Username | Email | Password | Display Name |
Expand Down Expand Up @@ -138,144 +133,140 @@ const logout = async () => {
return getTokenAndState();
};

beforeAll(async () => {
// Use launch({ headless: false, slowMo: 500 }) as options to debug
browser = await chromium.launch();
});
afterAll(async () => {
await browser.close();
await cleanupTelescopeUsers();
});

beforeEach(async () => {
// We need some Telescope users created in our Users service, so we can try
// logging into both the Login service and Users. In case we somehow have
// these users created from some other test, remove then recreate.
await cleanupTelescopeUsers();
await createTelescopeUsers();

context = await browser.newContext();
page = await browser.newPage();
await page.goto(`http://localhost:8888/`);
});
afterEach(async () => {
await context.close();
await page.close();
});

it('should use the same origin in .env for ALLOWED_APP_ORIGINS that test cases use', () => {
const origins = ALLOWED_APP_ORIGINS.split(' ');
expect(origins).toContain('http://localhost:8888');
});

it('should use the same AUTH_URL as we have hard-coded in the test HTML', () => {
expect(AUTH_URL).toEqual('http://localhost/v1/auth');
});
describe('Authentication Flows', () => {
beforeEach(async () => {
await cleanupTelescopeUsers();
await createTelescopeUsers();

it('should have all expected Telescope users in Users service for test data accounts', () =>
Promise.all(
users.map((user) =>
fetch(`http://localhost/v1/users/${hash(user.email)}`).then((res) =>
expect(res.status).toEqual(200)
)
)
));
context = await browser.newContext();
page = await browser.newPage();
await page.goto(`http://localhost:8888/`);
});

it('Login flow preserves state param', async () => {
const { state } = await login('user2', 'user2pass');
// Expect the state to match what we sent originally (see index.html <a>)
expect(state).toEqual('abc123');
});
afterEach(async () => {
await cleanupTelescopeUsers();

it('Login flow issues JWT access token with with expected sub claim', async () => {
const { jwt } = await login('user2', 'user2pass');
// The sub claim should match our user's email
expect(typeof jwt === 'object').toBe(true);
expect(jwt.sub).toEqual(hash('[email protected]'));
});
await context.close();
await page.close();
});

it('Admin user can login, and has expected token payload', async () => {
const { jwt } = await login('user1', 'user1pass');
expect(jwt.sub).toEqual(hash('[email protected]'));
expect(jwt.email).toEqual('[email protected]');
expect(jwt.given_name).toEqual('Johannes');
expect(jwt.family_name).toEqual('Kepler');
expect(jwt.name).toEqual('Johannes Kepler');
expect(Array.isArray(jwt.roles)).toBe(true);
expect(jwt.roles.length).toBe(3);
expect(jwt.roles).toEqual(['seneca', 'telescope', 'admin']);
expect(jwt.picture).toEqual(
'https://avatars.githubusercontent.com/u/7242003?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4'
);
});
it('should use the same origin in .env for ALLOWED_APP_ORIGINS that test cases use', () => {
const origins = ALLOWED_APP_ORIGINS.split(' ');
expect(origins).toContain('http://localhost:8888');
});

it('Telescope user can login, and has expected token payload', async () => {
const { jwt } = await login('lippersheyh', 'telescope');
expect(jwt.sub).toEqual(hash('[email protected]'));
expect(jwt.email).toEqual('[email protected]');
expect(jwt.given_name).toEqual('Hans');
expect(jwt.family_name).toEqual('Lippershey');
expect(jwt.name).toEqual('Hans Lippershey');
expect(Array.isArray(jwt.roles)).toBe(true);
expect(jwt.roles.length).toBe(2);
expect(jwt.roles).toEqual(['seneca', 'telescope']);
expect(jwt.picture).toEqual(
'https://avatars.githubusercontent.com/u/33902374?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4'
);
});
it('should use the same AUTH_URL as we have hard-coded in the test HTML', () => {
expect(AUTH_URL).toEqual('http://localhost/v1/auth');
});

it('Seneca user can login, and has expected token payload', async () => {
const { jwt } = await login('user2', 'user2pass');
expect(jwt.sub).toEqual(hash('[email protected]'));
expect(jwt.email).toEqual('[email protected]');
expect(jwt.given_name).toEqual('Galileo');
expect(jwt.family_name).toEqual('Galilei');
expect(jwt.name).toEqual('Galileo Galilei');
expect(Array.isArray(jwt.roles)).toBe(true);
expect(jwt.roles.length).toBe(1);
expect(jwt.roles).toEqual(['seneca']);
expect(jwt.picture).toBe(undefined);
});
it('should have all expected Telescope users in Users service for test data accounts', () =>
Promise.all(
users.map((user) =>
fetch(`http://localhost/v1/users/${hash(user.email)}`, {
headers: {
Authorization: `bearer ${createServiceToken()}`,
'Content-Type': 'application/json',
},
}).then((res) => expect(res.status).toEqual(200))
)
));

it("Logging in twice doesn't require username and password again", async () => {
const firstLogin = await login('user1', 'user1pass');
expect(firstLogin.jwt.sub).toEqual(hash('[email protected]'));
it('Login flow preserves state param', async () => {
const { state } = await login('user2', 'user2pass');
// Expect the state to match what we sent originally (see index.html <a>)
expect(state).toEqual('abc123');
});

// Click login again, but we should get navigated back to this page right away
await Promise.all([
page.waitForNavigation({
url: /^http:\/\/localhost:\d+\/\?access_token=[^&]+&state=/,
waitUtil: 'load',
}),
page.click('#login'),
]);
it('Login flow issues JWT access token with with expected sub claim', async () => {
const { jwt } = await login('user2', 'user2pass');
// The sub claim should match our user's email
expect(typeof jwt === 'object').toBe(true);
expect(jwt.sub).toEqual(hash('[email protected]'));
});

// The sub claim should be the same as before (we're still logged in)
const secondLogin = getTokenAndState();
expect(secondLogin.jwt.sub).toEqual(firstLogin.jwt.sub);
});
it('Admin user can login, and has expected token payload', async () => {
const { jwt } = await login('user1', 'user1pass');
expect(jwt.sub).toEqual(hash('[email protected]'));
expect(jwt.email).toEqual('[email protected]');
expect(jwt.given_name).toEqual('Johannes');
expect(jwt.family_name).toEqual('Kepler');
expect(jwt.name).toEqual('Johannes Kepler');
expect(Array.isArray(jwt.roles)).toBe(true);
expect(jwt.roles.length).toBe(3);
expect(jwt.roles).toEqual(['seneca', 'telescope', 'admin']);
expect(jwt.picture).toEqual(
'https://avatars.githubusercontent.com/u/7242003?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4'
);
});

describe('Logout', () => {
it('Logout works after logging in', async () => {
const firstLogin = await login('user1', 'user1pass');
expect(firstLogin.jwt.sub).toEqual(hash('[email protected]'));
it('Telescope user can login, and has expected token payload', async () => {
const { jwt } = await login('lippersheyh', 'telescope');
expect(jwt.sub).toEqual(hash('[email protected]'));
expect(jwt.email).toEqual('[email protected]');
expect(jwt.given_name).toEqual('Hans');
expect(jwt.family_name).toEqual('Lippershey');
expect(jwt.name).toEqual('Hans Lippershey');
expect(Array.isArray(jwt.roles)).toBe(true);
expect(jwt.roles.length).toBe(2);
expect(jwt.roles).toEqual(['seneca', 'telescope']);
expect(jwt.picture).toEqual(
'https://avatars.githubusercontent.com/u/33902374?s=460&u=733c50a2f50ba297ed30f6b5921a511c2f43bfee&v=4'
);
});

// The sub claim should be the same as before (we're still logged in)
const logoutResult = await logout();
expect(logoutResult.state).toEqual('abc123');
expect(logoutResult.token).toEqual(undefined);
it('Seneca user can login, and has expected token payload', async () => {
const { jwt } = await login('user2', 'user2pass');
expect(jwt.sub).toEqual(hash('[email protected]'));
expect(jwt.email).toEqual('[email protected]');
expect(jwt.given_name).toEqual('Galileo');
expect(jwt.family_name).toEqual('Galilei');
expect(jwt.name).toEqual('Galileo Galilei');
expect(Array.isArray(jwt.roles)).toBe(true);
expect(jwt.roles.length).toBe(1);
expect(jwt.roles).toEqual(['seneca']);
expect(jwt.picture).toBe(undefined);
});

it('Logging in works after logout', async () => {
it("Logging in twice doesn't require username and password again", async () => {
const firstLogin = await login('user1', 'user1pass');
expect(firstLogin.jwt.sub).toEqual(hash('[email protected]'));

// Click login again, but we should get navigated back to this page right away
await Promise.all([
page.waitForNavigation({
url: /^http:\/\/localhost:\d+\/\?access_token=[^&]+&state=/,
waitUtil: 'load',
}),
page.click('#login'),
]);

// The sub claim should be the same as before (we're still logged in)
const logoutResult = await logout();
expect(logoutResult.state).toEqual('abc123');
expect(logoutResult.token).toEqual(undefined);
const secondLogin = getTokenAndState();
expect(secondLogin.jwt.sub).toEqual(firstLogin.jwt.sub);
});

const secondLogin = await login('user2', 'user2pass');
expect(secondLogin.jwt.sub).toEqual(hash('[email protected]'));
describe('Logout', () => {
it('Logout works after logging in', async () => {
const firstLogin = await login('user1', 'user1pass');
expect(firstLogin.jwt.sub).toEqual(hash('[email protected]'));

// The sub claim should be the same as before (we're still logged in)
const logoutResult = await logout();
expect(logoutResult.state).toEqual('abc123');
expect(logoutResult.token).toEqual(undefined);
});

it('Logging in works after logout', async () => {
const firstLogin = await login('user1', 'user1pass');
expect(firstLogin.jwt.sub).toEqual(hash('[email protected]'));

// The sub claim should be the same as before (we're still logged in)
const logoutResult = await logout();
expect(logoutResult.state).toEqual('abc123');
expect(logoutResult.token).toEqual(undefined);

const secondLogin = await login('user2', 'user2pass');
expect(secondLogin.jwt.sub).toEqual(hash('[email protected]'));
});
});
});

0 comments on commit 4be2c9c

Please sign in to comment.