graphql-dotnet / graphql-dotnet/authorization
Checking for a valid JWT and integrating with a Refresh-Token-Workflow
- Dominant language
- C#
- Stars
- 160
- Forks
- 38
- PR merge metrics
- No merged PRs in 30d
Description
I want to check if a user is authenticated (**NOT** authorized) for a specific query, resolver, etc. via a JWT Token in the `Authorization` Header. I also need to integrate this with a [Refresh-Token-Workflow](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them) on the client. I planned on using the [`authMiddleware`](https://github.com/relay-tools/react-relay-network-modern#built-in-middlewares) of [react-relay-network-modern](https://github.com/relay-tools/react-relay-network-modern).
This is the code I'm using at the moment:
```csharp
// GraphQLAuthExtensions.cs
public static class GraphQLAuthExtensions
{
public static IServiceCollection AddGraphQLAuth(this IServiceCollection services, Action configure)
{
services.TryAddSingleton();
services.TryAddTransient();
services.TryAddTransient(s =>
{
var authSettings = new AuthorizationSettings();
configure(authSettings);
return authSettings;
});
return services;
}
}
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddHttpContextAccessor();
services.AddGraphQL()
.AddUserContextBuilder(context => new GraphQLUserContext { User = context.User })
.AddSystemTextJson();
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(_jwtKey),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
services.AddGraphQLAuth(settings =>
{
settings.AddPolicy("Authenticated", p => p.RequireAuthenticatedUser());
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseAuthentication();
app.UseAuthorization();
app.UseGraphQL("/");
}
// ExampleQuery.cs
public class ExampleQuery : ObjectGraphType
{
public ExampleQuery()
{
this.AuthorizeWith("Authenticated");
// ...
}
}
```
I have a few questions regarding this:
1. Is this the correct way to check if a user has a valid JWT? It seems bloated for this rather simple use case, compared to just _slapping_ an `[Authorize]` attribute on a method or controller with ASP.NET Core REST. In the documentation I haven't found anything useful other than the `RequireAuthenticatedUser()` policy.
2. How do I return a 401 HTTP Error code to the client? At the moment I can't integrate this with a token refresh workflow, since the unauthorized response has a 200 HTTP code and the response has no real identifier that it is in fact an unauthorized response:
```json
{
"errors": [
{
"message": "You are not authorized to run this mutation.\nAn authenticated user is required.",
"locations": [{ "line": 5, "column": 3 }],
"extensions": { "code": "VALIDATION_ERROR", "codes": ["VALIDATION_ERROR"], "number": "authorization" }
}
]
}
```
The returned json only tells me that an authorization error happened:
1. The client can't differentiate if this is an Authorization error or an Authentication error and it should refresh the token.
2. It is also unnecessarily complex to query for the error code in the json, since it is an array of errors and in one query there could be errors relating to something other than authentication.
I also know that returning different HTTP error codes is not part of the GraphQL spec, since HTTP is just an agnostic transport protocol. But a Refresh-Token-Workflow is still something almost every modern SPA will have to implement. So having a solution for this would still be nice! :)
TL;DR:
1. How do I correctly check if the user has a valid JWT?
2. How do I handle refreshing authentication tokens for GraphQL in .NET?
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.