From d13b201bf39ff6068bc03c2c2df2b40061c699bd Mon Sep 17 00:00:00 2001 From: Matheus-Aguilar <43484640+Matheus-Aguilar@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:25:53 -0300 Subject: [PATCH] fix: get admin token from header (#146) * fix: get admin token from header * chore: update CHANGELOG * fix: add token on header metrics and avoid reusing variables * fix: allow api token --- CHANGELOG.md | 3 +++ node/directives/checkAdminAccess.ts | 26 +++++++++++++++++--- node/directives/checkUserAccess.ts | 27 +++++++++++++++++++-- node/directives/helper.ts | 28 ++++++++++++++++++++++ node/directives/validateStoreUserAccess.ts | 26 ++++++++++++++++++++ node/metrics/auth.ts | 2 ++ 6 files changed, 107 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3695109..c393318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Fixed +- Get tokens from headers when necessary + ## [1.41.1] - 2024-07-15 ### Added diff --git a/node/directives/checkAdminAccess.ts b/node/directives/checkAdminAccess.ts index dd35144..8a0b91b 100644 --- a/node/directives/checkAdminAccess.ts +++ b/node/directives/checkAdminAccess.ts @@ -4,7 +4,11 @@ import type { GraphQLField } from 'graphql' import { defaultFieldResolver } from 'graphql' import sendAuthMetric, { AuthMetric } from '../metrics/auth' -import { validateAdminToken, validateApiToken } from './helper' +import { + validateAdminToken, + validateAdminTokenOnHeader, + validateApiToken, +} from './helper' export class CheckAdminAccess extends SchemaDirectiveVisitor { public visitFieldDefinition(field: GraphQLField) { @@ -27,6 +31,12 @@ export class CheckAdminAccess extends SchemaDirectiveVisitor { hasValidAdminTokenFromStore, } = await validateAdminToken(context, adminUserAuthToken as string) + const { + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, + hasCurrentValidAdminTokenOnHeader, + } = await validateAdminTokenOnHeader(context) + const { hasApiToken, hasValidApiToken, hasValidApiTokenFromStore } = await validateApiToken(context) @@ -52,6 +62,8 @@ export class CheckAdminAccess extends SchemaDirectiveVisitor { hasApiToken, hasValidApiToken, hasStoreToken, + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, hasValidAdminTokenFromStore, hasValidApiTokenFromStore, }, @@ -60,7 +72,7 @@ export class CheckAdminAccess extends SchemaDirectiveVisitor { sendAuthMetric(logger, auditMetric) - if (!hasAdminToken) { + if (!hasAdminToken && !hasApiToken && !hasAdminTokenOnHeader) { logger.warn({ message: 'CheckAdminAccess: No token provided', userAgent, @@ -72,13 +84,19 @@ export class CheckAdminAccess extends SchemaDirectiveVisitor { hasApiToken, hasValidApiToken, hasStoreToken, + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, hasValidAdminTokenFromStore, hasValidApiTokenFromStore, }) throw new AuthenticationError('No token was provided') } - if (!hasCurrentValidAdminToken) { + if ( + !hasCurrentValidAdminToken && + !hasValidApiToken && + !hasCurrentValidAdminTokenOnHeader + ) { logger.warn({ message: 'CheckAdminAccess: Invalid token', userAgent, @@ -90,6 +108,8 @@ export class CheckAdminAccess extends SchemaDirectiveVisitor { hasApiToken, hasValidApiToken, hasStoreToken, + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, hasValidAdminTokenFromStore, hasValidApiTokenFromStore, }) diff --git a/node/directives/checkUserAccess.ts b/node/directives/checkUserAccess.ts index ed0cd72..313e69d 100644 --- a/node/directives/checkUserAccess.ts +++ b/node/directives/checkUserAccess.ts @@ -6,6 +6,7 @@ import { SchemaDirectiveVisitor } from 'graphql-tools' import sendAuthMetric, { AuthMetric } from '../metrics/auth' import { validateAdminToken, + validateAdminTokenOnHeader, validateApiToken, validateStoreToken, } from './helper' @@ -31,6 +32,12 @@ export class CheckUserAccess extends SchemaDirectiveVisitor { hasValidAdminTokenFromStore, } = await validateAdminToken(context, adminUserAuthToken as string) + const { + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, + hasCurrentValidAdminTokenOnHeader, + } = await validateAdminTokenOnHeader(context) + const { hasApiToken, hasValidApiToken, hasValidApiTokenFromStore } = await validateApiToken(context) @@ -58,6 +65,8 @@ export class CheckUserAccess extends SchemaDirectiveVisitor { hasValidApiToken, hasStoreToken, hasValidStoreToken, + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, hasValidAdminTokenFromStore, hasValidApiTokenFromStore, }, @@ -66,7 +75,12 @@ export class CheckUserAccess extends SchemaDirectiveVisitor { sendAuthMetric(logger, auditMetric) - if (!hasAdminToken && !hasStoreToken) { + if ( + !hasAdminToken && + !hasApiToken && + !hasStoreToken && + !hasAdminTokenOnHeader + ) { logger.warn({ message: 'CheckUserAccess: No token provided', userAgent, @@ -78,13 +92,20 @@ export class CheckUserAccess extends SchemaDirectiveVisitor { hasApiToken, hasValidApiToken, hasStoreToken, + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, hasValidAdminTokenFromStore, hasValidApiTokenFromStore, }) throw new AuthenticationError('No token was provided') } - if (!hasCurrentValidAdminToken && !hasCurrentValidStoreToken) { + if ( + !hasCurrentValidAdminToken && + !hasValidApiToken && + !hasCurrentValidStoreToken && + !hasCurrentValidAdminTokenOnHeader + ) { logger.warn({ message: `CheckUserAccess: Invalid token`, userAgent, @@ -97,6 +118,8 @@ export class CheckUserAccess extends SchemaDirectiveVisitor { hasValidApiToken, hasStoreToken, hasValidStoreToken, + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, hasValidAdminTokenFromStore, hasValidApiTokenFromStore, }) diff --git a/node/directives/helper.ts b/node/directives/helper.ts index df93939..58bf92a 100644 --- a/node/directives/helper.ts +++ b/node/directives/helper.ts @@ -166,3 +166,31 @@ export const validateStoreToken = async ( return { hasStoreToken, hasValidStoreToken, hasCurrentValidStoreToken } } + +export const validateAdminTokenOnHeader = async ( + context: Context +): Promise<{ + hasAdminTokenOnHeader: boolean + hasValidAdminTokenOnHeader: boolean + hasCurrentValidAdminTokenOnHeader: boolean +}> => { + const adminUserAuthToken = context?.headers.vtexidclientautcookie as string + const hasAdminTokenOnHeader = !!adminUserAuthToken?.length + + if (!hasAdminTokenOnHeader) { + return { + hasAdminTokenOnHeader: false, + hasValidAdminTokenOnHeader: false, + hasCurrentValidAdminTokenOnHeader: false, + } + } + + const { hasAdminToken, hasCurrentValidAdminToken, hasValidAdminToken } = + await validateAdminToken(context, adminUserAuthToken) + + return { + hasAdminTokenOnHeader: hasAdminToken, + hasValidAdminTokenOnHeader: hasValidAdminToken, + hasCurrentValidAdminTokenOnHeader: hasCurrentValidAdminToken, + } +} diff --git a/node/directives/validateStoreUserAccess.ts b/node/directives/validateStoreUserAccess.ts index 6020880..76f0149 100644 --- a/node/directives/validateStoreUserAccess.ts +++ b/node/directives/validateStoreUserAccess.ts @@ -7,6 +7,7 @@ import type { AuthAuditMetric } from '../metrics/auth' import sendAuthMetric, { AuthMetric } from '../metrics/auth' import { validateAdminToken, + validateAdminTokenOnHeader, validateApiToken, validateStoreToken, } from './helper' @@ -66,6 +67,31 @@ export class ValidateStoreUserAccess extends SchemaDirectiveVisitor { return resolve(root, args, context, info) } + // If there's no valid admin token on context, search for it on header + const { hasAdminTokenOnHeader, hasValidAdminTokenOnHeader } = + await validateAdminTokenOnHeader(context) + + // add admin header token metrics + metricFields = { + ...metricFields, + hasAdminTokenOnHeader, + hasValidAdminTokenOnHeader, + } + + // allow access if has valid admin token + if (hasValidAdminTokenOnHeader) { + sendAuthMetric( + logger, + new AuthMetric( + context?.vtex?.account, + metricFields, + 'ValidateStoreUserAccessAudit' + ) + ) + + return resolve(root, args, context, info) + } + const { hasApiToken, hasValidApiToken, hasValidApiTokenFromStore } = await validateApiToken(context) diff --git a/node/metrics/auth.ts b/node/metrics/auth.ts index 36293f5..9b6733f 100644 --- a/node/metrics/auth.ts +++ b/node/metrics/auth.ts @@ -16,6 +16,8 @@ export interface AuthAuditMetric { hasValidStoreToken?: boolean hasApiToken?: boolean hasValidApiToken?: boolean + hasAdminTokenOnHeader?: boolean + hasValidAdminTokenOnHeader?: boolean hasValidAdminTokenFromStore?: boolean hasValidApiTokenFromStore?: boolean }