44import { NextRequest } from 'next/server' ;
55import { getSessionServer } from '@utils/amplify-utils' ;
66import { middleware } from '../middleware' ;
7+ import { getClientIdFromToken } from '@utils/token-utils' ;
78
89jest . mock ( '@utils/amplify-utils' ) ;
10+ jest . mock ( '@utils/token-utils' ) ;
911
1012const getTokenMock = jest . mocked ( getSessionServer ) ;
13+ const getClientIdFromTokenMock = jest . mocked ( getClientIdFromToken ) ;
1114
1215function getCsp ( response : Response ) {
1316 const csp = response . headers . get ( 'Content-Security-Policy' ) ;
@@ -19,6 +22,13 @@ afterAll(() => {
1922 process . env = OLD_ENV ;
2023} ) ;
2124
25+ beforeEach ( ( ) => {
26+ jest . resetAllMocks ( ) ;
27+ getClientIdFromTokenMock . mockImplementation ( ( token ) =>
28+ token ? 'client1' : undefined
29+ ) ;
30+ } ) ;
31+
2232describe ( 'middleware function' , ( ) => {
2333 it ( 'If route is not registered in middleware, respond with 404' , async ( ) => {
2434 const url = new URL ( 'https://url.com/message-templates/does-not-exist' ) ;
@@ -28,14 +38,15 @@ describe('middleware function', () => {
2838 expect ( response . status ) . toBe ( 404 ) ;
2939 } ) ;
3040
31- it ( 'if request path is protected, and no access token is obtained, redirect to auth page' , async ( ) => {
41+ it ( 'if request path is protected, and no access/id token is obtained, redirect to auth page' , async ( ) => {
3242 const url = new URL ( 'https://url.com/message-templates' ) ;
3343 const request = new NextRequest ( url ) ;
3444 request . cookies . set ( 'csrf_token' , 'some-csrf-value' ) ;
3545
3646 getTokenMock . mockResolvedValueOnce ( {
3747 accessToken : undefined ,
3848 clientId : undefined ,
49+ idToken : undefined ,
3950 } ) ;
4051
4152 const response = await middleware ( request ) ;
@@ -50,17 +61,21 @@ describe('middleware function', () => {
5061 expect ( response . cookies . get ( 'csrf_token' ) ?. value ) . toEqual ( '' ) ;
5162 } ) ;
5263
53- it ( 'if request path is protected, and access token is obtained , respond with CSP' , async ( ) => {
64+ it ( 'if request path is protected, tokens exist AND token has client-id , respond with CSP' , async ( ) => {
5465 getTokenMock . mockResolvedValueOnce ( {
55- accessToken : 'token' ,
66+ accessToken : 'access- token' ,
5667 clientId : 'client1' ,
68+ idToken : 'id-token' ,
5769 } ) ;
5870
5971 const url = new URL ( 'https://url.com/message-templates' ) ;
6072 const request = new NextRequest ( url ) ;
6173 const response = await middleware ( request ) ;
6274 const csp = getCsp ( response ) ;
6375
76+ expect ( response . status ) . toBe ( 200 ) ;
77+ expect ( getClientIdFromTokenMock ) . toHaveBeenCalledTimes ( 2 ) ;
78+
6479 expect ( csp ) . toEqual ( [
6580 "base-uri 'self'" ,
6681 "default-src 'none'" ,
@@ -79,12 +94,59 @@ describe('middleware function', () => {
7994 ] ) ;
8095 } ) ;
8196
97+ it ( 'if request path is protected, tokens exist BUT token missing client-id, redirect to request-to-be-added page' , async ( ) => {
98+ getTokenMock . mockResolvedValueOnce ( {
99+ accessToken : 'access-token' ,
100+ idToken : 'id-token' ,
101+ } ) ;
102+
103+ getClientIdFromTokenMock . mockReturnValueOnce ( undefined ) ;
104+ getClientIdFromTokenMock . mockReturnValueOnce ( undefined ) ;
105+
106+ const url = new URL ( 'https://url.com/message-templates' ) ;
107+ const request = new NextRequest ( url ) ;
108+ const response = await middleware ( request ) ;
109+
110+ expect ( response . status ) . toBe ( 307 ) ;
111+ expect ( response . headers . get ( 'location' ) ) . toBe (
112+ 'https://url.com/auth/request-to-be-added-to-a-service?redirect=%2Ftemplates%2Fmessage-templates'
113+ ) ;
114+ } ) ;
115+
82116 it ( 'if request path is not protected, respond with CSP' , async ( ) => {
83117 const url = new URL ( 'https://url.com/create-and-submit-templates' ) ;
84118 const request = new NextRequest ( url ) ;
85119 const response = await middleware ( request ) ;
86120 const csp = getCsp ( response ) ;
87121
122+ expect ( response . status ) . toBe ( 200 ) ;
123+ expect ( csp ) . toEqual ( [
124+ "base-uri 'self'" ,
125+ "default-src 'none'" ,
126+ "frame-ancestors 'none'" ,
127+ "font-src 'self' https://assets.nhs.uk" ,
128+ "form-action 'self'" ,
129+ "frame-src 'self'" ,
130+ "connect-src 'self' https://cognito-idp.eu-west-2.amazonaws.com" ,
131+ "img-src 'self'" ,
132+ "manifest-src 'self'" ,
133+ "object-src 'none'" ,
134+ expect . stringMatching ( / ^ s c r i p t - s r c ' s e l f ' ' n o n c e - [ \d A - Z a - z ] + ' $ / ) ,
135+ expect . stringMatching ( / ^ s t y l e - s r c ' s e l f ' ' n o n c e - [ \d A - Z a - z ] + ' $ / ) ,
136+ 'upgrade-insecure-requests' ,
137+ '' ,
138+ ] ) ;
139+ } ) ;
140+
141+ it ( 'public path (/auth/request-to-be-added-to-a-service) responds with CSP' , async ( ) => {
142+ const url = new URL (
143+ 'https://url.com/auth/request-to-be-added-to-a-service'
144+ ) ;
145+ const request = new NextRequest ( url ) ;
146+ const response = await middleware ( request ) ;
147+ const csp = getCsp ( response ) ;
148+
149+ expect ( response . status ) . toBe ( 200 ) ;
88150 expect ( csp ) . toEqual ( [
89151 "base-uri 'self'" ,
90152 "default-src 'none'" ,
0 commit comments