Skip to content

Commit 6c00f7c

Browse files
committed
fix: return HTTP 401 for unauthorized mutations and handle expired tokens
1 parent 14713ac commit 6c00f7c

3 files changed

Lines changed: 33 additions & 4 deletions

File tree

src/auth/middleware.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ async function validateTokenAndExtractUser (req: Request): Promise<CustomContext
4545
}
4646
} catch (e) {
4747
logger.error(`Can't verify JWT token ${e.toString() as string}`)
48-
throw new Error("Unauthorized. Can't verify JWT token")
48+
// Return empty user instead of throwing - allows public queries to work
49+
// Mutations will be blocked by graphql-shield permissions
50+
return { user: EMTPY_USER }
4951
}
5052
}
5153

src/auth/permissions.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { GraphQLError } from 'graphql'
12
import { allow, and, or, shield } from 'graphql-shield'
23
import { isEditor, isMediaOwner, isOwner, isUserAdmin, isValidEmail } from './rules.js'
34

@@ -24,7 +25,10 @@ const permissions = shield({
2425
},
2526
{
2627
allowExternalErrors: true,
27-
fallbackRule: allow
28+
fallbackRule: allow,
29+
fallbackError: new GraphQLError('Not Authorised!', {
30+
extensions: { code: 'FORBIDDEN' }
31+
})
2832
})
2933

3034
export default permissions

src/server.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ApolloServer } from '@apollo/server'
1+
import { ApolloServer, ApolloServerPlugin } from '@apollo/server'
22
import { expressMiddleware } from '@apollo/server/express4'
33
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer'
44
import express from 'express'
@@ -8,6 +8,26 @@ import bodyParser from 'body-parser'
88
import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache'
99

1010
import { applyMiddleware } from 'graphql-middleware'
11+
12+
/**
13+
* Plugin to return HTTP 401 for FORBIDDEN errors (unauthorized mutations)
14+
*/
15+
const httpStatusPlugin: ApolloServerPlugin = {
16+
async requestDidStart () {
17+
return {
18+
async willSendResponse ({ response }) {
19+
// Check if any error has FORBIDDEN code
20+
const hasForbiddenError = response.body.kind === 'single' &&
21+
response.body.singleResult.errors?.some(
22+
(err) => err.extensions?.code === 'FORBIDDEN'
23+
)
24+
if (hasForbiddenError) {
25+
response.http.status = 401
26+
}
27+
}
28+
}
29+
}
30+
}
1131
import { graphqlSchema } from './graphql/resolvers.js'
1232
import MutableAreaDataSource from './model/MutableAreaDataSource.js'
1333
import ChangeLogDataSource from './model/ChangeLogDataSource.js'
@@ -47,7 +67,10 @@ export async function createServer (): Promise<{ app: express.Application, serve
4767
const server = new ApolloServer({
4868
introspection: true,
4969
schema,
50-
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
70+
plugins: [
71+
ApolloServerPluginDrainHttpServer({ httpServer }),
72+
httpStatusPlugin
73+
],
5174
cache: new InMemoryLRUCache({
5275
max: 50,
5376
maxSize: 1024 * 1024 * 10

0 commit comments

Comments
 (0)