@open-mercato/enterprise 0.4.6-develop-34aa847ce6 → 0.4.6-develop-d0884fe560

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/enterprise",
3
- "version": "0.4.6-develop-34aa847ce6",
3
+ "version": "0.4.6-develop-d0884fe560",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -64,9 +64,9 @@
64
64
  }
65
65
  },
66
66
  "dependencies": {
67
- "@open-mercato/core": "0.4.6-develop-34aa847ce6",
68
- "@open-mercato/shared": "0.4.6-develop-34aa847ce6",
69
- "@open-mercato/ui": "0.4.6-develop-34aa847ce6",
67
+ "@open-mercato/core": "0.4.6-develop-d0884fe560",
68
+ "@open-mercato/shared": "0.4.6-develop-d0884fe560",
69
+ "@open-mercato/ui": "0.4.6-develop-d0884fe560",
70
70
  "openid-client": "^6.3.3"
71
71
  },
72
72
  "devDependencies": {
@@ -1,281 +0,0 @@
1
- # Microsoft Entra ID Setup Guide for Open Mercato SSO + SCIM
2
-
3
- This guide walks through setting up Microsoft Entra ID (formerly Azure AD) as the identity provider for both OIDC login and SCIM user provisioning in Open Mercato.
4
-
5
- **Free tier**: Entra ID Free is included with any Azure subscription — no paid license required for basic OIDC + SCIM.
6
-
7
- ---
8
-
9
- ## 1. Create an Azure Account + Entra ID Tenant
10
-
11
- 1. Go to https://azure.microsoft.com/free and create a free account (or use an existing one)
12
- 2. Navigate to https://entra.microsoft.com (the Entra admin center)
13
- 3. You'll have a default tenant — note your **Tenant ID** from **Overview** → **Tenant ID**
14
-
15
- ## 2. Create Test Users
16
-
17
- 1. In the Entra admin center, go to **Identity** → **Users** → **All users**
18
- 2. Click **+ New user** → **Create new user**
19
- 3. Fill in:
20
- - **User principal name**: e.g., `testuser@yourtenant.onmicrosoft.com`
21
- - **Display name**: e.g., `Test User`
22
- - **First name** / **Last name**
23
- - **Password**: auto-generate or set manually
24
- 4. Click **Create**
25
- 5. Repeat for 2-3 test users
26
-
27
- ## 3. Register the OIDC Application (SSO Login)
28
-
29
- 1. In the Entra admin center, go to **Identity** → **Applications** → **App registrations**
30
- 2. Click **+ New registration**
31
- 3. Configure:
32
-
33
- | Field | Value |
34
- |-------|-------|
35
- | **Name** | `Open Mercato` |
36
- | **Supported account types** | `Accounts in this organizational directory only` (Single tenant) |
37
- | **Redirect URI** | Platform: `Web`, URI: `http://localhost:3000/api/sso/callback/oidc` |
38
-
39
- 4. Click **Register**
40
- 5. You'll land on the app's **Overview** page — note:
41
- - **Application (client) ID** — this is your Client ID
42
- - **Directory (tenant) ID** — used in the issuer URL
43
-
44
- ### Create a Client Secret
45
-
46
- 1. Go to **Certificates & secrets** → **Client secrets** tab
47
- 2. Click **+ New client secret**
48
- 3. Description: `Open Mercato Dev`, Expiry: `6 months` (or your preference)
49
- 4. Click **Add**
50
- 5. **Copy the secret Value immediately** — it's shown only once
51
-
52
- ### OIDC Credentials Summary
53
-
54
- | Credential | Where to find it | Value |
55
- |------------|-----------------|-------|
56
- | **Issuer URL** | Computed from Tenant ID | `https://login.microsoftonline.com/{tenant-id}/v2.0` |
57
- | **Client ID** | App registration → Overview | Copy from portal |
58
- | **Client Secret** | App registration → Certificates & secrets | Copy the **Value** (not Secret ID) |
59
- | **Redirect URI** | You configured this | `http://localhost:3000/api/sso/callback/oidc` |
60
-
61
- ### Configure Token Claims
62
-
63
- By default, Entra ID v2.0 tokens may not include `email` in the ID token. Fix this:
64
-
65
- 1. Go to your App registration → **Token configuration**
66
- 2. Click **+ Add optional claim**
67
- 3. Token type: **ID**
68
- 4. Check: `email`, `given_name`, `family_name`
69
- 5. Click **Add**
70
- 6. When prompted about Microsoft Graph permissions, check the box and click **Add**
71
-
72
- ### API Permissions
73
-
74
- 1. Go to **API permissions**
75
- 2. Verify these are present (they should be by default):
76
- - `Microsoft Graph` → `openid` (Delegated)
77
- - `Microsoft Graph` → `profile` (Delegated)
78
- - `Microsoft Graph` → `email` (Delegated)
79
- 3. If any are missing, click **+ Add a permission** → **Microsoft Graph** → **Delegated permissions** → search and add them
80
- 4. Click **Grant admin consent for [your tenant]** (green checkmark button)
81
-
82
- ### Assign Users to the Application
83
-
84
- 1. Go to **Identity** → **Applications** → **Enterprise applications**
85
- 2. Find and click **Open Mercato**
86
- 3. Go to **Users and groups** → **+ Add user/group**
87
- 4. Select your test users (or a group containing them)
88
- 5. Click **Assign**
89
-
90
- **Important**: If "Assignment required?" is set to **Yes** (under Properties), only assigned users can log in. Set to **No** for dev if you want all tenant users to access it.
91
-
92
- ---
93
-
94
- ## 4. Create the SSO Config in Open Mercato
95
-
96
- 1. Log into Open Mercato as admin
97
- 2. Go to **Settings** → **Single Sign-On** → **Create New**
98
- 3. Select **OIDC** as the protocol
99
- 4. Enter:
100
- - **Name**: `Entra ID`
101
- - **Issuer URL**: `https://login.microsoftonline.com/{your-tenant-id}/v2.0`
102
- - **Client ID**: (paste from Entra)
103
- - **Client Secret**: (paste the secret Value from Entra)
104
- 5. Add allowed email domains (e.g., `yourtenant.onmicrosoft.com`)
105
- 6. Test the connection (Verify Discovery)
106
- 7. Activate the config
107
-
108
- ### Verify OIDC Login
109
-
110
- 1. Open a private/incognito browser window
111
- 2. Go to the Open Mercato login page
112
- 3. Enter an email address belonging to one of your test users (e.g., `testuser@yourtenant.onmicrosoft.com`)
113
- 4. The HRD check should detect SSO and redirect to Microsoft login
114
- 5. Authenticate at Microsoft
115
- 6. You should be redirected back to Open Mercato and logged in
116
-
117
- ---
118
-
119
- ## 5. Configure SCIM Provisioning
120
-
121
- **Prerequisite**: You need a SCIM bearer token from Open Mercato. Generate one via:
122
- - The admin UI: SSO config → Provisioning tab → Generate Token
123
- - Or the API: `POST /api/sso/scim/tokens` with the SSO config ID
124
-
125
- ### Set Up Provisioning in Entra ID
126
-
127
- 1. Go to **Identity** → **Applications** → **Enterprise applications**
128
- 2. Find and click **Open Mercato**
129
- 3. Go to **Provisioning** → click **Get started**
130
- 4. Set **Provisioning Mode** to **Automatic**
131
- 5. In **Admin Credentials**:
132
-
133
- | Field | Value |
134
- |-------|-------|
135
- | **Tenant URL** | `http://localhost:3000/api/sso/scim/v2` (dev) or `https://<your-domain>/api/sso/scim/v2` (prod) |
136
- | **Secret Token** | Paste the SCIM bearer token from Open Mercato |
137
-
138
- 6. Click **Test Connection** — should show "The supplied credentials are authorized to enable provisioning"
139
- 7. Click **Save**
140
-
141
- ### Configure Attribute Mappings
142
-
143
- 1. Under **Mappings**, click **Provision Microsoft Entra ID Users**
144
- 2. Verify these mappings exist:
145
-
146
- | Entra ID Attribute | SCIM Attribute | Notes |
147
- |--------------------|----------------|-------|
148
- | `userPrincipalName` | `userName` | Required |
149
- | `Switch([IsSoftDeleted]...)` | `active` | Required — Entra uses a Switch expression |
150
- | `givenName` | `name.givenName` | Required |
151
- | `surname` | `name.familyName` | Required |
152
- | `mail` | `emails[type eq "work"].value` | Required — user's email |
153
- | `displayName` | `displayName` | Optional |
154
- | `objectId` | `externalId` | Required — Entra's unique ID |
155
-
156
- 3. Keep default mappings — they should work out of the box
157
- 4. Click **Save**
158
-
159
- ### Start Provisioning
160
-
161
- 1. Back on the **Provisioning** page, set **Provisioning Status** to **On**
162
- 2. Click **Save**
163
- 3. Entra will run an initial provisioning cycle (may take up to 40 minutes for the first cycle)
164
- 4. Check **Provisioning logs** for results
165
-
166
- ### Provisioning Cycle Timing
167
-
168
- - **Initial cycle**: Processes all users in scope. Can take 20-40 minutes.
169
- - **Incremental cycles**: Every 40 minutes, processes changes since last cycle.
170
- - **On-demand provisioning**: Click **Provision on demand** to immediately provision a specific user (useful for testing).
171
-
172
- ---
173
-
174
- ## 6. Test the Full Flow
175
-
176
- ### Test SCIM Provisioning
177
-
178
- 1. In Entra, go to **Enterprise applications** → **Open Mercato** → **Provisioning**
179
- 2. Click **Provision on demand**
180
- 3. Search for a test user and click **Provision**
181
- 4. **Expected**: Entra sends `POST /Users` to your SCIM endpoint → user appears in Open Mercato
182
- 5. Check the provisioning log in Open Mercato admin UI
183
-
184
- ### Test User Update
185
-
186
- 1. In Entra, go to **Users** → edit a test user's display name
187
- 2. Wait for the next provisioning cycle (or use Provision on demand)
188
- 3. **Expected**: Entra sends `PATCH /Users/{id}` → user's name updated in Open Mercato
189
-
190
- ### Test User Deactivation
191
-
192
- 1. In Entra, either:
193
- - **Delete** the user (soft-delete moves to Deleted users)
194
- - **Block sign-in** for the user (Users → select user → Edit properties → Block sign in: Yes)
195
- - **Remove** the user from the application assignment
196
- 2. **Expected**: Entra sends `PATCH /Users/{id}` with `active: false` → user deactivated in Open Mercato, all sessions revoked
197
-
198
- ### Test OIDC + SCIM Together
199
-
200
- 1. **Create a new user in Entra** and assign them to the Open Mercato Enterprise app
201
- 2. **Provision on demand** (or wait for cycle)
202
- 3. **Verify** the user exists in Open Mercato (pre-provisioned, no login needed)
203
- 4. **Log in as that user** via OIDC (Open Mercato login → redirect to Microsoft → authenticate → redirect back)
204
- 5. **Expected**: The SCIM-provisioned account is used (no JIT provisioning, `provisioningMethod` stays `scim`)
205
- 6. **Block sign-in for the user in Entra**
206
- 7. **Expected**: SCIM deactivates the user → existing sessions revoked → OIDC login no longer works
207
-
208
- ---
209
-
210
- ## Entra ID SCIM Quirks
211
-
212
- When building the SCIM endpoint, account for these Entra-specific behaviors:
213
-
214
- | Quirk | Description | How to handle |
215
- |-------|-------------|---------------|
216
- | **PascalCase `op` in PATCH** | Entra sends `"op": "Replace"` instead of `"op": "replace"` | Case-insensitive comparison on PATCH operations |
217
- | **String booleans** | `active` may be sent as `"True"` / `"False"` strings | Parse with `parseBooleanToken` |
218
- | **Non-standard PATCH paths** | Sometimes sends `emails[type eq "work"].value` in PATCH path | Support bracket-notation in PATCH path parser |
219
- | **Mixed-case filter operators** | Sends `Eq` instead of `eq` in filters | Case-insensitive filter parsing |
220
- | **`externalId` mapping** | Maps `objectId` → `externalId` by default | Always store `externalId` from SCIM requests |
221
- | **Soft delete** | Uses `IsSoftDeleted` Switch expression → `active: false` | Handle as user deactivation |
222
-
223
- ---
224
-
225
- ## Troubleshooting
226
-
227
- ### OIDC login redirects but fails
228
-
229
- - Verify the Redirect URI in App Registration matches exactly: `http://localhost:3000/api/sso/callback/oidc`
230
- - Check that the Issuer URL includes the tenant ID: `https://login.microsoftonline.com/{tenant-id}/v2.0`
231
- - Verify Client ID and Client Secret (the Value, not the Secret ID)
232
- - Ensure `email` optional claim is added to the ID token
233
- - Ensure API permissions have admin consent granted
234
-
235
- ### "AADSTS50011: The redirect URI does not match"
236
-
237
- The redirect URI in the authorization request doesn't match what's registered. Check:
238
- - `APP_URL` in `.env` matches what you registered (e.g., `http://localhost:3000`)
239
- - No trailing slash differences
240
- - Protocol matches (http vs https)
241
-
242
- ### Users not provisioning
243
-
244
- - Check that users are assigned to the Enterprise application
245
- - Check **Provisioning logs** in Entra for error details
246
- - Verify the SCIM token is valid and not revoked
247
- - For local dev, Entra needs to reach your server — use ngrok for SCIM (even though OIDC works with localhost)
248
-
249
- ### SCIM "Test Connection" fails
250
-
251
- - For local dev, Entra's provisioning service needs to reach your endpoint over the internet
252
- - Use ngrok: `ngrok http 3000`
253
- - Set Tenant URL to: `https://<id>.ngrok-free.app/api/sso/scim/v2`
254
- - **Note**: OIDC redirect URIs can use `localhost`, but SCIM provisioning requires a publicly reachable URL
255
-
256
- ### email claim missing from ID token
257
-
258
- 1. Go to App registration → **Token configuration** → **+ Add optional claim** → ID token → check `email`
259
- 2. Go to **API permissions** → verify `email` permission → click **Grant admin consent**
260
-
261
- ---
262
-
263
- ## Key Differences from JumpCloud
264
-
265
- | Aspect | Entra ID | JumpCloud |
266
- |--------|----------|-----------|
267
- | **Issuer URL** | `https://login.microsoftonline.com/{tenant-id}/v2.0` | `https://oauth.id.jumpcloud.com/` |
268
- | **Redirect URI** | Supports `http://localhost` for dev | Requires HTTPS |
269
- | **SCIM provisioning** | Enterprise App → Provisioning (automatic) | SSO App → Identity Management (SCIM API) |
270
- | **Provisioning cycles** | Every 40 minutes (or on-demand) | Near real-time |
271
- | **SCIM quirks** | PascalCase ops, string booleans, mixed-case filters | Mostly spec-compliant |
272
- | **Free tier** | Free with any Azure account | 10 users forever |
273
-
274
- ---
275
-
276
- ## Reference
277
-
278
- - [Entra ID App Registration - OIDC](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app)
279
- - [Entra ID SCIM Provisioning](https://learn.microsoft.com/en-us/entra/identity/app-provisioning/use-scim-to-provision-users-and-groups)
280
- - [Entra ID Optional Claims](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims)
281
- - [Entra ID SCIM Known Issues](https://learn.microsoft.com/en-us/entra/identity/app-provisioning/application-provisioning-config-problem-scim-compatibility)
@@ -1,174 +0,0 @@
1
- # Google Workspace Setup Guide for Open Mercato SSO
2
-
3
- This guide walks through setting up Google Workspace as the identity provider for OIDC login in Open Mercato. Google Workspace supports JIT (Just-In-Time) provisioning only — SCIM push provisioning is not available.
4
-
5
- **Free tier**: Google Cloud OAuth 2.0 is free for internal Workspace applications. No paid APIs required.
6
-
7
- ---
8
-
9
- ## 1. Prerequisites
10
-
11
- - A Google Workspace account with admin access
12
- - A custom domain verified in Google Workspace (e.g., `company.com`)
13
- - Access to the Google Cloud Console (https://console.cloud.google.com)
14
-
15
- ## 2. Create a Google Cloud Project
16
-
17
- 1. Go to https://console.cloud.google.com
18
- 2. Click the project selector in the top bar → **New Project**
19
- 3. Name: `Open Mercato SSO` (or your preference)
20
- 4. Click **Create**
21
- 5. Switch to the new project in the project selector
22
-
23
- ## 3. Configure the OAuth Consent Screen
24
-
25
- 1. In the left sidebar, go to **APIs & Services** → **OAuth consent screen**
26
- 2. Select **Internal** (restricts login to your Workspace organization only)
27
- 3. Click **Create**
28
- 4. Fill in:
29
-
30
- | Field | Value |
31
- |-------|-------|
32
- | **App name** | `Open Mercato` |
33
- | **User support email** | Your admin email |
34
- | **Authorized domains** | Your Workspace domain (e.g., `company.com`) |
35
- | **Developer contact email** | Your admin email |
36
-
37
- 5. Click **Save and Continue**
38
- 6. On the **Scopes** step, click **Add or Remove Scopes** and add:
39
- - `openid`
40
- - `email`
41
- - `profile`
42
- 7. Click **Update** → **Save and Continue**
43
- 8. Review and click **Back to Dashboard**
44
-
45
- ## 4. Create OAuth 2.0 Credentials
46
-
47
- 1. Go to **APIs & Services** → **Credentials**
48
- 2. Click **+ Create Credentials** → **OAuth client ID**
49
- 3. Configure:
50
-
51
- | Field | Value |
52
- |-------|-------|
53
- | **Application type** | `Web application` |
54
- | **Name** | `Open Mercato SSO` |
55
- | **Authorized redirect URIs** | `http://localhost:3000/api/sso/callback/oidc` |
56
-
57
- 4. Click **Create**
58
- 5. **Copy the Client ID and Client Secret immediately** — you can also retrieve them later from the credentials list
59
-
60
- ### OIDC Credentials Summary
61
-
62
- | Credential | Value |
63
- |------------|-------|
64
- | **Issuer URL** | `https://accounts.google.com` |
65
- | **Client ID** | Copy from Credentials page |
66
- | **Client Secret** | Copy from Credentials page |
67
- | **Redirect URI** | `http://localhost:3000/api/sso/callback/oidc` |
68
-
69
- **Note**: Google's OIDC discovery document is at `https://accounts.google.com/.well-known/openid-configuration`.
70
-
71
- ## 5. Create the SSO Config in Open Mercato
72
-
73
- 1. Log into Open Mercato as admin
74
- 2. Go to **Settings** → **Single Sign-On** → **Create New**
75
- 3. Select **OIDC** as the protocol
76
- 4. Enter:
77
- - **Name**: `Google Workspace`
78
- - **Issuer URL**: `https://accounts.google.com`
79
- - **Client ID**: (paste from Google Cloud Console)
80
- - **Client Secret**: (paste from Google Cloud Console)
81
- 5. Add your Workspace domain as an allowed domain (e.g., `company.com`)
82
- 6. Enable **JIT Provisioning** (recommended — creates accounts on first login)
83
- 7. Enable **Auto-link by email** (recommended — links existing accounts by email match)
84
- 8. Click **Verify Discovery** to test the OIDC configuration
85
- 9. Save and activate the configuration
86
-
87
- ## 6. Verify OIDC Login
88
-
89
- 1. Open a private/incognito browser window
90
- 2. Go to the Open Mercato login page
91
- 3. Enter an email address with your Workspace domain (e.g., `user@company.com`)
92
- 4. The login page should detect SSO and show "Continue with SSO"
93
- 5. Click it — you'll be redirected to Google's login page
94
- 6. Authenticate with your Google Workspace account
95
- 7. You should be redirected back to Open Mercato and logged in
96
-
97
- ---
98
-
99
- ## Google Workspace Specifics
100
-
101
- ### No SCIM Provisioning
102
-
103
- Google Workspace does not support SCIM push provisioning to third-party applications. Users are provisioned via JIT on their first SSO login. The Provisioning tab in the Open Mercato admin UI will show an informational message for Google Workspace configurations.
104
-
105
- To manage user access:
106
- - **Provision**: Users are created automatically on first login via JIT
107
- - **Deprovision**: Remove the user's Workspace account or change their domain to stop SSO access
108
-
109
- ### No Group Claims by Default
110
-
111
- Google's standard OIDC tokens do not include group membership claims. If you need role-based access from Google groups, you would need to configure a custom claim via Google's Directory API (advanced setup, not covered here).
112
-
113
- For most setups: leave **Role Mappings** empty in the SSO config. Users will log in with their default assigned role.
114
-
115
- ### `hd` Claim
116
-
117
- Google OIDC returns a `hd` (hosted domain) claim for Workspace accounts. This identifies the user's organization domain. Open Mercato validates the user's email domain against the allowed domains configured in the SSO config.
118
-
119
- ### `email_verified`
120
-
121
- Google Workspace accounts always return `email_verified: true`. Personal Gmail accounts may have unverified emails — configuring the consent screen as **Internal** prevents personal accounts from accessing the application.
122
-
123
- ---
124
-
125
- ## Troubleshooting
126
-
127
- ### "Access blocked: Open Mercato has not completed the Google verification process"
128
-
129
- This appears when the consent screen is set to **External** without Google verification. Solution: set the consent screen to **Internal** (Workspace users only).
130
-
131
- ### OIDC login redirects but fails
132
-
133
- - Verify the Redirect URI matches exactly: `http://localhost:3000/api/sso/callback/oidc`
134
- - Verify the Issuer URL is `https://accounts.google.com` (not a tenant-specific URL)
135
- - Ensure `openid`, `email`, and `profile` scopes are configured on the consent screen
136
- - Check that the user's email domain matches an allowed domain in the SSO config
137
-
138
- ### "Error 400: redirect_uri_mismatch"
139
-
140
- The redirect URI in the authorization request doesn't match what's registered in Google Cloud Console. Check:
141
- - `APP_URL` in `.env` matches what you registered (e.g., `http://localhost:3000`)
142
- - No trailing slash differences
143
- - Protocol matches (http vs https)
144
- - The URI is listed in **Authorized redirect URIs**, not **Authorized JavaScript origins**
145
-
146
- ### User gets "No roles could be resolved"
147
-
148
- This happens when **Role Mappings** are configured but Google doesn't send group claims. Solution: clear the Role Mappings section in the SSO config (leave it empty) to allow login without IdP-based role assignment.
149
-
150
- ### Personal Gmail accounts can log in
151
-
152
- If you set the consent screen to **External**, any Google account can authenticate. To restrict to your organization only, set the consent screen to **Internal**.
153
-
154
- ---
155
-
156
- ## Key Differences from Entra ID
157
-
158
- | Aspect | Google Workspace | Entra ID |
159
- |--------|-----------------|----------|
160
- | **Issuer URL** | `https://accounts.google.com` (same for all orgs) | `https://login.microsoftonline.com/{tenant-id}/v2.0` |
161
- | **SCIM provisioning** | Not supported | Enterprise App → Provisioning |
162
- | **Group claims** | Not in standard OIDC tokens | Via optional claims configuration |
163
- | **User provisioning** | JIT only (on first login) | JIT or SCIM (automatic sync) |
164
- | **Org restriction** | OAuth consent screen: Internal | App registration: Single tenant |
165
- | **Free tier** | Free with any Workspace plan | Free with any Azure account |
166
- | **Redirect URIs** | Supports `http://localhost` for dev | Supports `http://localhost` for dev |
167
-
168
- ---
169
-
170
- ## Reference
171
-
172
- - [Google Cloud OAuth 2.0 Setup](https://developers.google.com/identity/protocols/oauth2)
173
- - [Google OpenID Connect](https://developers.google.com/identity/openid-connect/openid-connect)
174
- - [Google Workspace Admin: OAuth Apps](https://support.google.com/a/answer/7281227)
@@ -1,218 +0,0 @@
1
- # SSO Module Overview
2
-
3
- Open Mercato's SSO module provides enterprise-grade Single Sign-On with OIDC and SCIM 2.0 support. This document covers architecture, configuration, and operational guidance.
4
-
5
- ---
6
-
7
- ## Supported Identity Providers
8
-
9
- | IdP | OIDC Login | SCIM Provisioning | JIT Provisioning | Notes |
10
- |-----|------------|-------------------|------------------|-------|
11
- | **Microsoft Entra ID** | Yes | Yes | Yes | Full OIDC + SCIM support. See [Entra ID Setup Guide](./entra-id-setup.md) |
12
- | **Zitadel** | Yes | Yes | Yes | OIDC + SCIM via Actions/native. See [Zitadel Setup Guide](./zitadel-setup.md) |
13
- | **Google Workspace** | Yes | No | Yes (recommended) | OIDC only, no SCIM push. See [Google Workspace Setup Guide](./google-workspace-setup.md) |
14
-
15
- ---
16
-
17
- ## Architecture
18
-
19
- ### Authentication Flow (OIDC)
20
-
21
- ```
22
- User → Login Page → HRD Check → IdP Redirect → IdP Login → Callback → Session
23
- ```
24
-
25
- 1. **Home Realm Discovery (HRD)**: User enters email, the system checks if the email domain matches an active SSO config
26
- 2. **Authorization Request**: OIDC Authorization Code + PKCE flow initiated with encrypted state cookie
27
- 3. **IdP Authentication**: User authenticates at the identity provider
28
- 4. **Callback Processing**: Authorization code exchanged for tokens, ID token validated
29
- 5. **Account Linking**: User matched to existing account (by email or SSO subject) or JIT-provisioned
30
- 6. **Session Creation**: Auth session established, user redirected to the application
31
-
32
- ### User Provisioning
33
-
34
- Two provisioning methods are supported, **mutually exclusive** per SSO config:
35
-
36
- **JIT (Just-In-Time) Provisioning**
37
- - Users are created automatically on first OIDC login
38
- - Profile data extracted from ID token claims
39
- - Best for: Google Workspace, small organizations, simple setups
40
-
41
- **SCIM 2.0 Provisioning**
42
- - Users are pre-provisioned by the IdP before first login
43
- - Supports create, update, deactivate, and delete operations
44
- - Best for: Entra ID, large organizations needing lifecycle management
45
-
46
- ### Mutual Exclusivity
47
-
48
- JIT and SCIM cannot be enabled simultaneously on the same SSO config:
49
- - Enabling JIT blocks SCIM token creation
50
- - Creating SCIM tokens blocks enabling JIT
51
- - Switching requires disabling one before enabling the other
52
-
53
- ---
54
-
55
- ## Configuration
56
-
57
- ### Admin Setup Steps
58
-
59
- 1. **Create SSO Config**: Settings → Single Sign-On → Create New
60
- 2. **Enter IdP Credentials**: Issuer URL, Client ID, Client Secret
61
- 3. **Add Allowed Domains**: Email domains that should use this SSO config
62
- 4. **Choose Provisioning**: Enable JIT or configure SCIM tokens
63
- 5. **Test Connection**: Verify the IdP discovery endpoint is reachable
64
- 6. **Activate**: Enable the config for production use
65
-
66
- ### API Endpoints
67
-
68
- | Endpoint | Method | Description |
69
- |----------|--------|-------------|
70
- | `/api/sso/config` | POST | Create SSO config |
71
- | `/api/sso/config` | GET | List SSO configs |
72
- | `/api/sso/config/:id` | GET | Get config by ID |
73
- | `/api/sso/config/:id` | PUT | Update config |
74
- | `/api/sso/config/:id` | DELETE | Delete config (must be inactive) |
75
- | `/api/sso/config/:id/activate` | POST | Activate/deactivate config |
76
- | `/api/sso/config/:id/domains` | POST | Add domain |
77
- | `/api/sso/config/:id/domains` | DELETE | Remove domain |
78
- | `/api/sso/config/:id/test` | POST | Test IdP connection |
79
- | `/api/sso/hrd` | POST | Home Realm Discovery lookup |
80
- | `/api/sso/initiate` | GET | Start SSO login flow |
81
- | `/api/sso/callback/oidc` | GET | OIDC callback |
82
- | `/api/sso/scim/tokens` | POST | Create SCIM token |
83
- | `/api/sso/scim/tokens` | GET | List SCIM tokens |
84
- | `/api/sso/scim/tokens/:id` | DELETE | Revoke SCIM token |
85
- | `/api/sso/scim/v2/Users` | POST | SCIM: Create user |
86
- | `/api/sso/scim/v2/Users` | GET | SCIM: List users |
87
- | `/api/sso/scim/v2/Users/:id` | GET | SCIM: Get user |
88
- | `/api/sso/scim/v2/Users/:id` | PATCH | SCIM: Update user |
89
- | `/api/sso/scim/v2/Users/:id` | DELETE | SCIM: Delete user |
90
-
91
- ---
92
-
93
- ## Security
94
-
95
- ### OIDC Security Controls
96
-
97
- | Control | Implementation |
98
- |---------|---------------|
99
- | **PKCE** | S256 with 32-byte random code verifier |
100
- | **State Parameter** | AES-256-GCM encrypted state cookie with HKDF key derivation |
101
- | **Nonce** | 16-byte random nonce validated in ID token |
102
- | **State Comparison** | Timing-safe (`crypto.timingSafeEqual`) |
103
- | **TTL** | 5-minute state cookie lifetime |
104
- | **CSRF** | SameSite=Lax cookies + encrypted state parameter |
105
- | **Return URL** | Sanitized to prevent open redirects |
106
-
107
- ### SCIM Security Controls
108
-
109
- | Control | Implementation |
110
- |---------|---------------|
111
- | **Token Format** | `omscim_` prefix + 32 random bytes (hex) |
112
- | **Storage** | bcrypt-hashed (cost 10), only prefix stored |
113
- | **One-Time Display** | Raw token returned only at creation |
114
- | **Timing Attack** | Dummy bcrypt hash on zero candidates |
115
- | **Tenant Isolation** | Organization ID derived from token, not request |
116
-
117
- ### Data Protection
118
-
119
- - OIDC client secrets encrypted at rest (AES via `TenantDataEncryptionService`)
120
- - SCIM tokens bcrypt-hashed, never retrievable after creation
121
- - No PII in server logs
122
- - All admin endpoints require authentication + feature-based RBAC
123
-
124
- ---
125
-
126
- ## Provisioning Methods
127
-
128
- ### JIT Provisioning
129
-
130
- When JIT is enabled on an SSO config:
131
-
132
- 1. User authenticates via OIDC at the IdP
133
- 2. If the user does not exist in Open Mercato, a new account is created
134
- 3. Profile data (name, email) extracted from ID token claims
135
- 4. User is assigned to the organization associated with the SSO config
136
- 5. On subsequent logins, profile data is updated from the latest ID token
137
-
138
- **Limitations**:
139
- - User lifecycle not managed (no automatic deactivation)
140
- - No pre-provisioning (user must log in first)
141
- - Role assignment requires manual configuration or IdP group claims
142
-
143
- ### SCIM 2.0 Provisioning
144
-
145
- When SCIM is configured:
146
-
147
- 1. IdP pushes user create/update/delete operations to the SCIM endpoint
148
- 2. Users are pre-provisioned before their first login
149
- 3. Profile changes in the IdP are automatically synced
150
- 4. User deactivation in the IdP triggers deactivation + session revocation
151
- 5. On OIDC login, the existing SCIM-provisioned account is linked (no duplicate)
152
-
153
- **Supported SCIM Operations**:
154
- - `POST /Users` — Create user
155
- - `GET /Users` — List users (with `eq` filter support)
156
- - `GET /Users/:id` — Get user
157
- - `PATCH /Users/:id` — Update user (replace operations on `displayName`, `active`, `name.*`, `emails`)
158
- - `DELETE /Users/:id` — Delete user (soft-delete + deactivation)
159
-
160
- ---
161
-
162
- ## Role Mapping
163
-
164
- SSO configs support IdP group-to-application role mapping:
165
-
166
- 1. Configure `appRoleMappings` on the SSO config (map IdP group names to app role names)
167
- 2. When the IdP sends group claims in the ID token, roles are automatically assigned
168
- 3. If no mappings are configured, role sync is skipped (user retains existing roles)
169
-
170
- **Google Workspace note**: Google does not send group claims by default. Role mapping is not available for Google OIDC without additional configuration.
171
-
172
- ---
173
-
174
- ## Environment Variables
175
-
176
- | Variable | Required | Description |
177
- |----------|----------|-------------|
178
- | `SSO_STATE_SECRET` | Yes (production) | 32+ byte secret for state cookie encryption |
179
- | `APP_URL` / `NEXT_PUBLIC_APP_URL` | Recommended | Base URL for redirect URI construction |
180
- | `SSO_DEV_SEED` | No | Set to `true` to seed demo SSO config in development |
181
-
182
- ---
183
-
184
- ## Troubleshooting
185
-
186
- ### Common Issues
187
-
188
- **"State mismatch — possible CSRF attack"**
189
- - State cookie expired (5-minute TTL). User took too long at the IdP.
190
- - Browser blocking third-party cookies. Ensure SameSite=Lax cookies are allowed.
191
-
192
- **"No roles could be resolved from IdP groups"**
193
- - Role mappings are configured but the IdP isn't sending matching group claims.
194
- - Remove role mappings if not needed, or configure the IdP to send group claims.
195
-
196
- **User created with wrong provisioning method**
197
- - Check if both JIT and SCIM have been toggled. The system enforces mutual exclusivity.
198
- - Verify the `provisioningMethod` field on the user's SSO link record.
199
-
200
- **SCIM requests return 401**
201
- - Token may be revoked. Check token status in the admin UI.
202
- - Token format: must include `Authorization: Bearer omscim_...` header.
203
- - Check that the SSO config is active.
204
-
205
- **SCIM requests return 403**
206
- - The SSO config associated with the token is inactive. Activate it first.
207
-
208
- **HRD not detecting SSO for an email domain**
209
- - Verify the domain is added to the SSO config's allowed domains.
210
- - Verify the SSO config is activated (inactive configs are not returned by HRD).
211
-
212
- ---
213
-
214
- ## IdP-Specific Setup Guides
215
-
216
- - [Microsoft Entra ID Setup Guide](./entra-id-setup.md) — Full OIDC + SCIM setup
217
- - [Zitadel Setup Guide](./zitadel-setup.md) — OIDC + SCIM setup
218
- - [Google Workspace Setup Guide](./google-workspace-setup.md) — OIDC-only setup (JIT recommended)
@@ -1,118 +0,0 @@
1
- # SSO Module Security Audit Report
2
-
3
- **Module:** `packages/enterprise/src/modules/sso/`
4
- **Date:** 2026-02-27
5
- **Branch:** `feat/sso-support`
6
-
7
- ---
8
-
9
- ## Executive Summary
10
-
11
- The SSO module demonstrates strong security fundamentals: AES-256-GCM encrypted state cookies, PKCE enforcement, timing-safe state comparison, bcrypt-hashed SCIM tokens, and encrypted OIDC client secrets. The audit uncovered **2 High**, **3 Medium**, and **4 Low** severity findings. Both HIGH findings have been remediated in this milestone.
12
-
13
- ---
14
-
15
- ## Findings Summary
16
-
17
- | # | Finding | Severity | Status |
18
- |---|---------|----------|--------|
19
- | F1 | SCIM debug `console.log` statements dump PII to logs | **HIGH** | **FIXED** |
20
- | F2 | Missing org ownership check in SCIM token generation | **HIGH** | **FIXED** |
21
- | F3 | SCIM payloads lack string length constraints | MEDIUM | Open |
22
- | F4 | SCIM logs `ssoConfigId` query param not UUID-validated | MEDIUM | Open |
23
- | F5 | `emailVerified` defaults to `true` when claim absent | MEDIUM | Documented |
24
- | F6 | Domain DELETE query param not format-validated | LOW | Open |
25
- | F7 | SCIM v2 `startIndex`/`count` not zod-validated | LOW | Open |
26
- | F8 | State cookie not enforced as single-use | LOW | Accepted |
27
- | F9 | Host header fallback in `toAbsoluteUrl` if `APP_URL` unset | LOW | Accepted |
28
-
29
- ---
30
-
31
- ## 1. CSRF Protection -- PASS
32
-
33
- All admin write endpoints are protected by:
34
- - Cookie-based auth via `resolveSsoAdminContext()` with `requireAuth: true` and `requireFeatures` guards
35
- - `SameSite: lax` on auth cookies (appropriate for SSO flows that require cross-site IdP redirects)
36
-
37
- SCIM v2 endpoints use Bearer token authentication (inherently CSRF-safe).
38
-
39
- **Files verified:** All routes under `api/config/`, `api/scim/tokens/`, and `api/scim/v2/`.
40
-
41
- ## 2. Replay Protection -- PASS
42
-
43
- - **State cookie:** AES-256-GCM encrypted with HKDF-SHA256 key derivation, 12-byte random IV, 16-byte auth tag
44
- - **TTL:** 5 minutes (checked at decryption time + cookie maxAge)
45
- - **PKCE:** S256 with 32-byte random code verifier stored in encrypted state cookie
46
- - **Nonce:** 16-byte random nonce validated by `openid-client` library against ID token
47
- - **State comparison:** Timing-safe (`crypto.timingSafeEqual`) with length pre-check
48
- - **Cleanup:** State cookie cleared after successful callback (maxAge: 0)
49
-
50
- **F8 (LOW, Accepted):** No server-side single-use enforcement on state cookie. Mitigated by IdP's single-use authorization code policy (OAuth 2.0 spec requirement).
51
-
52
- ## 3. Tenant Isolation -- PASS (with F2 fixed)
53
-
54
- - **Admin endpoints:** All queries scoped by `organizationId` for non-superadmins via `resolveSsoAdminContext()`
55
- - **SCIM endpoints:** `organizationId` derived from verified bearer token, not from request parameters
56
- - **HRD:** Intentional cross-org lookup (by design) -- only exposes `hasSso`, `configId`, `protocol`
57
- - **F2 (FIXED):** `ScimTokenService.generateToken()` now verifies SSO config ownership before minting tokens
58
-
59
- ## 4. Token Security -- PASS (with F1 fixed)
60
-
61
- - **OIDC client secrets:** Encrypted at rest via `TenantDataEncryptionService`; never exposed in API responses
62
- - **SCIM tokens:** bcrypt-hashed (cost 10), prefix-indexed, timing-attack resistant (dummy hash on miss), raw value returned only once at creation
63
- - **State cookies:** AES-256-GCM encrypted
64
- - **F1 (FIXED):** Removed 5 `[SCIM DEBUG]` console.log statements that serialized full SCIM payloads (PII) to logs
65
-
66
- ## 5. Input Validation -- PASS (with notes)
67
-
68
- - **Admin APIs:** All write endpoints validated with zod schemas from `data/validators.ts`
69
- - **SCIM filter:** Strict regex parser with attribute allowlist, parameterized ORM queries
70
- - **Domain validation:** DNS hostname regex, max 253 chars, must contain dot, max 20 per config
71
- - **Return URL:** `sanitizeReturnUrl()` prevents open redirects (requires `/` prefix, rejects `//`, validates origin)
72
-
73
- ### Open Validation Items
74
-
75
- - **F3 (MEDIUM):** SCIM payloads parsed via manual extraction without max-length constraints. Database column constraints provide backstop.
76
- - **F4 (MEDIUM):** SCIM logs `ssoConfigId` param not UUID-validated (DB rejects non-UUID values).
77
- - **F5 (MEDIUM, Documented):** `emailVerified` defaults to `true` when IdP omits the claim. Acceptable for enterprise IdPs (Entra, Google, Zitadel) which reliably send this claim. Domain allowlist provides mitigating control.
78
-
79
- ---
80
-
81
- ## Remediation Status
82
-
83
- ### Completed (This Milestone)
84
-
85
- 1. **F1** -- Removed all `[SCIM DEBUG]` console.log from `api/scim/v2/Users/route.ts` and `api/scim/v2/Users/[id]/route.ts`
86
- 2. **F2** -- Added `organizationId` filter to `ScimTokenService.generateToken()` in `services/scimTokenService.ts`
87
- 3. Removed 3 console.log statements from `lib/oidc-provider.ts` (raw ID token logging)
88
-
89
- ### Future Hardening (Priority 2)
90
-
91
- 4. **F3** -- Add `.slice(0, 255)` length constraints to SCIM mapper fields
92
- 5. **F4** -- Add `z.string().uuid()` validation to SCIM logs endpoint
93
- 6. **F5** -- Document `emailVerified` default behavior in admin guide
94
-
95
- ### Accepted Risks (Priority 3)
96
-
97
- 7. **F6-F9** -- Input validation consistency improvements and defense-in-depth
98
-
99
- ---
100
-
101
- ## Security Controls Summary
102
-
103
- | Control | Status |
104
- |---------|--------|
105
- | Admin inputs validated (zod) | PASS |
106
- | No hardcoded secrets | PASS |
107
- | Auth on all admin endpoints | PASS |
108
- | SCIM Bearer token auth | PASS |
109
- | Parameterized ORM queries | PASS |
110
- | HTTPS enforced (production) | PASS |
111
- | CSRF protection (SameSite + encrypted state) | PASS |
112
- | Error messages don't leak info | PASS |
113
- | Client secrets encrypted at rest | PASS |
114
- | SCIM tokens bcrypt-hashed | PASS |
115
- | PKCE enforced | PASS |
116
- | Open redirect protection | PASS |
117
- | No PII in server logs | PASS (after F1 fix) |
118
- | Org ownership on token generation | PASS (after F2 fix) |
@@ -1,195 +0,0 @@
1
- # Zitadel Setup Guide for Open Mercato SSO
2
-
3
- This guide walks through setting up Zitadel as the identity provider for OIDC login and SCIM user provisioning in Open Mercato.
4
-
5
- **Free tier**: Zitadel Cloud offers a free tier with up to 25,000 monthly active users.
6
-
7
- ---
8
-
9
- ## 1. Create a Zitadel Instance
10
-
11
- 1. Go to https://zitadel.com and sign up for a free account
12
- 2. Create a new instance (or use the default one)
13
- 3. Note your instance domain: `https://<instance>.zitadel.cloud`
14
-
15
- ## 2. Create Test Users
16
-
17
- 1. In the Zitadel Console, go to **Users** → **+ New**
18
- 2. Fill in:
19
- - **Username**: e.g., `testuser@yourdomain.com`
20
- - **First name** / **Last name**
21
- - **Email**: the user's email address
22
- - **Password**: set an initial password
23
- 3. Click **Create**
24
- 4. Repeat for 2-3 test users
25
-
26
- ## 3. Register the OIDC Application
27
-
28
- 1. In the Zitadel Console, go to **Projects** → **+ New**
29
- 2. Name the project `Open Mercato` and click **Continue**
30
- 3. Click **+ New Application**
31
- 4. Configure:
32
-
33
- | Field | Value |
34
- |-------|-------|
35
- | **Name** | `Open Mercato` |
36
- | **Type** | `Web` |
37
- | **Authentication Method** | `Code (PKCE)` |
38
- | **Redirect URIs** | `http://localhost:3000/api/sso/callback/oidc` |
39
- | **Post-Logout URIs** | `http://localhost:3000/login` |
40
-
41
- 5. Click **Create**
42
- 6. On the application overview, note:
43
- - **Client ID**
44
- - **Client Secret** (generate one if using Code flow)
45
-
46
- ### OIDC Credentials Summary
47
-
48
- | Credential | Where to find it | Value |
49
- |------------|-----------------|-------|
50
- | **Issuer URL** | Instance domain | `https://<instance>.zitadel.cloud` |
51
- | **Client ID** | Application → General | Copy from console |
52
- | **Client Secret** | Application → General → Generate | Copy immediately |
53
- | **Redirect URI** | You configured this | `http://localhost:3000/api/sso/callback/oidc` |
54
-
55
- ### Configure Token Claims
56
-
57
- Zitadel includes `email`, `given_name`, `family_name`, and `email_verified` in ID tokens by default when the `openid`, `profile`, and `email` scopes are requested. No additional configuration is needed.
58
-
59
- ### Assign Users
60
-
61
- By default, all users in the organization can access the application. To restrict access:
62
-
63
- 1. Go to your Project → **Authorizations** → **+ New**
64
- 2. Select specific users or grant roles
65
- 3. Enable "Require authorization" on the project settings if you want to restrict access
66
-
67
- ---
68
-
69
- ## 4. Create the SSO Config in Open Mercato
70
-
71
- 1. Log into Open Mercato as admin
72
- 2. Go to **Settings** → **Single Sign-On** → **Create New**
73
- 3. Select **OIDC** as the protocol
74
- 4. Enter:
75
- - **Name**: `Zitadel`
76
- - **Issuer URL**: `https://<instance>.zitadel.cloud`
77
- - **Client ID**: (paste from Zitadel)
78
- - **Client Secret**: (paste from Zitadel)
79
- 5. Add allowed email domains (e.g., `yourdomain.com`)
80
- 6. Test the connection (Verify Discovery)
81
- 7. Activate the config
82
-
83
- ### Verify OIDC Login
84
-
85
- 1. Open a private/incognito browser window
86
- 2. Go to the Open Mercato login page
87
- 3. Enter an email address belonging to one of your test users
88
- 4. The HRD check should detect SSO and redirect to Zitadel login
89
- 5. Authenticate at Zitadel
90
- 6. You should be redirected back to Open Mercato and logged in
91
-
92
- ---
93
-
94
- ## 5. Configure SCIM Provisioning
95
-
96
- **Prerequisite**: Generate a SCIM bearer token from Open Mercato via the admin UI (SSO config → Provisioning tab → Generate Token).
97
-
98
- ### Zitadel SCIM Support
99
-
100
- Zitadel supports outbound SCIM provisioning through its **Actions** feature (custom workflows). As of 2026, Zitadel also offers a native SCIM provisioning option:
101
-
102
- 1. Go to your Project → **Open Mercato** application
103
- 2. Navigate to **Provisioning** or **Actions**
104
- 3. Configure SCIM outbound provisioning:
105
-
106
- | Field | Value |
107
- |-------|-------|
108
- | **SCIM Base URL** | `http://localhost:3000/api/sso/scim/v2` (dev) or `https://<your-domain>/api/sso/scim/v2` (prod) |
109
- | **Bearer Token** | Paste the SCIM token from Open Mercato |
110
-
111
- 4. Test the connection
112
-
113
- ### Alternative: Manual/API-Based Provisioning
114
-
115
- If Zitadel's native SCIM outbound is not available in your version, use the Zitadel Management API to sync users:
116
-
117
- 1. Create a Service User in Zitadel with Management API access
118
- 2. Use the Zitadel Management API to list users
119
- 3. Push user changes to Open Mercato's SCIM endpoint
120
-
121
- ---
122
-
123
- ## 6. Test the Full Flow
124
-
125
- ### Test OIDC Login
126
-
127
- 1. Navigate to Open Mercato login
128
- 2. Enter a test user's email
129
- 3. **Expected**: Redirect to Zitadel → authenticate → redirect back to Open Mercato
130
- 4. Verify the user appears in the Open Mercato admin panel
131
-
132
- ### Test JIT Provisioning
133
-
134
- If SCIM is not configured and JIT is enabled:
135
-
136
- 1. Log in as a new user via OIDC
137
- 2. **Expected**: User is automatically created in Open Mercato with `provisioningMethod: jit`
138
- 3. Verify user profile (name, email) matches Zitadel
139
-
140
- ### Test SCIM Provisioning (if configured)
141
-
142
- 1. Create a new user in Zitadel
143
- 2. Wait for provisioning cycle (or trigger manually)
144
- 3. **Expected**: User appears in Open Mercato with `provisioningMethod: scim`
145
- 4. Update the user in Zitadel → verify changes propagate
146
- 5. Deactivate the user in Zitadel → verify deactivation in Open Mercato
147
-
148
- ---
149
-
150
- ## Zitadel SCIM Quirks
151
-
152
- | Quirk | Description | How to handle |
153
- |-------|-------------|---------------|
154
- | **Standard-compliant** | Zitadel follows SCIM 2.0 spec closely | Standard parsing works |
155
- | **`email_verified` claim** | Always included in ID tokens | No special handling needed |
156
- | **Group claims** | Available via project roles | Configure role mappings if needed |
157
- | **PKCE support** | Natively supports S256 PKCE | Automatically used by Open Mercato |
158
-
159
- ---
160
-
161
- ## Troubleshooting
162
-
163
- ### OIDC login redirects but fails
164
-
165
- - Verify the Redirect URI matches exactly: `http://localhost:3000/api/sso/callback/oidc`
166
- - Check that the Issuer URL matches your instance: `https://<instance>.zitadel.cloud`
167
- - Verify Client ID and Client Secret
168
- - Check the Zitadel Console → **Events** for error details
169
-
170
- ### "redirect_uri_mismatch" error
171
-
172
- - Ensure the redirect URI registered in Zitadel matches exactly (including protocol and port)
173
- - No trailing slash differences
174
- - For production, use HTTPS
175
-
176
- ### Users can't log in
177
-
178
- - Check that users exist in the same Zitadel organization
179
- - If "Require authorization" is enabled on the project, ensure users have project grants
180
- - Check that the email domain matches the allowed domains in Open Mercato SSO config
181
-
182
- ### SCIM connection fails
183
-
184
- - For local dev, Zitadel needs to reach your server over the internet
185
- - Use ngrok: `ngrok http 3000`
186
- - Update the SCIM Base URL to the ngrok URL
187
-
188
- ---
189
-
190
- ## Reference
191
-
192
- - [Zitadel OIDC Documentation](https://zitadel.com/docs/guides/integrate/login/oidc)
193
- - [Zitadel SCIM Documentation](https://zitadel.com/docs/guides/integrate/scim)
194
- - [Zitadel Actions](https://zitadel.com/docs/guides/manage/customize/actions)
195
- - [Zitadel Cloud](https://zitadel.com/pricing)