Skip to content

Commit 82badfd

Browse files
committed
Fix authContext tests and dbauth handler
1 parent 67c4ace commit 82badfd

File tree

12 files changed

+406
-253
lines changed

12 files changed

+406
-253
lines changed

packages/api/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@babel/runtime-corejs3": "7.23.5",
3535
"@prisma/client": "5.7.0",
3636
"@whatwg-node/fetch": "0.9.14",
37+
"cookie": "0.6.0",
3738
"core-js": "3.33.3",
3839
"humanize-string": "2.1.0",
3940
"jsonwebtoken": "9.0.2",
@@ -45,6 +46,7 @@
4546
"@babel/cli": "7.23.4",
4647
"@babel/core": "^7.22.20",
4748
"@types/aws-lambda": "8.10.126",
49+
"@types/cookie": "^0",
4850
"@types/jsonwebtoken": "9.0.5",
4951
"@types/memjs": "1",
5052
"@types/pascalcase": "1.0.3",

packages/api/src/auth/__tests__/getAuthenticationContext.test.ts

+28-15
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,12 @@ import type { APIGatewayProxyEvent, Context } from 'aws-lambda'
22

33
import { getAuthenticationContext } from '../index'
44

5-
export const createMockedEvent = ({
6-
authProvider,
7-
}: {
8-
authProvider: string
9-
}): APIGatewayProxyEvent => {
5+
export const createMockedEvent = (
6+
headers: Record<string, string>
7+
): APIGatewayProxyEvent => {
108
return {
119
body: null,
12-
headers: {
13-
'auth-provider': authProvider,
14-
authorization: 'Bearer auth-test-token',
15-
},
10+
headers,
1611
multiValueHeaders: {},
1712
httpMethod: 'POST',
1813
isBase64Encoded: false,
@@ -55,7 +50,7 @@ export const createMockedEvent = ({
5550
}
5651
}
5752

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

7166
const result = await getAuthenticationContext({
7267
authDecoder: authDecoderOne,
73-
event: createMockedEvent({ authProvider: 'one' }),
68+
event: createMockedEvent({
69+
'auth-provider': 'one',
70+
authorization: 'Bearer auth-test-token',
71+
}),
7472
context: {} as Context,
7573
})
7674

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

104102
const result = await getAuthenticationContext({
105103
authDecoder: authDecoderOne,
106-
event: createMockedEvent({ authProvider: 'some-other' }),
104+
event: createMockedEvent({
105+
'auth-provider': 'some-other',
106+
authorization: 'Bearer auth-test-token',
107+
}),
107108
context: {} as Context,
108109
})
109110

@@ -122,7 +123,10 @@ describe('getAuthenticationContext', () => {
122123
it('Can take an empty array of auth decoders', async () => {
123124
const result = await getAuthenticationContext({
124125
authDecoder: [],
125-
event: createMockedEvent({ authProvider: 'two' }),
126+
event: createMockedEvent({
127+
'auth-provider': 'two',
128+
authorization: 'Bearer auth-test-token',
129+
}),
126130
context: {} as Context,
127131
})
128132

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

164168
const result = await getAuthenticationContext({
165169
authDecoder: [authDecoderOne, authDecoderTwo],
166-
event: createMockedEvent({ authProvider: 'two' }),
170+
event: createMockedEvent({
171+
'auth-provider': 'two',
172+
authorization: 'Bearer auth-test-token',
173+
}),
167174
context: {} as Context,
168175
})
169176

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

185192
it('Works even without any auth decoders', async () => {
186193
const result = await getAuthenticationContext({
187-
event: createMockedEvent({ authProvider: 'two' }),
194+
event: createMockedEvent({
195+
'auth-provider': 'two',
196+
authorization: 'Bearer auth-test-token',
197+
}),
188198
context: {} as Context,
189199
})
190200

@@ -200,3 +210,6 @@ describe('getAuthenticationContext', () => {
200210
expect(token).toEqual('auth-test-token')
201211
})
202212
})
213+
214+
// @TODO add tests for requests with Cookie headers
215+
describe('getAuthenticationContext with cookies', () => {})

packages/api/src/auth/index.ts

+60-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
export * from './parseJWT'
22

33
import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda'
4+
// @ts-expect-error Types are incorrect in 0.6, but will be fixed in the next version probably
5+
import { parse as parseCookie } from 'cookie'
6+
7+
import { isFetchApiRequest } from '../transforms'
48

59
import type { Decoded } from './parseJWT'
610
export type { Decoded }
@@ -23,19 +27,33 @@ export interface AuthorizationHeader {
2327
token: string
2428
}
2529

30+
export const parseAuthorizationCookie = (
31+
event: APIGatewayProxyEvent | Request
32+
) => {
33+
const cookie = isFetchApiRequest(event)
34+
? event.headers.get('Cookie')
35+
: event.headers?.Cookie || event.headers.cookie
36+
37+
// Unauthenticated request
38+
if (!cookie) {
39+
return null
40+
}
41+
42+
const parsedCookie = parseCookie(cookie)
43+
44+
return {
45+
parsedCookie,
46+
rawCookie: cookie,
47+
type: parsedCookie.authProvider,
48+
}
49+
}
50+
2651
/**
2752
* Split the `Authorization` header into a schema and token part.
2853
*/
2954
export const parseAuthorizationHeader = (
3055
event: APIGatewayProxyEvent
3156
): AuthorizationHeader => {
32-
console.log(`👉 \n ~ file: index.ts:33 ~ event.headers:`, event.headers)
33-
if (event.headers.cookie) {
34-
return {
35-
schema: 'cookie',
36-
token: event.headers.cookie,
37-
}
38-
}
3957
const parts = (
4058
event.headers?.authorization || event.headers?.Authorization
4159
)?.split(' ')
@@ -51,13 +69,13 @@ export const parseAuthorizationHeader = (
5169

5270
export type AuthContextPayload = [
5371
Decoded,
54-
{ type: string | null } & AuthorizationHeader,
72+
{ type: string } & AuthorizationHeader,
5573
{ event: APIGatewayProxyEvent; context: LambdaContext }
5674
]
5775

5876
export type Decoder = (
5977
token: string,
60-
type: string | null,
78+
type: string,
6179
req: { event: APIGatewayProxyEvent; context: LambdaContext }
6280
) => Promise<Decoded>
6381

@@ -74,8 +92,38 @@ export const getAuthenticationContext = async ({
7492
event: APIGatewayProxyEvent
7593
context: LambdaContext
7694
}): Promise<undefined | AuthContextPayload> => {
77-
const { schema, token } = parseAuthorizationHeader(event)
95+
const typeFromHeader = getAuthProviderHeader(event)
96+
const cookieHeader = parseAuthorizationCookie(event)
97+
98+
// Shortcircuit - if no auth-provider or cookie header, its
99+
// an unauthenticated request
100+
if (!typeFromHeader && !cookieHeader) {
101+
return undefined
102+
}
103+
104+
let token: string | undefined
105+
let type: string | undefined
106+
let schema: string | undefined
107+
108+
// If type is set in the header, use Bearer token auth
109+
if (typeFromHeader) {
110+
const parsedAuthHeader = parseAuthorizationHeader(event)
111+
token = parsedAuthHeader.token
112+
type = typeFromHeader
113+
schema = parsedAuthHeader.schema
114+
} else if (cookieHeader) {
115+
// The actual session parsing is done by the auth decoder
116+
token = cookieHeader.rawCookie
117+
type = cookieHeader.type
118+
schema = 'cookie'
119+
}
120+
121+
// Unauthenticatd request
122+
if (!token || !type || !schema) {
123+
return undefined
124+
}
78125

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

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

89137
let i = 0
90138
while (!decoded && i < authDecoders.length) {
91-
decoded = await authDecoders[i](token, null, { event, context })
139+
decoded = await authDecoders[i](token, type, { event, context })
92140
i++
93141
}
94142

95-
return [decoded, { type: null, schema, token }, { event, context }]
143+
return [decoded, { type, schema, token }, { event, context }]
96144
}

0 commit comments

Comments
 (0)