From d92fd4f981a2a57209f5e3ce88c9580d55596f2a Mon Sep 17 00:00:00 2001 From: Johan Sebastian Cortes Montenegro Date: Wed, 26 Aug 2020 19:58:20 +0100 Subject: [PATCH 1/8] fix typo --- .../integration/providers/aws/functionality/auth.integration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/framework-integration-tests/integration/providers/aws/functionality/auth.integration.ts b/packages/framework-integration-tests/integration/providers/aws/functionality/auth.integration.ts index c1d078252..0a43aeeda 100644 --- a/packages/framework-integration-tests/integration/providers/aws/functionality/auth.integration.ts +++ b/packages/framework-integration-tests/integration/providers/aws/functionality/auth.integration.ts @@ -36,7 +36,7 @@ describe('With the auth API', () => { mockCartId = random.uuid() }) - context('an internet rando', () => { + context('an internet random', () => { let client: DisconnectableApolloClient before(async () => { From f6f709b3bea0ac3ee2361993f8a3a76b91e9d882 Mon Sep 17 00:00:00 2001 From: Johan Sebastian Cortes Montenegro Date: Wed, 26 Aug 2020 20:29:04 +0100 Subject: [PATCH 2/8] allow the preflight request for /auth/sign-up --- .../providers/aws/cors.integration.ts | 40 +++++++++++++++++++ .../src/infrastructure/stacks/auth-stack.ts | 31 ++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 packages/framework-integration-tests/integration/providers/aws/cors.integration.ts diff --git a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts b/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts new file mode 100644 index 000000000..8a6adbaff --- /dev/null +++ b/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts @@ -0,0 +1,40 @@ +import { signUpURL } from './utils' +import { internet } from 'faker' +import fetch from 'cross-fetch' +import { expect } from '@boostercloud/framework-provider-aws/test/expect' + +describe('The Authentication API', () => { + context('/auth/sign-up', () => { + context('sending OPTIONS', () => { + function methodToPreflightOptions() { + return (methodToPreflight: string): RequestInit => { + return { + method: 'OPTIONS', + headers: { + 'Access-Control-Request-Method': methodToPreflight, + 'Access-Control-Request-Headers': 'X-any-header', + Origin: internet.url(), + }, + } + } + } + const httpMethodsToPreflight = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'] + + it('should allow all the headers and methods regardless the requests values', async () => { + const optionsList = httpMethodsToPreflight.map(methodToPreflightOptions()) + const url = await signUpURL() + + const responses = await Promise.all(optionsList.map((options) => fetch(url, options))) + + responses.forEach((response) => { + expect(response.status).to.be.eq(204) + expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') + expect(response.headers.get('Access-Control-Allow-Headers')).to.be.eq('*') + expect(response.headers.get('Access-Control-Allow-Methods')) + .to.include('OPTIONS') + .and.to.include('POST') + }) + }) + }) + }) +}) diff --git a/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts b/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts index 4fe809e02..51b2250cb 100644 --- a/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts +++ b/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts @@ -5,7 +5,7 @@ import { Code, Function } from '@aws-cdk/aws-lambda' import * as params from '../params' import { APIs } from '../params' import { Effect, IRole, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from '@aws-cdk/aws-iam' -import { AwsIntegration, PassthroughBehavior } from '@aws-cdk/aws-apigateway' +import { AwsIntegration, Cors, CorsOptions, MethodOptions, PassthroughBehavior } from '@aws-cdk/aws-apigateway' import { CognitoTemplates } from './api-stack-velocity-templates' export class AuthStack { @@ -100,20 +100,36 @@ export class AuthStack { const cognitoIntegrationRole = this.buildCognitoIntegrationRole(userPool) const authResource = this.apis.restAPI.root.addResource('auth') - const methodOptions = { + const methodOptions: MethodOptions = { methodResponses: [ { statusCode: '200', + responseParameters: { + 'method.response.header.Access-Control-Allow-Origin': true, + }, }, { statusCode: '400', + responseParameters: { + 'method.response.header.Access-Control-Allow-Origin': true, + }, }, { statusCode: '500', + responseParameters: { + 'method.response.header.Access-Control-Allow-Origin': true, + }, }, ], } - const signUpResource = authResource.addResource('sign-up') + const defaultCorsPreflightOptions: CorsOptions = { + allowHeaders: ['*'], + allowOrigins: Cors.ALL_ORIGINS, + allowMethods: ['POST', 'OPTIONS'], + } + const signUpResource = authResource.addResource('sign-up', { + defaultCorsPreflightOptions: defaultCorsPreflightOptions, + }) signUpResource.addMethod('POST', this.buildSignUpIntegration(cognitoIntegrationRole), methodOptions) signUpResource .addResource('confirm') @@ -216,14 +232,23 @@ export class AuthStack { { selectionPattern: '5\\d\\d', statusCode: '500', + responseParameters: { + ['method.response.header.Access-Control-Allow-Origin']: '\'*\'', + }, }, { selectionPattern: '4\\d\\d', statusCode: '400', + responseParameters: { + ['method.response.header.Access-Control-Allow-Origin']: '\'*\'', + }, }, { selectionPattern: '2\\d\\d', statusCode: '200', + responseParameters: { + ['method.response.header.Access-Control-Allow-Origin']: '\'*\'', + }, responseTemplates: { 'application/json': templates.responseTemplate, }, From 61d268a9324d2d7042c6c781d1175e1072a42e47 Mon Sep 17 00:00:00 2001 From: Johan Sebastian Cortes Montenegro Date: Thu, 27 Aug 2020 17:55:53 +0100 Subject: [PATCH 3/8] test post requests to auth/sign-up return the allow origin header --- .../providers/aws/cors.integration.ts | 82 +++++++++++++++---- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts b/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts index 8a6adbaff..ca278336a 100644 --- a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts +++ b/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts @@ -1,30 +1,49 @@ -import { signUpURL } from './utils' -import { internet } from 'faker' +import {authClientID, createPassword, signUpURL} from './utils' +import {internet} from 'faker' import fetch from 'cross-fetch' -import { expect } from '@boostercloud/framework-provider-aws/test/expect' +import {expect} from '@boostercloud/framework-provider-aws/test/expect' describe('The Authentication API', () => { + let clientId: string + before(async () => { + clientId = await authClientID() + }); + context('/auth/sign-up', () => { - context('sending OPTIONS', () => { - function methodToPreflightOptions() { - return (methodToPreflight: string): RequestInit => { - return { - method: 'OPTIONS', - headers: { - 'Access-Control-Request-Method': methodToPreflight, - 'Access-Control-Request-Headers': 'X-any-header', - Origin: internet.url(), - }, - } + let signUpUrl: string + let validSignUpBody: string; + const invalidSignUpBody = JSON.stringify({}) + + before(async () => { + signUpUrl = await signUpURL(); + validSignUpBody = JSON.stringify({ + clientId: clientId, + username: internet.email(), + password: createPassword(), + userAttributes: { + role: 'UserWithEmail', + }, + }); + }); + + context('OPTIONS', () => { + function methodToPreflightOptions(methodToPreflight: string): RequestInit { + return { + method: 'OPTIONS', + headers: { + 'Access-Control-Request-Method': methodToPreflight, + 'Access-Control-Request-Headers': 'X-any-header', + Origin: internet.url(), + }, } } + const httpMethodsToPreflight = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'] it('should allow all the headers and methods regardless the requests values', async () => { - const optionsList = httpMethodsToPreflight.map(methodToPreflightOptions()) - const url = await signUpURL() + const optionsList = httpMethodsToPreflight.map(methodToPreflightOptions) - const responses = await Promise.all(optionsList.map((options) => fetch(url, options))) + const responses = await Promise.all(optionsList.map((options) => fetch(signUpUrl, options))) responses.forEach((response) => { expect(response.status).to.be.eq(204) @@ -36,5 +55,34 @@ describe('The Authentication API', () => { }) }) }) + + context('POST', () => { + it('should return the Access-Control-Allow-Origin header for 200 responses', async () => { + const response = await fetch(signUpUrl, { + method: 'POST', + headers: { + 'Content-Type': 'Application/json' + }, + body: validSignUpBody, + }) + + expect(response.status).to.be.eq(200, `Response body was: ${JSON.stringify((await response.json()))}`) + expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') + }) + + it('should return the Access-Control-Allow-Origin header for 400 responses', async () => { + const response = await fetch(signUpUrl, { + method: 'POST', + headers: { + 'Content-Type': 'Application/json' + }, + body: invalidSignUpBody, + }) + + expect(response.status).to.be.eq(400) + expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') + }) + it('should return the Access-Control-Allow-Origin header for 500 responses') + }) }) }) From c195b541612f18767118273d7b441ee7becec936 Mon Sep 17 00:00:00 2001 From: Johan Sebastian Cortes Montenegro Date: Thu, 27 Aug 2020 19:44:48 +0100 Subject: [PATCH 4/8] add support for the preflight request to /auth/sign-in and /auth/sign-out --- .../providers/aws/cors.integration.ts | 110 ++++++++++++------ .../integration/providers/aws/utils.ts | 4 + .../src/infrastructure/stacks/auth-stack.ts | 29 ++--- 3 files changed, 91 insertions(+), 52 deletions(-) diff --git a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts b/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts index ca278336a..91f46b853 100644 --- a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts +++ b/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts @@ -1,21 +1,24 @@ -import {authClientID, createPassword, signUpURL} from './utils' -import {internet} from 'faker' +import { authClientID, createPassword, signInURL, signOutURL, signUpURL } from './utils' +import { internet } from 'faker' import fetch from 'cross-fetch' -import {expect} from '@boostercloud/framework-provider-aws/test/expect' +import { expect } from '@boostercloud/framework-provider-aws/test/expect' describe('The Authentication API', () => { let clientId: string + const methodsToCheck = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'] + const preflightOptions = generatePreflightOptionsList(methodsToCheck) + before(async () => { clientId = await authClientID() - }); + }) context('/auth/sign-up', () => { let signUpUrl: string - let validSignUpBody: string; + let validSignUpBody: string const invalidSignUpBody = JSON.stringify({}) before(async () => { - signUpUrl = await signUpURL(); + signUpUrl = await signUpURL() validSignUpBody = JSON.stringify({ clientId: clientId, username: internet.email(), @@ -23,36 +26,14 @@ describe('The Authentication API', () => { userAttributes: { role: 'UserWithEmail', }, - }); - }); + }) + }) context('OPTIONS', () => { - function methodToPreflightOptions(methodToPreflight: string): RequestInit { - return { - method: 'OPTIONS', - headers: { - 'Access-Control-Request-Method': methodToPreflight, - 'Access-Control-Request-Headers': 'X-any-header', - Origin: internet.url(), - }, - } - } - - const httpMethodsToPreflight = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'] - it('should allow all the headers and methods regardless the requests values', async () => { - const optionsList = httpMethodsToPreflight.map(methodToPreflightOptions) + const responses = await Promise.all(preflightOptions.map(performPreflightRequest(signUpUrl))) - const responses = await Promise.all(optionsList.map((options) => fetch(signUpUrl, options))) - - responses.forEach((response) => { - expect(response.status).to.be.eq(204) - expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') - expect(response.headers.get('Access-Control-Allow-Headers')).to.be.eq('*') - expect(response.headers.get('Access-Control-Allow-Methods')) - .to.include('OPTIONS') - .and.to.include('POST') - }) + responses.forEach(assertResponseContainsPreflightHeaders) }) }) @@ -61,12 +42,12 @@ describe('The Authentication API', () => { const response = await fetch(signUpUrl, { method: 'POST', headers: { - 'Content-Type': 'Application/json' + 'Content-Type': 'Application/json', }, body: validSignUpBody, }) - expect(response.status).to.be.eq(200, `Response body was: ${JSON.stringify((await response.json()))}`) + expect(response.status).to.be.eq(200, `Response body was: ${JSON.stringify(await response.json())}`) expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') }) @@ -74,7 +55,7 @@ describe('The Authentication API', () => { const response = await fetch(signUpUrl, { method: 'POST', headers: { - 'Content-Type': 'Application/json' + 'Content-Type': 'Application/json', }, body: invalidSignUpBody, }) @@ -85,4 +66,63 @@ describe('The Authentication API', () => { it('should return the Access-Control-Allow-Origin header for 500 responses') }) }) + + context('/auth/sign-in', () => { + let signInUrl: string + + before(async () => { + signInUrl = await signInURL() + }) + + context('OPTIONS', () => { + it('should allow all the headers and methods regardless the requests values', async () => { + const responses = await Promise.all(preflightOptions.map(performPreflightRequest(signInUrl))) + + responses.forEach(assertResponseContainsPreflightHeaders) + }) + }) + }) + + context('/auth/sign-out', () => { + let signOutUrl: string + + before(async () => { + signOutUrl = await signOutURL() + }) + + context('OPTIONS', () => { + it('should allow all the headers and methods regardless the requests values', async () => { + const responses = await Promise.all(preflightOptions.map(performPreflightRequest(signOutUrl))) + + responses.forEach(assertResponseContainsPreflightHeaders) + }) + }) + }) + + function generatePreflightOptionsList(desiredHttpMethods: string[]): RequestInit[] { + // For more info about preflight requests see: https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request + return desiredHttpMethods.map( + (method: string): RequestInit => ({ + method: 'OPTIONS', + headers: { + 'Access-Control-Request-Method': method, + 'Access-Control-Request-Headers': 'X-any-header', + Origin: internet.url(), + }, + }) + ) + } + + function performPreflightRequest(url: string) { + return (options: RequestInit) => fetch(url, options) + } + + function assertResponseContainsPreflightHeaders(response: Response) { + expect(response.status).to.be.eq(204) + expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') + expect(response.headers.get('Access-Control-Allow-Headers')).to.be.eq('*') + expect(response.headers.get('Access-Control-Allow-Methods')) + .to.include('OPTIONS') + .and.to.include('POST') + } }) diff --git a/packages/framework-integration-tests/integration/providers/aws/utils.ts b/packages/framework-integration-tests/integration/providers/aws/utils.ts index d82619fec..24a5fd653 100644 --- a/packages/framework-integration-tests/integration/providers/aws/utils.ts +++ b/packages/framework-integration-tests/integration/providers/aws/utils.ts @@ -296,6 +296,10 @@ export async function signInURL(): Promise { return new URL('auth/sign-in', await baseHTTPURL()).href } +export async function signOutURL(): Promise { + return new URL('auth/sign-out', await baseHTTPURL()).href +} + export async function refreshTokenURL(): Promise { return new URL('auth/refresh-token', await baseHTTPURL()).href } diff --git a/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts b/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts index 51b2250cb..178d0f99e 100644 --- a/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts +++ b/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts @@ -100,25 +100,22 @@ export class AuthStack { const cognitoIntegrationRole = this.buildCognitoIntegrationRole(userPool) const authResource = this.apis.restAPI.root.addResource('auth') + const allowedOriginHeaderForCors = { + 'method.response.header.Access-Control-Allow-Origin': true, + } const methodOptions: MethodOptions = { methodResponses: [ { statusCode: '200', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': true, - }, + responseParameters: allowedOriginHeaderForCors, }, { statusCode: '400', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': true, - }, + responseParameters: allowedOriginHeaderForCors, }, { statusCode: '500', - responseParameters: { - 'method.response.header.Access-Control-Allow-Origin': true, - }, + responseParameters: allowedOriginHeaderForCors, }, ], } @@ -127,21 +124,19 @@ export class AuthStack { allowOrigins: Cors.ALL_ORIGINS, allowMethods: ['POST', 'OPTIONS'], } - const signUpResource = authResource.addResource('sign-up', { - defaultCorsPreflightOptions: defaultCorsPreflightOptions, - }) + const signUpResource = authResource.addResource('sign-up', { defaultCorsPreflightOptions }) signUpResource.addMethod('POST', this.buildSignUpIntegration(cognitoIntegrationRole), methodOptions) signUpResource .addResource('confirm') .addMethod('POST', this.buildConfirmSignUpIntegration(cognitoIntegrationRole), methodOptions) authResource - .addResource('sign-in') + .addResource('sign-in', { defaultCorsPreflightOptions }) .addMethod('POST', this.buildSignInIntegration(cognitoIntegrationRole), methodOptions) authResource .addResource('refresh-token') .addMethod('POST', this.buildRefreshTokenIntegration(cognitoIntegrationRole), methodOptions) authResource - .addResource('sign-out') + .addResource('sign-out', { defaultCorsPreflightOptions }) .addMethod('POST', this.buildSignOutIntegration(cognitoIntegrationRole), methodOptions) } @@ -233,21 +228,21 @@ export class AuthStack { selectionPattern: '5\\d\\d', statusCode: '500', responseParameters: { - ['method.response.header.Access-Control-Allow-Origin']: '\'*\'', + ['method.response.header.Access-Control-Allow-Origin']: "'*'", }, }, { selectionPattern: '4\\d\\d', statusCode: '400', responseParameters: { - ['method.response.header.Access-Control-Allow-Origin']: '\'*\'', + ['method.response.header.Access-Control-Allow-Origin']: "'*'", }, }, { selectionPattern: '2\\d\\d', statusCode: '200', responseParameters: { - ['method.response.header.Access-Control-Allow-Origin']: '\'*\'', + ['method.response.header.Access-Control-Allow-Origin']: "'*'", }, responseTemplates: { 'application/json': templates.responseTemplate, From 832a93d7ccb1e6a9d95fee6f16eb74fd657c9f9f Mon Sep 17 00:00:00 2001 From: Johan Sebastian Cortes Montenegro Date: Thu, 27 Aug 2020 20:38:17 +0100 Subject: [PATCH 5/8] test post requests to /auth/sign-in and /auth/sign-out return the allow origin --- .../providers/aws/cors.integration.ts | 188 ++++++++++++------ 1 file changed, 122 insertions(+), 66 deletions(-) diff --git a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts b/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts index 91f46b853..e8e312ee8 100644 --- a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts +++ b/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts @@ -3,28 +3,30 @@ import { internet } from 'faker' import fetch from 'cross-fetch' import { expect } from '@boostercloud/framework-provider-aws/test/expect' -describe('The Authentication API', () => { +describe('Given the Authentication API', () => { let clientId: string - const methodsToCheck = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'] - const preflightOptions = generatePreflightOptionsList(methodsToCheck) - + const username = internet.email() + const password = createPassword() + const role = 'SuperUserNoConfirmation' before(async () => { clientId = await authClientID() }) - context('/auth/sign-up', () => { + context('When /auth/sign-up', () => { let signUpUrl: string - let validSignUpBody: string - const invalidSignUpBody = JSON.stringify({}) + let validAuthBody: string + const invalidAuthBody = JSON.stringify({}) + const methodsToCheck = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'] + const preflightOptions = generatePreflightOptionsList(methodsToCheck) before(async () => { signUpUrl = await signUpURL() - validSignUpBody = JSON.stringify({ - clientId: clientId, - username: internet.email(), - password: createPassword(), + validAuthBody = JSON.stringify({ + clientId, + username, + password, userAttributes: { - role: 'UserWithEmail', + role, }, }) }) @@ -44,11 +46,10 @@ describe('The Authentication API', () => { headers: { 'Content-Type': 'Application/json', }, - body: validSignUpBody, + body: validAuthBody, }) - expect(response.status).to.be.eq(200, `Response body was: ${JSON.stringify(await response.json())}`) - expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') + await verifyResponseAndAllowedOriginHeader(response, 200, '*', await response.json()) }) it('should return the Access-Control-Allow-Origin header for 400 responses', async () => { @@ -57,72 +58,127 @@ describe('The Authentication API', () => { headers: { 'Content-Type': 'Application/json', }, - body: invalidSignUpBody, + body: invalidAuthBody, }) - expect(response.status).to.be.eq(400) - expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') + await verifyResponseAndAllowedOriginHeader(response, 400, '*', await response.json()) }) + it('should return the Access-Control-Allow-Origin header for 500 responses') }) - }) - context('/auth/sign-in', () => { - let signInUrl: string + context('And then /auth/sign-in', () => { + let signInUrl: string + let accessToken: string - before(async () => { - signInUrl = await signInURL() - }) + before(async () => { + signInUrl = await signInURL() + }) - context('OPTIONS', () => { - it('should allow all the headers and methods regardless the requests values', async () => { - const responses = await Promise.all(preflightOptions.map(performPreflightRequest(signInUrl))) + context('OPTIONS', () => { + it('should allow all the headers and methods regardless the requests values', async () => { + const responses = await Promise.all(preflightOptions.map(performPreflightRequest(signInUrl))) - responses.forEach(assertResponseContainsPreflightHeaders) + responses.forEach(assertResponseContainsPreflightHeaders) + }) }) - }) - }) - context('/auth/sign-out', () => { - let signOutUrl: string + context('POST', () => { - before(async () => { - signOutUrl = await signOutURL() - }) + it('should return the Access-Control-Allow-Origin header for 200 responses', async () => { + const response = await fetch(signInUrl, { + method: 'POST', + headers: { 'Content-Type': 'Application/json' }, + body: validAuthBody, + }) + const jsonBody = await response.json() + accessToken = jsonBody['accessToken'] - context('OPTIONS', () => { - it('should allow all the headers and methods regardless the requests values', async () => { - const responses = await Promise.all(preflightOptions.map(performPreflightRequest(signOutUrl))) + await verifyResponseAndAllowedOriginHeader(response, 200, '*', jsonBody) + }) + it('should return the Access-Control-Allow-Origin header for 400 responses', async () => { + const response = await fetch(signInUrl, { + method: 'POST', + headers: { 'Content-Type': 'Application/json' }, + body: invalidAuthBody, + }) + + await verifyResponseAndAllowedOriginHeader(response, 400, '*', await response.json()) + }) + it('should return the Access-Control-Allow-Origin header for 500 responses') + }) - responses.forEach(assertResponseContainsPreflightHeaders) + context('And then /auth/sign-out', () => { + let signOutUrl: string + + before(async () => { + signOutUrl = await signOutURL() + }) + + context('OPTIONS', () => { + it('should allow all the headers and methods regardless the requests values', async () => { + const responses = await Promise.all(preflightOptions.map(performPreflightRequest(signOutUrl))) + + responses.forEach(assertResponseContainsPreflightHeaders) + }) + }) + + context('POST', () => { + it('should return the Access-Control-Allow-Origin header for 200 responses', async () => { + const response = await fetch(signOutUrl, { + method: 'POST', + headers: { 'Content-Type': 'Application/json' }, + body: JSON.stringify({ + accessToken: accessToken, + }), + }) + + await verifyResponseAndAllowedOriginHeader(response, 200, '*', await response.json()) + }) + it('should return the Access-Control-Allow-Origin header for 400 responses', async () => { + const response = await fetch(signOutUrl, { + method: 'POST', + headers: { 'Content-Type': 'Application/json' }, + body: invalidAuthBody, + }) + + await verifyResponseAndAllowedOriginHeader(response, 400, '*', await response.json()) + }) + it('should return the Access-Control-Allow-Origin header for 500 responses') + }) }) }) - }) - function generatePreflightOptionsList(desiredHttpMethods: string[]): RequestInit[] { - // For more info about preflight requests see: https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request - return desiredHttpMethods.map( - (method: string): RequestInit => ({ - method: 'OPTIONS', - headers: { - 'Access-Control-Request-Method': method, - 'Access-Control-Request-Headers': 'X-any-header', - Origin: internet.url(), - }, - }) - ) - } - - function performPreflightRequest(url: string) { - return (options: RequestInit) => fetch(url, options) - } - - function assertResponseContainsPreflightHeaders(response: Response) { - expect(response.status).to.be.eq(204) - expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') - expect(response.headers.get('Access-Control-Allow-Headers')).to.be.eq('*') - expect(response.headers.get('Access-Control-Allow-Methods')) - .to.include('OPTIONS') - .and.to.include('POST') - } + function generatePreflightOptionsList(desiredHttpMethods: string[]): RequestInit[] { + // For more info about preflight requests see: https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request + return desiredHttpMethods.map( + (method: string): RequestInit => ({ + method: 'OPTIONS', + headers: { + 'Access-Control-Request-Method': method, + 'Access-Control-Request-Headers': 'X-any-header', + Origin: internet.url(), + }, + }), + ) + } + + function performPreflightRequest(url: string) { + return (options: RequestInit) => fetch(url, options) + } + + function assertResponseContainsPreflightHeaders(response: Response) { + expect(response.status).to.be.eq(204) + expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') + expect(response.headers.get('Access-Control-Allow-Headers')).to.be.eq('*') + expect(response.headers.get('Access-Control-Allow-Methods')) + .to.include('OPTIONS') + .and.to.include('POST') + } + + async function verifyResponseAndAllowedOriginHeader(response: Response, expectedHttpStatus: number, expectedAllowedOrigin: string, jsonBody: any) { + expect(response.status).to.be.eq(expectedHttpStatus, `Response body was: ${JSON.stringify(jsonBody)}`) + expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq(expectedAllowedOrigin) + } + }) }) From 7bbdc59afde9f401a4bd2c40ae5cf73d31afbf67 Mon Sep 17 00:00:00 2001 From: Johan Sebastian Cortes Montenegro Date: Fri, 28 Aug 2020 11:12:54 +0100 Subject: [PATCH 6/8] extract variable for duplicated code --- .../src/infrastructure/stacks/auth-stack.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts b/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts index 178d0f99e..c4e59aa1f 100644 --- a/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts +++ b/packages/framework-provider-aws-infrastructure/src/infrastructure/stacks/auth-stack.ts @@ -216,6 +216,9 @@ export class AuthStack { withRole: IRole, templates: { requestTemplate: string; responseTemplate: string } ): AwsIntegration { + const responseParameters = { + ['method.response.header.Access-Control-Allow-Origin']: "'*'", + } return new AwsIntegration({ service: 'cognito-idp', action: forAction, @@ -227,23 +230,17 @@ export class AuthStack { { selectionPattern: '5\\d\\d', statusCode: '500', - responseParameters: { - ['method.response.header.Access-Control-Allow-Origin']: "'*'", - }, + responseParameters, }, { selectionPattern: '4\\d\\d', statusCode: '400', - responseParameters: { - ['method.response.header.Access-Control-Allow-Origin']: "'*'", - }, + responseParameters, }, { selectionPattern: '2\\d\\d', statusCode: '200', - responseParameters: { - ['method.response.header.Access-Control-Allow-Origin']: "'*'", - }, + responseParameters, responseTemplates: { 'application/json': templates.responseTemplate, }, From bcc8aa1303b8e9b5354a855ef5d798a974c2ecd2 Mon Sep 17 00:00:00 2001 From: Johan Sebastian Cortes Montenegro Date: Wed, 9 Sep 2020 12:42:18 +0100 Subject: [PATCH 7/8] move test file to another directory --- .../providers/aws/{ => functionality}/cors.integration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename packages/framework-integration-tests/integration/providers/aws/{ => functionality}/cors.integration.ts (99%) diff --git a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts b/packages/framework-integration-tests/integration/providers/aws/functionality/cors.integration.ts similarity index 99% rename from packages/framework-integration-tests/integration/providers/aws/cors.integration.ts rename to packages/framework-integration-tests/integration/providers/aws/functionality/cors.integration.ts index e8e312ee8..78edebdc1 100644 --- a/packages/framework-integration-tests/integration/providers/aws/cors.integration.ts +++ b/packages/framework-integration-tests/integration/providers/aws/functionality/cors.integration.ts @@ -1,4 +1,4 @@ -import { authClientID, createPassword, signInURL, signOutURL, signUpURL } from './utils' +import { authClientID, createPassword, signInURL, signOutURL, signUpURL } from '../utils' import { internet } from 'faker' import fetch from 'cross-fetch' import { expect } from '@boostercloud/framework-provider-aws/test/expect' From 2f4ef78f844328a55aff0e62763e91addf46c424 Mon Sep 17 00:00:00 2001 From: Johan Sebastian Cortes Montenegro Date: Wed, 9 Sep 2020 12:50:38 +0100 Subject: [PATCH 8/8] apply linter fixes --- .../providers/aws/functionality/cors.integration.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/framework-integration-tests/integration/providers/aws/functionality/cors.integration.ts b/packages/framework-integration-tests/integration/providers/aws/functionality/cors.integration.ts index 78edebdc1..7d51e0be1 100644 --- a/packages/framework-integration-tests/integration/providers/aws/functionality/cors.integration.ts +++ b/packages/framework-integration-tests/integration/providers/aws/functionality/cors.integration.ts @@ -159,7 +159,7 @@ describe('Given the Authentication API', () => { 'Access-Control-Request-Headers': 'X-any-header', Origin: internet.url(), }, - }), + }) ) } @@ -167,7 +167,7 @@ describe('Given the Authentication API', () => { return (options: RequestInit) => fetch(url, options) } - function assertResponseContainsPreflightHeaders(response: Response) { + function assertResponseContainsPreflightHeaders(response: Response): void { expect(response.status).to.be.eq(204) expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq('*') expect(response.headers.get('Access-Control-Allow-Headers')).to.be.eq('*') @@ -176,7 +176,13 @@ describe('Given the Authentication API', () => { .and.to.include('POST') } - async function verifyResponseAndAllowedOriginHeader(response: Response, expectedHttpStatus: number, expectedAllowedOrigin: string, jsonBody: any) { + async function verifyResponseAndAllowedOriginHeader( + response: Response, + expectedHttpStatus: number, + expectedAllowedOrigin: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + jsonBody: any + ): Promise { expect(response.status).to.be.eq(expectedHttpStatus, `Response body was: ${JSON.stringify(jsonBody)}`) expect(response.headers.get('Access-Control-Allow-Origin')).to.be.eq(expectedAllowedOrigin) }