Skip to content

Commit

Permalink
Add allowlists for demo apps
Browse files Browse the repository at this point in the history
  • Loading branch information
danielnaab committed Aug 20, 2024
1 parent 10b9c83 commit e8bd32c
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 4 deletions.
13 changes: 13 additions & 0 deletions apps/server-doj/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,18 @@ export const createCustomServer = async (ctx: {
'urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:tts-10x-atj-dev-server-doj',
//clientSecret: '', // secrets.loginGovClientSecret,
},
isUserAuthorized: async (email: string) => {
return [
// 10x team members
'daniel.naab@gsa.gov',
'jim.moffet@gsa.gov',
'ethan.gardner@gsa.gov',
'natasha.pierre-louis@gsa.gov',
'emily.lordahl@gsa.gov',
// DOJ test users
'deserene.h.worsley@usdoj.gov',
'jordan.pendergrass@usdoj.gov',
].includes(email);
},
});
};
9 changes: 9 additions & 0 deletions apps/server-kansas/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,14 @@ export const createCustomServer = async (ctx: {
'urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:tts-10x-atj-dev-server-doj',
//clientSecret: '', // secrets.loginGovClientSecret,
},
isUserAuthorized: async (email: string) => {
return [
'daniel.naab@gsa.gov',
'jim.moffet@gsa.gov',
'ethan.gardner@gsa.gov',
'natasha.pierre-louis@gsa.gov',
'emily.lordahl@gsa.gov',
].includes(email);
},
});
};
3 changes: 2 additions & 1 deletion packages/auth/src/context/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export class BaseAuthContext implements AuthContext {
public provider: LoginGov,
public getCookie: (name: string) => string | undefined,
public setCookie: (cookie: Cookie) => void,
public setUserSession: (userSession: UserSession) => void
public setUserSession: (userSession: UserSession) => void,
public isUserAuthorized: (email: string) => Promise<boolean>
) {}

async getLucia() {
Expand Down
8 changes: 6 additions & 2 deletions packages/auth/src/context/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ type Options = {
getCookie: (name: string) => string | undefined;
setCookie: (cookie: Cookie) => void;
setUserSession: (userSession: UserSession) => void;
isUserAuthorized: (email: string) => Promise<boolean>;
};

export const createTestAuthContext = async (opts?: Partial<Options>) => {
const options: Options = {
getCookie: opts?.getCookie || vi.fn(),
setCookie: opts?.setCookie || vi.fn(),
setUserSession: opts?.setUserSession || vi.fn(),
isUserAuthorized: opts?.isUserAuthorized || vi.fn(async () => true),
};
const dbContext = await createInMemoryDatabaseContext();
const database = createDatabaseGateway(dbContext);
Expand All @@ -36,7 +38,8 @@ export const createTestAuthContext = async (opts?: Partial<Options>) => {
}),
options.getCookie,
options.setCookie,
options.setUserSession
options.setUserSession,
options.isUserAuthorized
);
};

Expand All @@ -48,7 +51,8 @@ export class TestAuthContext implements AuthContext {
public provider: LoginGov,
public getCookie: (name: string) => string | undefined,
public setCookie: (cookie: Cookie) => void,
public setUserSession: (userSession: UserSession) => void
public setUserSession: (userSession: UserSession) => void,
public isUserAuthorized: (email: string) => Promise<boolean>
) {}

async getLucia() {
Expand Down
1 change: 1 addition & 0 deletions packages/auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ export type AuthContext = {
setCookie: (cookie: Cookie) => void;
setUserSession: (userSession: UserSession) => void;
getLucia: () => Promise<Lucia>;
isUserAuthorized: (email: string) => Promise<boolean>;
};
26 changes: 26 additions & 0 deletions packages/auth/src/services/process-provider-callback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
import { processProviderCallback } from './process-provider-callback';
import { createTestAuthContext } from '../context/test';
import { AuthContext } from '..';
import { success } from '@atj/common';

describe('processProviderCallback', () => {
let ctx: AuthContext;
Expand Down Expand Up @@ -101,4 +102,29 @@ describe('processProviderCallback', () => {
},
});
});

it('fails with unauthorized email', async () => {
const ctx = await createTestAuthContext({
isUserAuthorized: async () => false,
});
const result = await processProviderCallback(
ctx,
{
code: 'params-code',
state: 'params-state',
},
{
code: 'params-code',
state: 'params-state',
nonce: 't0j5AY5k4oLAcxfaZdRsMfVZGBCgBjlfihgoVq34YGo',
}
);
expect(result).toEqual({
error: {
message: 'permission denied',
status: 403,
},
success: false,
});
});
});
4 changes: 4 additions & 0 deletions packages/auth/src/services/process-provider-callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export const processProviderCallback = async (
if (!userDataResult.success) {
return userDataResult;
}
const isAuthorized = await ctx.isUserAuthorized(userDataResult.data.email);
if (!isAuthorized) {
return r.failure({ status: 403, message: 'permission denied' });
}
let userId = await ctx.db.getUserId(userDataResult.data.email);
if (!userId) {
const newUser = await ctx.db.createUser(userDataResult.data.email);
Expand Down
10 changes: 9 additions & 1 deletion packages/server/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type ServerOptions = {
title: string;
db: DatabaseGateway;
loginGovOptions: LoginGovOptions;
isUserAuthorized: (email: string) => Promise<boolean>;
};

export const getAstroAppContext = async (Astro: any): Promise<AppContext> => {
Expand All @@ -42,6 +43,7 @@ const createAstroAppContext = async (
Astro,
db: serverOptions.db,
loginGovOptions: serverOptions.loginGovOptions,
isUserAuthorized: serverOptions.isUserAuthorized,
}),
baseUrl: env.BASE_URL,
formConfig: defaultFormConfig,
Expand All @@ -64,6 +66,9 @@ const getDefaultServerOptions = async (): Promise<ServerOptions> => {
//clientSecret: import.meta.env.SECRET_LOGIN_GOV_PRIVATE_KEY,
redirectURI: 'http://localhost:4322/signin/callback',
},
isUserAuthorized: async (email: string) => {
return true;
},
};
};

Expand Down Expand Up @@ -95,10 +100,12 @@ const createDefaultAuthContext = async ({
Astro,
db,
loginGovOptions,
isUserAuthorized,
}: {
Astro: AstroGlobal | APIContext;
db: DatabaseGateway;
loginGovOptions: LoginGovOptions;
isUserAuthorized: (email: string) => Promise<boolean>;
}) => {
const { LoginGov, BaseAuthContext } = await import('@atj/auth');
return new BaseAuthContext(
Expand All @@ -116,7 +123,8 @@ const createDefaultAuthContext = async ({
function setUserSession({ session, user }) {
Astro.locals.session = session;
Astro.locals.user = user;
}
},
isUserAuthorized
);
};

Expand Down

0 comments on commit e8bd32c

Please sign in to comment.