Commit 47ff318d by mpenmatsa@hycite.com

added new ADALActivity

parents 72c4a0a1 c5ee77ec
......@@ -32,12 +32,12 @@ android {
}
}
compileSdkVersion 27
compileSdkVersion 28
defaultConfig {
applicationId "com.vsoft.vera"
minSdkVersion 21
targetSdkVersion 27
targetSdkVersion 28
versionCode 1
versionName "0.0.1"
multiDexEnabled true
......@@ -68,26 +68,7 @@ android {
}
productFlavors {
uofl {
applicationId "com.vsoft.vera.uofl"
versionCode 1
versionName "0.0.1"
}
citrix {
applicationId "com.vsoft.vera.citrix"
versionCode 1
versionName "0.0.1"
}
ge {
applicationId "com.vsoft.vera.ge"
versionCode 1
versionName "0.0.1"
}
arrow {
applicationId "com.vsoft.vera.arrow"
versionCode 1
versionName "0.0.1"
}
vportal {
applicationId "com.vsoft.vera.vportal"
versionCode 1
......@@ -98,8 +79,14 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:support-annotations:27.1.1'
implementation 'com.android.volley:volley:1.1.+'
implementation('com.microsoft.aad:adal:1.14.+') {
exclude group: 'com.android.support'
}
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-annotations:28.0.0'
implementation 'android.arch.lifecycle:extensions:1.1.1'
testImplementation 'junit:junit:4.12'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
......@@ -117,12 +104,12 @@ dependencies {
}
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.github.nkzawa:socket.io-client:0.3.0'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.android.support:animated-vector-drawable:27.1.1'
implementation 'com.android.support:support-media-compat:27.1.1'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:animated-vector-drawable:28.0.0'
implementation 'com.android.support:support-media-compat:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation('com.google.firebase:firebase-messaging:17.6.0') {
exclude group: 'com.google.firebase', module: 'firebase-iid'
}
......
......@@ -41,10 +41,20 @@
android:name=".ui.LoginScreen"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize|stateHidden" />
<activity
android:name=".ui.OtpValidationActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize|stateHidden" />
<activity
android:name=".ui.LoginChooseActivity"
android:screenOrientation="portrait"
/> <activity
android:name=".ui.ADALActivity"
android:screenOrientation="portrait"
/>
<activity android:name=".ui.ResetPasswordActivity"
android:screenOrientation="portrait"
android:fitsSystemWindows="true"
......
package com.vsoft.vera.ui;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
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.aad.adal.ADALError;
import com.microsoft.aad.adal.AuthenticationCallback;
import com.microsoft.aad.adal.AuthenticationContext;
import com.microsoft.aad.adal.AuthenticationException;
import com.microsoft.aad.adal.AuthenticationResult;
import com.microsoft.aad.adal.IDispatcher;
import com.microsoft.aad.adal.Logger;
import com.microsoft.aad.adal.PromptBehavior;
import com.microsoft.aad.adal.Telemetry;
import com.vsoft.vera.R;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class ADALActivity extends AppCompatActivity {
/* UI & Debugging Variables */
private static final String TAG = ADALActivity.class.getSimpleName();
Button callGraphButton;
Button signOutButton;
/* Azure AD Constants */
/* Authority is in the form of https://login.microsoftonline.com/yourtenant.onmicrosoft.com */
private static final String AUTHORITY = "https://login.microsoftonline.com/common";
/* The clientID of your application is a unique identifier which can be obtained from the app registration portal */
private static final String CLIENT_ID = "b9808fea-11a7-4ed5-aab0-baae8688adb4";
/* Resource URI of the endpoint which will be accessed */
private static final String RESOURCE_ID = "https://graph.microsoft.com/";
/* The Redirect URI of the application (Optional) */
private static final String REDIRECT_URI = "x-msauth-com.com.vsoft.vmetrics://com.vsoft.vmetrics";
/* Microsoft Graph Constants */
private final static String MSGRAPH_URL = "https://graph.microsoft.com/v1.0/me";
/* Azure AD Variables */
private AuthenticationContext mAuthContext;
private AuthenticationResult mAuthResult;
/* Handler to do an interactive sign in and acquire token */
private Handler mAcquireTokenHandler;
/* Boolean variable to ensure invocation of interactive sign-in only once in case of multiple acquireTokenSilent call failures */
private static AtomicBoolean sIntSignInInvoked = new AtomicBoolean();
/* Constant to send message to the mAcquireTokenHandler to do acquire token with Prompt.Auto*/
private static final int MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO = 1;
/* Constant to send message to the mAcquireTokenHandler to do acquire token with Prompt.Always */
private static final int MSG_INTERACTIVE_SIGN_IN_PROMPT_ALWAYS = 2;
/* Constant to store user id in shared preferences */
private static final String USER_ID = "user_id";
/* Telemetry variables */
// Flag to turn event aggregation on/off
private static final boolean sTelemetryAggregationIsRequired = true;
/* Telemetry dispatcher registration */
static {
Telemetry.getInstance().registerDispatcher(new IDispatcher() {
@Override
public void dispatchEvent(Map<String, String> events) {
// Events from ADAL will be sent to this callback
for(Map.Entry<String, String> entry: events.entrySet()) {
Log.d(TAG, entry.getKey() + ": " + entry.getValue());
}
}
}, sTelemetryAggregationIsRequired);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.adal_main);
callGraphButton = (Button) findViewById(R.id.callGraph);
signOutButton = (Button) findViewById(R.id.clearCache);
callGraphButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
onCallGraphClicked();
}
});
signOutButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
onSignOutClicked();
}
});
mAuthContext = new AuthenticationContext(getApplicationContext(), AUTHORITY, false);
/* Instantiate handler which can invoke interactive sign-in to get the Resource
* sIntSignInInvoked ensures interactive sign-in is invoked one at a time */
mAcquireTokenHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
if( sIntSignInInvoked.compareAndSet(false, true)) {
if (msg.what == MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO){
mAuthContext.acquireToken(getActivity(), RESOURCE_ID, CLIENT_ID, REDIRECT_URI, PromptBehavior.Auto, getAuthInteractiveCallback());
}else if(msg.what == MSG_INTERACTIVE_SIGN_IN_PROMPT_ALWAYS){
mAuthContext.acquireToken(getActivity(), RESOURCE_ID, CLIENT_ID, REDIRECT_URI, PromptBehavior.Always, getAuthInteractiveCallback());
}
}
}
};
/* ADAL Logging callback setup */
Logger.getInstance().setExternalLogger(new Logger.ILogger() {
@Override
public void Log(String tag, String message, String additionalMessage, Logger.LogLevel level, ADALError errorCode) {
// You can filter the logs depending on level or errorcode.
Log.d(TAG, message + " " + additionalMessage);
}
});
/*Attempt an acquireTokenSilent call to see if we're signed in*/
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String userId = preferences.getString(USER_ID, "");
if(!TextUtils.isEmpty(userId)){
mAuthContext.acquireTokenSilentAsync(RESOURCE_ID, CLIENT_ID, userId, getAuthSilentCallback());
}
}
//
// Core Auth methods used by ADAL
// ==================================
// onActivityResult() - handles redirect from System browser
// onCallGraphClicked() - attempts to get tokens for graph, if it succeeds calls graph & updates UI
// callGraphAPI() - called on successful token acquisition which makes an HTTP request to graph
// onSignOutClicked() - Signs user out of the app & updates UI
//
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mAuthContext.onActivityResult(requestCode, resultCode, data);
}
/*
* End user clicked call Graph API button, time for Auth
* Use ADAL to get an Access token for the Microsoft Graph API
*/
private void onCallGraphClicked() {
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO);
}
private void callGraphAPI() {
Log.d(TAG, "Starting volley request to graph");
/* Make sure we have a token to send to graph */
if (mAuthResult.getAccessToken() == null) {return;}
RequestQueue queue = Volley.newRequestQueue(this);
JSONObject parameters = new JSONObject();
try {
parameters.put("key", "value");
} catch (Exception e) {
Log.d(TAG, "Failed to put parameters: " + e.toString());
}
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, MSGRAPH_URL,
parameters,new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
/* Successfully called graph, process data and send to UI */
Log.d(TAG, "Response: " + response.toString());
updateGraphUI(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error: " + error.toString());
}
}) {
@Override
public Map<String, String> getHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + mAuthResult.getAccessToken());
return headers;
}
};
Log.d(TAG, "Adding HTTP GET to Queue, Request: " + request.toString());
request.setRetryPolicy(new DefaultRetryPolicy(
3000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
}
private void onSignOutClicked() {
// End user has clicked the Sign Out button
// Kill the token cache
// Optionally call the signout endpoint to fully sign out the user account
mAuthContext.getCache().removeAll();
updateSignedOutUI();
}
//
// UI Helper methods
// ================================
// updateGraphUI() - Sets graph response in UI
// updateSuccessUI() - Updates UI when token acquisition succeeds
// updateSignedOutUI() - Updates UI when app sign out succeeds
//
private void updateGraphUI(JSONObject response) {
// Called on success from /me endpoint
// Writes graph data to the UI
TextView graphText = (TextView) findViewById(R.id.graphData);
graphText.setText(response.toString());
}
@SuppressLint("SetTextI18n")
private void updateSuccessUI() {
// Called on success from /me endpoint
// Removed call Graph API button and paint Sign out
callGraphButton.setVisibility(View.INVISIBLE);
signOutButton.setVisibility(View.VISIBLE);
findViewById(R.id.welcome).setVisibility(View.VISIBLE);
((TextView) findViewById(R.id.welcome)).setText("Welcome, " +
mAuthResult.getUserInfo().getGivenName());
findViewById(R.id.graphData).setVisibility(View.VISIBLE);
}
@SuppressLint("SetTextI18n")
private void updateSignedOutUI() {
callGraphButton.setVisibility(View.VISIBLE);
signOutButton.setVisibility(View.INVISIBLE);
findViewById(R.id.welcome).setVisibility(View.INVISIBLE);
findViewById(R.id.graphData).setVisibility(View.INVISIBLE);
((TextView) findViewById(R.id.graphData)).setText("No Data");
}
//
// ADAL Callbacks
// ======================
// getActivity() - returns activity so we can acquireToken within a callback
// getAuthSilentCallback() - callback defined to handle acquireTokenSilent() case
// getAuthInteractiveCallback() - callback defined to handle acquireToken() case
//
public Activity getActivity() {
return this;
}
/* 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.
*/
private AuthenticationCallback<AuthenticationResult> getAuthSilentCallback() {
return new AuthenticationCallback<AuthenticationResult>() {
@Override
public void onSuccess(AuthenticationResult authenticationResult) {
if(authenticationResult==null || TextUtils.isEmpty(authenticationResult.getAccessToken())
|| authenticationResult.getStatus()!= AuthenticationResult.AuthenticationStatus.Succeeded){
Log.d(TAG, "Silent acquire token Authentication Result is invalid, retrying with interactive");
/* retry with interactive */
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO);
return;
}
/* Successfully got a token, call graph now */
Log.d(TAG, "Successfully authenticated");
/* Store the mAuthResult */
mAuthResult = authenticationResult;
/* call graph */
callGraphAPI();
/* update the UI to post call graph state */
runOnUiThread(new Runnable() {
@Override
public void run() {
updateSuccessUI();
}
});
}
@Override
public void onError(Exception exception) {
/* Failed to acquireToken */
Log.e(TAG, "Authentication failed: " + exception.toString());
if (exception instanceof AuthenticationException) {
AuthenticationException authException = ((AuthenticationException) exception);
ADALError error = authException.getCode();
logHttpErrors(authException);
/* Tokens expired or no session, retry with interactive */
if (error == ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED ) {
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO);
}else if(error == ADALError.NO_NETWORK_CONNECTION_POWER_OPTIMIZATION){
/* Device is in Doze mode or App is in stand by mode.
Wake up the app or show an appropriate prompt for the user to take action
More information on this : https://github.com/AzureAD/azure-activedirectory-library-for-android/wiki/Handle-Doze-and-App-Standby */
Log.e(TAG, "Device is in doze mode or the app is in standby mode");
}
return;
}
/* Attempt an interactive on any other exception */
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO);
}
};
}
private void logHttpErrors(AuthenticationException authException){
int httpResponseCode = authException.getServiceStatusCode();
Log.d(TAG , "HTTP Response code: " + authException.getServiceStatusCode());
if(httpResponseCode< 200 || httpResponseCode >300) {
// logging http response headers in case of a http error.
HashMap<String, List<String>> headers = authException.getHttpResponseHeaders();
if (headers != null) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
sb.append(entry.getKey());
sb.append(":");
sb.append(entry.getValue().toString());
sb.append("; ");
}
Log.e(TAG, "HTTP Response headers: " + sb.toString());
}
}
}
/* Callback used for interactive request. If succeeds we use the access
* token to call the Microsoft Graph. Does not check cache
*/
private AuthenticationCallback<AuthenticationResult> getAuthInteractiveCallback() {
return new AuthenticationCallback<AuthenticationResult>() {
@Override
public void onSuccess(AuthenticationResult authenticationResult) {
if(authenticationResult==null || TextUtils.isEmpty(authenticationResult.getAccessToken())
|| authenticationResult.getStatus()!= AuthenticationResult.AuthenticationStatus.Succeeded){
Log.e(TAG, "Authentication Result is invalid");
return;
}
/* Successfully got a token, call graph now */
Log.d(TAG, "Successfully authenticated");
Log.d(TAG, "ID Token: " + authenticationResult.getIdToken());
/* Store the auth result */
mAuthResult = authenticationResult;
/* Store User id to SharedPreferences to use it to acquire token silently later */
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
preferences.edit().putString(USER_ID, authenticationResult.getUserInfo().getUserId()).apply();
/* call graph */
callGraphAPI();
/* update the UI to post call graph state */
runOnUiThread(new Runnable() {
@Override
public void run() {
updateSuccessUI();
}
});
/* set the sIntSignInInvoked boolean back to false */
sIntSignInInvoked.set(false);
}
@Override
public void onError(Exception exception) {
/* Failed to acquireToken */
Log.e(TAG, "Authentication failed: " + exception.toString());
if (exception instanceof AuthenticationException) {
ADALError error = ((AuthenticationException)exception).getCode();
if(error==ADALError.AUTH_FAILED_CANCELLED){
Log.e(TAG, "The user cancelled the authorization request");
}else if(error== ADALError.AUTH_FAILED_NO_TOKEN){
// In this case ADAL has found a token in cache but failed to retrieve it.
// Retry interactive with Prompt.Always to ensure we do an interactive sign in
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_ALWAYS);
}else if(error == ADALError.NO_NETWORK_CONNECTION_POWER_OPTIMIZATION){
/* Device is in Doze mode or App is in stand by mode.
Wake up the app or show an appropriate prompt for the user to take action
More information on this : https://github.com/AzureAD/azure-activedirectory-library-for-android/wiki/Handle-Doze-and-App-Standby */
Log.e(TAG, "Device is in doze mode or the app is in standby mode");
}
}
/* set the sIntSignInInvoked boolean back to false */
sIntSignInInvoked.set(false);
}
};
}
}
package com.vsoft.vera.ui;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
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.aad.adal.ADALError;
import com.microsoft.aad.adal.AuthenticationCallback;
import com.microsoft.aad.adal.AuthenticationContext;
import com.microsoft.aad.adal.AuthenticationException;
import com.microsoft.aad.adal.AuthenticationResult;
import com.microsoft.aad.adal.IDispatcher;
import com.microsoft.aad.adal.Logger;
import com.microsoft.aad.adal.PromptBehavior;
import com.microsoft.aad.adal.Telemetry;
import com.vsoft.vera.CatalogueApplication;
import com.vsoft.vera.R;
import com.vsoft.vera.db.models.UserApiValues;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class LoginChooseActivity extends Activity {
TextView loginSSO,loginOTP;
/* UI & Debugging Variables */
private static final String TAG = ADALActivity.class.getSimpleName();
/* Azure AD Constants */
/* Authority is in the form of https://login.microsoftonline.com/yourtenant.onmicrosoft.com */
private static final String AUTHORITY = "https://login.microsoftonline.com/common";
/* The clientID of your application is a unique identifier which can be obtained from the app registration portal */
private static final String CLIENT_ID = "b9808fea-11a7-4ed5-aab0-baae8688adb4";
/* Resource URI of the endpoint which will be accessed */
private static final String RESOURCE_ID = "https://graph.microsoft.com/";
/* The Redirect URI of the application (Optional) */
private static final String REDIRECT_URI = "x-msauth-com.com.vsoft.vmetrics://com.vsoft.vmetrics";
/* Microsoft Graph Constants */
private final static String MSGRAPH_URL = "https://graph.microsoft.com/v1.0/me";
/* Azure AD Variables */
private AuthenticationContext mAuthContext;
private AuthenticationResult mAuthResult;
/* Handler to do an interactive sign in and acquire token */
private Handler mAcquireTokenHandler;
/* Boolean variable to ensure invocation of interactive sign-in only once in case of multiple acquireTokenSilent call failures */
private static AtomicBoolean sIntSignInInvoked = new AtomicBoolean();
/* Constant to send message to the mAcquireTokenHandler to do acquire token with Prompt.Auto*/
private static final int MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO = 1;
/* Constant to send message to the mAcquireTokenHandler to do acquire token with Prompt.Always */
private static final int MSG_INTERACTIVE_SIGN_IN_PROMPT_ALWAYS = 2;
/* Constant to store user id in shared preferences */
private static final String USER_ID = "user_id";
/* Telemetry variables */
// Flag to turn event aggregation on/off
private static final boolean sTelemetryAggregationIsRequired = true;
/* Telemetry dispatcher registration */
static {
Telemetry.getInstance().registerDispatcher(new IDispatcher() {
@Override
public void dispatchEvent(Map<String, String> events) {
// Events from ADAL will be sent to this callback
for(Map.Entry<String, String> entry: events.entrySet()) {
Log.d(TAG, entry.getKey() + ": " + entry.getValue());
}
}
}, sTelemetryAggregationIsRequired);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
loginSSO = findViewById(R.id.login_with_sso);
loginOTP = findViewById(R.id.login_with_otp);
loginSSO.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(LoginChooseActivity.this,ADALActivity.class);
startActivity(intent);
// onCallGraphClicked();
}
});
loginOTP.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
// callGraphButton = (Button) findViewById(R.id.callGraph);
// signOutButton = (Button) findViewById(R.id.clearCache);
mAuthContext = new AuthenticationContext(getApplicationContext(), AUTHORITY, false);
/* Instantiate handler which can invoke interactive sign-in to get the Resource
* sIntSignInInvoked ensures interactive sign-in is invoked one at a time */
mAcquireTokenHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
if( sIntSignInInvoked.compareAndSet(false, true)) {
if (msg.what == MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO){
mAuthContext.acquireToken(getActivity(), RESOURCE_ID, CLIENT_ID, REDIRECT_URI, PromptBehavior.Auto, getAuthInteractiveCallback());
}else if(msg.what == MSG_INTERACTIVE_SIGN_IN_PROMPT_ALWAYS){
mAuthContext.acquireToken(getActivity(), RESOURCE_ID, CLIENT_ID, REDIRECT_URI, PromptBehavior.Always, getAuthInteractiveCallback());
}
}
}
};
/* ADAL Logging callback setup */
Logger.getInstance().setExternalLogger(new Logger.ILogger() {
@Override
public void Log(String tag, String message, String additionalMessage, Logger.LogLevel level, ADALError errorCode) {
// You can filter the logs depending on level or errorcode.
Log.d(TAG, message + " " + additionalMessage);
}
});
/*Attempt an acquireTokenSilent call to see if we're signed in*/
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String userId = preferences.getString(USER_ID, "");
if(!TextUtils.isEmpty(userId)){
mAuthContext.acquireTokenSilentAsync(RESOURCE_ID, CLIENT_ID, userId, getAuthSilentCallback());
}
}
/*
* End user clicked call Graph API button, time for Auth
* Use ADAL to get an Access token for the Microsoft Graph API
*/
private void onCallGraphClicked() {
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO);
}
private void callGraphAPI() {
Log.d(TAG, "Starting volley request to graph");
/* Make sure we have a token to send to graph */
if (mAuthResult.getAccessToken() == null) {return;}
RequestQueue queue = Volley.newRequestQueue(this);
JSONObject parameters = new JSONObject();
try {
parameters.put("key", "value");
} catch (Exception e) {
Log.d(TAG, "Failed to put parameters: " + e.toString());
}
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, MSGRAPH_URL,
parameters,new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
/* Successfully called graph, process data and send to UI */
Log.d(TAG, "Response: " + response.toString());
updateGraphUI(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error: " + error.toString());
}
}) {
@Override
public Map<String, String> getHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + mAuthResult.getAccessToken());
return headers;
}
};
Log.d(TAG, "Adding HTTP GET to Queue, Request: " + request.toString());
request.setRetryPolicy(new DefaultRetryPolicy(
3000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
}
private void onSignOutClicked() {
// End user has clicked the Sign Out button
// Kill the token cache
// Optionally call the signout endpoint to fully sign out the user account
mAuthContext.getCache().removeAll();
updateSignedOutUI();
}
//
// UI Helper methods
// ================================
// updateGraphUI() - Sets graph response in UI
// updateSuccessUI() - Updates UI when token acquisition succeeds
// updateSignedOutUI() - Updates UI when app sign out succeeds
//
private void updateGraphUI(JSONObject response) {
// Called on success from /me endpoint
// Writes graph data to the UI
TextView graphText = (TextView) findViewById(R.id.graphData);
graphText.setText(response.toString());
}
@SuppressLint("SetTextI18n")
private void updateSuccessUI() {
// Called on success from /me endpoint
// Removed call Graph API button and paint Sign out
findViewById(R.id.welcome).setVisibility(View.VISIBLE);
((TextView) findViewById(R.id.welcome)).setText("Welcome, " +
mAuthResult.getUserInfo().getGivenName());
findViewById(R.id.graphData).setVisibility(View.VISIBLE);
}
@SuppressLint("SetTextI18n")
private void updateSignedOutUI() {
findViewById(R.id.welcome).setVisibility(View.INVISIBLE);
findViewById(R.id.graphData).setVisibility(View.INVISIBLE);
((TextView) findViewById(R.id.graphData)).setText("No Data");
}
//
// ADAL Callbacks
// ======================
// getActivity() - returns activity so we can acquireToken within a callback
// getAuthSilentCallback() - callback defined to handle acquireTokenSilent() case
// getAuthInteractiveCallback() - callback defined to handle acquireToken() case
//
public Activity getActivity() {
return this;
}
/* 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.
*/
private AuthenticationCallback<AuthenticationResult> getAuthSilentCallback() {
return new AuthenticationCallback<AuthenticationResult>() {
@Override
public void onSuccess(AuthenticationResult authenticationResult) {
if(authenticationResult==null || TextUtils.isEmpty(authenticationResult.getAccessToken())
|| authenticationResult.getStatus()!= AuthenticationResult.AuthenticationStatus.Succeeded){
Log.d(TAG, "Silent acquire token Authentication Result is invalid, retrying with interactive");
/* retry with interactive */
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO);
return;
}
/* Successfully got a token, call graph now */
Log.d(TAG, "Successfully authenticated");
/* Store the mAuthResult */
mAuthResult = authenticationResult;
/* call graph */
callGraphAPI();
/* update the UI to post call graph state */
runOnUiThread(new Runnable() {
@Override
public void run() {
updateSuccessUI();
}
});
}
@Override
public void onError(Exception exception) {
/* Failed to acquireToken */
Log.e(TAG, "Authentication failed: " + exception.toString());
if (exception instanceof AuthenticationException) {
AuthenticationException authException = ((AuthenticationException) exception);
ADALError error = authException.getCode();
logHttpErrors(authException);
/* Tokens expired or no session, retry with interactive */
if (error == ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED ) {
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO);
}else if(error == ADALError.NO_NETWORK_CONNECTION_POWER_OPTIMIZATION){
/* Device is in Doze mode or App is in stand by mode.
Wake up the app or show an appropriate prompt for the user to take action
More information on this : https://github.com/AzureAD/azure-activedirectory-library-for-android/wiki/Handle-Doze-and-App-Standby */
Log.e(TAG, "Device is in doze mode or the app is in standby mode");
}
return;
}
/* Attempt an interactive on any other exception */
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_AUTO);
}
};
}
private void logHttpErrors(AuthenticationException authException){
int httpResponseCode = authException.getServiceStatusCode();
Log.d(TAG , "HTTP Response code: " + authException.getServiceStatusCode());
if(httpResponseCode< 200 || httpResponseCode >300) {
// logging http response headers in case of a http error.
HashMap<String, List<String>> headers = authException.getHttpResponseHeaders();
if (headers != null) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
sb.append(entry.getKey());
sb.append(":");
sb.append(entry.getValue().toString());
sb.append("; ");
}
Log.e(TAG, "HTTP Response headers: " + sb.toString());
}
}
}
/* Callback used for interactive request. If succeeds we use the access
* token to call the Microsoft Graph. Does not check cache
*/
private AuthenticationCallback<AuthenticationResult> getAuthInteractiveCallback() {
return new AuthenticationCallback<AuthenticationResult>() {
@Override
public void onSuccess(AuthenticationResult authenticationResult) {
if(authenticationResult==null || TextUtils.isEmpty(authenticationResult.getAccessToken())
|| authenticationResult.getStatus()!= AuthenticationResult.AuthenticationStatus.Succeeded){
Log.e(TAG, "Authentication Result is invalid");
return;
}
/* Successfully got a token, call graph now */
Log.d(TAG, "Successfully authenticated");
Log.d(TAG, "ID Token: " + authenticationResult.getIdToken());
/* Store the auth result */
mAuthResult = authenticationResult;
/* Store User id to SharedPreferences to use it to acquire token silently later */
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
preferences.edit().putString(USER_ID, authenticationResult.getUserInfo().getUserId()).apply();
/* call graph */
callGraphAPI();
/* update the UI to post call graph state */
runOnUiThread(new Runnable() {
@Override
public void run() {
updateSuccessUI();
}
});
/* set the sIntSignInInvoked boolean back to false */
sIntSignInInvoked.set(false);
}
@Override
public void onError(Exception exception) {
/* Failed to acquireToken */
Log.e(TAG, "Authentication failed: " + exception.toString());
if (exception instanceof AuthenticationException) {
ADALError error = ((AuthenticationException)exception).getCode();
if(error==ADALError.AUTH_FAILED_CANCELLED){
Log.e(TAG, "The user cancelled the authorization request");
}else if(error== ADALError.AUTH_FAILED_NO_TOKEN){
// In this case ADAL has found a token in cache but failed to retrieve it.
// Retry interactive with Prompt.Always to ensure we do an interactive sign in
mAcquireTokenHandler.sendEmptyMessage(MSG_INTERACTIVE_SIGN_IN_PROMPT_ALWAYS);
}else if(error == ADALError.NO_NETWORK_CONNECTION_POWER_OPTIMIZATION){
/* Device is in Doze mode or App is in stand by mode.
Wake up the app or show an appropriate prompt for the user to take action
More information on this : https://github.com/AzureAD/azure-activedirectory-library-for-android/wiki/Handle-Doze-and-App-Standby */
Log.e(TAG, "Device is in doze mode or the app is in standby mode");
}
}
/* set the sIntSignInInvoked boolean back to false */
sIntSignInInvoked.set(false);
}
};
}
}
......@@ -49,7 +49,7 @@ public class SplashScreen extends Activity {
public void run() {
// This method will be executed once the timer is over
// Start your app main activity
Intent i = new Intent(SplashScreen.this, LoginScreen.class);
Intent i = new Intent(SplashScreen.this, LoginChooseActivity.class);
startActivity(i);
// close this activity
......
......@@ -14,43 +14,37 @@
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
android:background="@drawable/ic_login_banner" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="50dp"
android:layout_marginTop="100dp"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<EditText
android:id="@+id/login_screen_username_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/extra_large_margin"
android:layout_marginRight="@dimen/extra_large_margin"
android:background="@drawable/username_under_bg_box"
android:drawableLeft="@mipmap/ic_user_icon"
android:hint="@string/username_string"
android:lines="1"
android:padding="@dimen/normal_margin"
android:singleLine="true" />
<EditText
android:id="@+id/login_screen_password_edit_text"
<TextView
android:id="@+id/login_with_sso"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/normal_margin"
android:layout_marginLeft="@dimen/extra_large_margin"
android:layout_marginRight="@dimen/extra_large_margin"
android:background="@drawable/username_under_bg_box"
android:drawableLeft="@mipmap/ic_password_icon"
android:hint="@string/password_string"
android:inputType="textPassword"
android:lines="1"
android:padding="@dimen/normal_margin"
android:singleLine="true" />
android:layout_marginTop="30dp"
android:background="@drawable/login_bg"
android:gravity="center"
android:paddingBottom="@dimen/normal_margin"
android:paddingTop="@dimen/normal_margin"
android:text="Login with ADAL"
android:textColor="@android:color/white"
android:textSize="@dimen/large_text_size" />
<TextView
android:id="@+id/login_screen_login_text_view"
android:id="@+id/login_with_otp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/normal_margin"
......@@ -61,7 +55,7 @@
android:gravity="center"
android:paddingBottom="@dimen/normal_margin"
android:paddingTop="@dimen/normal_margin"
android:text="@string/login_screen_login_string"
android:text="Login with otp"
android:textColor="@android:color/white"
android:textSize="@dimen/large_text_size" />
</LinearLayout>
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical"
tools:context="com.vsoft.vera.ui.ADALActivity">
<TextView
android:text="WelCome"
android:textColor="#3f3f3f"
android:textSize="50px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="15dp"
android:id="@+id/welcome"
android:visibility="invisible"/>
<Button
android:id="@+id/callGraph"
android:text="Call Graph"
android:textColor="#FFFFFF"
android:background="#00a1f1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:textAllCaps="false" />
<TextView
android:text="Loading"
android:textColor="#3f3f3f"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:id="@+id/graphData"
android:visibility="invisible"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center|bottom"
android:orientation="vertical" >
<Button
android:text="Signoutt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:textColor="#FFFFFF"
android:background="#00a1f1"
android:textAllCaps="false"
android:id="@+id/clearCache"
android:visibility="invisible" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
......@@ -8,7 +8,7 @@ buildscript {
maven { url "https://jitpack.io" }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.2.0'
......
......@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment