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

feat: add directive to validate auth token #123

Merged
merged 9 commits into from
Dec 14, 2023
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added

- add directive to validate auth token for some operations

## [1.37.3] - 2023-12-07

### Fixed
Expand Down
33 changes: 17 additions & 16 deletions graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ type Query {
@cacheControl(scope: PRIVATE)
@settings(settingsType: "workspace")
@withSender
@auditAccess

getRole(id: ID!): Role @cacheControl(scope: PRIVATE, maxAge: SHORT)

Expand All @@ -14,7 +13,6 @@ type Query {
@settings(settingsType: "workspace")
@withSession
@withSender
@auditAccess

getFeaturesByModule(module: String!): Feature
@settings(settingsType: "workspace")
Expand All @@ -24,31 +22,28 @@ type Query {
@settings(settingsType: "workspace")
@cacheControl(scope: PRIVATE, maxAge: SHORT)
@withSender
@auditAccess

getUser(id: ID!): User @cacheControl(scope: PRIVATE)
getUser(id: ID!): User @cacheControl(scope: PRIVATE) @checkUserAccess
getB2BUser(id: ID!): User @cacheControl(scope: PRIVATE)

checkCustomerSchema: Boolean
@cacheControl(scope: PRIVATE)
@withSender
@auditAccess
checkCustomerSchema: Boolean @cacheControl(scope: PRIVATE) @withSender

getUserByEmail(email: String!): [User]
@cacheControl(scope: PRIVATE)
@withSender
@auditAccess
@checkUserAccess

listAllUsers: [User]
@cacheControl(scope: PRIVATE, maxAge: SHORT)
@withSender
@auditAccess
@checkUserAccess

listUsers(organizationId: ID, costCenterId: ID, roleId: ID): [User]
@cacheControl(scope: PRIVATE, maxAge: SHORT)
@deprecated(
reason: "This query is deprecated, use listUsersPaginated query instead."
)
@checkUserAccess

listUsersPaginated(
organizationId: ID
Expand All @@ -59,7 +54,9 @@ type Query {
search: String
sortOrder: String
sortedBy: String
): UserPagination @cacheControl(scope: PRIVATE, maxAge: SHORT)
): UserPagination
@cacheControl(scope: PRIVATE, maxAge: SHORT)
@checkUserAccess

checkImpersonation: UserImpersonation
@settings(settingsType: "workspace")
Expand All @@ -74,12 +71,17 @@ type Query {

getSessionWatcher: Boolean @cacheControl(scope: PRIVATE)

getUsersByEmail(email: String!): [User] @cacheControl(scope: PRIVATE)
getUsersByEmail(email: String!): [User]
@cacheControl(scope: PRIVATE)
@checkUserAccess

getActiveUserByEmail(email: String!): User @cacheControl(scope: PRIVATE)
getActiveUserByEmail(email: String!): User
@cacheControl(scope: PRIVATE)
@checkUserAccess

getOrganizationsByEmail(email: String!): [Organization]
@cacheControl(scope: PRIVATE)
@checkUserAccess
}

type Mutation {
Expand All @@ -90,12 +92,12 @@ type Mutation {
name: String!
slug: String
features: [FeatureInput]
): MutationResponse @cacheControl(scope: PRIVATE) @withSender @auditAccess
): MutationResponse @cacheControl(scope: PRIVATE) @withSender @checkUserAccess

deleteRole(id: ID!): MutationResponse
@cacheControl(scope: PRIVATE)
@withSender
@auditAccess
@checkUserAccess

saveUser(
id: ID
Expand Down Expand Up @@ -159,7 +161,6 @@ type Mutation {
@withSession
@cacheControl(scope: PRIVATE)
@withSender
@auditAccess
}

type UserImpersonation {
Expand Down
24 changes: 0 additions & 24 deletions node/clients/GraphQLServer.ts

This file was deleted.

88 changes: 88 additions & 0 deletions node/clients/Organizations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { InstanceOptions, IOContext } from '@vtex/api'
import { AppClient, GraphQLClient } from '@vtex/api'

import { QUERIES } from '../resolvers/Routes/utils'
import { getTokenToHeader } from './index'

const getPersistedQuery = () => {
return {
persistedQuery: {
provider: '[email protected]',
sender: '[email protected]',
},
}
}

export class OrganizationsGraphQLClient extends AppClient {
protected graphql: GraphQLClient

constructor(ctx: IOContext, options?: InstanceOptions) {
super('[email protected]', ctx, options)
this.graphql = new GraphQLClient(this.http)
}

public getOrganizationById = async (orgId: string): Promise<unknown> => {
return this.query({
extensions: getPersistedQuery(),
query: QUERIES.getOrganizationById,
variables: {
id: orgId,
},
})
}

public getB2BSettings = async (): Promise<unknown> => {
return this.query({
extensions: getPersistedQuery(),
query: QUERIES.getB2BSettings,
variables: {},
})
}

public getCostCenterById = async (costId: string): Promise<unknown> => {
return this.query({
extensions: getPersistedQuery(),
query: QUERIES.getCostCenterById,
variables: {
id: costId,
},
})
}

public getMarketingTags = async (costId: string): Promise<unknown> => {
return this.query({
extensions: getPersistedQuery(),
query: QUERIES.getMarketingTags,
variables: {
costId,
},
})
}

public getOrganizationsByEmail = async (email: string): Promise<unknown> => {
return this.query({
extensions: getPersistedQuery(),
query: QUERIES.getOrganizationsByEmail,
variables: { email },
})
}

private query = async (param: {
query: string
variables: any

Check warning on line 72 in node/clients/Organizations.ts

View workflow job for this annotation

GitHub Actions / QE / Lint Node.js

Unexpected any. Specify a different type
extensions: any

Check warning on line 73 in node/clients/Organizations.ts

View workflow job for this annotation

GitHub Actions / QE / Lint Node.js

Unexpected any. Specify a different type
}) => {
const { query, variables, extensions } = param

return this.graphql.query(
{ query, variables, extensions },
{
params: {
headers: getTokenToHeader(this.context),
locale: this.context.locale,
},
url: '/graphql',
}
)
}
}
25 changes: 20 additions & 5 deletions node/clients/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import type { IOContext } from '@vtex/api'
import { IOClients } from '@vtex/api'

import { LMClient } from '../utils/LicenseManager'
import { ProfileSystemClient } from '../utils/ProfileSystem'
import { Checkout } from './checkout'
import { GraphQLServer } from './GraphQLServer'
import FullSessions from './FullSessions'
import IdentityClient from './IdentityClient'
import { OrganizationsGraphQLClient } from './Organizations'
import { SalesChannel } from './salesChannel'
import { Schema } from './schema'
import VtexId from './vtexId'
import { SalesChannel } from './salesChannel'
import FullSessions from './FullSessions'

export const getTokenToHeader = (ctx: IOContext) => {
const token =
ctx.storeUserAuthToken ?? ctx.adminUserAuthToken ?? ctx.authToken

const { sessionToken } = ctx

return {
'x-vtex-credential': ctx.authToken,
VtexIdclientAutCookie: token,
cookie: `VtexIdclientAutCookie=${token}`,
'x-vtex-session': sessionToken ?? '',
}
}

// Extend the default IOClients implementation with our own custom clients.
export class Clients extends IOClients {
Expand All @@ -24,8 +39,8 @@ export class Clients extends IOClients {
return this.getOrSet('checkout', Checkout)
}

public get graphqlServer() {
return this.getOrSet('graphqlServer', GraphQLServer)
public get organizations() {
return this.getOrSet('organizations', OrganizationsGraphQLClient)
}

public get schema() {
Expand Down
6 changes: 3 additions & 3 deletions node/clients/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { InstanceOptions, IOContext } from '@vtex/api'
import { JanusClient } from '@vtex/api'

import { getTokenToHeader } from './index'

const getRouteSchema = (dataEntity: string) =>
`/api/dataentities/${dataEntity}/schemas`

Expand All @@ -10,9 +12,7 @@ export class Schema extends JanusClient {
...options,
headers: {
...options?.headers,
...(ctx.storeUserAuthToken
? { VtexIdclientAutCookie: ctx.storeUserAuthToken }
: { VtexIdclientAutCookie: ctx.authToken }),
...getTokenToHeader(ctx),
'x-vtex-user-agent': ctx.userAgent,
},
})
Expand Down
Loading
Loading