<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tousif</title>
    <description>The latest articles on DEV Community by Tousif (@mrtousif).</description>
    <link>https://dev.to/mrtousif</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F384967%2Fab44ce2d-ea0e-4d73-b093-116573fd19bc.jpg</url>
      <title>DEV Community: Tousif</title>
      <link>https://dev.to/mrtousif</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrtousif"/>
    <language>en</language>
    <item>
      <title>Hasura and Keycloak integration with NestJS server</title>
      <dc:creator>Tousif</dc:creator>
      <pubDate>Thu, 07 Dec 2023 17:08:07 +0000</pubDate>
      <link>https://dev.to/mrtousif/hasura-and-keycloak-integration-with-nestjs-server-without-using-passportjs-4kjh</link>
      <guid>https://dev.to/mrtousif/hasura-and-keycloak-integration-with-nestjs-server-without-using-passportjs-4kjh</guid>
      <description>&lt;p&gt;&lt;a href="https://hasura.io/" rel="noopener noreferrer"&gt;Hasura&lt;/a&gt; is an open-source real-time GraphQL API server with a strong authorization layer on your database. You can subscribe to database events via webhooks. It can combine multiple API servers into one unified graphQL API. Hasura is a great tool to build any CRUD GraphQL API. Hasura does not have any authentication mechanisms; e.g., you need an auth server to handle sign-up and sign-in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.keycloak.org/" rel="noopener noreferrer"&gt;Keycloak&lt;/a&gt; is an OpenID-compliant auth server.&lt;/p&gt;

&lt;p&gt;Hasura will validate the JWT token passed in header. The JWT token will have necessary information to handle the authorization in Hasura. Hasura can’t perform the OAuth flow. For that we’ll need an app server to handle it. App server will initiate the Sign-up or sign-in process and pass the auth tokens to client. Client app will need to send the access token in header to Hasura to get access to the protected data.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll learn how to integrate Hasura and Keycloak with RBAC.&lt;/p&gt;

&lt;p&gt;The code is available on Github&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mrtousif/hasura-keycloak-nx" rel="noopener noreferrer"&gt;GitHub - hasura-keycloak-nx&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Let’s create a docker compose yaml file and paste the following code. You can also clone the repo, which contains all the code you need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;#docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15-alpine&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres_data:/var/lib/postgresql/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./init/db:/docker-entrypoint-initdb.d/&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres -c wal_level=logical&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;5433:5432'&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${POSTGRES_DB}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${POSTGRES_USER}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${POSTGRES_PASSWORD}&lt;/span&gt;

  &lt;span class="na"&gt;hasura&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hasura&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hasura/graphql-engine:v2.29.0&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="c1"&gt;#   - keycloak&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;6080:8080'&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./hasura/metadata:/hasura-metadata&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;## postgres database to store Hasura metadata&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_METADATA_DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/hasura_metadata&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_LOG_LEVEL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warn&lt;/span&gt;
      &lt;span class="c1"&gt;## enable the console served by server&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_ENABLE_CONSOLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt; &lt;span class="c1"&gt;# set to "false" to disable console&lt;/span&gt;
      &lt;span class="c1"&gt;## enable debugging mode. It is recommended to disable this in production&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_DEV_MODE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_ENABLED_LOG_TYPES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;startup, http-log, webhook-log, websocket-log, query-log&lt;/span&gt;
      &lt;span class="c1"&gt;## enable jwt secret when keycloak realm is ready&lt;/span&gt;
      &lt;span class="c1"&gt;# HASURA_GRAPHQL_JWT_SECRET: '{ "type": "RS256", "jwk_url": "http://keycloak:8080/realms/development/protocol/openid-connect/certs" }'&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_ADMIN_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${HASURA_GRAPHQL_ADMIN_SECRET}&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_UNAUTHORIZED_ROLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anonymous&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_MIGRATIONS_SERVER_TIMEOUT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
  &lt;span class="c1"&gt;# To view tables in Postgres&lt;/span&gt;
  &lt;span class="c1"&gt;# pgweb:&lt;/span&gt;
  &lt;span class="c1"&gt;#   container_name: pgweb&lt;/span&gt;
  &lt;span class="c1"&gt;#   image: sosedoff/pgweb:latest&lt;/span&gt;
  &lt;span class="c1"&gt;#   restart: unless-stopped&lt;/span&gt;
  &lt;span class="c1"&gt;#   ports:&lt;/span&gt;
  &lt;span class="c1"&gt;#     - '8081:8081'&lt;/span&gt;
  &lt;span class="c1"&gt;#   environment:&lt;/span&gt;
  &lt;span class="c1"&gt;#     - DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable&lt;/span&gt;
  &lt;span class="c1"&gt;#   depends_on:&lt;/span&gt;
  &lt;span class="c1"&gt;#     - postgres&lt;/span&gt;
  &lt;span class="na"&gt;keycloak&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;quay.io/keycloak/keycloak:22.0.5&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;start-dev'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Uncomment following if you want to import realm configuration on start up&lt;/span&gt;
    &lt;span class="c1"&gt;# command: ['start-dev', '--import-realm']&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;## https://www.keycloak.org/server/all-config&lt;/span&gt;
      &lt;span class="na"&gt;KEYCLOAK_ADMIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
      &lt;span class="na"&gt;KEYCLOAK_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password123&lt;/span&gt;
      &lt;span class="na"&gt;KC_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;KC_DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres_pass&lt;/span&gt;
      &lt;span class="na"&gt;KC_DB_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;KC_DB_SCHEMA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
      &lt;span class="na"&gt;KC_DB_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jdbc:postgresql://postgres:5432/keycloak_db&lt;/span&gt;
      &lt;span class="na"&gt;KC_HOSTNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8090:8080&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="c1"&gt;# Uncomment following if you want to import realm configuration on start up&lt;/span&gt;
    &lt;span class="c1"&gt;# volumes:&lt;/span&gt;
    &lt;span class="c1"&gt;#   - ./realm-export.json:/opt/keycloak/data/import/realm.json:ro&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s create an env file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .env.local&lt;/span&gt;
&lt;span class="s"&gt;POSTGRES_USER=postgres&lt;/span&gt;
&lt;span class="s"&gt;POSTGRES_PASSWORD=postgres_pass&lt;/span&gt;
&lt;span class="s"&gt;POSTGRES_DB=my_app_db&lt;/span&gt;
&lt;span class="s"&gt;HASURA_GRAPHQL_ADMIN_SECRET=secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create a SQL file for the initial Postgres configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;--  init/db/00-setup.sql&lt;/span&gt;
&lt;span class="c1"&gt;--  Creating user and database for Hasura metadata&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;hasura&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'postgres'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;hasura_metadata&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;hasura_metadata&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;hasura&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;--  Creating user and database for Keycloak&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;keycloak&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;keycloak_db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;keycloak_db&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;keycloak&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets start up the containers by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose &lt;span class="nt"&gt;--env-file&lt;/span&gt; ./.env.development up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will take some time for the initial start up&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Hasura client in Keycloak
&lt;/h3&gt;

&lt;p&gt;Open up the Keycloak admin console at &lt;a href="http://localhost:8090" rel="noopener noreferrer"&gt;localhost:8090&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Admin credential: admin / password123&lt;/p&gt;

&lt;p&gt;Create a realm. Let’s name it &lt;code&gt;development&lt;/code&gt;. You should not use the &lt;code&gt;master&lt;/code&gt; realm, which is for managing Keycloak itself. Once you’ve created a realm, let’s set up hasura client in Keycloak.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jpf7lwm8n58zcis6rjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jpf7lwm8n58zcis6rjy.png" alt="01"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Clients&lt;/strong&gt; page and click on &lt;strong&gt;Create client.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enter the client name (e.g. hasura ) in &lt;strong&gt;Client ID&lt;/strong&gt; field and click next&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkexowqczr2crbyjyyxh8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkexowqczr2crbyjyyxh8.png" alt="02"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next page, enable &lt;strong&gt;Client authentication&lt;/strong&gt; and click &lt;strong&gt;Next&lt;/strong&gt;. You can also choose to enable implicit flow for easier authentication handling. You can modify this later&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fub8678563fmigs8jtk5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fub8678563fmigs8jtk5c.png" alt="03"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide a valid redirect URI and a post-logout URI and &lt;strong&gt;Save&lt;/strong&gt;. I’ve entered the URL of our NestJS app running on &lt;a href="http://localhost:7000" rel="noopener noreferrer"&gt;localhost:7000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjd6xfpbonhrvojlgkkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjd6xfpbonhrvojlgkkm.png" alt="04"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  User Roles in Keycloak
&lt;/h3&gt;

&lt;p&gt;Keycloak has two type of roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Realm roles: All the clients share them&lt;/li&gt;
&lt;li&gt;Client roles: They are available only to the client for whom it was created&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this case, we will create client roles.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Roles&lt;/strong&gt; tab of hasura client and click &lt;strong&gt;Create Role&lt;/strong&gt; then create two roles: &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;admin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslmy6ai32mvur2d52wqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslmy6ai32mvur2d52wqj.png" alt="05"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Create Mappers for Hasura JWT Claims&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When Keycloak generates the JWT, the custom claims in the JWT &lt;strong&gt;must contain the following&lt;/strong&gt; in a custom &lt;code&gt;https://hasura.io/jwt/claims&lt;/code&gt; namespace&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;x-hasura-allowed-roles&lt;/code&gt; field. A list of allowed roles for the user&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;x-hasura-default-role&lt;/code&gt; field. The role that will be used when the optional &lt;code&gt;x-hasura-role&lt;/code&gt; &lt;em&gt;header&lt;/em&gt; is not passed.&lt;/li&gt;
&lt;li&gt;Add any other optional &lt;code&gt;x-hasura-*&lt;/code&gt; claim fields (required as per your defined permissions) to the custom namespace&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Navigate to Clients → hasura → Client Scopes and click &lt;code&gt;hasura-dedicated&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuaol0t4wpnihwpwdqn8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuaol0t4wpnihwpwdqn8.png" alt="06"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Configure a new mapper&lt;/strong&gt; and select &lt;strong&gt;User Property.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add the following values and save&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;x-hasura-user-id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Property&lt;/td&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token Claim Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://hasura\.io/jwt/claims.x-hasura-user-id&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe81fqfcomecv93pjxt18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe81fqfcomecv93pjxt18.png" alt="07"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add mapper for &lt;strong&gt;User Attribute&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add the followings and save.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;x-hasura-default-role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;User Attribute&lt;/td&gt;
&lt;td&gt;x-hasura-default-role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token Claim Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://hasura\.io/jwt/claims.x-hasura-default-role&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claim value&lt;/td&gt;
&lt;td&gt;user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1c7dodam5m2wseu5mnxo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1c7dodam5m2wseu5mnxo.png" alt="08"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add mapper for &lt;strong&gt;User Client Role&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add the followings and save.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;x-hasura-allowed-roles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Client ID&lt;/td&gt;
&lt;td&gt;hasura&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token Claim Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://hasura\.io/jwt/claims.x-hasura-allowed-roles&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx1i3gdkdzm7yugs1h3u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx1i3gdkdzm7yugs1h3u.png" alt="09"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create groups&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Groups&lt;/strong&gt; tab and create two groups Admins &amp;amp; Users&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhwkw08xf98b8nwss2rcf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhwkw08xf98b8nwss2rcf.png" alt="10"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add attributes to the Groups&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Group: Users&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x-hasura-default-role&lt;/td&gt;
&lt;td&gt;user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Group: Admins&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x-hasura-default-role&lt;/td&gt;
&lt;td&gt;admin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxhxas93i9pt5qjye0a1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxhxas93i9pt5qjye0a1.png" alt="11"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assign Roles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We need to assign the role we created at the beginning. Every member of this group will have that role&lt;/p&gt;

&lt;p&gt;Group: &lt;strong&gt;Users&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click on Role mapping → Assign role → Filter by clients → &lt;code&gt;hasura user&lt;/code&gt; → Assign&lt;/p&gt;

&lt;p&gt;Group: &lt;strong&gt;Admins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click on Role mapping → Assign role → Filter by clients → &lt;code&gt;hasura admin&lt;/code&gt; → Assign&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34m64snad1t0d4lghnhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34m64snad1t0d4lghnhb.png" alt="12"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the Users tab and create two users. One is an admin user, and another is a regular user.&lt;/p&gt;

&lt;p&gt;Join the group &lt;code&gt;Admins&lt;/code&gt; when creating an admin user and &lt;code&gt;Users&lt;/code&gt; when creating regular user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ixv1pxk3wvx8f48af3q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ixv1pxk3wvx8f48af3q.png" alt="13"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After user creation, set their password. You can do this by navigating to the &lt;strong&gt;Credentials&lt;/strong&gt; tab of that user. You may turn off the &lt;strong&gt;Temporary&lt;/strong&gt; option.&lt;/p&gt;

&lt;p&gt;To make things easy to test, let’s increase the access token lifespan.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Relam settings&lt;/strong&gt; → Tokens → Access Token Lifespan → Enter 10 Hours → Save&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjq33ssp8y3hg4b2ma1ze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjq33ssp8y3hg4b2ma1ze.png" alt="14"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now set the &lt;code&gt;HASURA_GRAPHQL_JWT_SECRET&lt;/code&gt; environment variable in the docker compose file for hasura with this&lt;/p&gt;

&lt;p&gt;&lt;code&gt;'{ "type": "RS256", "jwk_url": "http://keycloak:8080/realms/development/protocol/openid-connect/certs" }'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Hasura will get the public certificate from Keycloak.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Setup app login&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you haven’t already, let's clone the repo and cd into &lt;code&gt;apps/backend_server&lt;/code&gt; in your terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tousifws/hasura-keycloak" rel="noopener noreferrer"&gt;GitHub - tousifws/hasura-keycloak&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll integrate keycloak with our NodeJS server to initiate the login. Lets grab the &lt;strong&gt;Client secret&lt;/strong&gt; by navigating to &lt;strong&gt;Clients&lt;/strong&gt; → &lt;strong&gt;hasura&lt;/strong&gt; → &lt;strong&gt;Credentials&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftg2c94c382hfc019dtnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftg2c94c382hfc019dtnk.png" alt="15"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the copied client secret to &lt;code&gt;OPENID_CLIENT_REGISTRATION_LOGIN_CLIENT_SECRET&lt;/code&gt; environment variable present in &lt;code&gt;.env.development.local&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, do &lt;code&gt;npx nx run main-server:dev&lt;/code&gt;. It’ll start the NestJS server. It’ll do the OAuth dance.&lt;/p&gt;

&lt;p&gt;Open up &lt;a href="http://localhost:7000/auth/login" rel="noopener noreferrer"&gt;http://localhost:7000/auth/login&lt;/a&gt; in your browser; it’ll redirect you to the Keycloak login page. Here you’ll need to enter the credentials of the users you created earlier.&lt;/p&gt;

&lt;p&gt;Next, open up &lt;a href="http://localhost:7000/auth/user" rel="noopener noreferrer"&gt;http://localhost:7000/auth/user&lt;/a&gt; You’ll get user information and associated tokens. You can use the access token in Hasura. Here’s a demo&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/l4lrjCC0Qes"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/janhapke/hasura-keycloak" rel="noopener noreferrer"&gt;https://github.com/janhapke/hasura-keycloak&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/h2SLNwAEQVE" rel="noopener noreferrer"&gt;https://youtu.be/h2SLNwAEQVE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nestjs</category>
      <category>hasura</category>
      <category>keycloak</category>
    </item>
  </channel>
</rss>
