Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ORCID flow debugging #736

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b1af0b3
Merge pull request #421 from desci-labs/develop
hubsmoke Jul 8, 2024
78b1c19
Merge branch 'develop' of https://github.com/desci-labs/nodes
hubsmoke Jul 8, 2024
6bc029f
additional logging
hubsmoke Jul 10, 2024
8f82885
fix attach user for prod
hubsmoke Jul 10, 2024
3c9206c
thumbnail error handle
hubsmoke Jul 10, 2024
c9b02c3
Merge pull request #437 from desci-labs/develop
hubsmoke Jul 17, 2024
275c237
Merge pull request #452 from desci-labs/develop
hubsmoke Aug 17, 2024
dcc9c0a
adjust cookie setting
hubsmoke Sep 4, 2024
3333711
Merge pull request #469 from desci-labs/cookie-adjust
hubsmoke Sep 4, 2024
2eaaac7
samesite cookie
hubsmoke Sep 4, 2024
3294d8d
Merge pull request #470 from desci-labs/publish-switch
hubsmoke Sep 4, 2024
7aa2ff7
Merge pull request #498 from desci-labs/develop
hubsmoke Sep 18, 2024
503b2c5
Merge pull request #505 from desci-labs/develop
hubsmoke Sep 20, 2024
6c9e80d
Merge pull request #541 from desci-labs/develop
hubsmoke Sep 30, 2024
2d35938
Merge pull request #568 from desci-labs/develop
hubsmoke Oct 15, 2024
d1e1e64
Merge pull request #571 from desci-labs/develop
hubsmoke Oct 15, 2024
6ba04d2
Merge branch 'develop' of https://github.com/desci-labs/nodes
hubsmoke Oct 28, 2024
13709b2
Merge pull request #602 from desci-labs/develop
kadamidev Oct 29, 2024
2573cd6
Merge branch 'develop'
shadrach-tayo Oct 29, 2024
122c279
Merge pull request #611 from desci-labs/develop
hubsmoke Oct 31, 2024
7796ae4
Merge pull request #614 from desci-labs/develop
shadrach-tayo Nov 4, 2024
37723f4
Merge pull request #615 from desci-labs/develop
hubsmoke Nov 4, 2024
657cf13
Merge pull request #622 from desci-labs/develop
shadrach-tayo Nov 7, 2024
45dea5b
fix infinite loop in analytics fetch
kadamidev Dec 4, 2024
1cdb2a1
add pending month
kadamidev Dec 4, 2024
c6f3721
Merge pull request #691 from desci-labs/fix-analytics
kadamidev Dec 4, 2024
c08d04c
Merge pull request #641 from desci-labs/develop
hubsmoke Dec 6, 2024
6923bfa
Merge pull request #717 from desci-labs/develop
hubsmoke Dec 13, 2024
f3b0811
Merge pull request #718 from desci-labs/develop
kadamidev Dec 16, 2024
774a241
improve logs around auth
kadamidev Dec 23, 2024
eda4c91
more logs
kadamidev Dec 23, 2024
0d43f86
minor adjustments to link query, improve debugging auth further
kadamidev Dec 23, 2024
8c2df74
add secure logging mechanism, improve logging further
kadamidev Dec 23, 2024
d4b1154
debug orcid flow issues
kadamidev Dec 24, 2024
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
1 change: 1 addition & 0 deletions desci-server/kubernetes/deployment_dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ spec:
export ELASTIC_SEARCH_USER="{{ .Data.ELASTIC_SEARCH_USER }}"
export ELASTIC_SEARCH_PW="{{ .Data.ELASTIC_SEARCH_PW }}"
export OPEN_ALEX_DATABASE_URL="{{ .Data.OPEN_ALEX_DATABASE_URL }}"
export LOG_ENCRYPTION_KEY="{{ .Data.LOG_ENCRYPTION_KEY }}"
export DEBUG_TEST=0;
echo "appfinish";
{{- end -}}
Expand Down
1 change: 1 addition & 0 deletions desci-server/kubernetes/deployment_prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ spec:
export ELASTIC_SEARCH_USER="{{ .Data.ELASTIC_SEARCH_USER }}"
export ELASTIC_SEARCH_PW="{{ .Data.ELASTIC_SEARCH_PW }}"
export OPEN_ALEX_DATABASE_URL="{{ .Data.OPEN_ALEX_DATABASE_URL }}"
export LOG_ENCRYPTION_KEY="{{ .Data.LOG_ENCRYPTION_KEY }}"
export IGNORE_LINE=0;
export DEBUG_TEST=0;
echo "appfinish";
Expand Down
4 changes: 3 additions & 1 deletion desci-server/src/controllers/admin/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const createCsv = async (req: Request, res: Response) => {
const endMonth = new Date().getMonth();
let curYear = endYear - 1;
let curMonth = endMonth;
let monthsCovered = 0;
interface DataRow {
month: string;
year: string;
Expand All @@ -40,7 +41,7 @@ export const createCsv = async (req: Request, res: Response) => {
bytesUploaded: number;
}
const data: DataRow[] = [];
while (curYear < endYear || curMonth <= endMonth) {
while (monthsCovered <= 12) {
const newUsers = await getCountNewUsersInMonth(curMonth, curYear);
const newNodes = await getCountNewNodesInMonth(curMonth, curYear);
const activeUsers = await getCountActiveUsersInMonth(curMonth, curYear);
Expand All @@ -61,6 +62,7 @@ export const createCsv = async (req: Request, res: Response) => {
curYear++;
curMonth = 0;
}
monthsCovered++;
}
// export data to csv

Expand Down
6 changes: 3 additions & 3 deletions desci-server/src/controllers/auth/logout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const logout = async (req: Request, res: Response, next: NextFunction) =>
httpOnly: true, // Ineffective whilst we still return the bearer token to the client in the response
secure: process.env.NODE_ENV === 'production',
domain: process.env.NODE_ENV === 'production' ? '.desci.com' : 'localhost',
sameSite: 'strict',
sameSite: 'none',
path: '/',
});

Expand All @@ -21,7 +21,7 @@ export const logout = async (req: Request, res: Response, next: NextFunction) =>
httpOnly: true, // Ineffective whilst we still return the bearer token to the client in the response
secure: process.env.NODE_ENV === 'production',
domain: process.env.NODE_ENV === 'production' ? domain || '.desci.com' : 'localhost',
sameSite: 'strict',
sameSite: 'none',
path: '/',
});
});
Expand All @@ -31,7 +31,7 @@ export const logout = async (req: Request, res: Response, next: NextFunction) =>
res.cookie(AUTH_COOKIE_FIELDNAME, 'unset', {
maxAge: 0,
httpOnly: true,
sameSite: 'strict',
sameSite: 'none',
path: '/',
});
}
Expand Down
49 changes: 39 additions & 10 deletions desci-server/src/controllers/auth/magic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

import { prisma as prismaClient } from '../../client.js';
import { logger } from '../../logger.js';
import { logger as parentLogger } from '../../logger.js';
import { magicLinkRedeem, sendMagicLink } from '../../services/auth.js';
import { contributorService } from '../../services/Contributors.js';
import { saveInteraction } from '../../services/interactionLog.js';
Expand All @@ -20,15 +20,27 @@ export const oneYear = 1000 * 60 * 60 * 24 * 365;
export const oneDay = 1000 * 60 * 60 * 24;
export const oneMinute = 1000 * 60;
export const magic = async (req: Request, res: Response, next: NextFunction) => {
const { email, code, dev, orcid, access_token, refresh_token, expires_in } = req.body;
const cleanEmail = email.toLowerCase().trim();

const logger = parentLogger.child({
module: '[Auth]::Magic',
email: email,
cleanEmail: cleanEmail,
code: `${code ? 'XXXX' + code.slice(-2) : ''}`,
orcid,
});

if (process.env.NODE_ENV === 'production') {
logger.info({ fn: 'magic', email: req.body.email }, `magic link requested`);
if (code) {
logger.info({ email: req.body.email }, `[MAGIC] User attempting to auth with magic code: XXXX${code.slice(-2)}`);
} else {
logger.info({ email: req.body.email }, `[MAGIC] User requested a magic code, cleanEmail: ${cleanEmail}`);
}
} else {
logger.info({ fn: 'magic', reqBody: req.body }, `magic link`);
}

const { email, code, dev, orcid, access_token, refresh_token, expires_in } = req.body;
const cleanEmail = email.toLowerCase().trim();

if (!code) {
// we are sending the magic code

Expand Down Expand Up @@ -68,18 +80,32 @@ export const magic = async (req: Request, res: Response, next: NextFunction) =>
try {
const ip = req.ip;
const ok = await sendMagicLink(cleanEmail, ip);
res.send({ ok });
logger.info({ ok }, 'Magic link sent');
res.send({ ok: !!ok });
} catch (err) {
logger.error({ ...err, fn: 'magic' });
res.status(400).send({ ok: false, error: err.message });
logger.error({ err }, 'Failed sending code');
res.status(400).send({ ok: false, error: 'Failed sending code' });
}
} else {
// we are validating the magic code is correct
try {
const user = await magicLinkRedeem(cleanEmail, code);

if (!user) throw new Error('User not found');

if (orcid && user) {
logger.trace({ fn: 'magic', orcid }, `setting orcid for user`);
logger.trace(
{
orcid,
accessTokenLength: access_token?.length,
accessTokenPresent: !!access_token,
refreshTokenLength: refresh_token?.length,
refreshTokenPresent: !!refresh_token,
orcidAccessExpiry: expires_in,
},

`setting orcid for user`,
);

if (!user.name) {
const orcidRecord = await getOrcidRecord(orcid, access_token);
Expand All @@ -106,12 +132,15 @@ export const magic = async (req: Request, res: Response, next: NextFunction) =>
// TODO: Bearer token still returned for backwards compatability, should look to remove in the future.
res.send({ ok: true, user: { email: user.email, token, termsAccepted } });

logger.info('[MAGIC] User logged in successfully');

if (!termsAccepted) {
// saveInteraction(req, ActionType.USER_TERMS_CONSENT, { userId: user.id, email: user.email }, user.id);
}
saveInteraction(req, ActionType.USER_LOGIN, { userId: user.id }, user.id);
} catch (err) {
res.status(200).send({ ok: false, error: err.message });
logger.error({ err }, 'Failed redeeming code');
res.status(400).send({ ok: false, error: 'Failed redeeming code' });
}
}
};
29 changes: 24 additions & 5 deletions desci-server/src/controllers/nodes/preparePublishPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,19 @@ export const preparePublishPackage = async (
// debugger; //
logger.trace({ fn: 'Retrieving Publish Package' });

if (!nodeUuid) return res.status(400).json({ ok: false, error: 'nodeUuid is required.' });
if (!nodeUuid) {
logger.warn({}, 'nodeUuid is required');
return res.status(400).json({ ok: false, error: 'nodeUuid is required.' });
}
// if (!doi) return res.status(400).json({ ok: false, error: 'doi is required.' });
if (!pdfCid) return res.status(400).json({ ok: false, error: 'pdfCid is required.' });
if (!manifestCid) return res.status(400).json({ ok: false, error: 'manifestCid is required.' });
if (!pdfCid) {
logger.warn({}, 'pdfCid is required');
return res.status(400).json({ ok: false, error: 'pdfCid is required.' });
}
if (!manifestCid) {
logger.warn({}, 'manifestCid is required');
return res.status(400).json({ ok: false, error: 'manifestCid is required.' });
}

try {
const node = await prisma.node.findFirst({
Expand All @@ -59,11 +68,18 @@ export const preparePublishPackage = async (
},
});

if (!node) return res.status(404).json({ ok: false, error: 'Node not found' });
if (!node) {
logger.warn({ nodeUuid }, 'Node not found');
return res.status(404).json({ ok: false, error: 'Node not found' });
}

const manifest = await getManifestByCid(manifestCid);
if (!manifest) return res.status(404).json({ ok: false, error: 'Manifest not found' });
if (!manifest) {
logger.warn({ manifestCid }, 'Manifest not found');
return res.status(404).json({ ok: false, error: 'Manifest not found' });
}
// debugger;
logger.trace({ nodeUuid, pdfCid, doi, manifestCid }, 'Preparing distribution package');
const { pdfCid: distPdfCid } = await publishPackageService.prepareDistributionPdf({
pdfCid,
node,
Expand All @@ -77,9 +93,12 @@ export const preparePublishPackage = async (

let previewMap: PreviewMap = {};
if (withPreviews) {
logger.trace({ distPdfCid, fn: 'Generating PDF previews' });
previewMap = await publishPackageService.generatePdfPreview(distPdfCid, 1000, [1, 2], node.uuid);
}

logger.trace({ distPdfCid, previewMap, fn: 'Distribution package prepared' });

return res.status(200).json({
ok: true,
distPdfCid,
Expand Down
4 changes: 2 additions & 2 deletions desci-server/src/middleware/attachUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export const attachUser = async (req: Request, res: Response, next: NextFunction
const authHeader = req.headers['authorization'];
const apiKeyHeader = req.headers['api-key'];

const token = authHeader ? await extractAuthToken(req) : undefined;
const token = await extractAuthToken(req);
const apiKey = apiKeyHeader ? await await extractApiKey(req) : undefined;
const authTokenRetrieval = authHeader ? await extractUserFromToken(token) : undefined;
const authTokenRetrieval = await extractUserFromToken(token);
const apiKeyRetrieval = apiKeyHeader ? await extractUserFromApiKey(apiKey, req.ip) : undefined;

const retrievedUser = authTokenRetrieval || apiKeyRetrieval;
Expand Down
2 changes: 1 addition & 1 deletion desci-server/src/middleware/checkJwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const checkJwt = (req: Request, res: Response, next: NextFunction) => {
httpOnly: true, // Ineffective whilst we still return the bearer token to the client in the response
secure: process.env.NODE_ENV === 'production',
domain: process.env.NODE_ENV === 'production' ? '.desci.com' : 'localhost',
sameSite: 'strict',
sameSite: 'none',
});

return next();
Expand Down
22 changes: 21 additions & 1 deletion desci-server/src/routes/v1/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import multer from 'multer';

import { ephemeralThumbnail } from '../../controllers/proxy/ephemeralThumbnail.js';
import { orcidDid, orcidProfile } from '../../controllers/proxy/orcidProfile.js';
import { logger as parentLogger } from '../../logger.js';
import { ensureUser } from '../../middleware/permissions.js';

const router = Router();
Expand All @@ -12,6 +13,25 @@ const upload = multer();
router.get('/orcid/profile/:orcidId', [], orcidProfile);
router.get('/orcid/did/:did', [], orcidDid);

router.post('/thumbnails/ephemeral', [ensureUser, upload.single('file')], ephemeralThumbnail);
const logger = parentLogger.child({ module: 'Services UploadHandler' });

const wrappedHandler = (req, res, next) => {
upload.single('file')(req, res, (err) => {
// debugger
if (err) {
if (err instanceof multer.MulterError) {
logger.error({ err, type: 'MulterError' }, 'MulterError');
throw err;
} else {
logger.error({ err }, 'Upload Handler Error encountered');
res.status(401).send({ msg: 'unauthorized', code: '5412419' });
return;
}
}
next();
});
};

router.post('/thumbnails/ephemeral', [ensureUser, wrappedHandler], ephemeralThumbnail);

export default router;
51 changes: 48 additions & 3 deletions desci-server/src/services/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { prisma as client } from '../client.js';
import { logger as parentLogger } from '../logger.js';
import { MagicCodeEmailHtml } from '../templates/emails/utils/emailRenderer.js';
import createRandomCode from '../utils/createRandomCode.js';
import { hideEmail } from '../utils.js';
import { encryptForLog, hideEmail } from '../utils.js';

AWS.config.update({ region: 'us-east-2' });
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
Expand All @@ -29,21 +29,43 @@ const registerUser = async (email: string) => {

const magicLinkRedeem = async (email: string, token: string): Promise<User> => {
email = email.toLowerCase();
if (!email) throw Error('Email is required');
logger.trace({ fn: 'magicLinkRedeem', email: hideEmail(email) }, 'auth::magicLinkRedeem');

const link = await client.magicLink.findFirst({
where: {
email,
},
orderBy: {
createdAt: 'desc',
id: 'desc',
},
});

if (!link) {
throw Error('No magic link found for the provided email.');
}

const logEncryptionKeyPresent = process.env.LOG_ENCRYPTION_KEY && process.env.LOG_ENCRYPTION_KEY.length > 0;
logger.trace(
{
fn: 'magicLinkRedeem',
email: hideEmail(email),
tokenProvided: 'XXXX' + token.slice(-2),
tokenProvidedLength: token.length,
latestLinkFound: 'XXXX' + link.token.slice(-2),
linkEqualsToken: link.token === token,
latestLinkExpiry: link.expiresAt,
latestLinkId: link.id,
...(logEncryptionKeyPresent
? {
eTokenProvided: encryptForLog(token, process.env.LOG_ENCRYPTION_KEY),
eEmail: encryptForLog(email, process.env.LOG_ENCRYPTION_KEY),
}
: {}),
},
'[MAGIC]auth::magicLinkRedeem comparison debug',
);

if (link.failedAttempts >= 5) {
// Invalidate the token immediately
await client.magicLink.update({
Expand All @@ -69,6 +91,21 @@ const magicLinkRedeem = async (email: string, token: string): Promise<User> => {
},
},
});
logger.info(
{
fn: 'magicLinkRedeem',
linkId: link.id,
token: 'XXXX' + token.slice(-2),
...(logEncryptionKeyPresent
? {
eTokenProvided: encryptForLog(token, process.env.LOG_ENCRYPTION_KEY),
eEmail: encryptForLog(email, process.env.LOG_ENCRYPTION_KEY),
}
: {}),
newFailedAttempts: link.failedAttempts + 1,
},
'Invalid token attempt',
);
throw Error('Invalid token.');
}

Expand Down Expand Up @@ -176,10 +213,18 @@ const sendMagicLinkEmail = async (email: string, ip?: string) => {
// .sendEmail(params)
// .promise();
// const data = await sendPromise;
logger.info({ fn: 'sendMagicLinkEmail', email, msg }, 'Email sent');
logger.info({ fn: 'sendMagicLinkEmail', email, tokenSent: 'XXXX' + token.slice(-2) }, '[MAGIC]Email sent');
} catch (err) {
logger.error({ fn: 'sendMagicLinkEmail', err, email }, 'Mail error');
}
if (process.env.NODE_ENV === 'dev') {
// Print this anyway whilst developing, even if emails are being sent
const Reset = '\x1b[0m';
const BgGreen = '\x1b[42m';
const BgYellow = '\x1b[43m';
const BIG_SIGNAL = `\n\n${BgYellow}$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$${Reset}\n\n`;
logger.info(`${BIG_SIGNAL}Email sent to ${email}\n\nToken: ${BgGreen}${token}${Reset}${BIG_SIGNAL}`);
}
return true;
} else {
const Reset = '\x1b[0m';
Expand Down
Loading