You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 7 Next »

Problem statement

The Qpid JMS clients for AMQP 1.0 and AMQP 0-x support XOAUTH2 SASL mechanism to authenticate messaging connection against OAUTH2 based services. The XOAUTH2 SASL mechanism is described on Google Developers page OAuth 2.0 mechanism and involves sending a bearer token as part of  initial client response as illustrated below:

base64("user=" {User} "^Aauth=Bearer " {Access Token} "^A^A")

The broker is supposed to validate the token and accept connections from the clients providing valid token. Depending from OAUTH2 authentication provider the broker can perform some extra client validation by invoking OAUTH2 authentication provider specific REST API. 

The XOAUTH2 SASL mechanism works perfectly for an initial connection establishment. However, the client implementations of XOAUTH2 SASL mechanism do not have a mechanism to update an expired access token. Thus, when failover is used and connectivity is lost after token being expired, the restoration of connectivity is not possible, as the same expired token would be used in every re-connection attempt. As result, the existing failover mechanism will not be able to re-establish connectivity with XOAUTH2 SASL. The only way the client application can work around this issue is by tracking the token expiration timeout, closing an existing connection after token expiration and re-opening a new connection with a new token. Thus, using XOAUTH2 SASL authentication in production environments requires users to write a complex logic around tracking of expiration time and re-opening connections which can interrupt a messaging flow and might cause disruption in underlying business use cases.

Possible solutions

There are several ways to address the limitation. Though, every of them would involve changes to the client code. This wiki describes some possible approaches for fixing the issue.

Add CredentialSupplier hook to supply the credentials

In JMS API the password based credentials can be passed either via connection URL or directly into API calls: ConnectionFactory#createConnection(String userName, String password), ConnectionFactory#createContext(String userName, String password). When credentials are set there is no way in JMS API to update the credentials. We need an extension to JMS API which would allow to pass into JMS Connection special objects capable of supplying credentials. Such objects can be used for an initial connection establishment and restoring the connectivity during failover.

A possible example of JMS connection extension

ConnectionFactory connectionFactory =...
CredentialSupplier passwordCredentialSupplier = new PasswordCredentialSupplier(password);
CredentialSupplier userNameCredentialSupplier = new UserNameCredentialSupplier(userName);
Connection connection = connectionFactory.createConnection(userNameCredentialSupplier , passwordCredentialSupplier )

The API methods might look like below

ConnectionFactory#createConnection(CredentialSupplier... credentialSuppliers)
ConnectionFactory#createContext(CredentialSupplier... credentialSuppliers)

CredentialSupplier Concepts

The end-user can implement a custom CredentialSupplier to supply credentials on runtime when connection is being established or re-established. It would be up to CredentialSupplier  implementation to check the current credentials and renew them when they expire.

The SASL mechanism implementations would need to be changed to operate with CredentialSupplier interface and its derivatives (UserNameCredentialSupplier, PasswordCredentialSupplier) to fetch the required credentials. The new interfaces and implementations extending CredentialSupplier  can be added for new SASL mechanisms if required.

Observe authentication failure and update credentials on notification

A special Listener can be introduced to call on authentication failures. Thus, when authentication fails due to an expired credentials the messaging application can be notified through this Listener, which can be set on JMS Connection or Context. The credentials set methods (setPassword(), setUserName) can be added into JMS Connection or Context implementations. That  should  allow to update the credentials  if required.

The following class diagram illustrates the idea

Authentication failure notification with Listener

Thus,  for OAUTH2 based authentication, a special implementation of AuthenticationListener can be configured on the connection. When connection recovery fails with AuthenticationException, the listener gets invoked. The code can be added into the listener to get new access token and update it on a connection by invoking setPassword(String) method. The next failover attempt would use an updated token to establish connectivity.


Introduce a special failover mechanism

A special failover mechanism can be added with hooks to get the credentials. Both JMS implementations have different failover API. The failover mechanism supporting credential update would need to be different. Such failover hook needs to be configured via connection URL.

Generate token inside of the client via special pluggable mechanism

A special OAUTH2 hooks can be added to clients in order to generate the token. Every OAUTH2 provider API (google, microsoft, github, etc) would need to have a separate implementation to get the token. Though, we need to develop a common interface which would allow to inject and invoke the hooks.

Connection Pools

The Pool connection factories (like Pooled-JMS or PooledConnectionFactory from JMS for AMQP-0-x ) have the same issue with token expiration. The expired token needs to be updated in some ways on cached connections or on connection creation. Though, an introduction of a special failover mechanism ( configurable via connection URL) or generation of the token inside of the client by means of special hooks would solve this problem.



  • No labels