Skip to content

Security Considerations

When using Nikcio.UHeadless, it is important to consider security measures to protect your GraphQL data and ensure that access to sensitive information is properly controlled. This section highlights some key security considerations and provides guidance on implementing security measures.

Using Authentication and Authorization in Nikcio.UHeadless

To enable authentication and authorization in Nikcio.UHeadless follow the code example below:

Step 1: Configure the Services in the Program.cs file

In the AddUHeadless options add the following code snippet:

.AddUHeadless(options =>
{
options.AddAuth(new()
{
ApiKey = builder.Configuration.GetValue<string>("UHeadless:ApiKey") ?? throw new InvalidOperationException("No value for UHeadless:ApiKey was found"),
Secret = builder.Configuration.GetValue<string>("UHeadless:Secret") ?? throw new InvalidOperationException("No value for UHeadless:Secret was found"),
});
// Other configuration
})

The ApiKey can be any value over 32 characters long. This value is used in the X-UHeadless-Api-Key header when creating tokens used for quering the GraphQL endpoint.

The Secret can be any value over 64 characters long. This value is used as the signing key when creating the JWT tokens used for quering the GraphQL endpoint.

Also make sure to add the following code snippet to the request pipeline after the app.BootUmbracoAsync() method:

app.UseAuthentication();
app.UseAuthorization();

Step 2: Create a token for queries to the GraphQL endpoint

Once the configuration above have been added all the default queries in the package will automatically be protected and require a token to be queried. To create a token run the application, go to /graphql and run the following query:

mutation {
createToken(claims: [
{
name: "headless-scope",
value: "global.content.read"
}
]) {
expires
header
prefix
token
}
}

Make sure to include the X-UHeadless-Api-Key header with the value of the ApiKey you provided in the configuration. This will create a token that can query all content queries.

Create token example image

Step 3: Use the token to query the GraphQL endpoint

The createToken mutation will return a token in the following format:

{
"data": {
"createToken": {
"expires": 1717883090,
"header": "X-UHeadless-Token",
"prefix": "Bearer ",
"token": "<TOKEN>"
}
}
}

To query a query use the token provided along with the prefix in the header X-UHeadless-Token like so:

query {
contentByRoute(baseUrl: "", route: "/") {
id
key
name
statusCode
templateId
updateDate
url(urlMode: ABSOLUTE)
urlSegment
}
}
X-UHeadless-Token: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJoZWFkbGVzcy1zY29wZSI6Imdsb2JhbC5jb250ZW50LnJlYWQiLCJleHAiOjE3MTc3OTIyMTYsImlzcyI6Ik5pa2Npby5VSGVhZGxlc3MiLCJhdWQiOiJOaWtjaW8uVUhlYWRsZXNzIn0.Iwis2EPUjk4uY9_ZyDLYg3NRs2lTh8S-7KEaVCQTFv6nKywMmZRiPMN4Fe2uanq44WqmzWeHRqOWhp5tCGHfKA

Use token example image

Now you have successfully added authentication and authorization to your Nikcio.UHeadless application.

Different claims for different queries

To create a token with different claims for different queries you can use the createToken mutation like so:

mutation {
createToken(claims: [
{
name: "headless-scope",
value: ["global.content.read", "global.media.read"]
}
]) {
expires
header
prefix
token
}
}

or

mutation {
createToken(claims: [
{
name: "headless-scope",
value: "global.content.read"
},
{
name: "headless-scope",
value: "global.media.read"
}
]) {
expires
header
prefix
token
}
}

There exists a few different claims in the default queries for UHeadless. What claims are required for each query can be found in the documentation for the query.

See content queries, media queries or member queries for more information.

A special case for claim values are for the member picker editor. To access the data of the member picker you will need one of the following claim values: property.values.member.picker or global.member.read.

Adding security to your own queries

If you have created your own queries you can add security to them by adding the policy configuration in the ApplyConfiguration method and adding the [Authorize] attribute from HotChocolate.Authorization to the query method like so:

using HotChocolate.Authorization;
using Nikcio.UHeadless;
using Nikcio.UHeadless.Defaults.Authorization;
public class MyCustomQuery : IGraphQLQuery
{
public const string PolicyName = "MyCustomQuery";
public const string ClaimValue = "my.custom.query";
[GraphQLIgnore]
public virtual void ApplyConfiguration(UHeadlessOptions options)
{
ArgumentNullException.ThrowIfNull(options);
options.UmbracoBuilder.Services.AddAuthorizationBuilder().AddPolicy(PolicyName, policy =>
{
policy.AddAuthenticationSchemes(DefaultAuthenticationSchemes.UHeadless);
policy.RequireAuthenticatedUser();
policy.RequireClaim(DefaultClaims.UHeadlessScope, ClaimValue);
});
}
[Authorize(Policy = PolicyName)]
[GraphQLDescription("My custom query.")]
public virtual List<string> MyQuery()
{
return ["Hello", "World"];
}
}

Utillty queries

There is one utility query that can be used to find all default claims are used by the queries you have added to your application. This query is called utility_GetClaimGroups and can be used like so:

.AddUHeadless(options =>
{
options.AddQuery<UtilityClaimGroupsQuery>();
})

Query:

query {
utility_GetClaimGroups {
groupName
claimValues {
name
values
}
}
}

Response example:

{
"data": {
"utility_GetClaimGroups": [
{
"groupName": "Members",
"claimValues": [
{
"name": "headless-scope",
"values": [
"property.values.member.picker",
"global.member.read",
"find.members.by.display.name.query",
"find.members.by.email.query",
"find.members.by.role.query",
"find.members.by.username.query",
"member.by.email.query",
"member.by.guid.query",
"member.by.id.query",
"member.by.username.query"
]
}
]
},
{
"groupName": "Content",
"claimValues": [
{
"name": "headless-scope",
"values": [
"content.by.route.query",
"global.content.read",
"content.by.contentType.query",
"content.at.root.query",
"content.by.id.query",
"content.by.guid.query",
"content.by.tag.query"
]
}
]
},
{
"groupName": "Media",
"claimValues": [
{
"name": "headless-scope",
"values": [
"media.by.contentType.query",
"global.media.read",
"media.at.root.query",
"media.by.id.query",
"media.by.guid.query"
]
}
]
}
]
}
}

HotChocolate Documentation

Nikcio.UHeadless leverages the HotChocolate NuGet package, which is a powerful GraphQL server implementation for .NET. HotChocolate provides documentation on various security topics, including how to add authentication and authorization to your GraphQL queries and data.

For detailed insights into how the implementation works with HotChocolate, refer to the official HotChocolate documentation on security at:

HotChocolate Security Documentation

This documentation covers different authentication and authorization mechanisms supported by HotChocolate and provides guidelines on securing your GraphQL API effectively.

Additional or Alternative Security Measures

In addition to leveraging built-in security features and following best practices for authentication and authorization, you can implement additional security measures to protect your Nikcio.UHeadless GraphQL data. Here are some suggestions:

One suggestion is to use a reverse proxy or similar technology to route all traffic to and from the /graphql endpoint internally. By configuring the reverse proxy to allow access only from trusted sources, you can ensure that your GraphQL data remains inaccessible to the public.

Another suggestion is to only route GraphQL traffic server side to your FE application as this will prevent leaking access tokens to the client.