Skip to content

Commit

Permalink
Just keep the dbAuth fetch-api related changes, revert everything else
Browse files Browse the repository at this point in the history
  • Loading branch information
dac09 committed Jan 16, 2024
1 parent c3a2ebb commit 6a31c00
Show file tree
Hide file tree
Showing 16 changed files with 83 additions and 318 deletions.
2 changes: 0 additions & 2 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"@babel/runtime-corejs3": "7.23.6",
"@prisma/client": "5.7.0",
"@whatwg-node/fetch": "0.9.14",
"cookie": "0.6.0",
"core-js": "3.34.0",
"humanize-string": "2.1.0",
"jsonwebtoken": "9.0.2",
Expand All @@ -47,7 +46,6 @@
"@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
86 changes: 15 additions & 71 deletions packages/api/src/auth/__tests__/getAuthenticationContext.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import type { APIGatewayProxyEvent, Context } from 'aws-lambda'

import { getAuthenticationContext } from '../index'

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

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

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

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

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

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

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

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

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

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

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

// @TODO add tests for requests with Cookie headers
describe('getAuthenticationContext with cookies', () => {
it('Can take a single auth decoder for the given provider', async () => {
const authDecoderOne = async (_token: string, type: string) => {
if (type !== 'one') {
return null
}

return {
iss: 'one',
sub: 'user-id',
}
}

const fetchRequest = new Request('http://localhost:3000', {
method: 'POST',
body: '',
headers: {
cookie: 'auth-provider=one; session=xx/yy/zz',
},
})

const result = await getAuthenticationContext({
authDecoder: authDecoderOne,
event: fetchRequest,
context: {} as Context,
})

if (!result) {
fail('Result is undefined')
}

const [decoded, { type, schema, token }] = result

expect(decoded).toMatchObject({
iss: 'one',
sub: 'user-id',
})
expect(type).toEqual('one')
expect(schema).toEqual('cookie')
// @TODO we need to rename this. It's not actually the token, because
// some auth providers will have a cookie where we don't know the key
expect(token).toEqual('auth-provider=one; session=xx/yy/zz')
})
})
73 changes: 19 additions & 54 deletions packages/api/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export * from './parseJWT'

import type { APIGatewayProxyEvent, Context as LambdaContext } from 'aws-lambda'
import { parse as parseCookie } from 'cookie'

import { getEventHeader } from '../event'

Expand All @@ -28,25 +27,6 @@ export interface AuthorizationHeader {
token: string
}

export const parseAuthorizationCookie = (
event: APIGatewayProxyEvent | Request
) => {
const cookie = getEventHeader(event, 'cookie')

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

const parsedCookie = parseCookie(cookie)

return {
parsedCookie,
rawCookie: cookie,
type: parsedCookie['auth-provider'],
}
}

/**
* Split the `Authorization` header into a schema and token part.
*/
Expand All @@ -64,16 +44,24 @@ export const parseAuthorizationHeader = (
return { schema, token }
}

/** @MARK Note that we do not send LambdaContext when making fetch requests
*
* This part is incomplete, as we need to decide how we will make the breaking change to
* 1. getCurrentUser
* 2. authDecoders
*/

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

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

/**
Expand All @@ -89,38 +77,16 @@ export const getAuthenticationContext = async ({
event: APIGatewayProxyEvent | Request
context: LambdaContext
}): Promise<undefined | AuthContextPayload> => {
const typeFromHeader = getAuthProviderHeader(event)
const cookieHeader = parseAuthorizationCookie(event) //?
const type = getAuthProviderHeader(event)

// Shortcircuit - if no auth-provider or cookie header, its
// an unauthenticated request
if (!typeFromHeader && !cookieHeader) {
// No `auth-provider` header means that the user is logged out,
// and none of this auth malarky is required.
if (!type) {
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 as any)
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
}
const { schema, token } = parseAuthorizationHeader(event)

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

if (Array.isArray(authDecoder)) {
Expand All @@ -134,14 +100,13 @@ export const getAuthenticationContext = async ({
let i = 0
while (!decoded && i < authDecoders.length) {
decoded = await authDecoders[i](token, type, {
// @TODO We will need to make a breaking change to auth decoders maybe
event: event as any,
// @TODO: We will need to make a breaking change to support `Request` objects.
// We can remove this typecast
event: event,
context,
})
i++
}

// @TODO we need to rename this. It's not actually the token, because
// some auth providers will have a cookie where we don't know the key
return [decoded, { type, schema, token }, { event: event as any, context }]
return [decoded, { type, schema, token }, { event, context }]
}
13 changes: 11 additions & 2 deletions packages/auth-providers/dbAuth/api/src/DbAuthHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
hashToken,
webAuthnSession,
extractHashingOptions,
isLegacySession,
} from './shared'

type SetCookieHeader = { 'set-cookie': string }
Expand Down Expand Up @@ -610,8 +611,16 @@ export class DbAuthHandler<

async getToken() {
try {
// Just return the encrypted session cookie, to be passed back in the Authorization header
return [this.encryptedSession || '']
const user = await this._getCurrentUser()
let headers = {}

// if the session was encrypted with the old algorithm, re-encrypt it
// with the new one
if (isLegacySession(this.cookie)) {
headers = this._loginResponse(user)[1]
}

return [user[this.options.authFields.id], headers]
} catch (e: any) {
if (e instanceof DbAuthError.NotLoggedInError) {
return this._logoutResponse()
Expand Down
2 changes: 1 addition & 1 deletion packages/auth-providers/dbAuth/api/src/decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const createAuthDecoder = (cookieNameOption: string): Decoder => {
export const authDecoder: Decoder = async (
_authHeaderValue: string,
type: string,
req: { event: APIGatewayProxyEvent }
req: { event: APIGatewayProxyEvent | Request }
) => {
if (type !== 'dbAuth') {
return null
Expand Down
4 changes: 0 additions & 4 deletions packages/auth-providers/dbAuth/web/src/dbAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@ export function createDbAuthClient({
body: JSON.stringify({ username, password, method: 'login' }),
})

if (typeof window !== undefined) {
document.cookie = 'auth-provider=dbAuth'
}

return response.json()
}

Expand Down
2 changes: 0 additions & 2 deletions packages/auth/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ declare global {
__REDWOOD__APP_TITLE: string
}

var __REDWOOD__SERVER__AUTH_STATE__: AuthProviderState<any>

namespace NodeJS {
interface Global {
/** URL or absolute path to the GraphQL serverless function */
Expand Down
15 changes: 3 additions & 12 deletions packages/auth/src/AuthProvider/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { ReactNode } from 'react'
import React, { useContext, useEffect, useState } from 'react'
import React, { useEffect, useState } from 'react'

import type { AuthContextInterface, CurrentUser } from '../AuthContext'
import type { AuthImplementation } from '../AuthImplementation'

import type { AuthProviderState } from './AuthProviderState'
import { defaultAuthProviderState } from './AuthProviderState'
import { ServerAuthContext } from './ServerAuthProvider'
import { useCurrentUser } from './useCurrentUser'
import { useForgotPassword } from './useForgotPassword'
import { useHasRole } from './useHasRole'
Expand Down Expand Up @@ -83,11 +82,9 @@ export function createAuthProvider<
}: AuthProviderProps) => {
// const [hasRestoredState, setHasRestoredState] = useState(false)

const serverAuthState = useContext(ServerAuthContext)

const [authProviderState, setAuthProviderState] = useState<
AuthProviderState<TUser>
>(serverAuthState || defaultAuthProviderState)
>(defaultAuthProviderState)

const getToken = useToken(authImplementation)

Expand Down Expand Up @@ -147,13 +144,7 @@ export function createAuthProvider<
signUp,
logIn,
logOut,
getToken:
// When its rendering on the server, just get the token from the serverAuthState
typeof window === 'undefined'
? async () => {
return serverAuthState.encryptedSession || null
}
: getToken,
getToken,
getCurrentUser,
hasRole,
reauthenticate,
Expand Down
Loading

0 comments on commit 6a31c00

Please sign in to comment.