Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions frontend/src/__tests__/app/signin/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ test('returns redirect', async () => {
secure: true,
});
expect(response.status).toEqual(307);
expect(response.headers.get('Location')).toEqual('/redirect-url');
expect(response.headers.get('Location')).toEqual(
'http://localhost:3000/redirect-url'
);
});

test('returns redirect - sanitizes redirect path', async () => {
Expand All @@ -43,7 +45,9 @@ test('returns redirect - sanitizes redirect path', async () => {
const response = await GET(request);

expect(response.status).toEqual(307);
expect(response.headers.get('Location')).toEqual('/redirect-url');
expect(response.headers.get('Location')).toEqual(
'http://localhost:3000/redirect-url'
);
});

test('returns redirect to /templates/message-templates if no redirect given', async () => {
Expand All @@ -56,7 +60,7 @@ test('returns redirect to /templates/message-templates if no redirect given', as

expect(response.status).toEqual(307);
expect(response.headers.get('Location')).toEqual(
'/templates/message-templates'
'http://localhost:3000/templates/message-templates'
);
});

Expand All @@ -71,7 +75,7 @@ test('returns redirect to /auth if no session detected', async () => {
expect(cookiesMock.delete).toHaveBeenCalledWith('csrf_token');

expect(response.status).toEqual(307);
expect(response.headers.get('Location')).toEqual('/auth');
expect(response.headers.get('Location')).toEqual('https://test/auth');
});

test('retains redirect search param on /auth redirect', async () => {
Expand All @@ -86,6 +90,6 @@ test('retains redirect search param on /auth redirect', async () => {

expect(response.status).toEqual(307);
expect(response.headers.get('Location')).toEqual(
'/auth?redirect=%2Fredirect-path'
'https://test/auth?redirect=%2Fredirect-path'
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ exports[`Header component renders component correctly 1`] = `
aria-label="NHS Notify templates"
class="nhsuk-header__service-logo"
data-testid="header-logo-service-link"
href="/templates/create-and-submit-templates"
href="http://localhost:3000/templates/create-and-submit-templates"
>
<svg
class="nhsuk-header__logo"
Expand Down
78 changes: 78 additions & 0 deletions frontend/src/__tests__/utils/get-templates-url.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { getTemplatesUrl } from '@/utils/get-templates-url';

describe('getTemplatesUrl', () => {
const originalEnv = process.env;

beforeEach(() => {
jest.resetModules();
process.env = { ...originalEnv };
});

afterAll(() => {
process.env = originalEnv;
});

describe('in production', () => {
beforeEach(() => {
Object.defineProperty(process.env, 'NODE_ENV', {
value: 'production',
configurable: true,
});
process.env.NOTIFY_DOMAIN_NAME = 'nhsnotify.nhs.uk';
});

it('should use https protocol', () => {
const result = getTemplatesUrl('/templates/message-templates');
expect(result).toBe(
'https://nhsnotify.nhs.uk/templates/message-templates'
);
});

it('should generate absolute URLs for templates app', () => {
const result = getTemplatesUrl('/templates/create-email-template');
expect(result).toBe(
'https://nhsnotify.nhs.uk/templates/create-email-template'
);
});

it('should handle query parameters', () => {
const result = getTemplatesUrl('/templates/message-templates?tab=drafts');
expect(result).toBe(
'https://nhsnotify.nhs.uk/templates/message-templates?tab=drafts'
);
});

it('should fallback to localhost when NOTIFY_DOMAIN_NAME is not set', () => {
delete process.env.NOTIFY_DOMAIN_NAME;
const result = getTemplatesUrl('/templates/message-templates');
expect(result).toBe('https://localhost:3000/templates/message-templates');
});
});

describe('in development', () => {
beforeEach(() => {
Object.defineProperty(process.env, 'NODE_ENV', {
value: 'development',
configurable: true,
});
});

it('should use http protocol', () => {
const result = getTemplatesUrl('/templates/message-templates');
expect(result).toBe('http://localhost:3000/templates/message-templates');
});

it('should handle query parameters', () => {
const result = getTemplatesUrl('/templates/message-templates?tab=drafts');
expect(result).toBe(
'http://localhost:3000/templates/message-templates?tab=drafts'
);
});

it('should fallback to localhost when NOTIFY_DOMAIN_NAME is not set', () => {
delete process.env.NOTIFY_DOMAIN_NAME;
const result = getTemplatesUrl('/templates/message-templates');
expect(result).toBe('http://localhost:3000/templates/message-templates');
});
});
});
17 changes: 9 additions & 8 deletions frontend/src/app/signin/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { cookies } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
import { getSessionId } from '@/utils/amplify-utils';
import { generateSessionCsrfToken } from '@/utils/csrf-utils';
import { getTemplatesUrl } from '@/utils/get-templates-url';

export const GET = async (request: NextRequest) => {
const sessionId = await getSessionId();
Expand All @@ -23,21 +24,21 @@ export const GET = async (request: NextRequest) => {
sameSite: 'strict',
secure: true,
});

redirectPath = getTemplatesUrl(redirectPath);
} else {
cookieStore.delete('csrf_token');
redirectPath = '/auth';

const authUrl = new URL('/auth', request.url);

const redirectParam = request.nextUrl.searchParams.get('redirect');

if (redirectParam) {
redirectPath += `?redirect=${encodeURIComponent(redirectParam)}`;
authUrl.searchParams.set('redirect', redirectParam);
}

redirectPath = authUrl.toString();
}

return NextResponse.json(null, {
status: 307,
headers: {
Location: redirectPath,
},
});
return NextResponse.redirect(redirectPath, { status: 307 });
};
3 changes: 2 additions & 1 deletion frontend/src/content/content.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { formatTime } from '@/utils/format-time';
import { getConstants } from '@/utils/public-constants';
import { getTemplatesUrl } from '@/utils/get-templates-url';

const { BASE_PATH, TIME_TILL_LOGOUT_SECONDS } = getConstants();

Expand All @@ -15,7 +16,7 @@ const header = {
logoLink: {
ariaLabel: 'NHS Notify templates',
logoTitle: 'NHS logo',
href: '/templates/create-and-submit-templates',
href: getTemplatesUrl('/templates/create-and-submit-templates'),
},
accountInfo: {
ariaLabel: 'Account',
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/utils/get-templates-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function getTemplatesUrl(path: string): string {
const domain = process.env.NOTIFY_DOMAIN_NAME ?? 'localhost:3000';
const protocol = process.env.NODE_ENV === 'production' ? 'https:' : 'http:';

return `${protocol}//${domain}${path}`;
}
1 change: 1 addition & 0 deletions infrastructure/terraform/components/app/amplify_app.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ resource "aws_amplify_app" "main" {
environment_variables = {
AMPLIFY_MONOREPO_APP_ROOT = "frontend"
HOSTED_LOGIN_DOMAIN = local.auth_domain_name
NOTIFY_DOMAIN_NAME = local.root_domain_name
NOTIFY_GROUP = var.group
NOTIFY_ENVIRONMENT = var.environment
NEXT_PUBLIC_ENABLE_COGNITO_IDP = var.enable_cognito_built_in_idp
Expand Down
Loading