OAuth 2.0 Jira user-directory connection test fails when initial client-credentials token is persisted in a read-only transaction

XMLWordPrintable

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Medium
    • None
    • Affects Version/s: 10.2.14, 10.2.15
    • None
    • 1
    • Severity 2 - Major

      Issue Summary

      When executing a connection test on a Remote Crowd Directory (for example, Jira) that uses OAuth 2.0, we need to get an access token. If there’s no valid access token, atlassian-oauth2 will refresh the token (see com.atlassian.oauth2.client.storage.DefaultTokenHandler#getClientCredentialsToken), which will write the new token to the database. We reach this method through com.atlassian.crowd.directory.RemoteCrowdDirectory#fetchAccessToken.

      While that is completely harmless for existing directories, it can be problematic for connection tests. Connection tests are required to create a new directory (at least in Embedded Crowd) and are not an operation that is expected to mutate. Confluence provides a read-only transaction for the testConnection method, which causes the connection test to fail and prevents the creation of a new directory.

      Steps to Reproduce

      On Jira v11.3.8 admin UI

      1. Go to Settings, then Application links.
      2. Select Create link, and then:
        • For Application type, select External application.
        • For Direction, select Incoming.
        • Select Continue. In the Configure an incoming link window, fill in the required fields:
        • Enter a unique name for your link (for example, Confluence).
        • In Redirect URL, enter the base URL of your Jira instance.
        • For Permissions, select Admin.
        • Select Save.
        • Keep a note of the Client ID and Secret.

      To connect Confluence to a Jira Data Center application:

      1. In your Jira application, go to User Management > Jira User Server.
        1. Click Add Application.
        2. Enter the application name (confluence) and the Authentication method, OAuth 2.0:Confluence created in the previous step that Confluence will use when accessing Jira.
        3. Enter the IP address or addresses of your Confluence server. Valid values are: 
          • A full IP address, e.g. 192.168.10.12.
          • A wildcard IP range, using CIDR notation, e.g. 192.168.10.1/16. For more information, see the introduction to CIDR notation on Wikipedia and RFC 4632.
          • Save the new application.

      On Confluence v10.2.14 admin UI,

      1. Go to AdministrationApplication Links
      2. Select Create link, and then:
        • For Application type, select External application.
        • For Direction, select Outgoing.
      3. Select Continue.
      4. In the Configure an outgoing link window:
        • For Grant types, select Client credentials.
        • For the service provider, select your service provider (custom).
        • Enter a unique name for your application link (Jira Server)
      5. In the Application details section:
      6. Save your settings.

      Add the Jira user directory in Confluence

      1. In Confluence, go to the Administration menu, then General Configuration > User directories.
      2. To add a directory, select Add, and then select the Atlassian Jira type. Enter the settings as described below.
        • Server URLhttps://your-jira-instance-baseurl
        • Application Name → The one created for Confluence in Jira (confluence)
        • embedded.crowd.directory.edit.jira.field.applicationOAuth2ConfigId.descriptionOAuth 2.0: Jira Server (the one configured in the previous step)
        • Leave the rest of the configuration as it is.
        • Click on Test Settings.

      Expected Results

      The Test Connection should succeed, and the Jira user directory will be added.

      Actual Results

      The Test Connection fails with the following error on the User directory screen on the admin UI: 

      Connection test failed. Response from the server: Failed to create remote crowd client  

      The error appears in atlassian-confluence-security.log 

      2026-08-27 12:19:10,331 ERROR [http-nio-8090-exec-3 url: /confluence/plugins/servlet/embedded-crowd/configure/jira/; user: admin] [crowd.embedded.admin.ConfigurationController] handleSubmit Configuration test failed for user directory: [ Jira Server], type: [ CROWD ] -- url: /confluence/plugins/servlet/embedded-crowd/configure/jira/ | userName: admin | referer: https://linux-131100.prod.atl-cd.net/confluence/plugins/servlet/embedded-crowd/configure/jira/?cspNonceId=DbiTCe84PTlyPxG9VSOpcH9k9IQ%3D | traceId: bacad144262b43f2com.atlassian.crowd.exception.runtime.OperationFailedException: Failed to create remote crowd client	at com.atlassian.crowd.embedded.core.CrowdDirectoryServiceImpl.testConnection(CrowdDirectoryServiceImpl.java:137)	at com.atlassian.confluence.user.crowd.ConfluenceCrowdDirectoryService.testConnection(ConfluenceCrowdDirectoryService.java:103)	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)	at java.base/java.lang.reflect.Method.invoke(Method.java:580)    
      [.......]
      Caused by: com.atlassian.crowd.exception.OperationFailedException: Failed to create remote crowd client	
      at com.atlassian.crowd.directory.RemoteCrowdDirectory.getCrowdClient(RemoteCrowdDirectory.java:802)	
      at com.atlassian.crowd.directory.RemoteCrowdDirectory.testConnection(RemoteCrowdDirectory.java:637)	
      at com.atlassian.crowd.embedded.core.CrowdDirectoryServiceImpl.testConnection(CrowdDirectoryServiceImpl.java:123)	... 449 more
      Caused by: com.atlassian.activeobjects.internal.ActiveObjectsSqlException: There was a SQL exception thrown by the Active Objects library:Database:	
      - name:PostgreSQL	
      - version:16.15 (Debian 16.15-1.pgdg13+2)	
      - minor version:15	
      - major version:16Driver:	
      - name:PostgreSQL JDBC Driver	
      - version:42.7.3
      org.postgresql.util.PSQLException: ERROR: cannot execute INSERT in a read-only transaction	
      at com.atlassian.activeobjects.internal.EntityManagedActiveObjects.create(EntityManagedActiveObjects.java:104)	
      at com.atlassian.activeobjects.osgi.ActiveObjectsDelegate.create(ActiveObjectsDelegate.java:246)	
      at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)	
      at java.base/java.lang.reflect.Method.invoke(Method.java:580)    
      [.....]    
      at com.atlassian.oauth2.client.storage.token.dao.ClientTokenStoreImpl.create(ClientTokenStoreImpl.java:55)	
      at com.atlassian.oauth2.client.storage.token.dao.SecuredClientTokenStore.lambda$create$0(SecuredClientTokenStore.java:51)	
      at com.atlassian.sal.core.transaction.HostContextTransactionTemplate$1.doInTransaction(HostContextTransactionTemplate.java:21)    
      [.....]    
      at com.atlassian.oauth2.client.storage.token.dao.SecuredClientTokenStore.create(SecuredClientTokenStore.java:44)	at com.atlassian.oauth2.client.storage.token.DefaultClientTokenStorageService.save(DefaultClientTokenStorageService.java:33)	at com.atlassian.oauth2.client.storage.DefaultTokenHandler.updateToken(DefaultTokenHandler.java:306)	
      at com.atlassian.oauth2.client.storage.DefaultTokenHandler.getNewClientCredentialsToken(DefaultTokenHandler.java:203)	at com.atlassian.oauth2.client.storage.DefaultTokenHandler.lambda$getClientCredentialsToken$3(DefaultTokenHandler.java:140)	
      at com.atlassian.oauth2.common.concurrent.KeyedLocks.executeWithLock(KeyedLocks.java:37)	
      at com.atlassian.oauth2.client.storage.DefaultTokenHandler.getClientCredentialsToken(DefaultTokenHandler.java:139)	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)	
      at java.base/java.lang.reflect.Method.invoke(Method.java:580)	
      at com.atlassian.confluence.impl.osgi.OsgiProxyFactory$ServiceTrackingProxy.invoke(OsgiProxyFactory.java:74)	
      at jdk.proxy4/jdk.proxy4.$Proxy259.getClientCredentialsToken(Unknown Source)	
      at com.atlassian.crowd.directory.RemoteCrowdDirectory.fetchAccessToken(RemoteCrowdDirectory.java:757)	
      at com.atlassian.crowd.directory.RemoteCrowdDirectory.getClientProperties(RemoteCrowdDirectory.java:774)	
      at com.atlassian.crowd.directory.RemoteCrowdDirectory$1.create(RemoteCrowdDirectory.java:746)	
      at com.atlassian.crowd.directory.RemoteCrowdDirectory$1.create(RemoteCrowdDirectory.java:743)	
      at io.atlassian.util.concurrent.LazyReference$Sync.run(LazyReference.java:336)	
      at io.atlassian.util.concurrent.LazyReference.getInterruptibly(LazyReference.java:148)	
      at io.atlassian.util.concurrent.LazyReference.get(LazyReference.java:115)	
      at com.atlassian.crowd.directory.RemoteCrowdDirectory.getCrowdClient(RemoteCrowdDirectory.java:800)	
      ... 451 more
      Caused by: org.postgresql.util.PSQLException: ERROR: cannot execute INSERT in a read-only transaction	
      at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2725)	
      at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2412)	
      at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:371)	
      at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:502)	
      at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:419)	
      at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:194)	
      at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:155)	
      at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)	
      at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)	
      at net.java.ao.ForwardingPreparedStatement.executeUpdate(ForwardingPreparedStatement.java:45)	
      at net.java.ao.ParameterMetadataCachingPreparedStatement.executeUpdate(ParameterMetadataCachingPreparedStatement.java:10)	
      at net.java.ao.db.PostgreSQLDatabaseProvider.executeInsertReturningKey(PostgreSQLDatabaseProvider.java:362)	
      at net.java.ao.DatabaseProvider.insertReturningKey(DatabaseProvider.java:1876)	
      at net.java.ao.db.PostgreSQLDatabaseProvider.insertReturningKey(PostgreSQLDatabaseProvider.java:318)	
      at net.java.ao.EntityManager.create(EntityManager.java:413)	
      at net.java.ao.EntityManager.create(EntityManager.java:450)	
      at com.atlassian.activeobjects.internal.EntityManagedActiveObjects.create(EntityManagedActiveObjects.java:102)	
      ... 543 more
      

      Workaround

      Allow Basic Authentication for the REST API on the Jira side, which is the older way to setup Jira user directory sync in Confluence.

              Assignee:
              Unassigned
              Reporter:
              Saurabh
              Votes:
              1 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated: