Skip to content

Commit

Permalink
FIX ALL THE TESTS!
Browse files Browse the repository at this point in the history
  • Loading branch information
dac09 committed Jan 18, 2024
1 parent d773116 commit 8750800
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 149 deletions.
6 changes: 3 additions & 3 deletions packages/api/src/__tests__/normalizeRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe('Lambda Request', () => {
headers: new Headers(corsEventB64.headers as Record<string, string>), // headers returned as symbol
method: 'OPTIONS',
query: null,
jsonBody: undefined,
jsonBody: {},
})

const corsEventWithoutB64 = createMockedLambdaEvent(
Expand All @@ -93,7 +93,7 @@ describe('Lambda Request', () => {
headers: new Headers(corsEventB64.headers as Record<string, string>), // headers returned as symbol
method: 'OPTIONS',
query: null,
jsonBody: undefined,
jsonBody: {},
})
})
})
Expand Down Expand Up @@ -151,7 +151,7 @@ describe('Fetch API Request', () => {
whatsup: 'doc',
its: 'bugs',
},
jsonBody: undefined,
jsonBody: {}, // @NOTE empty body is {} not undefined
})

expect(partial.headers.get('content-type')).toEqual(headers['content-type'])
Expand Down
20 changes: 8 additions & 12 deletions packages/api/src/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { APIGatewayProxyEvent } from 'aws-lambda'
// We do this to keep the API consistent between the two
// When we support only the FetchAPI request, we should remove this
export interface PartialRequest<TBody = Record<string, any>> {
jsonBody?: TBody
jsonBody: TBody
headers: Headers
method: string
query: any
Expand All @@ -16,7 +16,7 @@ export interface PartialRequest<TBody = Record<string, any>> {
*/
export const parseLambdaEventBody = (event: APIGatewayProxyEvent) => {
if (!event.body) {
return
return {}
}

if (event.isBase64Encoded) {
Expand All @@ -28,23 +28,19 @@ export const parseLambdaEventBody = (event: APIGatewayProxyEvent) => {

/**
* Extracts and parses body payload from Fetch Request
* with check for empty body (and base64 later)
* with check for empty body
*
* NOTE: whatwg/server expects that you will decode the base64 body yourself
* see readme here: https://github.com/ardatan/whatwg-node/tree/master/packages/server#aws-lambda
*/
export const parseFetchEventBody = async (event: Request) => {
if (!event.body) {
return
return {}
}

// @TODO: We need to understand what happens on Vercel
// if (event.isBase64Encoded) {
// return JSON.parse(Buffer.from(event.body, 'base64').toString('utf-8'))
// } else {
// return JSON.parse(event.body)
// }

const body = await event.text()

return body ? JSON.parse(body) : undefined
return body ? JSON.parse(body) : {}
}

export const isFetchApiRequest = (
Expand Down
5 changes: 4 additions & 1 deletion packages/auth-providers/dbAuth/api/src/DbAuthHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,8 @@ export class DbAuthHandler<

// checks the CSRF token in the header against the CSRF token in the session
// and throw an error if they are not the same (not used yet)
_validateCsrf() {
async _validateCsrf() {
await this.init()
if (
this.sessionCsrfToken !== this.normalizedRequest.headers.get('csrf-token')
) {
Expand Down Expand Up @@ -1394,6 +1395,8 @@ export class DbAuthHandler<

// gets the user from the database and returns only its ID
async _getCurrentUser() {
await this.init()

if (!this.session?.id) {
throw new DbAuthError.NotLoggedInError()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,19 +523,20 @@ describe('dbAuth', () => {
})
})

it.skip('parses an empty plain text body and still sets params', async () => {
// @TODO(Rob): This test is failing due to refactor, not sure its necessary
req = { isBase64Encoded: false, headers: {}, body: '' }
it('parses an empty plain text body and still sets params', async () => {
const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
body: '',
})

context = { foo: 'bar' }
const dbAuth = new DbAuthHandler(req, context, options)
await dbAuth.init()

expect(dbAuth.normalizedRequest.jsonBody).toEqual({})
})

it.skip('parses params from an undefined body when isBase64Encoded == false', async () => {
// @TODO(Rob): This test is failing due to refactor, not sure its necessary

it('parses params from an undefined body', async () => {
req = {
isBase64Encoded: false,
headers: {},
Expand All @@ -547,47 +548,6 @@ describe('dbAuth', () => {
expect(dbAuth.normalizedRequest.jsonBody).toEqual({})
})

it('parses params from a base64 encoded body', async () => {
req = {
isBase64Encoded: true,
headers: {},
body: Buffer.from(`{"foo":"bar", "baz":123}`, 'utf8'),
}
const dbAuth = new DbAuthHandler(req, context, options)
await dbAuth.init()
expect(dbAuth.normalizedRequest.jsonBody).toEqual({
foo: 'bar',
baz: 123,
})
})

it('parses params from an undefined body when isBase64Encoded == true', async () => {
// @TODO(Rob): Not sure this is necessary any more?
req = {
isBase64Encoded: true,
headers: {},
}
context = { foo: 'bar' }
const dbAuth = new DbAuthHandler(req, context, options)
await dbAuth.init()

expect(dbAuth.normalizedRequest.jsonBody).toEqual(undefined)
})

it('parses params from an empty body when isBase64Encoded == true', async () => {
// @TODO(Rob): Not sure this is necessary any more?
req = {
isBase64Encoded: true,
headers: {},
body: '',
}
context = { foo: 'bar' }
const dbAuth = new DbAuthHandler(req, context, options)
await dbAuth.init()

expect(dbAuth.normalizedRequest.jsonBody).toEqual(undefined)
})

it('sets header-based CSRF token', async () => {
req = { headers: { 'csrf-token': 'qwerty' } }
const dbAuth = new DbAuthHandler(req, context, options)
Expand Down Expand Up @@ -936,14 +896,15 @@ describe('dbAuth', () => {
body,
})

options.forgotPassword.handler = (handlerUser) => {
// user should have the raw resetToken NOT the hash
options.forgotPassword.handler = (handlerUser, token) => {
// tokens should be the raw resetToken NOT the hash
// resetToken consists of 16 base64 characters
expect(handlerUser.resetToken).toMatch(/^\w{16}$/)
expect(handlerUser.resetToken).toBeUndefined()
expect(token).toMatch(/^[A-Za-z0-9/+]{16}$/)
}
const dbAuth = new DbAuthHandler(req, context, options)
await dbAuth.forgotPassword()
expect.assertions(1)
expect.assertions(2)
})

it('removes the token from the forgotPassword response', async () => {
Expand Down Expand Up @@ -1190,7 +1151,7 @@ describe('dbAuth', () => {

const response = await dbAuth.login()

expect(response[0]).toEqual({ id: user.id })
expect(response[0].id).toEqual(user.id)
})

it('returns a CSRF token in the header', async () => {
Expand Down Expand Up @@ -1887,7 +1848,6 @@ describe('dbAuth', () => {
JSON.stringify({ id: user.id }) + ';' + 'token'
)

const justEncryptedSession = cookie.split('session=')[1]
const headers = {
cookie,
}
Expand All @@ -1901,7 +1861,7 @@ describe('dbAuth', () => {
const dbAuth = new DbAuthHandler(req, context, options)
const response = await dbAuth.getToken()

expect(response[0]).toEqual(justEncryptedSession)
expect(response[0]).toEqual(user.id)
})

it('returns nothing if user is not logged in', async () => {
Expand All @@ -1911,7 +1871,6 @@ describe('dbAuth', () => {
expect(response[0]).toEqual('')
})

// @TODO(Rob) HELP, change in behaviour
it('returns any other error', async () => {
req = {
headers: {
Expand All @@ -1927,7 +1886,6 @@ describe('dbAuth', () => {
expect(response[0]).toEqual('{"error":"User not found"}')
})

// @TODO(Rob) HELP, change in behaviour
it('re-encrypts the session cookie if using the legacy algorithm', async () => {
await createDbUser({ id: 7 })
req = {
Expand All @@ -1950,11 +1908,7 @@ describe('dbAuth', () => {
})
})

// @TODO: Studio should not use body to send auth impersonation details
// @TODO: Studio should not use body to send auth impersonation details
// @TODO: Studio should not use body to send auth impersonation details
// @TODO: Studio should not use body to send auth impersonation details
describe.skip('When a developer has set GraphiQL headers to mock a session cookie', () => {
describe('When a developer has set GraphiQL headers to mock a session cookie', () => {
describe('when in development environment', () => {
const curNodeEnv = process.env.NODE_ENV

Expand All @@ -1968,42 +1922,45 @@ describe('dbAuth', () => {
expect(process.env.NODE_ENV).toBe('test')
})

it('authenticates the user based on GraphiQL headers when no event.headers present', async () => {
// setup graphiQL header cookie in extensions
it('authenticates the user based on GraphiQL impersonated headers when no cookie present', async () => {
// Making Fetch API Requests with GraphiQL Headers in the body does not work because it's async
// but we can set the new 'rw-studio-impersonation-cookie' header
const dbUser = await createDbUser()
req.body = JSON.stringify({
extensions: {
headers: {
'auth-provider': 'dbAuth',
cookie: encryptToCookie(JSON.stringify({ id: dbUser.id })),
authorization: 'Bearer ' + dbUser.id,
},
},
const headers = {
'auth-provider': 'dbAuth',
'rw-studio-impersonation-cookie': encryptToCookie(
JSON.stringify({ id: dbUser.id })
),
}

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

const dbAuth = new DbAuthHandler(req, context, options)
const user = await dbAuth._getCurrentUser()
expect(user.id).toEqual(dbUser.id)
})

it('Cookie from GraphiQLHeaders takes precedence over event headers when authenticating user', async () => {
it('Cookie from GraphiQLHeaders takes precedence over real headers when authenticating user', async () => {
// setup session cookie in GraphiQL header
const dbUser = await createDbUser()
const dbUserId = dbUser.id

req.body = JSON.stringify({
extensions: {
headers: {
'auth-provider': 'dbAuth',
cookie: encryptToCookie(JSON.stringify({ id: dbUserId })),
authorization: 'Bearer ' + dbUserId,
},
const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
headers: {
'auth-provider': 'dbAuth',
authorization: 'Bearer ' + dbUserId,
cookie: encryptToCookie(JSON.stringify({ id: 9999999999 })), // The "real" cookie with an invalid userId
// 👇 The impersonated header takes precendence
'rw-studio-impersonation-cookie': encryptToCookie(
JSON.stringify({ id: dbUser.id })
),
},
})

// create session cookie in event header
req.headers.cookie = encryptToCookie(JSON.stringify({ id: 9999999999 }))

// should read session from graphiQL header, not from cookie
const dbAuth = new DbAuthHandler(req, context, options)
const user = await dbAuth._getCurrentUser()
Expand All @@ -2016,13 +1973,14 @@ describe('dbAuth', () => {
const dbUser = await createDbUser()
const dbUserId = dbUser.id

req.body = JSON.stringify({
extensions: {
headers: {
'auth-provider': 'dbAuth',
cookie: encryptToCookie(JSON.stringify({ id: dbUserId })),
authorization: 'Bearer ' + dbUserId,
},
const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
headers: {
'auth-provider': 'dbAuth',
'rw-studio-impersonation-cookie': encryptToCookie(
JSON.stringify({ id: dbUserId })
),
authorization: 'Bearer ' + dbUserId,
},
})

Expand Down Expand Up @@ -2053,7 +2011,6 @@ describe('dbAuth', () => {
expect.assertions(1)
})

// @TODO(Rob) Failing test here
it('throws an error if WebAuthn is disabled', async () => {
const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
Expand Down Expand Up @@ -2183,7 +2140,6 @@ describe('dbAuth', () => {
})

describe('webAuthnAuthOptions', () => {
// @TODO(Rob): HELP, failing test here
it('throws an error if user is not logged in', async () => {
const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
Expand Down Expand Up @@ -2564,20 +2520,23 @@ describe('dbAuth', () => {
})
})

// @TODO(Rob): Not used yet.
describe.skip('_validateCsrf()', () => {
it('returns true if session and header token match', () => {
describe('_validateCsrf()', () => {
it('returns true if session and header token match', async () => {
const data = { foo: 'bar' }
const token = 'abcd'
req = {

const req = new Request('http://localhost:8910/_rw_mw', {
method: 'POST',
headers: {
cookie: encryptToCookie(JSON.stringify(data) + ';' + token),
'csrf-token': token,
},
}
})

const dbAuth = new DbAuthHandler(req, context, options)
const output = await dbAuth._validateCsrf()

expect(dbAuth._validateCsrf()).toEqual(true)
expect(output).toEqual(true)
})

it('throws an error if session and header token do not match', () => {
Expand All @@ -2591,9 +2550,9 @@ describe('dbAuth', () => {
}
const dbAuth = new DbAuthHandler(req, context, options)

expect(() => {
dbAuth._validateCsrf()
}).toThrow(dbAuthError.CsrfTokenMismatchError)
expect(async () => {
await dbAuth._validateCsrf()
}).rejects.toThrow(dbAuthError.CsrfTokenMismatchError)
})
})

Expand Down
Loading

0 comments on commit 8750800

Please sign in to comment.