0

I have an Android application in which I'm using Azure AD B2C to authenticate users. Users login and logout of the application as needed. I would like to give the user the option to delete their own account.

I understand that I need to use the Azure AD Graph API to delete the user. This is what I have so far:

According to this link, it looks like deleting a user from a personal account (which is what the B2C users are using) is not possible. Is that correct?

Here's my code snippet for the Graph API call. Feel free to ignore it if I'm off track and there is a better way to solve this.

I believe I need a separate access token than what my app currently has (as the graph API requires other API consent). So, I'm getting the access token as follows:

AcquireTokenParameters parameters = new AcquireTokenParameters.Builder()
      .startAuthorizationFromActivity(getActivity())
      .fromAuthority(B2CConfiguration.getAuthorityFromPolicyName(B2CConfiguration.Policies.get("SignUpSignIn")))
      .withScopes(B2CConfiguration.getGraphAPIScopes())
      .withPrompt(Prompt.CONSENT)
      .withCallback(getGraphAPIAuthCallback())
      .build();

taxApp.acquireToken(parameters);

In the getGraphAPIAuthCallback() method, I'm calling the Graph API using a separate thread (in the background):

boolean resp = new DeleteUser().execute(authenticationResult.getAccessToken()).get();

Finally, in my DeleterUser() AsyncTask, I'm doing the following:

@Override
    protected Boolean doInBackground(String... aToken) {

        final String asToken = aToken[0];
        //this method will be running on background thread so don't update UI from here
        //do your long running http tasks here,you dont want to pass argument and u can access the parent class' variable url over here
        IAuthenticationProvider mAuthenticationProvider = new IAuthenticationProvider() {
            @Override
            public void authenticateRequest(final IHttpRequest request) {
                request.addHeader("Authorization",
                        "Bearer " + asToken);
            }
        };

        final IClientConfig mClientConfig = DefaultClientConfig
                .createWithAuthenticationProvider(mAuthenticationProvider);

        final IGraphServiceClient graphClient = new GraphServiceClient.Builder()
                .fromConfig(mClientConfig)
                .buildClient();

        try {
            graphClient.getMe().buildRequest().delete();
        } catch (Exception e) {
            Log.d(AccountSettingFragment.class.toString(), "Error deleting user. Error Details: " + e.getStackTrace());
        }

        return true;
    }

Currently, my app fails when trying to get an access token with a null pointer exception:

com.microsoft.identity.client.exception.MsalClientException: Attempt to invoke virtual method 'long java.lang.Long.longValue()' on a null object reference

Any idea what I need to do to provide the user the option to users to delete their own account? Thank you!

DataGeek
  • 490
  • 3
  • 20

2 Answers2

2

Thanks for the help, @allen-wu. Due to his help, this azure feedback request and this azure doc, I was able to figure out how to get and delete users silently (without needing intervention).

As @allen-wu stated, you cannot have a user delete itself. So, I decided to have the mobile app call my server-side NodeJS API when the user clicks the 'Delete Account' button (as I do not want to store the client secret in the android app) and have the NodeJS API call the Azure AD endpoint to delete the user silently. The one caveat is that admin consent is needed the first time you try to auth. Also, I have only tested this for Graph API. I'm not a 100% sure if it works for other APIs as well.

Here are the steps:

  1. Create your application in your AAD B2C tenant. Create a client secret and give it the following API permissions: Directory.ReadWrite.All ; AuditLog.Read.All (I'm not a 100% sure if we need the AuditLog permission. I haven't tested without it yet).

  2. In a browser, paste the following link:

GET https://login.microsoftonline.com/{tenant}/adminconsent?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&state=12345
&redirect_uri=http://localhost/myapp/permissions
  1. Login using an existing admin account and provide the consent to the app.
  2. Once you've given admin consent, you do not have to repeat steps 1-3 again. Next, make the following call to get an access token:
POST https://login.microsoftonline.com/{B2c_tenant_name}.onmicrosoft.com/oauth2/v2.0/token

In the body, include your client_id, client_secret, grant_type (the value for which should be client_credentials) and scope (value should be 'https://graph.microsoft.com/.default')

  1. Finally, you can call the Graph API to manage your users, including deleting them:
DELETE https://graph.microsoft.com/v1.0/users/{upn}

Don't forget to include the access token in the header. I noticed that in Postman, the graph api had a bug and returned an error if I include the word 'Bearer' at the start of the Authorization header. Try without it and it works. I haven't tried it in my NodeJS API yet, so, can't comment on it so far.

@allen-wu also suggested using the ROPC flow, which I have not tried yet, so, cannot compare the two approaches.

I hope this helps!

DataGeek
  • 490
  • 3
  • 20
0

There is a line of code: graphClient.getUsers("").buildRequest().delete();

It seems that you didn't put the user object id in it.

However, we can ignore this problem because Microsoft Graph doesn't allow a user to delete itself.

Here is the error when I try to do it.

{
    "error": {
        "code": "Request_BadRequest",
        "message": "The principal performing this request cannot delete itself.",
        "innerError": {
            "request-id": "8f44118f-0e49-431f-a0a0-80bdd954a7f0",
            "date": "2020-06-04T06:41:14"
        }
    }
}
Allen Wu
  • 15,529
  • 1
  • 9
  • 20
  • Thanks Allen. I updated my code. It was supposed to be ```getMe()```. I was testing the ```getUsers()```, but, my code wasn't even reaching there. Interesting to know that Graph doesn't let you delete itself. Any idea what the recommended approach would be in this case? How can I delete the user in a secure way? – DataGeek Jun 04 '20 at 18:59
  • 1
    @DataGeekd We should use an admin account to delete the user (not itself) via portal or Microsoft Graph. The maintenance of AD resources should be done by an admin. If my answer is helpful for you, you can accept it as answer. Thank you. – Allen Wu Jun 05 '20 at 01:16
  • Thank you for that answer, @allen-wu. Your answer does help me, although your comment helped me more than your actual answer. If you could post/edit your answer to include information on what the steps should be to have a user delete itself, that would be super helpful! My Android app calls a nodejs api, so, i could setup the deletion in the nodejs API. However, I am concerned that if someone gets a hold of the access token, they may impersonate the user and send a delete request to the API. Thoughts? Thanks again for your help! – DataGeek Jun 09 '20 at 05:19
  • 1
    @DataGeek A user cannot delete itself. If you want to know how to delete users by using a admin account, just refer to https://learn.microsoft.com/en-us/graph/api/user-delete?view=graph-rest-1.0&tabs=javascript#response. You can make use of application roles:https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps#:~:text=Declare%20roles%20for%20an%20application,and%20from%20their%20group%20membership. to let only admin can delete users. – Allen Wu Jun 09 '20 at 06:59
  • Thanks for that. I am not quite sure I follow the flow. Can you please help explain it better? I'm lost of Step #4 here. Do I have the flow correct? 1. B2C user logs into Android app with live account 2. Once logged in, the user clicks the button to delete their account 3. This send a DELETE API request to my NodeJS API 4. In my NodeJS API, I validate the auth token received from the android client. Once validated, I get an auth token from Azure AD for an admin app registration using 'client_credentials' 5. I use the newly received auth token to connect to the graph api and delete user – DataGeek Jun 10 '20 at 06:32
  • Also, it looks Azure AD B2C doesn't support client credentials: https://stackoverflow.com/questions/49757722/client-credential-gran-type-not-support-with-a-custom-b2c-policy Please help! This is super confusing and not very well documented. Thank you! – DataGeek Jun 10 '20 at 06:36
  • @DataGeek Client_credentials flow is not supported in B2C. You need to consider auth code flow: https://learn.microsoft.com/en-us/azure/active-directory-b2c/authorization-code-flow. – Allen Wu Jun 10 '20 at 08:24
  • I'm a little confused. The auth code flow seems to have an interactive step. Is that right? If so, how will I get an admin access token to delete the user if a regular user is logged into my app? I appreciate your help so far, but, I'm not sure what the steps look like for me to have a user click on a 'Delete Account' on the android app and get deleted from Azure AD B2C. Can you please help? – DataGeek Jun 11 '20 at 06:30
  • @DataGeek I'm not sure if the step 4 could be implemented here. But if your NodeJS API doesn't provide interactive login, you should consider ROPC flow: https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-ropc?tabs=app-reg-ga. Please note that Microsoft recommends you do not use the ROPC flow because it's not secure enough. – Allen Wu Jun 11 '20 at 06:44