Skip to content

Commit

Permalink
Refactor and reuse getEventHeader
Browse files Browse the repository at this point in the history
Improve Fetch Request detection
  • Loading branch information
dac09 committed Dec 25, 2023
1 parent 5e0c1c2 commit 4e268aa
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 48 deletions.
16 changes: 5 additions & 11 deletions packages/api/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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 { getEventHeader } from '../event'

import type { Decoded } from './parseJWT'
export type { Decoded }
Expand All @@ -19,9 +19,7 @@ export const getAuthProviderHeader = (
(key) => key.toLowerCase() === AUTH_PROVIDER_HEADER
)
if (authProviderKey) {
return isFetchApiRequest(event)
? event?.headers.get(authProviderKey)
: event?.headers[authProviderKey]
return getEventHeader(event, authProviderKey)
}
return undefined
}
Expand All @@ -34,9 +32,7 @@ export interface AuthorizationHeader {
export const parseAuthorizationCookie = (
event: APIGatewayProxyEvent | Request
) => {
const cookie = isFetchApiRequest(event)
? event.headers.get('Cookie')
: event.headers?.Cookie || event.headers.cookie
const cookie = getEventHeader(event, 'cookie')

// Unauthenticated request
if (!cookie) {
Expand All @@ -56,11 +52,9 @@ export const parseAuthorizationCookie = (
* Split the `Authorization` header into a schema and token part.
*/
export const parseAuthorizationHeader = (
event: APIGatewayProxyEvent
event: APIGatewayProxyEvent | Request
): AuthorizationHeader => {
const parts = (
event.headers?.authorization || event.headers?.Authorization
)?.split(' ')
const parts = getEventHeader(event, 'authorization')?.split(' ')
if (parts?.length !== 2) {
throw new Error('The `Authorization` header is not valid.')
}
Expand Down
15 changes: 15 additions & 0 deletions packages/api/src/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { APIGatewayProxyEvent } from 'aws-lambda'

import { isFetchApiRequest } from './transforms'

// Extracts the header from an event, handling lower and upper case header names.
export const getEventHeader = (
event: APIGatewayProxyEvent | Request,
headerName: string
) => {
if (isFetchApiRequest(event)) {
return event.headers.get(headerName)
}

return event.headers[headerName] || event.headers[headerName.toLowerCase()]
}
1 change: 1 addition & 0 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './types'

export * from './transforms'
export * from './cors'
export * from './event'

// @NOTE: use require, to avoid messing around with tsconfig and nested output dirs
const packageJson = require('../package.json')
Expand Down
18 changes: 12 additions & 6 deletions packages/api/src/transforms.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Headers } from '@whatwg-node/fetch'
import { Headers, Request as PonyfillRequest } from '@whatwg-node/fetch'
import type { APIGatewayProxyEvent } from 'aws-lambda'

// This is part of the request, dreived either from a LambdaEvent or FetchAPI Request
Expand Down Expand Up @@ -50,13 +50,19 @@ export const parseFetchEventBody = async (event: Request) => {
export const isFetchApiRequest = (
event: Request | APIGatewayProxyEvent
): event is Request => {
// Arda has suggested it's better to check for a lambda event than a fetch event
// as there are some edgecases with constructor name in PonyfillRequest
if ('httpMethod' in event || 'resource' in event) {
return false
if (
event.constructor.name === 'Request' ||
event.constructor.name === PonyfillRequest.name
) {
return true
}

return true
// Also do an extra check on type of headers
if (Symbol.iterator in Object(event.headers)) {
return true
}

return false
}

function getQueryStringParams(reqUrl: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2801,11 +2801,15 @@ describe('dbAuth', () => {

it('throw an error if user is not found', async () => {
const data = { id: 999999999999 }
req = {
headers: {
cookie: encryptToCookie(JSON.stringify(data) + ';' + 'token'),
},
const headers = {
cookie: encryptToCookie(JSON.stringify(data) + ';' + 'token'),
}

const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
headers,
})

const dbAuth = new DbAuthHandler(req, context, options)

try {
Expand All @@ -2819,13 +2823,17 @@ describe('dbAuth', () => {

it('throws a generic error for an invalid client', async () => {
const dbUser = await createDbUser()
req = {
headers: {
cookie: encryptToCookie(
JSON.stringify({ id: dbUser.id }) + ';' + 'token'
),
},
const headers = {
cookie: encryptToCookie(
JSON.stringify({ id: dbUser.id }) + ';' + 'token'
),
}

const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
headers,
})

// invalid db client
const dbAuth = new DbAuthHandler(req, context, options)
dbAuth.dbAccessor = undefined
Expand All @@ -2841,13 +2849,17 @@ describe('dbAuth', () => {

it('returns the user whos id is in session', async () => {
const dbUser = await createDbUser()
req = {
headers: {
cookie: encryptToCookie(
JSON.stringify({ id: dbUser.id }) + ';' + 'token'
),
},
const headers = {
cookie: encryptToCookie(
JSON.stringify({ id: dbUser.id }) + ';' + 'token'
),
}

const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
headers,
})

const dbAuth = new DbAuthHandler(req, context, options)
const user = await dbAuth._getCurrentUser()

Expand Down
18 changes: 3 additions & 15 deletions packages/auth-providers/dbAuth/api/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import crypto from 'node:crypto'

import type { APIGatewayProxyEvent } from 'aws-lambda'

import { isFetchApiRequest } from '@redwoodjs/api'
import { getEventHeader } from '@redwoodjs/api'
import { getConfig, getConfigPath } from '@redwoodjs/project-config'

import * as DbAuthError from './errors'
Expand All @@ -23,18 +23,6 @@ const DEFAULT_SCRYPT_OPTIONS: ScryptOptions = {
parallelization: 1,
}

// Extracts the header from an event, handling lower and upper case header names.
const eventGetHeader = (
event: APIGatewayProxyEvent | Request,
headerName: string
) => {
if (isFetchApiRequest(event)) {
return event.headers.get(headerName)
}

return event.headers[headerName] || event.headers[headerName.toLowerCase()]
}

const getPort = () => {
let configPath

Expand Down Expand Up @@ -81,13 +69,13 @@ export const extractCookie = (event: APIGatewayProxyEvent | Request) => {
// this feels a bit off, but also requires the parsing to become async

// return eventGraphiQLHeadersCookie(event) || eventHeadersCookie(event)
return eventGetHeader(event, 'Cookie')
return getEventHeader(event, 'Cookie')
}

function extractEncryptedSessionFromHeader(
event: APIGatewayProxyEvent | Request
) {
return eventGetHeader(event, 'Authorization')?.split(' ')[1]
return getEventHeader(event, 'Authorization')?.split(' ')[1]
}

// whether this encrypted session was made with the old CryptoJS algorithm
Expand Down

0 comments on commit 4e268aa

Please sign in to comment.