graphql-dotnet / graphql-dotnet/authorization

Checking for a valid JWT and integrating with a Refresh-Token-Workflow

Open
#117 10 comments 0 reactions 0 assignees View on GitHub
question
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.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.