-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Returns 401 when an AuthenticationError is thrown in the context creation function. #2269
Conversation
…nd doing the rest of the homework so it can be merged
…nd doing the rest of the homework so it can be merged
OK... All ready. any thoughts on when is the next release I could expect this could be merged into if it's all good for you guys? |
Fixes #2275 |
Thanks for submitting a contribution! I can respect that you're trying to reduce the scope of the problem and target a more specific surface area (specifically, errors of a particular type thrown within the Of course, that discussion is quickly complicated since GraphQL can return partial successes/failures within the same response and a 401 status code might only reflect a certain part of the failure. I don't believe anything I'm saying here will come as a surprise, since that's been the direction of the conversation in #1709. I can see how it's tempting to make a 1:1 pairing between a GraphQL Concretely though, trying to narrow the scope to errors sourced from context creation by special-casing them and treating them differently than the errors within resolver execution (which this PR does) would seem to make it more difficult to educate implementors about the difference since the behavior would vary depending on the error's source. Additionally, we need to keep in mind that GraphQL does not only run over HTTP; Apollo Server supports using WebSockets and WebSockets subscribe to a completely different notion of status codes. This would also be a major breaking change since there's no certainty that any client which is currently consuming from the current implementation is prepared for receiving a different status code; entire implementations are likely hinging on the fact that if there's an I want to make it clear that I think it's important that we provide those who need to support this for legacy reasons a way to do it, but I don't think the current implementation in this PR is correct. I think I have an idea on how we can provide that functionality (and I think that the proper API will also help resolve #631), but we should continue to think about that in the original issue and close this PR. I realize you opened this PR to try to fix it, but I don't think we had enough buy-in to adopt this approach. Ultimately, I think we should make sure that anyone building GraphQL applications today makes sure that their client implementations are built around a multiple-response/partial-error paradigm which doesn't constrain them by singular HTTP status codes, but allows those that want different behavior to opt-into it. |
Hi @abernix. Thanks for getting back to me on this one. I get your point, I'll look at solving my issue in a different way then. The point of HTTP not being the only protocol drove it home for me. A part of me still thinks that in that case the response was not partial, the whole thing was denied, but well, I can see your PoV. |
**Note:** This currently only covers error conditions. Read below for details. This commit aims to provide user-configurable opt-in to mapping GraphQL-specific behavior to HTTP status codes under error conditions, which are not always a 1:1 mapping and often implementation specific. HTTP status codes — traditionally used for a single resource and meant to represent the success or failure of an entire request — are less natural to GraphQL which supports partial successes and failures within the same response. For example, some developers might be leveraging client implementations which rely on HTTP status codes rather than checking the `errors` property in a GraphQL response for an `AuthenticationError`. These client implementations might necessitate a 401 status code. Or as another example, perhaps there's some monitoring infrastructure in place that doesn't understand the idea of partial successes and failures. (Side-note: Apollo Engine aims to consider these partial failures, and we're continuing to improve our error observability. Feedback very much appreciated.) I've provided some more thoughts on this matter in my comment on: #2269 (comment) This implementation falls short of providing the more complete implementation which I aim to provide via a `didEnounterErrors` life-cycle hook on the new request pipeline, but it's a baby-step forward. It was peculiar, for example, that we couldn't already mostly accomplish this through the `willSendResponse` life-cycle hook. Leveraging the `willSendResponse` life-cycle hook has its limitations though. Specifically, it requires that the implementer leverage the already-formatted errors (i.e. those that are destined for the client in the response), rather than the UN-formatted errors which are more ergonomic to server-code (read: internal friendly). Details on the `didEncounterErrors` proposal are roughly discussed in this comment: #1709 (comment) (tl;dr The `didEncounterErrors` hook would receive an `errors` property which is pre-`formatError`.) As I opened this commit message with: It's critical to note that this DOES NOT currently provide the ability to override the HTTP status code for "success" conditions, which will continue to return "200 OK" for the time-being. This requires more digging around in various places to get correct, and I'd like to give it a bit more consideration. Errors seem to be the pressing matter right now. Hopefully the `didEncounterErrors` hook will come together this week.
It's very unfortunate this PR did not go through. This behavior is incredibly problematic. Not returning 401 and not being able to reasonably patch it is simply broken from an http point of view. Partials I agree and am all for general but that does not make sense when you are rejecting the request wholistically because they aren't authenticated. Ie.. from lookup in context. Apollo Server plugin infrastructure doesn't hook in to that point as it is resolved before |
Could someone tell how to handle a simple case when I'm checking Authorization token in request headers and if it's missing I want just to return |
I worked around this issue by creating an ExpressJS middleware to promote authentication errors masquerading as user-input errors to have a import { Request, Response, NextFunction } from 'express'
import { GraphQLResponse } from 'apollo-server-types'
const contextAuthError = (_req: Request, res: Response, next: NextFunction) => {
const origSend = res.send
res.send = content => {
if (res.statusCode === 400) {
const errInfo: GraphQLResponse = JSON.parse(content)
if (errInfo.errors[0].extensions.code === 'UNAUTHENTICATED') {
res.statusCode = 401
}
}
return origSend.call(res, content)
}
next()
}
export default contextAuthError where this is applied immediately preceding your server middleware, like so: app.use('/api', contextAuthError)
server.applyMiddleware({ app, path: '/api' }) |
I looked at a few public GraphQL implementations I could find -- Github, Yelp, and Shopify -- and they all returned a 401 when I try to query them without auth. I find it odd that Apollo Server makes this so difficult to do. I tried a couple things, such as using plugins, and returning a 401 in middleware (but this affects the /graphql UI too), without success, until I found @dchambers's post -- thank you for posting that workaround! |
Fixes #2275
TODO: