Search code examples
javaauthenticationonedriveadaladal4j

OneDrive authentication using ADAL4J


The Active Directory Authentication Library for Java (ADAL4J) allows authentication via access token to the Microsoft Graph API, using the following (simplified) code:

public String authenticate(String authorizationUrl, String clientId, String clientSecret) throws Exception {
    ExecutorService service = Executors.newFixedThreadPool(1);
    AuthenticationContext context = new AuthenticationContext(authorizationUrl, false, service);
    ClientCredential credential = new ClientCredential(clientId, clientSecret);
    Future<AuthenticationResult> future = context.acquireToken(“https://graph.microsoft.com”, credential, null);
    return future.get().getAccessToken();
}

The above works for certain parts of Graph (e.g., for accessing Office 365 accounts), but does not work for OneDrive, where it returns an access token that does not have proper authorization.

Acquiring an access token via POSTMAN works as expected, with the following parameters:

authorizationUrl: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
accessTokenUrl: https://login.microsoftonline.com/common/oauth2/v2.0/token
clientId: <the clientId for the application>
clientSecret: <the clientSecret for the application> 
scope: https://graph.microsoft.com/.default
state: <empty>

More specifically, running the above in POSTMAN returns an access token with additional scopes, including https://graph.microsoft.com/Files.ReadWrite.All. Using that access token in the Java application that calls the authenticate() method above, does work, e.g. it lists the contents of the root directory using /me/drive/root/children as the REST path.

If, however, the access token returned by the authenticate() method is used, an error is returned by OneDrive. Removing the user name (me) from the path returns only 1 file name, if the specific tenant ID is used instead of common in the authorizationUrl.

There seems to be no way to add a scope value in ADAL4J and numerous other variations either result in an error, or in getting back 1 file (probably from a different context).

Is there any way to get a fully authorized access token via ADAL4J, for OneDrive?


Solution

  • There are two different permissions: one is application permission, the other is delegated permission.

    "Delegated" permissions, which specify scope-based access using delegated authorization from the signed-in resource owner, are presented to the resource at run-time as "scp" claims in the client's access token.

    "Application" permissions, which specify role-based access using the client application's credentials/identity, are presented to the resource at run-time as "roles" claims in the client's access token.

    When you get token from POSTMAN for the first time, you will be asked to provide your credentials. In this way, you will get a token with delegated permission, which represents the account. So that, you can call the Graph API under /me.

    However, the java code you used to get token can only get an access token with application permission. The application does not have a user identity, as a result, you will get an error while call the Graph API under /me. With application permission, you can only call the Graph API as /users/{user-id}

    Solution:

    You can get an access token with delegated permission in Java as following:

    A) Create an app with public client platform

    enter image description here

    B) Add necessary Graph API permission, and grant admin consent for your tenant

    enter image description here

    C) Get token

    ExecutorService service = Executors.newFixedThreadPool(1);
    AuthenticationContext context = null;
    try {
        context = new AuthenticationContext("https://login.microsoftonline.com/" + "TENANT_ID", false, service);
        Future<AuthenticationResult> future = context.acquireToken("https://graph.microsoft.com", "client_id", "username", "password",null);
        System.out.println( future.get().getAccessToken());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }finally {
        service.shutdown();
    }