diff --git a/app/build.gradle b/app/build.gradle index c840f0f..d6efb79 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,31 +1,48 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 27 - buildToolsVersion "28.0.3" + compileSdkVersion 28 defaultConfig { applicationId "com.azuresamples.msalandroidapp" minSdkVersion 21 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { + debug{ + + } release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + flavorDimensions "main" + productFlavors { + local { + applicationIdSuffix ".local" + versionNameSuffix "-local" + resValue("string", "application_name", "msal-local") + } + dist { + applicationIdSuffix ".dist" + versionNameSuffix "-dist" + resValue("string", "application_name", "msal-dist") + } + } } dependencies { + implementation project(':msal') implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "com.android.support:appcompat-v7:28.0.0" androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) implementation 'com.android.volley:volley:1.1.1' - implementation 'com.microsoft.identity.client:msal:0.3.+' + testImplementation 'junit:junit:4.12' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9060b41..518ab50 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,8 +30,8 @@ + android:host= "com.azuresamples.msalandroidapp" + android:path= "/hk4Z0/bBPPUC8ll5PHC83WbD65k=" /> diff --git a/app/src/main/java/com/azuresamples/msalandroidapp/MainActivity.java b/app/src/main/java/com/azuresamples/msalandroidapp/MainActivity.java index 8a3e00c..d9cff61 100644 --- a/app/src/main/java/com/azuresamples/msalandroidapp/MainActivity.java +++ b/app/src/main/java/com/azuresamples/msalandroidapp/MainActivity.java @@ -1,28 +1,44 @@ package com.azuresamples.msalandroidapp; import android.app.Activity; -import android.content.Intent; -import android.support.v7.app.AppCompatActivity; +import android.content.Context; +import android.os.AsyncTask; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; -import com.android.volley.*; + +import com.android.volley.DefaultRetryPolicy; +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.Response; +import com.android.volley.VolleyError; import com.android.volley.toolbox.JsonObjectRequest; import com.android.volley.toolbox.Volley; +import com.microsoft.identity.client.AuthenticationCallback; +import com.microsoft.identity.client.IAccount; +import com.microsoft.identity.client.IAuthenticationResult; +import com.microsoft.identity.client.ISingleAccountPublicClientApplication; +import com.microsoft.identity.client.PublicClientApplication; +import com.microsoft.identity.client.exception.MsalClientException; +import com.microsoft.identity.client.exception.MsalException; +import com.microsoft.identity.client.exception.MsalServiceException; +import com.microsoft.identity.client.exception.MsalUiRequiredException; + import org.json.JSONObject; + import java.util.HashMap; -import java.util.List; import java.util.Map; -import com.microsoft.identity.client.*; -import com.microsoft.identity.client.exception.*; public class MainActivity extends AppCompatActivity { /* Azure AD v2 Configs */ - final static String[] SCOPES = {"https://graph.microsoft.com/User.Read"}; + final static String[] SCOPES = {"User.Read"}; final static String MSGRAPH_URL = "https://graph.microsoft.com/v1.0/me"; /* UI & Debugging Variables */ @@ -31,7 +47,7 @@ public class MainActivity extends AppCompatActivity { Button signOutButton; /* Azure AD Variables */ - private PublicClientApplication sampleApp; + private ISingleAccountPublicClientApplication sampleApp; private IAuthenticationResult authResult; @Override @@ -54,29 +70,66 @@ public void onClick(View v) { } }); - /* Configure your sample app and save state for this activity */ - sampleApp = new PublicClientApplication( - this.getApplicationContext(), - R.raw.auth_config); + new CreateSingleAccountPublicClientApplication().execute(this.getApplicationContext()); + } - /* Attempt to get a user and acquireTokenSilent - * If this fails we do an interactive request - */ - sampleApp.getAccounts(new PublicClientApplication.AccountsLoadedCallback() { + // + + private class CreateSingleAccountPublicClientApplication extends AsyncTask { + + @Override + protected ISingleAccountPublicClientApplication doInBackground(Context... contexts) { + + ISingleAccountPublicClientApplication app = null; + try { + app = PublicClientApplication.createSingleAccountPublicClientApplication(contexts[0], R.raw.auth_config); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (MsalException e) { + e.printStackTrace(); + } + return app; + } + + @Override + protected void onPostExecute(ISingleAccountPublicClientApplication app){ + sampleApp = app; + checkForAccountAndSignInSilently(); + } + + } + + private void checkForAccountAndSignInSilently(){ + + sampleApp.getCurrentAccountAsync(new ISingleAccountPublicClientApplication.CurrentAccountCallback() { @Override - public void onAccountsLoaded(final List accounts) { + public void onAccountLoaded(@Nullable IAccount activeAccount) { + if(activeAccount != null) { + getSilentToken(); + } + } - if (!accounts.isEmpty()) { - /* This sample doesn't support multi-account scenarios, use the first account */ - sampleApp.acquireTokenSilentAsync(SCOPES, accounts.get(0), getAuthSilentCallback()); - } else { - /* No accounts or >1 account */ + @Override + public void onAccountChanged(@Nullable IAccount priorAccount, @Nullable IAccount currentAccount) { + if(currentAccount != null) { + getSilentToken(); } } + + @Override + public void onError(@NonNull Exception exception) { + + } }); } + private void getSilentToken(){ + + sampleApp.acquireTokenSilentAsync(SCOPES, getDefaultAuthority(), false, getAuthSilentCallback()); + + } + // // Core Identity methods used by MSAL // ================================== @@ -89,7 +142,23 @@ public void onAccountsLoaded(final List accounts) { * Callback will call Graph api w/ access token & update UI */ private void onCallGraphClicked() { - sampleApp.acquireToken(getActivity(), SCOPES, getAuthInteractiveCallback()); + //sampleApp.acquireToken(getActivity(), SCOPES, getAuthInteractiveCallback()); + sampleApp.signIn(getActivity(), SCOPES, new AuthenticationCallback() { + @Override + public void onSuccess(IAuthenticationResult authenticationResult) { + getSilentToken(); + } + + @Override + public void onError(MsalException exception) { + + } + + @Override + public void onCancel() { + + } + }); } /* Clears an account's tokens from the cache. @@ -97,36 +166,49 @@ private void onCallGraphClicked() { * User will get interactive SSO if trying to sign back-in. */ private void onSignOutClicked() { + + /* Attempt to get a user and acquireTokenSilent * If this fails we do an interactive request */ - sampleApp.getAccounts(new PublicClientApplication.AccountsLoadedCallback() { + sampleApp.getCurrentAccountAsync(new ISingleAccountPublicClientApplication.CurrentAccountCallback() { @Override - public void onAccountsLoaded(final List accounts) { - - if (accounts.isEmpty()) { - /* No accounts to remove */ - - } else { - for (final IAccount account : accounts) { - sampleApp.removeAccount( - account, - new PublicClientApplication.AccountsRemovedCallback() { - @Override - public void onAccountsRemoved(Boolean isSuccess) { - if (isSuccess) { - /* successfully removed account */ - } else { - /* failed to remove account */ - } - } - }); - } + public void onAccountLoaded(@Nullable IAccount activeAccount) { + if(activeAccount != null){ + signOut(); } + } + @Override + public void onAccountChanged(@Nullable IAccount priorAccount, @Nullable IAccount currentAccount) { + if(currentAccount != null){ + signOut(); + } + } + + @Override + public void onError(@NonNull Exception exception) { + + } + }); + + + } + + private void signOut(){ + + sampleApp.signOut(new ISingleAccountPublicClientApplication.SignOutCallback() { + @Override + public void onSignOut() { updateSignedOutUI(); } + + @Override + public void onError(@NonNull MsalException exception) { + + } }); + } /* Use Volley to make an HTTP request to the /me endpoint from MS Graph using an access token */ @@ -196,7 +278,7 @@ private void updateSuccessUI() { signOutButton.setVisibility(View.VISIBLE); findViewById(R.id.welcome).setVisibility(View.VISIBLE); ((TextView) findViewById(R.id.welcome)).setText("Welcome, " + - authResult.getAccount().getUsername()); + authResult.getAccount().getClaims().get("preferred_username")); findViewById(R.id.graphData).setVisibility(View.VISIBLE); } @@ -224,6 +306,14 @@ public Activity getActivity() { return this; } + private String getDefaultAuthority(){ + if(sampleApp != null){ + return sampleApp.getConfiguration().getDefaultAuthority().getAuthorityURL().toString(); + }else{ + return ""; + } + } + /* Callback used in for silent acquireToken calls. * Looks if tokens are in the cache (refreshes if necessary and if we don't forceRefresh) * else errors that we need to do an interactive request. @@ -278,7 +368,7 @@ private AuthenticationCallback getAuthInteractiveCallback() { public void onSuccess(IAuthenticationResult authenticationResult) { /* Successfully got a token, call graph now */ Log.d(TAG, "Successfully authenticated"); - Log.d(TAG, "ID Token: " + authenticationResult.getIdToken()); + Log.d(TAG, "ID Token: " + authenticationResult.getAccount().getClaims().get("id_token")); /* Store the auth result */ authResult = authenticationResult; diff --git a/app/src/main/res/raw/auth_config.json b/app/src/main/res/raw/auth_config.json index 1e4339f..18408f2 100644 --- a/app/src/main/res/raw/auth_config.json +++ b/app/src/main/res/raw/auth_config.json @@ -1,7 +1,8 @@ { - "client_id" : "Register your app at https://aka.ms/MobileAppReg", + "client_id" : "24be1a19-6d3b-49bc-940e-6e5ead8f4fbf", "authorization_user_agent" : "DEFAULT", - "redirect_uri" : "Register your app at https://aka.ms/MobileAppReg", + "redirect_uri" : "msauth://com.azuresamples.msalandroidapp/hk4Z0%2FbBPPUC8ll5PHC83WbD65k%3D", + "account_mode" : "SINGLE", "authorities" : [ { "type": "AAD",