Skip to content
This repository has been archived by the owner on Feb 8, 2024. It is now read-only.

[WIP]Updates for MSAL 0.4 #22

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -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')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the plan around using this value vs loading from Maven central?

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'
}
4 changes: 2 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@

<!--Add in your scheme/host from registered redirect URI-->
<data android:scheme="msauth"
android:host= "YOUR_PACKAGE_NAME - must be registered at https://aka.ms/MobileAppReg"
android:path= "/YOUR_DECODED_SIGNATURE_HASH - must be registered at https://aka.ms/MobileAppReg>" />
android:host= "com.azuresamples.msalandroidapp"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was on purpose to keep it in this way.

android:host= "YOUR_PACKAGE_NAME - must be registered at https://aka.ms/MobileAppReg"
android:path= "/YOUR_DECODED_SIGNATURE_HASH - must be registered at https://aka.ms/MobileAppReg>" />

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I'll put back the original values... prior to actually merging. Leaving it this way for testing.

android:path= "/hk4Z0/bBPPUC8ll5PHC83WbD65k=" />
</intent-filter>
</activity>

Expand Down
182 changes: 136 additions & 46 deletions app/src/main/java/com/azuresamples/msalandroidapp/MainActivity.java
Original file line number Diff line number Diff line change
@@ -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 */
Expand All @@ -31,7 +47,7 @@ public class MainActivity extends AppCompatActivity {
Button signOutButton;

/* Azure AD Variables */
private PublicClientApplication sampleApp;
private ISingleAccountPublicClientApplication sampleApp;
private IAuthenticationResult authResult;

@Override
Expand All @@ -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<Context, Void, ISingleAccountPublicClientApplication> {

@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<IAccount> 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
// ==================================
Expand All @@ -89,44 +142,73 @@ public void onAccountsLoaded(final List<IAccount> 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.
* Logically similar to "sign out" but only signs out of this app.
* 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<IAccount> 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) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should show a Toast or dialog if this error gets hit - otherwise, users of the samples might be confused with a silent failure

}
});


}

private void signOut(){

sampleApp.signOut(new ISingleAccountPublicClientApplication.SignOutCallback() {
@Override
public void onSignOut() {
updateSignedOutUI();
}

@Override
public void onError(@NonNull MsalException exception) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same feedback here regarding a Toast or dialog

}
});

}

/* Use Volley to make an HTTP request to the /me endpoint from MS Graph using an access token */
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/res/raw/auth_config.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down