diff --git a/packages/api/src/apollo.ts b/packages/api/src/apollo.ts index ab27e26e0..c55ee5ecf 100644 --- a/packages/api/src/apollo.ts +++ b/packages/api/src/apollo.ts @@ -5,7 +5,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/require-await */ import { ContextFunction } from 'apollo-server-core' -import { Claims, ClaimsToSet, ResolverContext } from './resolvers/types' +import { ClaimsToSet, ResolverContext } from './resolvers/types' import { SetClaimsRole } from './utils/dictionary' import Knex, { Transaction } from 'knex' import { ExpressContext } from 'apollo-server-express/dist/ApolloServer' @@ -24,7 +24,7 @@ import ScalarResolvers from './scalars' import * as Sentry from '@sentry/node' import { createPubSubClient } from './datalayer/pubsub' import { initModels } from './server' -import { claimsFromApiKey } from './utils/auth' +import { getClaimsByToken } from './utils/auth' const signToken = promisify(jwt.sign) const logger = buildLogger('app.dispatch') @@ -39,28 +39,13 @@ const contextFunc: ContextFunction = async ({ req, res, }) => { - let claims: Claims | undefined - - const token = req?.cookies?.auth || req?.headers?.authorization - logger.info(`handling gql request`, { query: req.body.query, variables: req.body.variables, }) - if (token) { - try { - jwt.verify(token, env.server.jwtSecret) && - (claims = jwt.decode(token) as Claims) - } catch (e) { - if (e instanceof jwt.JsonWebTokenError) { - logger.info(`not a jwt token, checking api key`, { token }) - claims = await claimsFromApiKey(token) - } else { - throw e - } - } - } + const token = req?.cookies?.auth || req?.headers?.authorization + const claims = await getClaimsByToken(token) async function setClaims( tx: Transaction, diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index 930a577ba..5774483d6 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -6,13 +6,12 @@ import express from 'express' import { CreateArticleErrorCode } from './../generated/graphql' import { isSiteBlockedForParse } from './../utils/blocked' import cors from 'cors' -import { env } from './../env' import { buildLogger } from './../utils/logger' -import * as jwt from 'jsonwebtoken' import { corsConfig } from '../utils/corsConfig' import { createPageSaveRequest } from '../services/create_page_save_request' import { initModels } from '../server' import { kx } from '../datalayer/knex_config' +import { getClaimsByToken } from '../utils/auth' const logger = buildLogger('app.dispatch') @@ -26,11 +25,12 @@ export function articleRouter() { } const token = req?.cookies?.auth || req?.headers?.authorization - if (!token || !jwt.verify(token, env.server.jwtSecret)) { - return res.status(401).send({ errorCode: 'UNAUTHORIZED' }) + const claims = await getClaimsByToken(token) + if (!claims) { + return res.status(401).send('UNAUTHORIZED') } - const { uid } = (jwt.decode(token) || {}) as { uid: string } + const { uid } = claims logger.info('Article saving request', { body: req.body, diff --git a/packages/api/src/routers/svc/pdf_attachments.ts b/packages/api/src/routers/svc/pdf_attachments.ts index c408d821b..85ea23d34 100644 --- a/packages/api/src/routers/svc/pdf_attachments.ts +++ b/packages/api/src/routers/svc/pdf_attachments.ts @@ -1,6 +1,5 @@ import express from 'express' import { env } from '../../env' -import * as jwt from 'jsonwebtoken' import { PageType, UploadFileStatus } from '../../generated/graphql' import { generateUploadFilePathName, @@ -17,6 +16,7 @@ import { generateSlug } from '../../utils/helpers' import { createPubSubClient } from '../../datalayer/pubsub' import { ArticleSavingRequestStatus, Page } from '../../elastic/types' import { createPage } from '../../elastic/pages' +import { getClaimsByToken } from '../../utils/auth' export function pdfAttachmentsRouter() { const router = express.Router() @@ -31,7 +31,7 @@ export function pdfAttachmentsRouter() { } const token = req?.headers?.authorization - if (!token || !jwt.verify(token, env.server.jwtSecret)) { + if (!(await getClaimsByToken(token))) { return res.status(401).send('UNAUTHORIZED') } @@ -94,7 +94,7 @@ export function pdfAttachmentsRouter() { } const token = req?.headers?.authorization - if (!token || !jwt.verify(token, env.server.jwtSecret)) { + if (!(await getClaimsByToken(token))) { return res.status(401).send('UNAUTHORIZED') } diff --git a/packages/api/src/utils/auth.ts b/packages/api/src/utils/auth.ts index 2d2481916..12b34cce3 100644 --- a/packages/api/src/utils/auth.ts +++ b/packages/api/src/utils/auth.ts @@ -4,6 +4,8 @@ import { Claims } from '../resolvers/types' import { getRepository } from '../entity/utils' import { ApiKey } from '../entity/api_key' import crypto from 'crypto' +import * as jwt from 'jsonwebtoken' +import { env } from '../env' export const hashPassword = async (password: string, salt = 10) => { return bcrypt.hash(password, salt) @@ -49,3 +51,31 @@ export const claimsFromApiKey = async (key: string): Promise => { exp, } } + +// verify jwt token first +// if valid then decode and return claims +// if expired then throw error +// if not valid then verify api key +export const getClaimsByToken = async ( + token: string | undefined +): Promise => { + let claims: Claims | undefined + + if (!token) { + return undefined + } + + try { + jwt.verify(token, env.server.jwtSecret) && + (claims = jwt.decode(token) as Claims) + } catch (e) { + if (e instanceof jwt.JsonWebTokenError) { + console.log(`not a jwt token, checking api key`, { token }) + claims = await claimsFromApiKey(token) + } else { + throw e + } + } + + return claims +}