import { AuthenticateRequestWithCredentials as RequestWithCredentials , AuthenticateRequestWithToken as RequestWithToken } from "common" ; import { post } from "handlers/authenticate" ; import { mockAuthenticationClientAuthenticateWithCredentialsWithError , mockAuthenticationClientAuthenticateWithCredentialsWithResult , mockAuthenticationClientAuthenticateWithTokenWithError , mockAuthenticationClientAuthenticateWithTokenWithResult } from "_/mocks/clients" ; import { mockAuthenticateRequestWithCredentials , mockAuthenticateRequestWithToken , mockSession } from "_/mocks/common" ; import { mockAPIGatewayProxyEvent } from "_/mocks/vendor/aws-lambda" ; describe ( "handlers/authenticate" , ( ) => { describe ( "post" , ( ) => { const session = mockSession ( true ) ; describe ( "with credentials" , ( ) => { const { username , password } = mockAuthenticateRequestWithCredentials ( ) ; const event = mockAPIGatewayProxyEvent < RequestWithCredentials > ( { body : { username , password } } ) ; it ( "should resolve with identity token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { id : jasmine . objectContaining ( { token : session . id . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; it ( "should resolve with access token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { access : jasmine . objectContaining ( { token : session . access . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; it ( "should resolve without refresh token (body)" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) . refresh ) . toBeUndefined ( ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; it ( "should resolve without refresh token (cookie)" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithResult ( session ) ; const { statusCode , headers } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( headers ) . toEqual ( { } ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; it ( "should resolve with authentication client error" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithError ( ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 400 ) ; expect ( body ) . toEqual ( JSON . stringify ( { type : "Error" , message : "authenticateWithCredentials" } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; } ) ; describe ( "with credentials and remember me" , ( ) => { const { username , password } = mockAuthenticateRequestWithCredentials ( ) ; const event = mockAPIGatewayProxyEvent < RequestWithCredentials > ( { body : { username , password , remember : true } } ) ; it ( "should resolve with identity token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { id : jasmine . objectContaining ( { token : session . id . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; it ( "should resolve with access token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { access : jasmine . objectContaining ( { token : session . access . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; it ( "should resolve with refresh token (body)" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { refresh : jasmine . objectContaining ( { token : session . refresh ! . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; it ( "should resolve with refresh token (cookie)" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithResult ( session ) ; const { statusCode , headers } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( headers ) . toEqual ( { "Set-Cookie" : `__Secure-token=${ encodeURIComponent ( session . refresh ! . token ) }; Domain=${ process . env . COGNITO_IDENTITY_POOL_PROVIDER ! }; Path=/${ process . env . API_BASE_PATH ! }/authenticate; Expires=${ session . refresh ! . expires . toUTCString ( ) }; HttpOnly; Secure; SameSite=Strict` } ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; it ( "should resolve with authentication client error" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithCredentialsWithError ( ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 400 ) ; expect ( body ) . toEqual ( JSON . stringify ( { type : "Error" , message : "authenticateWithCredentials" } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( username , password ) ; } ) ; } ) ; describe ( "with refresh token (body)" , ( ) => { const { token } = mockAuthenticateRequestWithToken ( ) ; const event = mockAPIGatewayProxyEvent < RequestWithToken > ( { body : { token } } ) ; it ( "should resolve with identity token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { id : jasmine . objectContaining ( { token : session . id . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( token ) ; } ) ; it ( "should resolve with access token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { access : jasmine . objectContaining ( { token : session . access . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( token ) ; } ) ; it ( "should resolve with error for missing token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithResult ( ) ; const { statusCode , body } = await post ( mockAPIGatewayProxyEvent < RequestWithToken > ( ) ) ; expect ( statusCode ) . toEqual ( 400 ) ; expect ( body ) . toEqual ( JSON . stringify ( { type : "TypeError" , message : "Invalid request" } ) ) ; expect ( authenticateMock ) . not . toHaveBeenCalled ( ) ; } ) ; it ( "should resolve with authentication client error" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithError ( ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 400 ) ; expect ( body ) . toEqual ( JSON . stringify ( { type : "Error" , message : "authenticateWithToken" } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( token ) ; } ) ; } ) ; describe ( "with refresh token (cookie)" , ( ) => { const { token } = mockAuthenticateRequestWithToken ( ) ; const event = mockAPIGatewayProxyEvent < RequestWithToken > ( { headers : { Cookie : `__Secure-token=${ token }` } } ) ; it ( "should resolve with identity token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { id : jasmine . objectContaining ( { token : session . id . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( token ) ; } ) ; it ( "should resolve with access token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithResult ( session ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 200 ) ; expect ( JSON . parse ( body ) ) . toEqual ( jasmine . objectContaining ( { access : jasmine . objectContaining ( { token : session . access . token } ) } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( token ) ; } ) ; it ( "should resolve with error for missing token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithResult ( ) ; const { statusCode , body } = await post ( mockAPIGatewayProxyEvent < RequestWithToken > ( ) ) ; expect ( statusCode ) . toEqual ( 400 ) ; expect ( body ) . toEqual ( JSON . stringify ( { type : "TypeError" , message : "Invalid request" } ) ) ; expect ( authenticateMock ) . not . toHaveBeenCalled ( ) ; } ) ; it ( "should resolve with authentication client error" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithError ( ) ; const { statusCode , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 400 ) ; expect ( body ) . toEqual ( JSON . stringify ( { type : "Error" , message : "authenticateWithToken" } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( token ) ; } ) ; it ( "should invalidate cookie for invalid token" , async ( ) => { const authenticateMock = mockAuthenticationClientAuthenticateWithTokenWithError ( ) ; const { statusCode , headers , body } = await post ( event ) ; expect ( statusCode ) . toEqual ( 400 ) ; expect ( headers ) . toEqual ( { "Set-Cookie" : `__Secure-token=; Domain=${ process . env . COGNITO_IDENTITY_POOL_PROVIDER ! }; Path=/${ process . env . API_BASE_PATH ! }/authenticate; Expires=${ new Date ( 0 ) . toUTCString ( ) }; HttpOnly; Secure; SameSite=Strict` } ) ; expect ( body ) . toEqual ( JSON . stringify ( { type : "Error" , message : "authenticateWithToken" } ) ) ; expect ( authenticateMock ) . toHaveBeenCalledWith ( token ) ; } ) ; } ) ; } ) ; } ) ;