How do I use MSAL4J to acquire a token for a daemon?

14k Views Asked by At

I have a daemon written in Java and running on AWS. It calls multiple Microsoft APIs using tokens based on client Id, client secret and tenant id for each of 100s of user accounts that I am supporting. All worked fine with MS Azure Active Directory Library for Java (ADAL4J). But that is going bye bye and so I am being forced over to MS Authentication Library for Java (MSAL4J).

Basically, I need to use a client id, secret and tenant to get an accessToken that is required for a MS API.

After much meandering through the examples (many of which compile), it seems that this is the closest code I can get to:

    public static String getToken( String apiUrl, 
            String clientId, 
            String clientSecret,
            String tenantId,
            String authUrl ) {

        String token = null ;

        if ( !authUrl.endsWith("/")){
            authUrl = authUrl + "/" ;
        }
/*
  NOTE: This is derived from the following:
  https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-acquire-token?tabs=java

  I simplified the code by taking out the SilentParameters support.

*/

        // BAD:  authUrl = authUrl + "organizations/";
        // BAD:  authUrl = "https://login.microsoftonline.com/" + tenantId + "/";
        // BAD:  authUrl = "https://login.microsoftonline.com/organizations/";
        authUrl = "https://login.microsoftonline.com/organizations/" + tenantId + "/" ;

        // BAD:  Set<String> SCOPE = Collections.singleton("https://graph.microsoft.com/.default");
        // BAD:  Set<String> scope = Collections.singleton(clientId);
        Set<String> scope = Collections.singleton("");

        // Load token cache from file and initialize token cache aspect. The token cache will have
        // dummy data, so the acquireTokenSilently call will fail.
        ITokenCacheAccessAspect tokenCacheAspect = new TokenPersistence("");

        PublicClientApplication pca;
        try {
            pca = PublicClientApplication 
            .builder(clientId)
            .authority(authUrl)
            .setTokenCacheAccessAspect(tokenCacheAspect)
            .build();
        } catch (MalformedURLException e) {
            return null ;
        }

        IAuthenticationResult result;

        /*
        BAD:  ClientCredentialParameters parameters =
        BAD:     ClientCredentialParameters
        BAD:         .builder(SCOPE)
        BAD:         .build();
        */
        UserNamePasswordParameters parameters =
                    UserNamePasswordParameters
                    .builder(scope, clientId, clientSecret.toCharArray())
                    .build();

        result = pca.acquireToken(parameters).join();

        token = result.accessToken() ;
        return token ;
    }

So, it compiles (even the BAD commented out code compiles), and it runs but it generates:

com.microsoft.aad.msal4j.MsalClientException: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.microsoft.aad.msal4j.InstanceDiscoveryMetadataEntry]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)

The above is generated on the acquireToken call (near the bottom).

I can't figure out what code needs a default constructor (to make the JSON happy). OTOH, I don't know if these are the calls that I should even be making; there seem to be about 47 different ways through and around this MSAL stuff and I am not at ALL sure if I have found "the right path".

Help Me, Obi-Wan Kenobi. You're My Only Hope!

2

There are 2 best solutions below

2
On

Try not using TokenCacheAccessAspect at all and see if that works? I.e. something like:

IClientCredential credential = ClientCredentialFactory.createFromSecret(clientSecret);    

ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, credential)
                    .authority(authUrl)
                    .build();

Set<String> scope = ImmutableSet.of();

ClientCredentialParameters parameters =
                     ClientCredentialParameters.builder(scope)
                             .build();

result = cca.acquireToken(parameters).join();

Where authUrl should look like https://login.microsoftonline.com/<tenantId>

See: https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-acquire-token?tabs=java

4
On

Checkout the ms-identity-java-daemon sample: https://github.com/Azure-Samples/ms-identity-java-daemon.