Hello OpenSearch Community,
I’m trying to implement Document Level Security (DLS) using a tenant_id claim from our OIDC provider’s JWT, but I’ve run into a specific issue. The DLS works perfectly when I hardcode a value, but fails as soon as I try to use the dynamic variable.
My Goal:
A user logs in via OIDC. Their JWT contains a tenant_id claim. Their searches should be automatically filtered to only show documents matching their tenant_id.
What I’ve Done & Tested:
I have successfully configured OIDC login. I then created a DLS role with the following query.
This WORKS:
When I set the DLS query with a static value (e.g., 1), the security works as expected. The user can log in, view dashboards, and only sees data where the tenant_id_field is 1.
{
"term": {
"my_tenant_id_field.keyword": 1
}
}
This FAILS:
The problem occurs when I switch to the dynamic variable to fetch the tenant_id from the JWT.
{
"term": {
"my_tenant_id_field.keyword": "${user.attrs.tenant_id}"
}
}
and
{
"term": {
"my_tenant_id_field.keyword": "${user.attrs.tenant_id}"
}
}
The Error:
When I use the dynamic ${user.attrs.tenant_id} variable, the user can still log in, but when they try to view data in Discover or on a dashboard, they receive a “Bad Request” error from the UI. This leads me to believe the ${user.attrs.tenant_id} variable is either not being populated or is being populated with a value that breaks the query syntax.
My Configuration Steps:
1. config.yml (Redacted):
I’ve configured my OIDC domain to map the tenant_id claim from the JWT to a user attribute.
_meta:
type: "config"
config_version: 2
config:
dynamic:
http:
anonymous_auth_enabled: false
authc:
oidc_auth_domain:
description: "Authenticate via OIDC (OpenID Connect)"
http_enabled: true
transport_enabled: true
order: 0
http_authenticator:
type: "openid"
challenge: true
config:
subject_key: "preferred_username"
roles_key: "roles"
openid_connect_url: "##################"
client_id: "#############"
client_secret: "###########"
user_mapping.attr_json_path.tenant_id: tenant_id
authentication_backend:
type: "noop"
basic_internal_auth_domain:
description: "Authenticate via HTTP Basic against internal users database"
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
type: "basic"
challenge: false
authentication_backend:
type: "internal"
2. OpenSearch DLS Role:
I created a role in the Dashboards UI with the necessary cluster and index permissions, and the DLS query mentioned above.
3. Role Mapping:
I mapped this DLS role to a “Backend Role” that corresponds to a role claim present in my user’s JWT.
My Question:
Since the DLS works with a hardcoded value, my role permissions and mappings seem to be correct. The issue appears to be isolated to the dynamic variable substitution.
Could there be something wrong with my user_mapping.attr_json_path.tenant_id: tenant_id configuration, or is there another place I should be looking to debug why ${user.attrs.tenant_id} is not resolving correctly?
Any help would be greatly appreciated. Thank you