Skip to content

Commit 0f572c4

Browse files
authored
Send Auth header for epic requests (#14794)
* Send Auth header for epic requests * Rename to getAuthHeaders
1 parent 231f326 commit 0f572c4

File tree

5 files changed

+93
-8
lines changed

5 files changed

+93
-8
lines changed

dotcom-rendering/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"@guardian/shimport": "1.0.2",
4444
"@guardian/source": "11.3.0",
4545
"@guardian/source-development-kitchen": "18.1.1",
46-
"@guardian/support-dotcom-components": "8.0.0",
46+
"@guardian/support-dotcom-components": "8.1.0",
4747
"@guardian/tsconfig": "0.2.0",
4848
"@playwright/test": "1.56.1",
4949
"@sentry/browser": "10.20.0",

dotcom-rendering/src/components/SlotBodyEnd/ReaderRevenueEpic.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type {
2020
import { useEffect, useState } from 'react';
2121
import { submitComponentEvent } from '../../client/ophan/ophan';
2222
import {
23+
getAuthHeaders,
2324
hasCmpConsentForWeeklyArticleCount,
2425
hasOptedOutOfArticleCount,
2526
shouldHideSupportMessaging,
@@ -64,7 +65,6 @@ const buildPayload = async (
6465
showSupportMessaging: !data.hideSupportMessagingForUser,
6566
epicViewLog: getEpicViewLog(storage.local),
6667
weeklyArticleHistory: await data.asyncArticleCount,
67-
6868
hasOptedOutOfArticleCount: await hasOptedOutOfArticleCount(),
6969
mvtId: Number(getCookie({ name: 'GU_mvt_id', shouldMemoize: true })),
7070
countryCode: data.countryCode,
@@ -107,9 +107,12 @@ export const canShowReaderRevenueEpic = async (
107107
hideSupportMessagingForUser,
108108
});
109109

110+
const headers = await getAuthHeaders();
111+
110112
const response: ModuleDataResponse<EpicProps> = await getEpic(
111113
contributionsServiceUrl,
112114
contributionsPayload,
115+
headers,
113116
);
114117
const module: ModuleData<EpicProps> | undefined = response.data?.module;
115118

dotcom-rendering/src/lib/contributions.test.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { setCookie, storage } from '@guardian/libs';
1+
import { onConsent as onConsent_, setCookie, storage } from '@guardian/libs';
22
import MockDate from 'mockdate';
33
import { createOrRenewCookie } from '../client/userFeatures/cookies/cookieHelpers';
44
import { HIDE_SUPPORT_MESSAGING_COOKIE } from '../client/userFeatures/cookies/hideSupportMessaging';
55
import { USER_BENEFITS_EXPIRY_COOKIE } from '../client/userFeatures/cookies/userBenefitsExpiry';
66
import {
7+
getAuthHeaders,
78
getLastOneOffContributionTimestamp,
89
hasHideSupportMessagingCookie,
910
isRecentOneOffContributor,
@@ -13,6 +14,7 @@ import {
1314
SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
1415
withinLocalNoBannerCachePeriod,
1516
} from './contributions';
17+
import { getAuthStatus as getAuthStatus_ } from './identity';
1618

1719
const clearAllCookies = () => {
1820
const cookies = document.cookie.split(';');
@@ -199,3 +201,63 @@ describe('hasSupporterCookie', () => {
199201
expect(hasHideSupportMessagingCookie(true)).toEqual('Pending');
200202
});
201203
});
204+
205+
jest.mock('@guardian/libs', () => ({
206+
...jest.requireActual('@guardian/libs'),
207+
onConsent: jest.fn(),
208+
}));
209+
jest.mock('./identity', () => ({
210+
...jest.requireActual('./identity'),
211+
getAuthStatus: jest.fn(),
212+
}));
213+
214+
const onConsent = onConsent_ as jest.MockedFunction<typeof onConsent_>;
215+
const getAuthStatus = getAuthStatus_ as jest.MockedFunction<
216+
typeof getAuthStatus_
217+
>;
218+
219+
describe('getAuthHeaders', () => {
220+
beforeEach(() => {
221+
jest.clearAllMocks();
222+
});
223+
224+
it('returns undefined when user has not consented to targeting', async () => {
225+
(onConsent as jest.Mock).mockResolvedValue({ canTarget: false });
226+
227+
const result = await getAuthHeaders();
228+
229+
expect(result).toBeUndefined();
230+
});
231+
232+
it('returns undefined when consent check fails', async () => {
233+
(onConsent as jest.Mock).mockRejectedValue(new Error('Consent error'));
234+
235+
const result = await getAuthHeaders();
236+
237+
expect(result).toBeUndefined();
238+
});
239+
240+
it('returns undefined when user has consented but is not signed in', async () => {
241+
(onConsent as jest.Mock).mockResolvedValue({ canTarget: true });
242+
(getAuthStatus as jest.Mock).mockResolvedValue({ kind: 'SignedOut' });
243+
244+
const result = await getAuthHeaders();
245+
246+
expect(result).toBeUndefined();
247+
});
248+
249+
it('returns Authorization header when user has consented and is signed in', async () => {
250+
(onConsent as jest.Mock).mockResolvedValue({ canTarget: true });
251+
(getAuthStatus as jest.Mock).mockResolvedValue({
252+
kind: 'SignedIn',
253+
accessToken: { accessToken: 'token' },
254+
});
255+
256+
const result = await getAuthHeaders();
257+
258+
expect(result).toEqual({
259+
Authorization: 'Bearer token',
260+
'X-GU-IS-OAUTH': 'true',
261+
});
262+
});
263+
});

dotcom-rendering/src/lib/contributions.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
2+
type ConsentState,
23
getCookie,
34
isUndefined,
5+
onConsent,
46
onConsentChange,
57
storage,
68
} from '@guardian/libs';
@@ -13,6 +15,7 @@ import type { ArticleDeprecated } from '../types/article';
1315
import type { Front } from '../types/front';
1416
import type { DCRNewslettersPageType } from '../types/newslettersPage';
1517
import type { TagPage } from '../types/tagPage';
18+
import { getAuthStatus, getOptionsHeaders } from './identity';
1619

1720
// User Attributes API cookies (created on sign-in)
1821
export const RECURRING_CONTRIBUTOR_COOKIE = 'gu_recurring_contributor';
@@ -223,3 +226,20 @@ export const getPurchaseInfo = (): PurchaseInfo => {
223226

224227
return purchaseInfo;
225228
};
229+
230+
const hasCanTargetConsent = (): Promise<boolean> =>
231+
onConsent()
232+
.then(({ canTarget }: ConsentState) => canTarget)
233+
.catch(() => false);
234+
235+
// Returns Auth headers only if user has targeting consent and is signed in
236+
export const getAuthHeaders = async (): Promise<HeadersInit | undefined> => {
237+
const hasConsented = await hasCanTargetConsent();
238+
if (hasConsented) {
239+
const authStatus = await getAuthStatus();
240+
if (authStatus.kind === 'SignedIn') {
241+
return getOptionsHeaders(authStatus).headers;
242+
}
243+
}
244+
return undefined;
245+
};

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)