Skip to content

Commit

Permalink
Fix authContext tests and dbauth handler
Browse files Browse the repository at this point in the history
  • Loading branch information
dac09 committed Dec 20, 2023
1 parent 67c4ace commit 82badfd
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 253 deletions.
2 changes: 2 additions & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@babel/runtime-corejs3": "7.23.5",
"@prisma/client": "5.7.0",
"@whatwg-node/fetch": "0.9.14",
"cookie": "0.6.0",
"core-js": "3.33.3",
"humanize-string": "2.1.0",
"jsonwebtoken": "9.0.2",
Expand All @@ -45,6 +46,7 @@
"@babel/cli": "7.23.4",
"@babel/core": "^7.22.20",
"@types/aws-lambda": "8.10.126",
"@types/cookie": "^0",
"@types/jsonwebtoken": "9.0.5",
"@types/memjs": "1",
"@types/pascalcase": "1.0.3",
Expand Down
43 changes: 28 additions & 15 deletions packages/api/src/auth/__tests__/getAuthenticationContext.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ import type { APIGatewayProxyEvent, Context } from 'aws-lambda'

import { getAuthenticationContext } from '../index'

export const createMockedEvent = ({
authProvider,
}: {
authProvider: string
}): APIGatewayProxyEvent => {
export const createMockedEvent = (
headers: Record<string, string>
): APIGatewayProxyEvent => {
return {
body: null,
headers: {
'auth-provider': authProvider,
authorization: 'Bearer auth-test-token',
},
headers,
multiValueHeaders: {},
httpMethod: 'POST',
isBase64Encoded: false,
Expand Down Expand Up @@ -55,7 +50,7 @@ export const createMockedEvent = ({
}
}

describe('getAuthenticationContext', () => {
describe('getAuthenticationContext with bearer tokens', () => {
it('Can take a single auth decoder for the given provider', async () => {
const authDecoderOne = async (_token: string, type: string) => {
if (type !== 'one') {
Expand All @@ -70,7 +65,10 @@ describe('getAuthenticationContext', () => {

const result = await getAuthenticationContext({
authDecoder: authDecoderOne,
event: createMockedEvent({ authProvider: 'one' }),
event: createMockedEvent({
'auth-provider': 'one',
authorization: 'Bearer auth-test-token',
}),
context: {} as Context,
})

Expand Down Expand Up @@ -103,7 +101,10 @@ describe('getAuthenticationContext', () => {

const result = await getAuthenticationContext({
authDecoder: authDecoderOne,
event: createMockedEvent({ authProvider: 'some-other' }),
event: createMockedEvent({
'auth-provider': 'some-other',
authorization: 'Bearer auth-test-token',
}),
context: {} as Context,
})

Expand All @@ -122,7 +123,10 @@ describe('getAuthenticationContext', () => {
it('Can take an empty array of auth decoders', async () => {
const result = await getAuthenticationContext({
authDecoder: [],
event: createMockedEvent({ authProvider: 'two' }),
event: createMockedEvent({
'auth-provider': 'two',
authorization: 'Bearer auth-test-token',
}),
context: {} as Context,
})

Expand Down Expand Up @@ -163,7 +167,10 @@ describe('getAuthenticationContext', () => {

const result = await getAuthenticationContext({
authDecoder: [authDecoderOne, authDecoderTwo],
event: createMockedEvent({ authProvider: 'two' }),
event: createMockedEvent({
'auth-provider': 'two',
authorization: 'Bearer auth-test-token',
}),
context: {} as Context,
})

Expand All @@ -184,7 +191,10 @@ describe('getAuthenticationContext', () => {

it('Works even without any auth decoders', async () => {
const result = await getAuthenticationContext({
event: createMockedEvent({ authProvider: 'two' }),
event: createMockedEvent({
'auth-provider': 'two',
authorization: 'Bearer auth-test-token',
}),
context: {} as Context,
})

Expand All @@ -200,3 +210,6 @@ describe('getAuthenticationContext', () => {
expect(token).toEqual('auth-test-token')
})
})

// @TODO add tests for requests with Cookie headers
describe('getAuthenticationContext with cookies', () => {})
72 changes: 60 additions & 12 deletions packages/api/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export * from './parseJWT'

import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda'
// @ts-expect-error Types are incorrect in 0.6, but will be fixed in the next version probably
import { parse as parseCookie } from 'cookie'

import { isFetchApiRequest } from '../transforms'

import type { Decoded } from './parseJWT'
export type { Decoded }
Expand All @@ -23,19 +27,33 @@ export interface AuthorizationHeader {
token: string
}

export const parseAuthorizationCookie = (
event: APIGatewayProxyEvent | Request
) => {
const cookie = isFetchApiRequest(event)
? event.headers.get('Cookie')
: event.headers?.Cookie || event.headers.cookie

// Unauthenticated request
if (!cookie) {
return null
}

const parsedCookie = parseCookie(cookie)

return {
parsedCookie,
rawCookie: cookie,
type: parsedCookie.authProvider,
}
}

/**
* Split the `Authorization` header into a schema and token part.
*/
export const parseAuthorizationHeader = (
event: APIGatewayProxyEvent
): AuthorizationHeader => {
console.log(`👉 \n ~ file: index.ts:33 ~ event.headers:`, event.headers)
if (event.headers.cookie) {
return {
schema: 'cookie',
token: event.headers.cookie,
}
}
const parts = (
event.headers?.authorization || event.headers?.Authorization
)?.split(' ')
Expand All @@ -51,13 +69,13 @@ export const parseAuthorizationHeader = (

export type AuthContextPayload = [
Decoded,
{ type: string | null } & AuthorizationHeader,
{ type: string } & AuthorizationHeader,
{ event: APIGatewayProxyEvent; context: LambdaContext }
]

export type Decoder = (
token: string,
type: string | null,
type: string,
req: { event: APIGatewayProxyEvent; context: LambdaContext }
) => Promise<Decoded>

Expand All @@ -74,8 +92,38 @@ export const getAuthenticationContext = async ({
event: APIGatewayProxyEvent
context: LambdaContext
}): Promise<undefined | AuthContextPayload> => {
const { schema, token } = parseAuthorizationHeader(event)
const typeFromHeader = getAuthProviderHeader(event)
const cookieHeader = parseAuthorizationCookie(event)

// Shortcircuit - if no auth-provider or cookie header, its
// an unauthenticated request
if (!typeFromHeader && !cookieHeader) {
return undefined
}

let token: string | undefined
let type: string | undefined
let schema: string | undefined

// If type is set in the header, use Bearer token auth
if (typeFromHeader) {
const parsedAuthHeader = parseAuthorizationHeader(event)
token = parsedAuthHeader.token
type = typeFromHeader
schema = parsedAuthHeader.schema
} else if (cookieHeader) {
// The actual session parsing is done by the auth decoder
token = cookieHeader.rawCookie
type = cookieHeader.type
schema = 'cookie'
}

// Unauthenticatd request
if (!token || !type || !schema) {
return undefined
}

// Run through decoders until one returns a decoded payload
let authDecoders: Array<Decoder> = []

if (Array.isArray(authDecoder)) {
Expand All @@ -88,9 +136,9 @@ export const getAuthenticationContext = async ({

let i = 0
while (!decoded && i < authDecoders.length) {
decoded = await authDecoders[i](token, null, { event, context })
decoded = await authDecoders[i](token, type, { event, context })
i++
}

return [decoded, { type: null, schema, token }, { event, context }]
return [decoded, { type, schema, token }, { event, context }]
}
Loading

0 comments on commit 82badfd

Please sign in to comment.