Skip to content

Commit dc0556c

Browse files
authored
Docs for JWT/OIDC authorization in v25.4 (#20921)
1 parent 66da887 commit dc0556c

File tree

9 files changed

+683
-16
lines changed

9 files changed

+683
-16
lines changed

src/current/releases/index.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ A cluster that is upgraded to an alpha binary of CockroachDB or a binary that wa
116116
| [v24.2]({% link releases/unsupported-versions.md %}#v24-2) | Innovation | 2024-08-12 |
117117
| [v24.1](#v24-1) | Regular | 2024-05-20 |
118118
| [v23.2](#v23-2) | Regular | 2024-02-05 |
119-
| [v23.1](#v23-1) | Regular | 2023-05-15 |
120119

121120
### Upcoming releases
122121

src/current/v25.4/authentication.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ CockroachDB offers the following methods for client authentication:
105105
Enter password:
106106
~~~
107107
108-
- [**Single sign-on authentication to DB Console**]({% link {{ page.version.version }}/sso-db-console.md %}).
108+
- [**Single sign-on authentication to DB Console**]({% link {{ page.version.version }}/sso-db-console.md %}). CockroachDB supports OpenID Connect (OIDC) for authenticating users to the DB Console. <span class="version-tag">New in v25.4:</span> You can also enable [automatic role synchronization]({% link {{ page.version.version }}/oidc-authorization.md %}) based on group memberships from your identity provider.
109+
110+
- [**JWT authentication for SQL clients**]({% link {{ page.version.version }}/sso-sql.md %}). CockroachDB supports JSON Web Token (JWT) authentication for SQL client connections. <span class="version-tag">New in v25.4:</span> You can enable [automatic user provisioning]({% link {{ page.version.version }}/sso-sql.md %}#configure-user-provisioning) and [automatic role synchronization]({% link {{ page.version.version }}/jwt-authorization.md %}) based on group claims in JWT tokens.
109111
110112
- [**GSSAPI authentication**]({% link {{ page.version.version }}/gssapi_authentication.md %}).
111113
@@ -335,5 +337,10 @@ The following cipher suites are rejected by default because they are not recomme
335337
- [`cockroach cert`]({% link {{ page.version.version }}/cockroach-cert.md %})
336338
- [`cockroach auth-session`]({% link {{ page.version.version }}/cockroach-auth-session.md %})
337339
- [GSSAPI Authentication]({% link {{ page.version.version }}/gssapi_authentication.md %})
340+
- [Single Sign-on (SSO) for DB Console]({% link {{ page.version.version }}/sso-db-console.md %})
341+
- [Cluster Single Sign-on (SSO) using JWTs]({% link {{ page.version.version }}/sso-sql.md %})
342+
- [JWT Authorization]({% link {{ page.version.version }}/jwt-authorization.md %})
343+
- [OIDC Authorization]({% link {{ page.version.version }}/oidc-authorization.md %})
344+
- [LDAP Authorization]({% link {{ page.version.version }}/ldap-authorization.md %})
338345
- [SQL Authentication]({% link {{ page.version.version }}/security-reference/authentication.md %})
339346
- [Cloud Storage Authentication]({% link {{ page.version.version }}/cloud-storage-authentication.md %})

src/current/v25.4/cockroachdb-feature-availability.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ The SQL built-in function [workload_index_recs]({% link {{ page.version.version
9595

9696
[Triggers]({% link {{ page.version.version }}/triggers.md %}) are in Preview. A trigger executes a function when one or more specified SQL operations is performed on a table. Triggers respond to data changes by adding logic within the database, rather than in an application. They can be used to modify data before it is inserted, maintain data consistency across rows or tables, or record an update to a row.
9797

98+
### JWT authorization
99+
100+
[JWT authorization]({% link {{ page.version.version }}/jwt-authorization.md %}) allows CockroachDB to automatically assign roles to users based on group claims in their JWT tokens. When a client connects using a JWT token, the cluster extracts group information and maps each group to a corresponding cluster role, simplifying access control for organizations using identity providers.
101+
102+
### OIDC authorization for DB Console
103+
104+
[OIDC authorization for DB Console]({% link {{ page.version.version }}/oidc-authorization.md %}) allows CockroachDB to automatically assign roles to users based on group claims when they log into the DB Console via OIDC. The cluster extracts group information from ID tokens or access tokens and maps each group to a corresponding cluster role, streamlining access management for DB Console users.
105+
98106
### Admission control for ingesting snapshots
99107

100108
The [cluster setting]({% link {{ page.version.version }}/cluster-settings.md %}) `kvadmission.store.snapshot_ingest_bandwidth_control.enabled` is in Preview. When configured, it limits the disk impact of ingesting snapshots on a node.
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
---
2+
title: Configure JWT Authorization
3+
summary: Learn how to configure role-based access control (authorization) using JWT tokens for SQL client connections.
4+
toc: true
5+
docs_area: manage
6+
---
7+
8+
{{site.data.alerts.callout_info}}
9+
{% include feature-phases/preview.md %}
10+
{{site.data.alerts.end}}
11+
12+
If you manage users through an identity provider (IdP) that supports JSON Web Tokens (JWT), you can configure CockroachDB to automatically assign [roles]({% link {{ page.version.version }}/security-reference/authorization.md %}) to users based on group claims in their JWT tokens, simplifying access control.
13+
14+
If JWT authorization is enabled:
15+
16+
1. When a client connects to the cluster using a JWT token, the cluster extracts the groups claim from the token.
17+
1. If the groups claim is not present in the token, the cluster queries the IdP's userinfo endpoint as a fallback.
18+
1. Each group is mapped to a cluster role by matching the group name to a role name.
19+
1. The user is granted each corresponding role, and roles that no longer match the user's groups are revoked.
20+
1. In conjunction with [automatic user provisioning]({% link {{ page.version.version }}/sso-sql.md %}#configure-user-provisioning) if enabled, users are created automatically during their first authentication and simultaneously receive specified role memberships.
21+
22+
## Prerequisites
23+
24+
- Enable [JWT Authentication]({% link {{ page.version.version }}/sso-sql.md %}).
25+
- Understand the structure of JWT tokens issued by your identity provider.
26+
- Know which claim in your JWT tokens contains group memberships.
27+
28+
## Configuration
29+
30+
Before you begin, it may be useful to enable authentication logging, which can help you confirm successful configuration or troubleshoot issues. For details, refer to [Troubleshooting](#troubleshooting).
31+
32+
### Step 1: Enable JWT authorization
33+
34+
Enable JWT authorization and configure the groups claim:
35+
36+
{% include_cached copy-clipboard.html %}
37+
~~~ sql
38+
-- Enable JWT authentication (if not already enabled)
39+
SET CLUSTER SETTING server.jwt_authentication.enabled = true;
40+
41+
-- Enable JWT authorization
42+
SET CLUSTER SETTING server.jwt_authentication.authorization.enabled = true;
43+
44+
-- Configure the JWT claim containing groups (default: 'groups')
45+
SET CLUSTER SETTING server.jwt_authentication.group_claim = 'groups';
46+
47+
-- (Optional) Configure the userinfo endpoint JSON key for groups fallback
48+
SET CLUSTER SETTING server.jwt_authentication.userinfo_group_key = 'groups';
49+
~~~
50+
51+
{{site.data.alerts.callout_info}}
52+
The `userinfo_group_key` setting is only used when the groups claim is missing from the JWT token. CockroachDB will query the IdP's userinfo endpoint using this key to retrieve group memberships.
53+
{{site.data.alerts.end}}
54+
55+
### Step 2: Configure IdP-specific settings
56+
57+
The configuration varies by identity provider:
58+
59+
#### Okta
60+
61+
Okta typically includes groups in the default `groups` claim:
62+
63+
{% include_cached copy-clipboard.html %}
64+
~~~ sql
65+
SET CLUSTER SETTING server.jwt_authentication.group_claim = 'groups';
66+
SET CLUSTER SETTING server.jwt_authentication.userinfo_group_key = 'groups';
67+
~~~
68+
69+
Example JWT token from Okta:
70+
71+
~~~json
72+
{
73+
"iss": "https://your-okta-domain.okta.com",
74+
"sub": "00u1abc2def3ghi4jkl",
75+
"aud": "your_client_id",
76+
"email": "[email protected]",
77+
"groups": ["developers", "team-alpha"]
78+
}
79+
~~~
80+
81+
#### Keycloak
82+
83+
For Keycloak Groups (default mapping):
84+
85+
{% include_cached copy-clipboard.html %}
86+
~~~ sql
87+
SET CLUSTER SETTING server.jwt_authentication.group_claim = 'groups';
88+
~~~
89+
90+
Example JWT token:
91+
92+
~~~json
93+
{
94+
"iss": "https://keycloak.example.com/realms/myrealm",
95+
"sub": "user123",
96+
"email": "[email protected]",
97+
"groups": ["developers", "team-alpha"]
98+
}
99+
~~~
100+
101+
{{site.data.alerts.callout_info}}
102+
**For Keycloak realm roles**: CockroachDB does not support nested JSON paths like `realm_access.roles`. If you need to use realm roles, create a Keycloak protocol mapper to flatten the roles into a top-level claim:
103+
104+
1. In Keycloak, go to your client settings
105+
1. Navigate to **Mappers** > **Create**
106+
1. Choose **User Realm Role** mapper type
107+
1. Set the **Token Claim Name** to `roles` (or another simple name)
108+
1. Configure CockroachDB to use this claim:
109+
110+
{% include_cached copy-clipboard.html %}
111+
~~~ sql
112+
SET CLUSTER SETTING server.jwt_authentication.group_claim = 'roles';
113+
~~~
114+
{{site.data.alerts.end}}
115+
116+
#### Azure AD / Microsoft Entra ID
117+
118+
Azure AD typically uses the `groups` claim:
119+
120+
{% include_cached copy-clipboard.html %}
121+
~~~ sql
122+
SET CLUSTER SETTING server.jwt_authentication.group_claim = 'groups';
123+
~~~
124+
125+
{{site.data.alerts.callout_info}}
126+
In Azure AD, you may need to configure group claims in your app registration. Refer to [Microsoft's documentation](https://learn.microsoft.com/en-us/azure/active-directory/hybrid/connect/how-to-connect-fed-group-claims) for details.
127+
{{site.data.alerts.end}}
128+
129+
### Step 3: Create matching roles
130+
131+
Create CockroachDB roles that match your IdP group names and grant appropriate privileges to each role. Remember that role names must comply with CockroachDB's [identifier requirements]({% link {{ page.version.version }}/create-user.md %}#user-names).
132+
133+
{{site.data.alerts.callout_info}}
134+
Group names from the IdP are normalized using case folding and Unicode normalization (NFC) before matching to role names. This means that group names are typically converted to lowercase for matching purposes.
135+
{{site.data.alerts.end}}
136+
137+
For example, if your JWT tokens contain groups named `developers` and `analysts`:
138+
139+
{% include_cached copy-clipboard.html %}
140+
~~~ sql
141+
-- Create role for developers
142+
CREATE ROLE developers;
143+
GRANT ALL ON DATABASE app TO developers;
144+
145+
-- Create role for analysts
146+
CREATE ROLE analysts;
147+
GRANT SELECT ON DATABASE analytics TO analysts;
148+
~~~
149+
150+
{{site.data.alerts.callout_info}}
151+
If you are going to use [automatic user provisioning]({% link {{ page.version.version }}/sso-sql.md %}#configure-user-provisioning) in conjunction with JWT authorization, be sure to complete the creation of group roles before enabling automatic user provisioning. Auto-provisioned users will only receive roles for groups that already exist as CockroachDB roles.
152+
{{site.data.alerts.end}}
153+
154+
### Step 4: Confirm configuration
155+
156+
1. On your identity provider, set up test users with memberships in groups that should be synced to CockroachDB roles.
157+
158+
1. If [automatic user provisioning]({% link {{ page.version.version }}/sso-sql.md %}#configure-user-provisioning) is not enabled, create the matching test users when logged in as an admin to CockroachDB:
159+
160+
{% include_cached copy-clipboard.html %}
161+
~~~ sql
162+
CREATE ROLE username1 LOGIN;
163+
CREATE ROLE username2 LOGIN;
164+
CREATE ROLE username3 LOGIN;
165+
~~~
166+
167+
If automatic user provisioning is enabled, users will be created automatically during their first login.
168+
169+
1. Obtain a JWT token from your identity provider and connect to CockroachDB using the token. Refer to [Authenticate to your cluster]({% link {{ page.version.version }}/sso-sql.md %}#authenticate-to-your-cluster).
170+
171+
1. Using your `admin` credentials, log in to the CockroachDB SQL shell and verify the user's role assignments:
172+
173+
{% include_cached copy-clipboard.html %}
174+
~~~ sql
175+
-- View roles granted to a specific user
176+
SHOW GRANTS FOR username1;
177+
178+
-- Check if user has a specific role
179+
SELECT pg_has_role('username1', 'developers', 'member');
180+
~~~
181+
182+
For auto-provisioned users, you can identify them by their `PROVISIONSRC` role option:
183+
184+
{% include_cached copy-clipboard.html %}
185+
~~~ sql
186+
-- View all users and their provisioning source
187+
SELECT rolname, rolprovisionsrc FROM pg_roles
188+
WHERE rolprovisionsrc LIKE 'jwt_token:%';
189+
~~~
190+
191+
## How it works
192+
193+
### Group extraction process
194+
195+
When a user authenticates with a JWT token and JWT authorization is enabled:
196+
197+
1. **Token validation**: CockroachDB first validates the JWT token signature, issuer, and claims.
198+
199+
1. **Group extraction**:
200+
- CockroachDB looks for the configured `group_claim` (default: `groups`) in the JWT token.
201+
- If the claim is missing or cannot be parsed, CockroachDB queries the IdP's userinfo endpoint and looks for the `userinfo_group_key` (default: `groups`).
202+
203+
1. **Normalization**: Group names are normalized using `MakeSQLUsernameFromUserInput`, which performs case folding and Unicode normalization (NFC).
204+
205+
1. **Role matching**: Each normalized group name is compared against existing CockroachDB roles.
206+
207+
### Role synchronization
208+
209+
On each login, CockroachDB synchronizes the user's role memberships:
210+
211+
1. **Grant new roles**: If a group matches an existing role name and the user doesn't already have that role, it is granted.
212+
213+
1. **Revoke stale roles**: If the user has roles that don't match any current groups, those roles are revoked.
214+
215+
1. **Skip non-existent roles**: If a group doesn't match any existing role, it is silently skipped (no error is raised).
216+
217+
{{site.data.alerts.callout_info}}
218+
IdP groups that don't correspond to CockroachDB roles are silently ignored. You must pre-create roles in CockroachDB for them to be granted.
219+
{{site.data.alerts.end}}
220+
221+
### Empty groups behavior
222+
223+
If the groups claim exists but contains an empty array (`[]`):
224+
225+
1. All existing role memberships are revoked from the user.
226+
1. Login is **rejected** with error: `JWT authorization: empty group list`
227+
228+
This behavior ensures that users without group memberships cannot access the cluster, which is a security feature to prevent unauthorized access.
229+
230+
### Userinfo endpoint fallback
231+
232+
If the JWT token does not contain the configured groups claim, CockroachDB will attempt to query the IdP's userinfo endpoint:
233+
234+
1. CockroachDB makes an HTTP GET request to the userinfo endpoint using the access token.
235+
1. The response is parsed as JSON, and CockroachDB looks for the `userinfo_group_key`.
236+
1. If the userinfo lookup fails, login is rejected with error: `JWT authorization: userinfo lookup failed`
237+
238+
## Troubleshooting
239+
240+
Enable [`SESSION` logging]({% link {{ page.version.version }}/logging.md %}#sessions) to preserve data that will help troubleshoot JWT authorization issues:
241+
242+
{% include_cached copy-clipboard.html %}
243+
~~~ sql
244+
SET CLUSTER SETTING server.auth_log.sql_sessions.enabled = true;
245+
~~~
246+
247+
{{site.data.alerts.callout_info}}
248+
Once all functionality is configured and tested successfully, we recommend disabling session logging to conserve system resources.
249+
{{site.data.alerts.end}}
250+
251+
To view the logs, open `cockroach-session.log` from your [logging directory]({% link {{ page.version.version }}/configure-logs.md %}#logging-directory).
252+
253+
Potential issues to investigate may pertain to:
254+
255+
- **JWT token validation**: Ensure the token is properly signed and issued by a trusted issuer.
256+
- **Missing groups claim**: Check if the token contains the configured claim name. Use a JWT decoder tool to inspect the token.
257+
- **Claim name mismatch**: Verify that `server.jwt_authentication.group_claim` matches the actual claim name in your tokens.
258+
- **Userinfo endpoint**: If relying on userinfo fallback, ensure the endpoint is accessible and returns the expected JSON structure.
259+
- **Role name mismatches**: Remember that group names are normalized (typically lowercased). Check that your role names match the normalized group names.
260+
- **Empty groups**: Verify that users have group memberships in the IdP.
261+
262+
### Common errors
263+
264+
**Error**: `JWT authorization: empty group list`
265+
266+
- **Cause**: The groups claim exists but contains an empty array.
267+
- **Solution**: Ensure users have appropriate group memberships in your identity provider.
268+
269+
**Error**: `JWT authorization: userinfo lookup failed`
270+
271+
- **Cause**: The token doesn't contain the groups claim, and the userinfo endpoint query failed.
272+
- **Solution**: Either include groups in the JWT token or ensure the userinfo endpoint is accessible and configured correctly.
273+
274+
**Error**: User can log in but has no privileges
275+
276+
- **Cause**: Groups from the IdP don't match any existing CockroachDB roles.
277+
- **Solution**: Create roles with names matching the normalized group names from your IdP.
278+
279+
## Security considerations
280+
281+
1. **Maintain backup authentication**: Always keep a backup authentication method (like password or client certificate) for administrative users in case of IdP outages.
282+
283+
1. **Validate token issuers**: Ensure `server.jwt_authentication.issuers` is properly configured to accept only trusted issuers.
284+
285+
1. **Pre-create roles with minimal privileges**: Create roles before enabling authorization and grant only the necessary privileges following the principle of least privilege.
286+
287+
1. **Monitor role synchronization**: Regularly audit role assignments and changes. Enable audit logging if available to track when roles are granted or revoked.
288+
289+
1. **Secure the userinfo endpoint**: If using userinfo fallback, ensure the endpoint requires proper authentication and is only accessible over HTTPS.
290+
291+
1. **Review empty groups policy**: Understand that empty groups will block login. Ensure this aligns with your security requirements.
292+
293+
1. **Regularly audit IdP groups**: Review and clean up group memberships in your identity provider to ensure they reflect current access requirements.
294+
295+
## See also
296+
297+
- [Cluster Single Sign-on (SSO) using JWTs]({% link {{ page.version.version }}/sso-sql.md %})
298+
- [LDAP Authorization]({% link {{ page.version.version }}/ldap-authorization.md %})
299+
- [OIDC Authorization]({% link {{ page.version.version }}/oidc-authorization.md %})
300+
- [Authorization]({% link {{ page.version.version }}/security-reference/authorization.md %})
301+
- [Security Reference: Authorization]({% link {{ page.version.version }}/security-reference/authorization.md %})

0 commit comments

Comments
 (0)