From 8d3c6dc68aa476de1a27392f94ca3249fb1845f3 Mon Sep 17 00:00:00 2001 From: glasstiger Date: Tue, 28 Oct 2025 15:32:02 +0000 Subject: [PATCH 1/3] PGWire OIDC integration --- .../configuration-utils/_oidc.config.json | 4 ++++ .../openid-connect-oidc-integration.mdx | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/documentation/configuration-utils/_oidc.config.json b/documentation/configuration-utils/_oidc.config.json index c57d09856..e084545c8 100644 --- a/documentation/configuration-utils/_oidc.config.json +++ b/documentation/configuration-utils/_oidc.config.json @@ -90,5 +90,9 @@ "acl.oidc.cache.ttl": { "default": 30000, "description": "User info cache entry TTL (time to live) in milliseconds, default value is 30 seconds. For improved performance QuestDB caches user info responses for each valid access token, this settings drives how often the access token should be validated and the user info updated." + }, + "acl.oidc.pg.token.as.password.enabled": { + "default": "false", + "description": "When enabled, the PGWire endpoint supports OIDC authentication. The OAuth2 token should be sent in the password field, while the username field should contain the string `_sso`, or left empty if that is an option." } } diff --git a/documentation/operations/openid-connect-oidc-integration.mdx b/documentation/operations/openid-connect-oidc-integration.mdx index 1cda31557..56976933c 100644 --- a/documentation/operations/openid-connect-oidc-integration.mdx +++ b/documentation/operations/openid-connect-oidc-integration.mdx @@ -576,6 +576,30 @@ with Sender.from_conf(conf) as sender: sender.dataframe(df, table_name="foo", at="ts") ``` +## OIDC for the PGWire endpoint + +If ROPC is not an option, we can still authenticate via OIDC on the PGWire endpoint. +However, in this case the client's responsibility to source the token required for authentication. +This method works wherever a Postgres client library is available, including jupyter notebooks. + +Token authentication for the PGWire endpoint should be enabled by adding the `acl.oidc.pg.token.as.password.enabled=true` setting to the server configuration. + +The token should be sent in the password field, while the username field should contain the string `_sso`, or left empty if that is an option: + +```python +import psycopg as pg + +token = "token_requested_from_the_oauth2_provider" + +conn_str = f"user=_sso password={token} host=localhost port=8812 dbname=qdb" +with pg.connect(conn_str, autocommit=True) as connection: + with connection.cursor() as cur: + cur.execute('select current_user()') + records = cur.fetchall() + for row in records: + print(row) +``` + ## User permissions QuestDB requires additional user information to be able to construct the user's From 8cfcd02870ab3dcd2cdb94b606d355bd4b39bc0a Mon Sep 17 00:00:00 2001 From: glasstiger Date: Tue, 28 Oct 2025 18:05:48 +0000 Subject: [PATCH 2/3] link to ROPC OAuth2 page --- .../openid-connect-oidc-integration.mdx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/documentation/operations/openid-connect-oidc-integration.mdx b/documentation/operations/openid-connect-oidc-integration.mdx index 56976933c..88e272175 100644 --- a/documentation/operations/openid-connect-oidc-integration.mdx +++ b/documentation/operations/openid-connect-oidc-integration.mdx @@ -348,11 +348,16 @@ The OAuthenticator documentation also contains using different identity providers. If Jupyter notebooks are used without JupyterHub, one option for OAuth2 -integration is to use the -[Resource Owner Password Credentials](https://oauth.net/2/grant-types/password) -flow. It is likely that to enable this flow in your OAuth2 provider will require -additional setup. The Resource Owner Password Credentials flow is legacy, and -should be used as a last resort. +integration is to use the +Resource Owner Password Credentials (ROPC) flow. +It is likely that enabling this flow in your OAuth2 provider will require +additional setup. + +:::caution + +The Resource Owner Password Credentials flow is legacy, and should be used as a last resort. + +::: We can use the code below to acquire an access token in our notebook: @@ -578,7 +583,8 @@ with Sender.from_conf(conf) as sender: ## OIDC for the PGWire endpoint -If ROPC is not an option, we can still authenticate via OIDC on the PGWire endpoint. +If the +Resource Owner Password Credentials (ROPC) flow is not an option, we can still authenticate via OIDC on the PGWire endpoint. However, in this case the client's responsibility to source the token required for authentication. This method works wherever a Postgres client library is available, including jupyter notebooks. From c4adab488adc05c2f74f4b9261c1795eba9a071f Mon Sep 17 00:00:00 2001 From: glasstiger Date: Tue, 28 Oct 2025 18:49:55 +0000 Subject: [PATCH 3/3] link to ROPC OAuth2 page --- documentation/operations/openid-connect-oidc-integration.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/operations/openid-connect-oidc-integration.mdx b/documentation/operations/openid-connect-oidc-integration.mdx index 88e272175..03c8294e7 100644 --- a/documentation/operations/openid-connect-oidc-integration.mdx +++ b/documentation/operations/openid-connect-oidc-integration.mdx @@ -348,7 +348,7 @@ The OAuthenticator documentation also contains using different identity providers. If Jupyter notebooks are used without JupyterHub, one option for OAuth2 -integration is to use the +integration is to use the Resource Owner Password Credentials (ROPC) flow. It is likely that enabling this flow in your OAuth2 provider will require additional setup. @@ -583,7 +583,7 @@ with Sender.from_conf(conf) as sender: ## OIDC for the PGWire endpoint -If the +If the Resource Owner Password Credentials (ROPC) flow is not an option, we can still authenticate via OIDC on the PGWire endpoint. However, in this case the client's responsibility to source the token required for authentication. This method works wherever a Postgres client library is available, including jupyter notebooks.