website/src/docs/hotchocolate/v16/securing-your-api/authentication.md
Authentication determines a user's identity. It is a prerequisite for authorization and also lets you access the authenticated user in your resolvers, for example to build a me field that returns the current user's profile.
Hot Chocolate integrates with the ASP.NET Core authentication system, so you can reuse existing authentication configuration and any supported authentication provider.
Learn more about authentication in ASP.NET Core
Setting up authentication follows the same pattern as any ASP.NET Core application. The example below uses JWT bearer tokens, but you can substitute any authentication scheme that ASP.NET Core supports.
// Program.cs
var signingKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes("MySuperSecretKey"));
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://auth.chillicream.com",
ValidAudience = "https://graphql.chillicream.com",
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey
};
});
This is an example configuration. Use a proper key management solution for production.
Register the authentication middleware in the request pipeline:
// Program.cs
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapGraphQL();
});
// Program.cs
builder
.AddGraphQL()
.AddAuthorization()
.AddQueryType<Query>();
Calling AddAuthorization() on the IRequestExecutorBuilder registers the @authorize directive and makes the authenticated user's identity available to resolvers. It does not lock out unauthenticated users. To restrict access, use authorization.
After authentication, the ClaimsPrincipal of the current user is available in your resolvers.
// Types/UserQueries.cs
[QueryType]
public static partial class UserQueries
{
public static User? GetMe(ClaimsPrincipal claimsPrincipal, UserService users)
{
var userId = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId is null)
{
return null;
}
return users.GetById(userId);
}
}
// Types/UserQueriesType.cs
public class UserQueriesType : ObjectType
{
protected override void Configure(IObjectTypeDescriptor descriptor)
{
descriptor
.Field("me")
.Resolve(context =>
{
var claimsPrincipal = context.GetUser();
var userId = claimsPrincipal?.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId is null)
{
return null;
}
var users = context.Service<UserService>();
return users.GetById(userId);
});
}
}
Use ClaimsPrincipal to read claims such as the user ID, email, or roles:
var userId = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);
var email = claimsPrincipal.FindFirstValue(ClaimTypes.Email);
var isAdmin = claimsPrincipal.IsInRole("Administrator");
If you need to add claims or identities to the ClaimsPrincipal before it reaches your resolvers, register an IHttpRequestInterceptor:
// Interceptors/HttpRequestInterceptor.cs
public class HttpRequestInterceptor : DefaultHttpRequestInterceptor
{
public override ValueTask OnCreateAsync(
HttpContext context,
IRequestExecutor requestExecutor,
OperationRequestBuilder requestBuilder,
CancellationToken cancellationToken)
{
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.Country, "us"));
context.User.AddIdentity(identity);
return base.OnCreateAsync(context, requestExecutor, requestBuilder, cancellationToken);
}
}
// Program.cs
builder
.AddGraphQL()
.AddHttpRequestInterceptor<HttpRequestInterceptor>();