Identity management using Zitadel

Andy MacConnell
8 min readNov 8, 2024

--

There are a number of Identity-as-a-service solution providers out there that offer taking on the complex task of handling user identity and access management off your hands. Coming to some form of commercial agreement and integrating one of these providers however inherently comes at a steep cost for business’s; perhaps forcing their hand into either implementing their own (if you’re considering this, please don’t) or using something off the shelf (IdentityServer, KeyCloak etc) and wrapping their own business specific implementations around it to save on cost and whilst (hopefully) solving their identity problems.

Recently, I’ve been working closely with another player in IdP game in Zitadel. In this article I’ll introduce who they are, what they provide as well as how to begin your journey with Zitadel as your IdP of choice with a working example in dotnet.

Photo by Towfiqu barbhuiya on Unsplash

Arguably now more than ever, identity plays an integral part to any business product. How can you appropriately authenticate users of your application, ensuring access is restricted to resources only suitable to their role or only enabling visibility on functionality to those within certain user groups? How can you begin to implement stricter authentication and authorization policies and mitigate risk associated with user authentication and authorization that have consistently have a foot in the OWASP Top 10?

Thankfully, this is not a problem that remains unsolved, and as mentioned above, there are a number of providers out there who offer solutions to them.

One of which is Zitadel.

Who are Zitadel?

Zitadel are an identity solution provider, based in Switzerland, who provide an open-source solution to the identity space. Their offering aims to:

foster collaboration between operations, developers, and security teams by providing a unified, transparent, and customizable solution

And having worked with the Zitadel product closely over the past few weeks, I can say that this has been my experience!

Written in Go, Zitadel is entirely open source under the Apache 2.0 licence. They offer a managed solution within the Zitadel cloud, or, the ability to self host in Linux, MacOS, Docker or Kubernetes. Despite its complexity under the hood being built container first on top of an event sourcing architecture with CQRS (as well as supporting both Postgres and CockroachDB), its simple to get up and running to give a whirl; providing a number of ways to manage your users, either through the use of their web based Console or by tapping into their suite of APIs, there are numerous ways in which to manage and configure authentication and authorization.

Topography and Structure

To understand how to configure a Zitadel instance, lets take a moment to understand the Zitadel topography:

Taken from https://zitadel.com/docs/concepts/structure/instance#instance-structure

Instance

The root of the Zitadel object structure, encompassing all other Zitadel objects

Organization

Based on Zitadel documentation, organizations are “vessels for users and projects”. An organization can be created and configured independently from others within the same instance, and hold their own policies, users and project.

Users

Supporting both “human” and “service” users based on your needs. Service users provide the ability for machine to machine communication through the use of access tokens or keys.

Project

A project is deemed “ a vessel for all components who are closely related to each other”. Within a project you can create and configure your client applications, roles, authorizations and grants to other other organizations.

Applications

Applications can be created within a given project and support a variety of client (web, native, api etc) and authorization types (PKCE, auth codes, JWT etc). As applications reside within a project, they share all the roles and authorizations of that project.

Working Example

If you don’t want to read on, then a working example of using Zitadel as an IdP can be found over on GitHub here. Smartive (author of Zitadel NuGet package) also offer examples of how to integrate.

First, let’s stand up a Zitadel instance, create an organization, project, application and user. To stand up an instance of Zitadel using Docker Compose, up the following:

services:
zitadel:
restart: 'always'
image: 'ghcr.io/zitadel/zitadel:latest'
container_name: zitadel
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled'
environment:
- 'ZITADEL_DATABASE_POSTGRES_HOST=zitadel.db'
- 'ZITADEL_DATABASE_POSTGRES_PORT=5432'
- 'ZITADEL_DATABASE_POSTGRES_DATABASE=zitadel'
- 'ZITADEL_DATABASE_POSTGRES_USER_USERNAME=zitadel'
- 'ZITADEL_DATABASE_POSTGRES_USER_PASSWORD=zitadel'
- 'ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE=disable'
- 'ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME=postgres'
- 'ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD=postgres'
- 'ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE=disable'
- 'ZITADEL_EXTERNALSECURE=false'
depends_on:
zitadel.db:
condition: 'service_healthy'
ports:
- '8080:8080'
networks:
- zitadel

zitadel.db:
restart: 'always'
image: postgres:16-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=zitadel
healthcheck:
test: ["CMD-SHELL", "pg_isready", "-d", "zitadel", "-U", "postgres"]
interval: '10s'
timeout: '30s'
retries: 5
start_period: '20s'
volumes:
- zitadel:/var/lib/postgresql/data
networks:
- zitadel

networks:
zitadel:

volumes:
zitadel: {}

The above is primarily based on the compose file provided by Zitadel, with the exception of the additional volume mount for postgres data. You should now (hopefully) see a running instance of both postgres and Zitadel:

With a running Zitadel container, head on over to http://localhost:8080/ui/console and you should be presented with the Zitadel login screen:

To log in, use the admin account that Zitadel comes boxed with and sign in. You will be prompted to change the password upon login as well as set up MFA too.

All going well, you should now be on the Zitadel console landing page

Feel free at this point to have a play around and see what Zitadel has to offer!

Creating an organization

You may have noticed that Zitadel comes out the box with a pre-configured ZITADEL organization. To access organization, the drop down at the top of the screen can be used. Creating a new organization can also be done from here. Lets create a new one:

Upon creating a new organization, a few things are pre-configured for you:

  • Default login behaviour
  • Default required password complexity and expiry
  • Verified Domains
  • Branding and copy for elements of the login process

I’ll not run through these, so feel free to have a poke about at your leisure.

Creating a project and application

With the organization in place, lets now create a project and organization. Head on over to Projects and create:

Create new project within organization
Set the project name and continue
Project landing page

With that, the project has now been created. Lets create a web client application:

We’ll revist this once we have an application up and running

Upon creation, you will be presented with a modal containing a client id and client secret. Copy these and store them somewhere for use within our application later.

With that, we’re ready to integrate with our application!

Note: if you dont have the sample application, it can be found here

Application

The application is a simple dotnet Razor web application that is configured to use Zitadel as its IdP.

It simply presents the user with a log in button on the home screen, redirects to Zitadel to authenticate, then redirects back to the home screen upon authentication. When authenticated the users name can be seen in a welcome message and their claims can be viewed from the “View Claims” screen.

Example application is available on GitHub here

First, add your client Id and client secret to configuration. To keep things simple, add these to appSettings (don’t do this beyond dev please…):

  "Zitadel": {
"BaseUri": "http://localhost:8080",
"ClientId": "<Zitadel-ProjectId-ClientId>",
"ClientSecret": "<Zitadel-ProjectId-ClientSecret>"
}

Next, lets configure the need for authentication within our web app and register Zitadel:

builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = ZitadelDefaults.AuthenticationScheme;
})
.AddCookie()
.AddZitadel(options =>
{
var zitadelSettings = builder.Configuration
.GetRequiredSection(ZitadelOptions.Section)
.Get<ZitadelOptions>();

options.ClientId = zitadelSettings!.ClientId;
options.ClientSecret = zitadelSettings.ClientSecret;
options.Authority = zitadelSettings.BaseUri;
options.RequireHttpsMetadata = false;
});

Here, we’re adding the Zitadel authentication scheme as the default challenge scheme for when we prompt the user to authenticate. When adding Zitadel, we’re mapping the above configuration to options and setting the properties for Authority, ClientId and ClientSecret.

Note: As this is development, RequireHttpsMetadata has been set to false as we’ll communicate over http. Again, this is for simplicity only and dont do this beyond dev!

With the above in place, fire up the applicaiton and attempt to log in. When authenticating, you should be presented with the following:

In order to connect the dots between our application and Zitadel, we must configure redirect uri’s within the Zitadel application we created earlier to allow Zitadel to redirect back to the application and complete the OIDC flow:

Note: if you register Zitadel using Microsofts OIDC extension method, the “signin-oidc” redirect uri is required. If you register Zitadel using the Zitadel NuGet package, the “signin-zitadel” uri is required. The port number can be taken from the launchSettings or from the browser when the application is running.

When the redirect settings have been saved, try and log in again and you should now be presented with the Zitadel log in screen:

Complete the log in process and you’ll be redirected back to the Home screen with a welcome message stating the users name:

The users claims can then be viewed via the “View Claims” page:

To Conclude

And with the above, you should now have successfully authenticated a user using Zitadel! Zitadel offers a wide range of identity management capabilities so if this has been of interest, it’s certainly worth the time to have a look around and find out a bit more. All their comprehensive documentation can be found over on their website, the source over on their GitHub and their engineering blog too. If you have Discord, you can join their server here too.

I am in no way endorsed by Zitadel and all of the above is purely based on my own experience independently.

--

--

No responses yet