Geoserver OIDC Authentication Failure

23 views Asked by At

I am running GeoServer 2.24.2 with the OIDC community extension and am having trouble integrating with Keycloak 21.1.2. I am able to successfully authenticate with Keycloak, but upon redirect to GeoServer it appears as if I am not logged in as an anonymous user.

Here is my keycloak OIDC filter configuration at security/filter/keycloak/config.xml:

<openIdConnectAuthentication>
  <id>467efa87:18e52c53a99:-7ffb</id>
  <name>keycloak</name>
  <className>org.geoserver.security.oauth2.OpenIdConnectAuthenticationFilter</className>
  <roleSource class="org.geoserver.security.oauth2.OpenIdConnectFilterConfig$OpenIdRoleSource">
    AccessToken
  </roleSource>
  <cliendId>...</cliendId>
  <clientSecret>...</clientSecret>
  <accessTokenUri>https://keycloak/realms/realm/protocol/openid-connect/token</accessTokenUri>
  <userAuthorizationUri>
    https://keycloak/realms/realm/protocol/openid-connect/auth
  </userAuthorizationUri>
  <redirectUri>https://geoserver/geoserver/web/</redirectUri>
  <checkTokenEndpointUrl>
    https://keycloak/realms/realm/protocol/openid-connect/userinfo
  </checkTokenEndpointUrl>
  <logoutUri>https://keycloak/realms/realm/protocol/openid-connect/logout</logoutUri>
  <scopes>openid</scopes>
  <enableRedirectAuthenticationEntryPoint>false</enableRedirectAuthenticationEntryPoint>
  <forceAccessTokenUriHttps>true</forceAccessTokenUriHttps>
  <forceUserAuthorizationUriHttps>true</forceUserAuthorizationUriHttps>
  <loginEndpoint>/j_spring_oauth2_openid_connect_login</loginEndpoint>
  <logoutEndpoint>/j_spring_oauth2_openid_connect_logout</logoutEndpoint>
  <allowUnSecureLogging>true</allowUnSecureLogging>
  <principalKey>email</principalKey>
  <jwkURI>https://keycloak/realms/realm/protocol/openid-connect/certs</jwkURI>
  <tokenRolesClaim>roles</tokenRolesClaim>
  <responseMode>query</responseMode>
  <postLogoutRedirectUri>https://geoserver/geoserver/web/</postLogoutRedirectUri>
  <sendClientSecret>true</sendClientSecret>
  <allowBearerTokens>false</allowBearerTokens>
  <usePKCE>true</usePKCE>
</openIdConnectAuthentication>

Here is my security config at security/config.xml:

<security>
    <roleServiceName>default</roleServiceName>
    <authProviderNames>
        <string>default</string>
    </authProviderNames>
    <configPasswordEncrypterName>strongPbePasswordEncoder</configPasswordEncrypterName>
    <encryptingUrlParams>false</encryptingUrlParams>
    <filterChain>
        <filters name="web" class="org.geoserver.security.HtmlLoginFilterChain" interceptorName="interceptor" exceptionTranslationName="exception" path="/web/**,/gwc/rest/web/**,/" disabled="false" allowSessionCreation="true" ssl="false" matchHTTPMethod="false">
            <filter>keycloak</filter>
            <filter>rememberme</filter>
            <filter>form</filter>
            <filter>anonymous</filter>
        </filters>
        <filters name="webLogin" class="org.geoserver.security.ConstantFilterChain" path="/j_spring_security_check,/j_spring_security_check/" disabled="false" allowSessionCreation="true" ssl="false" matchHTTPMethod="false">
            <filter>form</filter>
        </filters>
        <filters name="webLogout" class="org.geoserver.security.LogoutFilterChain" path="/j_spring_security_logout,/j_spring_security_logout/" disabled="false" allowSessionCreation="false" ssl="false" matchHTTPMethod="false">
            <filter>formLogout</filter>
        </filters>
        <filters name="rest" class="org.geoserver.security.ServiceLoginFilterChain" interceptorName="restInterceptor" exceptionTranslationName="exception" path="/rest/**" disabled="false" allowSessionCreation="false" ssl="false" matchHTTPMethod="false">
            <filter>keycloak</filter>
            <filter>basic</filter>
            <filter>anonymous</filter>
        </filters>
        <filters name="gwc" class="org.geoserver.security.ServiceLoginFilterChain" interceptorName="restInterceptor" exceptionTranslationName="exception" path="/gwc/rest/**" disabled="false" allowSessionCreation="false" ssl="false" matchHTTPMethod="false">
            <filter>keycloak</filter>
            <filter>basic</filter>
        </filters>
        <filters name="default" class="org.geoserver.security.ServiceLoginFilterChain" interceptorName="interceptor" exceptionTranslationName="exception" path="/**" disabled="false" allowSessionCreation="false" ssl="false" matchHTTPMethod="false">
            <filter>keycloak</filter>
            <filter>basic</filter>
            <filter>anonymous</filter>
        </filters>
    </filterChain>
    <rememberMeService>
        <className>org.geoserver.security.rememberme.GeoServerTokenBasedRememberMeServices</className>
        <key>geoserver</key>
    </rememberMeService>
    <bruteForcePrevention>
        <enabled>true</enabled>
        <minDelaySeconds>1</minDelaySeconds>
        <maxDelaySeconds>5</maxDelaySeconds>
        <maxBlockedThreads>100</maxBlockedThreads>
        <whitelistedMasks>
            <string>127.0.0.1</string>
        </whitelistedMasks>
    </bruteForcePrevention>
</security>

Here are the logs that I am seeing:

TRACE  [web.FilterChainProxy] - Invoking OpenIdConnectAuthenticationFilter (2/7)
DEBUG  [authentication.BearerTokenExtractor] - Token not found in headers. Trying request parameters.
DEBUG  [authentication.BearerTokenExtractor] - Token not found in request parameters.  Not an OAuth2 request.
DEBUG  [geoserver.security] - Inspecting the http request looking for the Custom Session ID.
DEBUG  [geoserver.security] - Found 1 cookies!
TRACE  [support.DefaultListableBeanFactory] - Creating instance of bean 'scopedTarget.accessTokenRequest'
TRACE  [support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration'
TRACE  [support.DefaultListableBeanFactory] - Finished creating instance of bean 'scopedTarget.accessTokenRequest'
DEBUG  [authentication.BearerTokenExtractor] - Token not found in headers. Trying request parameters.
DEBUG  [authentication.BearerTokenExtractor] - Token not found in request parameters.  Not an OAuth2 request.
DEBUG  [geoserver.security] - OIDC: redirecting to identity provider for user login: https://<keycloak>/realms/<realm>/protocol/openid-connect/auth?response_type=code&client_id=geoserver&scope=openid&redirect_uri=https://<geoserver>/geoserver/web/&response_mode=query
DEBUG  [geoserver.security] - OIDC: When complete, identity provider will redirect to: https://<geoserver>/geoserver/web/
DEBUG  [oauth2.pkce] - Generate code_verifier: ...
DEBUG  [oauth2.pkce] - CODE_CHALLENGE: ...
DEBUG  [oauth2.pkce] - CODE_CHALLENGE_METHOD: S256
DEBUG  [geoserver.security] - preAuthenticatedPrincipal = null, trying to authenticate
TRACE  [web.FilterChainProxy] - Invoking GeoServerRememberMeAuthenticationFilter (3/7)
.
.
.
TRACE  [web.FilterChainProxy] - Invoking OpenIdConnectAuthenticationFilter (2/7)
DEBUG  [authentication.BearerTokenExtractor] - Token not found in headers. Trying request parameters.
DEBUG  [authentication.BearerTokenExtractor] - Token not found in request parameters.  Not an OAuth2 request.
DEBUG  [geoserver.security] - Inspecting the http request looking for the Custom Session ID.
DEBUG  [geoserver.security] - Found 1 cookies!
TRACE  [support.DefaultListableBeanFactory] - Creating instance of bean 'scopedTarget.accessTokenRequest'
TRACE  [support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration'
TRACE  [support.DefaultListableBeanFactory] - Finished creating instance of bean 'scopedTarget.accessTokenRequest'
DEBUG  [authentication.BearerTokenExtractor] - Token not found in headers. Trying request parameters.
DEBUG  [authentication.BearerTokenExtractor] - Token not found in request parameters.  Not an OAuth2 request.
DEBUG  [oauth2.pkce] - CLIENT_SECRET: ...
DEBUG  [oauth2.pkce] - CODE_VERIFIER: ...
DEBUG  [code.AuthorizationCodeAccessTokenProvider] - Retrieving token from https://<keycloak>/realms/<realm>/protocol/openid-connect/token
DEBUG  [client.RestTemplate] - HTTP POST https://<keycloak>/realms/<realm>/protocol/openid-connect/token
DEBUG  [code.AuthorizationCodeAccessTokenProvider] - Encoding and sending form: {grant_type=[authorization_code], code=[...], redirect_uri=[https://<geoserver>/geoserver/web/], client_id=[geoserver], client_secret=[...], code_verifier=[...]}
DEBUG  [client.RestTemplate] - Response 200 OK
DEBUG  [client.HttpMessageConverterExtractor] - Reading to [org.springframework.security.oauth2.common.OAuth2AccessToken]
DEBUG  [security.oauth2] - OIDC: received a CODE from Identity Provider - handing it in for ID/Access Token
DEBUG  [security.oauth2] - OIDC: CODE=...
DEBUG  [security.oauth2] - OIDC: Identity Provider returned Token, type=bearer
DEBUG  [security.oauth2] - OIDC: SCOPES=email offline_access openid profile
DEBUG  [security.oauth2] - OIDC: ACCESS TOKEN:...
DEBUG  [security.oauth2] - OIDC: ID TOKEN:...
DEBUG  [client.RestTemplate] - HTTP GET https://<keycloak>/realms/<realm>/protocol/openid-connect/userinfo
DEBUG  [client.RestTemplate] - Accept=[application/xml, text/xml, application/json, application/*+xml, application/*+json]
DEBUG  [client.RestTemplate] - Writing [{}] with org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
DEBUG  [client.RestTemplate] - Response 200 OK
DEBUG  [geoserver.security] - preAuthenticatedPrincipal = null, trying to authenticate
TRACE  [web.FilterChainProxy] - Invoking GeoServerRememberMeAuthenticationFilter (3/7)

It appears that GeoServer is able to successfully exchange the authorization code for access and id tokens. Geoserver then hits the userinfo endpoint and potentially gets an empty response. Keycloak is correctly configured to expose the email claim, which I have set as the principal key, in the access token, id token, and user info endpoint response. I have confirmed that the email claim is in the JWTs that are logged by GeoServer. GeoServer seems to throw all of this away and move on to the next filter.

Cross posting a GIS Stack Exchange question: https://gis.stackexchange.com/questions/478742/geoserver-oidc-authentication-failure

0

There are 0 answers