Overview
Icon for Zitadel

Zitadel

Sign in with Zitadel (OIDC)

medusa-auth-zitadel

Zitadel authentication provider for Medusa v2.

A standalone Auth Module Provider that lets customers, admin users, or custom actor types log in with Zitadel using the OpenID Connect Authorization Code flow.

Zitadel endpoints are resolved through OIDC Discovery (Copy to clipboard{issuer}/.well-known/openid-configuration), so the provider works with Zitadel Cloud and self-hosted instances behind a custom domain. The returned Copy to clipboardid_token is verified cryptographically against Zitadel's JWKS.

Requirements

  • Medusa v2 (Copy to clipboard@medusajs/framework ^2.x)
  • Node.js 20+
  • A running Zitadel instance (Cloud or self-hosted)

Installation

npm install medusa-auth-zitadel

Zitadel setup

  1. In your Zitadel instance, create (or open) a Project.
  2. Add a new application:
    • Type: Web
    • Authentication method: Code — this uses Copy to clipboardclient_secret_basic, matching the provider's default.
  3. Set the Redirect URI to exactly the Copy to clipboardcallbackUrl you will configure below (e.g. Copy to clipboardhttps://your-storefront.com/auth/zitadel/callback). It must match character for character — Zitadel rejects any mismatch during the token exchange.
  4. Copy the generated Client ID and Client Secret.

Configuration

Register the provider in the Auth Module's Copy to clipboardproviders array in Copy to clipboardmedusa-config.ts:

import { defineConfig, Modules } from "@medusajs/framework/utils"
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/auth",
options: {
providers: [
// Keep emailpass if you still need email/password login:
// passing `providers` replaces the default list.
{ resolve: "@medusajs/medusa/auth-emailpass", id: "emailpass" },
{
resolve: "medusa-auth-zitadel",
id: "zitadel",
options: {
issuer: process.env.ZITADEL_ISSUER,
clientId: process.env.ZITADEL_CLIENT_ID,
clientSecret: process.env.ZITADEL_CLIENT_SECRET,
callbackUrl: process.env.ZITADEL_CALLBACK_URL,

The Copy to clipboardid (Copy to clipboardzitadel) is the value used in authentication route paths.

Options

Option Type Required Default Description Copy to clipboardissuer Copy to clipboardstring Yes — Zitadel issuer URL, e.g. Copy to clipboardhttps://myinstance.zitadel.cloud. Trailing slashes are normalised. Copy to clipboardclientId Copy to clipboardstring Yes — OIDC client ID of the Zitadel application. Copy to clipboardclientSecret Copy to clipboardstring Yes — OIDC client secret of the Zitadel application. Copy to clipboardcallbackUrl Copy to clipboardstring Yes — URL Zitadel redirects to after authentication. Can be overridden per request with a Copy to clipboardcallback_url body parameter. Copy to clipboardscope Copy to clipboardstring No Copy to clipboardopenid profile email Scopes requested from Zitadel. Copy to clipboardclientAuthMethod Copy to clipboard"client_secret_basic" | "client_secret_post" No Copy to clipboardclient_secret_basic How the client authenticates at the token endpoint. Must match the Zitadel application's authentication method.

Environment variables

Variable Maps to Copy to clipboardZITADEL_ISSUER Copy to clipboardissuer Copy to clipboardZITADEL_CLIENT_ID Copy to clipboardclientId Copy to clipboardZITADEL_CLIENT_SECRET Copy to clipboardclientSecret Copy to clipboardZITADEL_CALLBACK_URL Copy to clipboardcallbackUrl

Actor types

By default only the Copy to clipboarduser actor type is restricted to Copy to clipboardemailpass; other actor types can authenticate with any installed provider. To control which providers each actor type may use, set Copy to clipboardprojectConfig.http.authMethodsPerActor. Note that specifying this option overrides the default, so any actor type you omit can no longer authenticate with any provider:

module.exports = defineConfig({
projectConfig: {
http: {
authMethodsPerActor: {
user: ["emailpass", "zitadel"],
customer: ["emailpass", "zitadel"],
},
},
},
})

Authentication flow

The provider implements the standard Medusa third-party authentication flow. Replace Copy to clipboardcustomer with your actor type (Copy to clipboarduser, Copy to clipboardcustomer, or a custom actor type).

1. Start the flow. The storefront asks Medusa for the Zitadel authorize URL:

curl -X POST http://localhost:9000/auth/customer/zitadel \
-H 'Content-Type: application/json' \
--data-raw '{ "callback_url": "https://your-storefront.com/auth/zitadel/callback" }'

The response contains a Copy to clipboardlocation property — the Zitadel authorize URL. Redirect the browser to it. Omit Copy to clipboardcallback_url to use the configured Copy to clipboardcallbackUrl.

{
"location": "https://myinstance.zitadel.cloud/oauth/v2/authorize?client_id=...&redirect_uri=...&response_type=code&scope=openid+profile+email&state=..."
}

2. Zitadel redirects back. After the user authenticates, Zitadel redirects the browser to your Copy to clipboardcallback_url with Copy to clipboard?code=...&state=.... The storefront forwards both to Medusa:

curl -X POST http://localhost:9000/auth/customer/zitadel/callback \
-H 'Content-Type: application/json' \
--data-raw '{ "code": "<code>", "state": "<state>" }'

3. Receive the Medusa token. On success, Medusa returns a JWT and the auth identity:

{
"token": "<medusa-jwt>",
"authIdentity": {
"id": "au_zitadel_...",
"provider_metadata": {},
"user_metadata": { "email": "user@example.com", "name": "Jane Doe" }
}
}

Use Copy to clipboardtoken as the Copy to clipboardAuthorization: Bearer <token> header for authenticated store or admin requests. If the auth identity does not exist yet, the provider creates it on first successful login.

The provider's Copy to clipboardstate value (containing the callback URL used as Copy to clipboardredirect_uri) is stored server-side via the Auth Module and consumed on callback, which protects the flow against CSRF and session replay.

Claim mapping

Zitadel's claims are mapped into Medusa's auth identity as follows.

Zitadel claim Medusa field Copy to clipboardsub Copy to clipboardentity_id Copy to clipboardemail Copy to clipboarduser_metadata.email Copy to clipboardemail_verified Copy to clipboarduser_metadata.email_verified Copy to clipboardname Copy to clipboarduser_metadata.name Copy to clipboardgiven_name Copy to clipboarduser_metadata.given_name Copy to clipboardfamily_name Copy to clipboarduser_metadata.family_name Copy to clipboardpreferred_username Copy to clipboarduser_metadata.preferred_username Copy to clipboardpicture Copy to clipboarduser_metadata.picture Copy to clipboardlocale Copy to clipboarduser_metadata.locale

Only claims actually returned by Zitadel are stored.

Troubleshooting

Symptom Cause / fix Copy to clipboardCould not fetch Zitadel OIDC discovery document Copy to clipboardissuer is wrong or unreachable. It must be the Zitadel base domain, e.g. Copy to clipboardhttps://myinstance.zitadel.cloud — not the token or authorize endpoint. Copy to clipboard... discovery document issuer "..." does not match configured issuer "..." The Copy to clipboardissuer returned by discovery differs from your setting (Zitadel may add or omit a trailing slash, or you are using a different hostname than the one Zitadel self-reports). Use the issuer exactly as Zitadel reports it. Copy to clipboardCould not exchange token with Zitadel: 400 Usually a redirect-URI mismatch. The Copy to clipboardcallbackUrl must exactly match a Redirect URI registered on the Zitadel application, including scheme, host, and path. Copy to clipboardCould not exchange token with Zitadel: 401 Copy to clipboardclientAuthMethod does not match the Zitadel application's authentication method. Zitadel Web apps default to Copy to clipboardclient_secret_basic; if you configured Post, set Copy to clipboardclientAuthMethod: "client_secret_post". Copy to clipboardCould not verify ... id_token The Copy to clipboardid_token audience or issuer did not match. Confirm Copy to clipboardclientId belongs to the Zitadel application you are authenticating against. Copy to clipboardZitadel does not support registration Expected. Zitadel SSO requires users to be provisioned in Zitadel first; there is no registration route. User can authenticate in Zitadel but gets a Medusa 401 The actor type is not allowed to use Copy to clipboardzitadel. Check Copy to clipboardprojectConfig.http.authMethodsPerActor.

Publishing

For maintainers, from the repository root:

npm login
npm publish --access public

The Copy to clipboardprepublishOnly script runs the build automatically. After publishing, submit the package to the Medusa Integrations directory.

License

MIT

medusa-auth-zitadel

You may also like

Browse all integrations

Build your own

Develop your own custom integration

Build your own integration with our API to speed up your processes. Make your integration available via npm for it to be shared in our Library with the broader Medusa community.

gift card interface

Ready to build your custom commerce setup?