Both Opensearch backend and dashboard replys 401 in oidc configuration

Versions (relevant - OpenSearch/Dashboard/Server OS/Browser):
both Opensearch backend and dashboard are using official docker image release, in version 2.18.0

Describe the issue:
configured each-node tls cert and keys, configured both basic auth and openid in dashboard and backend.

configured my IDP to send back JWT token as both id_token and auth_token.

After auth step with IDP, my IDP always response “success” and issued JWT token. (from idp log)

however Opensearch always anwsers:


{

"statusCode": 401,

"error": "Unauthorized",

"message": "Unauthorized"

}

and the browser address bar path stop at e.g: <myopensearch-dashboard-url>/auth/openid/login?code=5a30144b53d56622c72e5657d2bd652a&state=TSuVruLpM0bKwSuTlhaYs3.

Tried to use curl to get openid auth from IDP, then send auth_token to connect to opensearch backend directly, also get the same 401 response.

i setup the log4j to trace level by:


logger.securityjwt.name = com.amazon.dlic.auth.http.jwt

logger.securityjwt.level = trace

then find below trace level message.

I see the AbstractHTTPJwtAuthenticator trys to parse an opaque like string as JWT, however it failed in the end.

But in my OIDC, both id_token and access_token are already issued as JWT token type, so I don’t know where does this opaque string came from.

this string is also not my basic auth admin password.


[2024-12-02T07:24:08,434][WARN ][o.o.s.h.HTTPBasicAuthenticator] [ssdl-app-logging-opensearch-manager-1] No 'Basic Authorization' header, send 401 and 'WWW-Authenticate Basic'

[2024-12-02T07:24:08,446][TRACE][c.a.d.a.h.j.AbstractHTTPJwtAuthenticator] [ssdl-app-logging-opensearch-manager-1] Extracting JWT token from NWM2MjY1MzgtZDM1NS00ZGZhLWExYWItOGU4NjYwMTljM2Q4Tzd0bnRaazZPTHFyRFgzTGpCeG1aTHVYUGdpX0dZeldLTmhIUno0ZGdOYw failed

com.amazon.dlic.auth.http.jwt.keybyoidc.BadCredentialsException: Invalid serialized unsecured/JWS/JWE object: Missing part delimiters

at com.amazon.dlic.auth.http.jwt.keybyoidc.JwtVerifier.getVerifiedJwtToken(JwtVerifier.java:80) ~[opensearch-security-2.18.0.0.jar:2.18.0.0]

at com.amazon.dlic.auth.http.jwt.AbstractHTTPJwtAuthenticator.extractCredentials0(AbstractHTTPJwtAuthenticator.java:130) [opensearch-security-2.18.0.0.jar:2.18.0.0]

......................

Caused by: java.text.ParseException: Invalid serialized unsecured/JWS/JWE object: Missing part delimiters

at com.nimbusds.jose.JOSEObject.split(JOSEObject.java:226) ~[nimbus-jose-jwt-9.41.2.jar:9.41.2]

...........................

... 47 more

Configuration:

I use admin basic auth to call opensearch backend with path /_plugins/_security/api/securityconfig
got below response:

{
    "config": {
        "dynamic": {
            "filtered_alias_mode": "disallow",
            "disable_rest_auth": false,
            "disable_intertransport_auth": false,
            "respect_request_indices_options": false,
            "kibana": {
                "multitenancy_enabled": false,
                "private_tenant_enabled": true,
                "default_tenant": "",
                "server_username": "kibanaserver",
                "index": ".kibana",
                "sign_in_options": [
                    "BASIC"
                ]
            },
            "http": {
                "anonymous_auth_enabled": false,
                "xff": {
                    "enabled": false,
                    "internalProxies": "192\\.168\\.0\\.10|192\\.168\\.0\\.11",
                    "remoteIpHeader": "X-Forwarded-For"
                }
            },
            "authc": {
                "openid_auth_domain": {
                    "http_enabled": true,
                    "order": 1,
                    "http_authenticator": {
                        "challenge": false,
                        "type": "openid",
                        "config": {
                            "jwt_header": "Authorization",
                            "subject_key": "sub",
                            "roles_key": "groups",
                            "openid_connect_url": "https://<my idp url>.com/.well-known/openid-configuration",
                            "openid_connect_idp": {
                                "enable_ssl": true,
                                "verify_hostnames": false
                            }
                        }
                    },
                    "authentication_backend": {
                        "type": "noop",
                        "config": {}
                    }
                },
                "jwt_auth_domain": {
                    "http_enabled": false,
                    "order": 3,
                    "http_authenticator": {
                        "challenge": true,
                        "config": {}
                    },
                    "authentication_backend": {
                        "type": "org.opensearch.security.auth.internal.InternalAuthenticationBackend",
                        "config": {}
                    }
                },
                "ldap": {
                    "http_enabled": false,
                    "order": 6,
                    "http_authenticator": {
                        "challenge": true,
                        "config": {}
                    },
                    "authentication_backend": {
                        "type": "org.opensearch.security.auth.internal.InternalAuthenticationBackend",
                        "config": {}
                    }
                },
                "basic_internal_auth_domain": {
                    "http_enabled": true,
                    "order": 0,
                    "http_authenticator": {
                        "challenge": false,
                        "type": "basic",
                        "config": {}
                    },
                    "authentication_backend": {
                        "type": "intern",
                        "config": {}
                    },
                    "description": "Authenticate via HTTP Basic against internal users database"
                },
                "proxy_auth_domain": {
                    "http_enabled": false,
                    "order": 4,
                    "http_authenticator": {
                        "challenge": true,
                        "config": {}
                    },
                    "authentication_backend": {
                        "type": "org.opensearch.security.auth.internal.InternalAuthenticationBackend",
                        "config": {}
                    }
                },
                "clientcert_auth_domain": {
                    "http_enabled": false,
                    "order": 5,
                    "http_authenticator": {
                        "challenge": true,
                        "config": {}
                    },
                    "authentication_backend": {
                        "type": "org.opensearch.security.auth.internal.InternalAuthenticationBackend",
                        "config": {}
                    }
                },
                "saml_auth_domain": {
                    "http_enabled": false,
                    "order": 0,
                    "http_authenticator": {
                        "challenge": true,
                        "config": {}
                    },
                    "authentication_backend": {
                        "type": "org.opensearch.security.auth.internal.InternalAuthenticationBackend",
                        "config": {}
                    }
                },
                "kerberos_auth_domain": {
                    "http_enabled": false,
                    "order": 7,
                    "http_authenticator": {
                        "challenge": true,
                        "config": {}
                    },
                    "authentication_backend": {
                        "type": "org.opensearch.security.auth.internal.InternalAuthenticationBackend",
                        "config": {}
                    }
                }
            },
            "authz": {
                "openid_auth_domain": {
                    "http_enabled": true,
                    "authorization_backend": {
                        "type": "noop",
                        "config": {}
                    }
                }
            },
            "auth_failure_listeners": {},
            "do_not_fail_on_forbidden": false,
            "multi_rolespan_enabled": true,
            "hosts_resolver_mode": "ip-only",
            "do_not_fail_on_forbidden_empty": false,
            "on_behalf_of": {
                "enabled": false
            }
        }
    }
}

But in my OIDC, both id_token and access_token are already issued as JWT token type, so I don’t know where does this opaque string came from.

Could the IdP be encrypting the header? A JWT has three parts: Header, Payload and Signature and will be of the form xxxxx.yyyyy.zzzzz.

Thanks, the issue showhow resolved.
I guess it was because the JWT parser tried to use the string from Authentication header, however the header didn’t contain proper JWT token during the parse.

I somehow changed the basic auth order from 0 to a number larger than the openid order, then it works

1 Like

Does the Authorization header contain the type of token sent with the request. i.e. Authorization: Bearer <jwt> or Authorization: Basic <...>. I believe if the request is missing Bearer or Basic in the Authorization header, that the JWT authenticator will take whatever the value in Authorization is.

Code ref: security/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java at main · opensearch-project/security · GitHub