Keycloak Authentication and Authorization in GraphQL
A lot of the challenges teams face on their GraphQL journey are things that have been fairly well understood in the RESTful world for a long time. One of those things is Authentication and Authorization.
There’s a lot of content covering authentication and authorization in GraphQL APIs. To summarise, there are two main ways to do it.
1. Authentication at the HTTP Level.
In this scenario you enforce authentication on your entire GraphQL endpoint. There are plenty of libraries and middlewares that do this. Depending on your needs, this approach is perfectly fine in a GraphQL API, but there is a lot more that can be done.
2. Authentication at the GraphQL Level.
With the GraphQL context
and schema directives, you can access the incoming request, perform your auth logic, and make the user details available in the GraphQL resolvers. While the first approach is pretty standard on regular HTTP servers, the second approach leads to deeper integration with the GraphQL Engine, providing more flexibility and control.
Note: You could also implement auth directly in your business logic but this is generally a bad idea.
These two approaches are widely recommended but the concrete examples out there are pretty basic. Like those simple username/password authentication using bcrypt
types of examples. These are good but when you start thinking about use cases like user management, third party login and single sign on you will find that these are really hard to implement well. In corporate enterprise environments, a more comprehensive solution is needed.
Keycloak
That’s where Keycloak comes in. Keycloak is the leading Open Source identity and access management solution that provides many features suited to enterprise needs.
- User Management
- Single Sign On
- Identity Brokering and Social Login (example: Google, Twitter, Facebook Logins)
- User Federation (LDAP and Active Directory)
Keycloak is deployed as a 3rd party service with client and server side libraries that do the heavy lifting for you.
keycloak-connect
is an Express middleware that adds authentication and authorization to your Node.js backend.keycloak-js
is a JavaScript adapter that helps you implement and manage your auth flows on the client side.
keycloak-connect
by itself will help you achieve authentication at the HTTP level. However, more work was needed to achieve it at a GraphQL level. That’s how we ended up developing keycloak-connect-graphql
.
Introducing keycloak-connect-graphql
keycloak-connect-graphql
is a comprehensive solution for adding Keycloak authentication and authorization to your Express based GraphQL server. It is used alongside keycloak-connect
to provide useful auth features at the GraphQL level.
🔒 Auth at the GraphQL layer. Authentication and Role Based Access Control (RBAC) on individual Queries, Mutations and fields.
⚡️ Auth on Subscriptions. Authentication and RBAC on incoming websocket connections for subscriptions.
🔑 Access to token/user information in resolver context via `context.kauth` (for regular resolvers and subscriptions).
📝 Declarative @auth
and @hasRole
directives that can be applied directly in your Schema.
⚙️ auth
and hasRole
middleware resolver functions that can be used directly in code (alternative to directives).
Getting Started
There are 3 steps to set up keycloak-connect-graphql
in your application.
- Add the
KeycloakTypeDefs
along with your own type defs. - Add the
KeycloakSchemaDirectives
(Apollo Server). - Add the
KeycloakContext
tocontext.kauth.
The example below shows a typical setup with comments beside each of the 3 steps mentioned.
keycloak.middleware()
parses user token information from requests, but will not block unauthenticated requests. This approach gives us the flexibility to implement authentication at a GraphQL level on individual Queries, Mutations and Fields.
The @auth and @hasRole Directives
The @auth
and @hasRole
directives can be used directly on the schema.
This declarative approach means auth logic is never mixed with business logic.
Check out the example schema and resolvers below.
A number of things are happening.
@auth
is applied to the `listArticles` Query. This means a user must be authenticated for this Query.@hasRole(role: “editor”)
is applied to thepublishArticle
Mutation. This means the Keycloak user must have the editor client role in Keycloak. An array of roles can be passed if needed.- The
publishArticle
resolver shows howcontext.kauth
can be used to get the Keycloak user details.
Note: you can use @auth
and @hasRole
on any field. For example, imagine if the Article
type had an extra field that was sensitive.
type Article {
id: ID!
title: String!
content: String!
analytics: !ArticleAnalytics @hasRole(role: “editor”)
}
In this case, @hasRole
will be enforced for any query, mutation or subscription where the client asks for that field. This is very powerful!
Some GraphQL environments do not support directives. In that case, you can use the auth
and hasRole
implementations directly at the resolver level.
This “middleware” type approach still keeps a clean separation between the auth logic and the actual business logic.
Authentication and Authorization on Subscriptions
SubscriptionServer
has an onConnect
function which can be used to validate incoming websocket connections. The KeycloakSubscriptionHandler
does this and adds the Keycloak user token to the context
in subscription resolvers.
In this example, the connectionParams
are parsed into a Keycloak token. By returning { kauth: new KeycloakSubscriptionContext }
we will have access to the token in our subscription resolvers (via context.kauth
).
If invalid connectionParams
or an expired/invalid Keycloak token is supplied, an error is thrown and the subscription is cancelled. This is an easy way to force authentication on all subscriptions.
You can also do auth on a more granular level in your subscription resolvers.
Check the README for more detail.
On the client side, The Keycloak token can be passed as part of the initial websocket connection. The example below shows how it can be done with Apollo Client.
See the Apollo Client documentation for Authentication Params Over Websocket for more information.
Working Examples
The keycloak-connect-graphql
repo also has several working example servers that you can use to see the features in action and to get sample code.
Starting and configuring a Keycloak server locally can be quite tricky and time consuming. The included scripts in the repo make it very simple! We invite you to try the examples out and of course, feedback and contributions are very welcome!