Skip to content

Commit b516bc6

Browse files
Merge pull request #1404 from guardian/ahe/newspaper-archive-check-support
Newspaper Archive auth endpoint - check supporter entitlement
2 parents a37d4af + 27df4b6 commit b516bc6

File tree

2 files changed

+93
-51
lines changed

2 files changed

+93
-51
lines changed

server/apiProxy.ts

+24-24
Original file line numberDiff line numberDiff line change
@@ -88,30 +88,6 @@ export const proxyApiHandler =
8888
outgoingURL,
8989
};
9090

91-
const authorizationOrCookieHeader = async ({
92-
req,
93-
host,
94-
}: {
95-
req: Request;
96-
host: string;
97-
}): Promise<Headers> => {
98-
// If Okta is disabled, always return the cookie header
99-
const { useOkta } = await getOktaConfig();
100-
if (!useOkta) {
101-
return {
102-
Cookie: getCookiesOrEmptyString(req),
103-
};
104-
}
105-
switch (host) {
106-
case 'members-data-api.' + conf.DOMAIN:
107-
return {
108-
Authorization: `Bearer ${req.signedCookies[OAuthAccessTokenCookieName]}`,
109-
};
110-
default:
111-
return {};
112-
}
113-
};
114-
11591
fetch(outgoingURL, {
11692
method: httpMethod,
11793
body: requestBody,
@@ -193,6 +169,30 @@ export const proxyApiHandler =
193169
});
194170
};
195171

172+
export const authorizationOrCookieHeader = async ({
173+
req,
174+
host,
175+
}: {
176+
req: Request;
177+
host: string;
178+
}): Promise<Headers> => {
179+
// If Okta is disabled, always return the cookie header
180+
const { useOkta } = await getOktaConfig();
181+
if (!useOkta) {
182+
return {
183+
Cookie: getCookiesOrEmptyString(req),
184+
};
185+
}
186+
switch (host) {
187+
case 'members-data-api.' + conf.DOMAIN:
188+
return {
189+
Authorization: `Bearer ${req.signedCookies[OAuthAccessTokenCookieName]}`,
190+
};
191+
default:
192+
return {};
193+
}
194+
};
195+
196196
export const customMembersDataApiHandler = proxyApiHandler(
197197
'members-data-api.' + conf.DOMAIN,
198198
);

server/routes/newspaperArchive.ts

+69-27
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Router } from 'express';
22
import type { Request, Response } from 'express';
33
import fetch from 'node-fetch';
4+
import { authorizationOrCookieHeader } from '../apiProxy';
45
import { s3ConfigPromise } from '../awsIntegration';
6+
import { conf } from '../config';
57
import { log } from '../log';
68
import { withIdentity } from '../middleware/identityMiddleware';
79

@@ -32,40 +34,80 @@ const router = Router();
3234
router.use(withIdentity(401));
3335

3436
router.get('/auth', async (req: Request, res: Response) => {
35-
const config = await newspaperArchiveConfigPromise;
36-
const authString = config?.authString;
37-
if (authString === undefined) {
38-
log.error(`Missing newspaper archive auth key`);
39-
res.status(500).send();
40-
}
37+
try {
38+
const config = await newspaperArchiveConfigPromise;
39+
const authString = config?.authString;
40+
if (authString === undefined) {
41+
log.error(`Missing newspaper archive auth key`);
42+
return res.sendStatus(500);
43+
}
44+
45+
const hasCorrectEntitlement = await checkSupporterEntitlement(req);
4146

42-
const authHeader = base64(`${authString}`);
43-
const requestBody: NewspapersRequestBody = {};
47+
if (!hasCorrectEntitlement) {
48+
// ToDo: show the user an error/info page
49+
return res.redirect('/');
50+
}
4451

45-
const response = await fetch(
46-
'https://www.newspapers.com/api/userauth/public/get-tpa-token',
47-
{
48-
headers: {
49-
Authorization: `Basic ${authHeader}`,
50-
'Content-Type': 'application/json',
52+
const authHeader = base64(`${authString}`);
53+
const requestBody: NewspapersRequestBody = {};
54+
55+
const response = await fetch(
56+
'https://www.newspapers.com/api/userauth/public/get-tpa-token',
57+
{
58+
headers: {
59+
Authorization: `Basic ${authHeader}`,
60+
'Content-Type': 'application/json',
61+
},
62+
method: 'POST',
63+
body: JSON.stringify(requestBody),
5164
},
52-
method: 'POST',
53-
body: JSON.stringify(requestBody),
54-
},
55-
);
65+
);
5666

57-
const responseJson = (await response.json()) as NewspapersResponseBody;
67+
// ToDo: we have zod on the server, we could parse the responses with that
68+
const responseJson = (await response.json()) as NewspapersResponseBody;
5869

59-
const archiveReturnUrlString = req.query['ncom-return-url'];
60-
if (archiveReturnUrlString && typeof archiveReturnUrlString === 'string') {
61-
const tpaToken = new URL(responseJson.url).searchParams.get('tpa');
70+
const archiveReturnUrlString = req.query['ncom-return-url'];
71+
if (
72+
archiveReturnUrlString &&
73+
typeof archiveReturnUrlString === 'string'
74+
) {
75+
const tpaToken = new URL(responseJson.url).searchParams.get('tpa');
6276

63-
const archiveReturnUrl = new URL(archiveReturnUrlString);
64-
archiveReturnUrl.searchParams.set('tpa', tpaToken ?? '');
65-
return res.redirect(archiveReturnUrl.toString());
66-
}
77+
const archiveReturnUrl = new URL(archiveReturnUrlString);
78+
archiveReturnUrl.searchParams.set('tpa', tpaToken ?? '');
79+
return res.redirect(archiveReturnUrl.toString());
80+
}
6781

68-
return res.redirect(responseJson.url);
82+
return res.redirect(responseJson.url);
83+
} catch (e) {
84+
log.error(
85+
`Something went wrong authenticating with newspapers.com. ${e}`,
86+
);
87+
return res.sendStatus(500);
88+
}
6989
});
7090

7191
export { router };
92+
93+
async function checkSupporterEntitlement(req: Request): Promise<boolean> {
94+
const supporterAttributesResponse = await getSupporterStatus(req);
95+
const supporterAttributes = await supporterAttributesResponse.json();
96+
97+
// ToDo: this should return a flag that represents either Tier 3 or a newspaperArchive specific entitlement
98+
return (
99+
supporterAttributes.contentAccess['guardianWeeklySubscriber'] &&
100+
supporterAttributes.contentAccess['supporterPlus']
101+
);
102+
}
103+
104+
async function getSupporterStatus(req: Request) {
105+
const host = 'members-data-api.' + conf.DOMAIN;
106+
107+
return fetch(`https://${host}/user-attributes/me`, {
108+
method: 'GET',
109+
headers: {
110+
...(await authorizationOrCookieHeader({ req, host })),
111+
},
112+
});
113+
}

0 commit comments

Comments
 (0)