OpenLiberty / open-liberty

Open Liberty is a highly composable, fast to start, dynamic application server runtime environment
https://openliberty.io
Eclipse Public License 2.0
1.15k stars 592 forks source link

Openid connect audiences validation not working as expected #22976

Closed dyapasrikanth closed 2 years ago

dyapasrikanth commented 2 years ago

I have a requirement to accept 2 different tokens with different audiences https://abc.com/aud1, https://abc.com/aud2. According the documentation I have configured openidConnectClient like below

  <variable name="openid.identifier.user.id" value="https://abc.com/username"/>
  <variable name="openid.identifier.user.groups" value="https://abc.com/memberof"/>
  <variable name="openid.jwt.audiences" value="https://abc.com/aud1,https://abc.com/aud2"/>
  <variable name="openid.provider.domain" value="test.com"/>
  <variable name="openid.provider.clientId" value="asdf"/>
  <variable name="openid.provider.clientSecret" value="adsf"/>
<openidConnectClient id="auth-handler" 
                       clientId="${openid.provider.clientId}"
                       clientSecret="${openid.provider.clientSecret}"
                       authFilterRef="appAuthFilter"

                       discoveryEndpointUrl="https://${openid.provider.domain}/.well-known/openid-configuration"
                       audiences="${openid.jwt.audiences}"
                       userIdentityToCreateSubject="${openid.identifier.user.id}"
                       uniqueUserIdentifier="${openid.identifier.user.id}"
                       groupIdentifier="${openid.identifier.user.groups}"
                       accessTokenInLtpaCookie="true"
                       realmName="defaultRealm"
                       disableLtpaCookie="false"
                       nonceEnabled="false"
                       includeIdTokenInSubject="true"
                       inboundPropagation="supported"
                       allowCustomCacheKey="true"
                       mapIdentityToRegistryUser="false"
                       useSystemPropertiesForHttpClientConnections="true"
                       responseType="code"
                       scope="openid offline_access"
                       signatureAlgorithm="RS256"
                       grantType="authorization_code"
                       validationMethod="userinfo"
  >
    <authzParameter name="audience" value="https://abc.com/backend"/>
  </openidConnectClient>

Audience validation is failing because of below error

 com.ibm.ws.security.openidconnect.token.JWTTokenValidationFailedException: CWWKS1774E: Validation failed for the token requested by [asdf] because the aud claim [https://abc.com/aud1, https://test.com/userinfo] in the token is not contained in the audiences attribute [https://abc.com/aud1,https://abc.com/aud2] of the OpenID Connect client configuration.
    at com.ibm.ws.security.openidconnect.token.JWTTokenValidationFailedException.format(JWTTokenValidationFailedException.java:38)
    at com.ibm.ws.security.openidconnect.token.JWTTokenValidationFailedException.format(JWTTokenValidationFailedException.java:33)
    at com.ibm.ws.security.openidconnect.jose4j.Jose4jValidator.parseJwtWithValidation(Jose4jValidator.java:102)
    at com.ibm.ws.security.openidconnect.client.jose4j.util.Jose4jUtil.parseJwtWithValidation(Jose4jUtil.java:291)
    at com.ibm.ws.security.openidconnect.client.jose4j.util.Jose4jUtil.createResultWithJose4JForJwt(Jose4jUtil.java:411)
    at com.ibm.ws.security.openidconnect.client.internal.AccessTokenAuthenticator.parseJwtToken(AccessTokenAuthenticator.java:528)
    at com.ibm.ws.security.openidconnect.client.internal.AccessTokenAuthenticator.authenticate(AccessTokenAuthenticator.java:153)
    at com.ibm.ws.security.openidconnect.client.internal.OidcClientImpl.authenticate(OidcClientImpl.java:407)
    at com.ibm.ws.security.openidconnect.client.internal.OidcClientImpl.authenticate(OidcClientImpl.java:370)
    at com.ibm.ws.security.openidconnect.client.internal.OidcClientImpl.authenticate(OidcClientImpl.java:311)
    at com.ibm.ws.webcontainer.security.WebProviderAuthenticatorProxy.handleOidcClient(WebProviderAuthenticatorProxy.java:604)
    at com.ibm.ws.webcontainer.security.WebProviderAuthenticatorProxy.authenticate(WebProviderAuthenticatorProxy.java:470)
    at com.ibm.ws.webcontainer.security.WebAuthenticatorProxy.authenticate(WebAuthenticatorProxy.java:67)
    at com.ibm.ws.webcontainer.security.WebAppSecurityCollaboratorImpl.authenticateRequest(WebAppSecurityCollaboratorImpl.java:1241)
    at com.ibm.ws.webcontainer.security.WebAppSecurityCollaboratorImpl.determineWebReply(WebAppSecurityCollaboratorImpl.java:997)
    at com.ibm.ws.webcontainer.security.WebAppSecurityCollaboratorImpl.performSecurityChecks(WebAppSecurityCollaboratorImpl.java:696)
    at com.ibm.ws.webcontainer.security.WebAppSecurityCollaboratorImpl.preInvoke(WebAppSecurityCollaboratorImpl.java:617)
    at com.ibm.wsspi.webcontainer.collaborator.CollaboratorHelper.preInvokeCollaborators(CollaboratorHelper.java:470)
    at com.ibm.ws.webcontainer.osgi.collaborator.CollaboratorHelperImpl.preInvokeCollaborators(CollaboratorHelperImpl.java:270)
    at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1115)
    at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:5059)
    at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.handleRequest(DynamicVirtualHost.java:316)
    at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:1007)
    at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.run(DynamicVirtualHost.java:281)
    at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:1199)
    at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.wrapHandlerAndExecute(HttpDispatcherLink.java:468)
    at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.ready(HttpDispatcherLink.java:427)
    at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:566)
    at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleNewRequest(HttpInboundLink.java:500)
    at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.processRequest(HttpInboundLink.java:360)
    at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.ready(HttpInboundLink.java:327)
    at com.ibm.ws.channel.ssl.internal.SSLConnectionLink.determineNextChannel(SSLConnectionLink.java:1100)
    at com.ibm.ws.channel.ssl.internal.SSLConnectionLink$MyReadCompletedCallback.complete(SSLConnectionLink.java:675)
    at com.ibm.ws.channel.ssl.internal.SSLReadServiceContext$SSLReadCompletedCallback.complete(SSLReadServiceContext.java:1824)
    at com.ibm.ws.tcpchannel.internal.WorkQueueManager.requestComplete(WorkQueueManager.java:514)
    at com.ibm.ws.tcpchannel.internal.WorkQueueManager.attemptIO(WorkQueueManager.java:584)
    at com.ibm.ws.tcpchannel.internal.WorkQueueManager.workerRun(WorkQueueManager.java:968)
    at com.ibm.ws.tcpchannel.internal.WorkQueueManager$Worker.run(WorkQueueManager.java:1057)
    at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:245)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1153)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.lang.Thread.run(Thread.java:785)

When I debug it, audiences property going as a single element array to the OidcClientConfigImpl like new String[]{"https://abc.com/aud1,https://abc.com/aud2"}and it is failing at audience validation

Diagnostic information:

teddyjtorres commented 2 years ago

The metatype.xml says that this is attribute accepts multiple values, <AD id="audiences" name="%audiences" description="%audiences.desc" required="false" type="String" cardinality="400" />

Therefore, config admin should have done that before passing these to the service. It will need to determined if there is an issue with config admin not parsing these properly when a variable is used.

teddyjtorres commented 2 years ago

As a workaround, please set the audiences attribute to "https://abc.com/aud1,https://abc.com/aud2".

Thanks!

brenthdaniel commented 2 years ago

You can use ${list(openid.jwt.audiences)} as documented here: https://openliberty.io/docs/latest/reference/config/server-configuration-overview.html#variable-substitution

`Variable values are always interpreted as a string with simple type conversion. Therefore, a list of ports (such as 80,443) might be interpreted as a single string rather than as two port numbers. You can force the variable substitution to split on the , by using a list function, as shown in the following example:

`

teddyjtorres commented 2 years ago

@dyapasrikanth The issue will be closed since the variable is working as documented. Please follow @brenthdaniel 's suggestion.

Regards, Teddy