Search code examples
spring-bootcloud-foundrycloudfoundry-java-clientjava-cfenv

Cloud Foundry Java Client in Springboot: login through /oauth/token fails


I have a spring boot project which interacts with Cloud Foundry

I have the following code:

CloudFoundryController.java:

package com.backend

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.ApplicationListener;

import org.cloudfoundry.operations.CloudFoundryOperations;
import org.cloudfoundry.operations.applications.GetApplicationRequest;
import org.cloudfoundry.operations.DefaultCloudFoundryOperations;
import org.cloudfoundry.reactor.DefaultConnectionContext;
import org.cloudfoundry.reactor.TokenProvider;
import org.cloudfoundry.reactor.client.ReactorCloudFoundryClient;
import org.cloudfoundry.reactor.tokenprovider.PasswordGrantTokenProvider;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;

@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class BackendApplication implements ApplicationListener<ApplicationFailedEvent> {
    private static final Logger logger = LoggerFactory.getLogger(BackendApplication.class);


    public static void main(String[] args) {

        // Hardcoded Cloud Foundry client configuration
        String target = "thisistheurl";
        String org = "thisisthename";
        String space = "thisisthename";
        String user = "thisisthelogin";
        String password = "thisisthepass";

        try {
// Configure and login to Cloud Foundry
            final PasswordGrantTokenProvider tokenProvider = PasswordGrantTokenProvider.builder()
                    .username(user)
                    .password(password)
                    .build();

            final DefaultConnectionContext connectionContext = DefaultConnectionContext.builder()
                    .apiHost(target)
                    .build();

            final ReactorCloudFoundryClient client = ReactorCloudFoundryClient.builder()
                    .connectionContext(connectionContext)
                    .tokenProvider(tokenProvider)
                    .build();

            final DefaultCloudFoundryOperations operations = DefaultCloudFoundryOperations.builder()
                    .cloudFoundryClient(client)
                    .organization(org)
                    .space(space)
                    .build();

            operations.applications().list().subscribe(application ->
                    System.out.printf("  %s%n", application.getName())
            );

        } catch (Exception e) {
            logger.error("Error interacting with Cloud Foundry: " + e.getMessage());
        }
    }

    @Override
    private static URL getTargetURL(String target) {
        try {
            return URI.create(target).toURL();
        } catch (MalformedURLException e) {
            throw new RuntimeException("The target URL is not valid: " + e.getMessage());
        }
    }
}

I have the following libraries:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

        <dependency>
            <groupId>org.cloudfoundry</groupId>
            <artifactId>cloudfoundry-operations</artifactId>
            <version>5.12.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.cloudfoundry</groupId>
            <artifactId>cloudfoundry-client-reactor</artifactId>
            <version>5.12.1.RELEASE</version>
        </dependency>

What i get is: HTTP/1.1 401 Unauthorized

And corresponding exception:

2024-04-21T23:26:04.769+12:00 ERROR 36848 --- [backend] [ry-client-nio-2] cloudfoundry-client.compatibility        : An error occurred while checking version compatibility:

org.cloudfoundry.uaa.UaaException: invalid_client: Bad credentials
    at org.cloudfoundry.reactor.util.ErrorPayloadMappers.lambda$null$8(ErrorPayloadMappers.java:104) ~[cloudfoundry-client-reactor-5.12.1.RELEASE.jar:na]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.MonoCacheTime] :
    reactor.core.publisher.Mono.checkpoint(Mono.java:2261)
    org.cloudfoundry.reactor.tokenprovider.AbstractUaaTokenProvider.token(AbstractUaaTokenProvider.java:330)
Error has been observed at the following site(s):
    *__checkpoint() ⇢ at org.cloudfoundry.reactor.tokenprovider.AbstractUaaTokenProvider.token(AbstractUaaTokenProvider.java:330)
    *__checkpoint() ⇢ at org.cloudfoundry.reactor.client.v2.info.ReactorInfo.get(ReactorInfo.java:52)

when I perform the following operation:

cf -a thisistheurl -u thisisthelogin -p thisisthepass

it works perfectly, login is working.

However from what i see, the java library does the following:

  • makes a GET request to api url provided and obtains cloud_controller_v2 property as new link
  • makes a GET request to newlink/v2/info and obtains authorization_endpoint, which is something like login.thisistheurl
  • tries to make POST to login.thisistheurl/oauth/token and gets 'unauthorized' which is completely true, as I can't login with my creds through browser (and probably should not be able to.

Can anybody help with corrections in my implementation?


Solution

  • The code actually works perfect. I had to check the accuracy in handling the variables. Exact issue is unknow.