forked from redwoodjs/redwood
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement dbAuth CORS support + Add cookie options to auth handler (r…
…edwoodjs#4150) * Update cors support + Cookie config * Remove typescript ignore comments * Adds `config` prop on <AuthProvider> for dbAuth fetch options * Typo in error message * Only set response if it isn't already set * Cookie options are all optional * Include config in all auth calls * Adds tests for missing/empty cookie options * Add @ts-ignore for now * Include credentials in call to current user, mark with TODO * Updates template to set `Secure: true` in all cases (seems to be an exception for localhost) * Fix syntax error in test * Fix constraints err * Change dbAuth invoke to return with short circuits * Set dbAuth options as { fetchConfig: { credentials }} * Remove @ts-ignore * Fix british spelling of 'Headders" ;) * Only allow config to be passed for dbAuth * config is optional * Adds tests for passing config through to createAuthClient and dbAuth client * Fix ts error in dbAuth client * Fix dbAuth credentials test * Add test for CORS options request to dbauth handler * Update normalizeRequest test in graphql-server Co-authored-by: Rob Cameron <[email protected]>
- Loading branch information
Showing
20 changed files
with
685 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import type { APIGatewayProxyEvent } from 'aws-lambda' | ||
import { Headers } from 'node-fetch' | ||
|
||
import { normalizeRequest } from '../transforms' | ||
|
||
export const createMockedEvent = ( | ||
httpMethod = 'POST', | ||
body: any = undefined, | ||
isBase64Encoded = false | ||
): APIGatewayProxyEvent => { | ||
return { | ||
body, | ||
headers: {}, | ||
multiValueHeaders: {}, | ||
httpMethod, | ||
isBase64Encoded, | ||
path: '/MOCK_PATH', | ||
pathParameters: null, | ||
queryStringParameters: null, | ||
multiValueQueryStringParameters: null, | ||
stageVariables: null, | ||
requestContext: { | ||
accountId: 'MOCKED_ACCOUNT', | ||
apiId: 'MOCKED_API_ID', | ||
authorizer: { name: 'MOCKED_AUTHORIZER' }, | ||
protocol: 'HTTP', | ||
identity: { | ||
accessKey: null, | ||
accountId: null, | ||
apiKey: null, | ||
apiKeyId: null, | ||
caller: null, | ||
clientCert: null, | ||
cognitoAuthenticationProvider: null, | ||
cognitoAuthenticationType: null, | ||
cognitoIdentityId: null, | ||
cognitoIdentityPoolId: null, | ||
principalOrgId: null, | ||
sourceIp: '123.123.123.123', | ||
user: null, | ||
userAgent: null, | ||
userArn: null, | ||
}, | ||
httpMethod: 'POST', | ||
path: '/MOCK_PATH', | ||
stage: 'MOCK_STAGE', | ||
requestId: 'MOCKED_REQUEST_ID', | ||
requestTimeEpoch: 1, | ||
resourceId: 'MOCKED_RESOURCE_ID', | ||
resourcePath: 'MOCKED_RESOURCE_PATH', | ||
}, | ||
resource: 'MOCKED_RESOURCE', | ||
} | ||
} | ||
|
||
test('Normalizes an aws event with base64', () => { | ||
const corsEventB64 = createMockedEvent( | ||
'POST', | ||
Buffer.from(JSON.stringify({ bazinga: 'hello_world' }), 'utf8').toString( | ||
'base64' | ||
), | ||
true | ||
) | ||
|
||
expect(normalizeRequest(corsEventB64)).toEqual({ | ||
headers: new Headers(corsEventB64.headers), | ||
method: 'POST', | ||
query: null, | ||
body: { | ||
bazinga: 'hello_world', | ||
}, | ||
}) | ||
}) | ||
|
||
test('Handles CORS requests with and without b64 encoded', () => { | ||
const corsEventB64 = createMockedEvent('OPTIONS', undefined, true) | ||
|
||
expect(normalizeRequest(corsEventB64)).toEqual({ | ||
headers: new Headers(corsEventB64.headers), // headers returned as symbol | ||
method: 'OPTIONS', | ||
query: null, | ||
body: undefined, | ||
}) | ||
|
||
const corsEventWithoutB64 = createMockedEvent('OPTIONS', undefined, false) | ||
|
||
expect(normalizeRequest(corsEventWithoutB64)).toEqual({ | ||
headers: new Headers(corsEventB64.headers), // headers returned as symbol | ||
method: 'OPTIONS', | ||
query: null, | ||
body: undefined, | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import type { Request } from 'graphql-helix' | ||
import { Headers } from 'node-fetch' | ||
|
||
export type CorsConfig = { | ||
origin?: boolean | string | string[] | ||
methods?: string | string[] | ||
allowedHeaders?: string | string[] | ||
exposedHeaders?: string | string[] | ||
credentials?: boolean | ||
maxAge?: number | ||
} | ||
|
||
export type CorsHeaders = Record<string, string> | ||
export type CorsContext = ReturnType<typeof createCorsContext> | ||
|
||
export function createCorsContext(cors: CorsConfig | undefined) { | ||
// Taken from apollo-server-env | ||
// @see: https://github.com/apollographql/apollo-server/blob/9267a79b974e397e87ad9ee408b65c46751e4565/packages/apollo-server-env/src/polyfills/fetch.js#L1 | ||
const corsHeaders = new Headers() | ||
|
||
if (cors) { | ||
if (cors.methods) { | ||
if (typeof cors.methods === 'string') { | ||
corsHeaders.set('access-control-allow-methods', cors.methods) | ||
} else if (Array.isArray(cors.methods)) { | ||
corsHeaders.set('access-control-allow-methods', cors.methods.join(',')) | ||
} | ||
} | ||
|
||
if (cors.allowedHeaders) { | ||
if (typeof cors.allowedHeaders === 'string') { | ||
corsHeaders.set('access-control-allow-headers', cors.allowedHeaders) | ||
} else if (Array.isArray(cors.allowedHeaders)) { | ||
corsHeaders.set( | ||
'access-control-allow-headers', | ||
cors.allowedHeaders.join(',') | ||
) | ||
} | ||
} | ||
|
||
if (cors.exposedHeaders) { | ||
if (typeof cors.exposedHeaders === 'string') { | ||
corsHeaders.set('access-control-expose-headers', cors.exposedHeaders) | ||
} else if (Array.isArray(cors.exposedHeaders)) { | ||
corsHeaders.set( | ||
'access-control-expose-headers', | ||
cors.exposedHeaders.join(',') | ||
) | ||
} | ||
} | ||
|
||
if (cors.credentials) { | ||
corsHeaders.set('access-control-allow-credentials', 'true') | ||
} | ||
if (typeof cors.maxAge === 'number') { | ||
corsHeaders.set('access-control-max-age', cors.maxAge.toString()) | ||
} | ||
} | ||
|
||
return { | ||
shouldHandleCors(request: Request) { | ||
return request.method === 'OPTIONS' | ||
}, | ||
getRequestHeaders(request: Request): CorsHeaders { | ||
const eventHeaders = new Headers( | ||
request.headers as Record<string, string> | ||
) | ||
const requestCorsHeaders = new Headers(corsHeaders) | ||
|
||
if (cors && cors.origin) { | ||
const requestOrigin = eventHeaders.get('origin') | ||
if (typeof cors.origin === 'string') { | ||
requestCorsHeaders.set('access-control-allow-origin', cors.origin) | ||
} else if ( | ||
requestOrigin && | ||
(typeof cors.origin === 'boolean' || | ||
(Array.isArray(cors.origin) && | ||
requestOrigin && | ||
cors.origin.includes(requestOrigin))) | ||
) { | ||
requestCorsHeaders.set('access-control-allow-origin', requestOrigin) | ||
} | ||
|
||
const requestAccessControlRequestHeaders = eventHeaders.get( | ||
'access-control-request-headers' | ||
) | ||
if (!cors.allowedHeaders && requestAccessControlRequestHeaders) { | ||
requestCorsHeaders.set( | ||
'access-control-allow-headers', | ||
requestAccessControlRequestHeaders | ||
) | ||
} | ||
} | ||
|
||
return Object.fromEntries(requestCorsHeaders.entries()) | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.