├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values-v21
│ │ │ │ └── styles.xml
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ ├── drawable
│ │ │ │ └── microsoft_logo.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ ├── raw
│ │ │ │ └── auth_config.json
│ │ │ └── layout
│ │ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── azuresamples
│ │ │ └── msalandroidapp
│ │ │ └── MainActivity.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── azuresamples
│ │ │ └── msalandroidapp
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── azuresamples
│ │ └── msalandroidapp
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── images
└── topology1.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── azure-pipelines.yml
├── gradle.properties
├── AppCreationScripts
└── apps.json
├── LICENSE
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/images/topology1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/ms-identity-android-native/HEAD/images/topology1.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/ms-identity-android-native/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/microsoft_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/ms-identity-android-native/HEAD/app/src/main/res/drawable/microsoft_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/ms-identity-android-native/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/ms-identity-android-native/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/ms-identity-android-native/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/ms-identity-android-native/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/ms-identity-android-native/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea/
5 | /.idea/workspace.xml
6 | /.idea/libraries
7 | .DS_Store
8 | /build
9 | msal/build
10 | /captures
11 | .externalNativeBuild
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Apr 21 11:48:57 PDT 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/auth_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "client_id" : "Register your app at https://aka.ms/MobileAppReg",
3 | "authorization_user_agent" : "DEFAULT",
4 | "redirect_uri" : "Register your app at https://aka.ms/MobileAppReg",
5 | "authorities" : [
6 | {
7 | "type": "AAD",
8 | "audience": {
9 | "type": "AzureADandPersonalMicrosoftAccount",
10 | "tenant_id": "common"
11 | }
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/azuresamples/msalandroidapp/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.azuresamples.msalandroidapp;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | # Android
2 | # Build your Android project with Gradle.
3 | # Add steps that test, sign, and distribute the APK, save build artifacts, and more:
4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/android
5 |
6 | trigger:
7 | - master
8 |
9 | pool:
10 | vmImage: 'macos-latest'
11 |
12 | steps:
13 | - task: Gradle@2
14 | inputs:
15 | workingDirectory: ''
16 | gradleWrapperFile: 'gradlew'
17 | gradleOptions: '-Xmx3072m'
18 | publishJUnitResults: false
19 | testResultsFiles: '**/TEST-*.xml'
20 | tasks: 'assembleDebug'
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/dadaboli/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/azuresamples/msalandroidapp/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.azuresamples.msalandroidapp;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.danieldobalian.msalandroidapp", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/AppCreationScripts/apps.json:
--------------------------------------------------------------------------------
1 | {
2 | "Sample": {
3 | "Title": "Calling Microsoft Graph from an Android app",
4 | "Level": 200,
5 | "Client": "Android"
6 | },
7 | "AppRegistrations": [
8 | {
9 | "x-ms-id": "active-directory-android-native-v2",
10 | "x-ms-name": "android-native-v2",
11 | "x-ms-version": "2.0",
12 | "replyUrlsWithType": [
13 | {
14 | "url": "msal{AppId}://auth",
15 | "type": "InstalledClient"
16 | }
17 | ],
18 | "requiredResourceAccess": [
19 | {
20 | "x-ms-resourceAppName": "Microsoft Graph",
21 | "resourceAppId": "00000003-0000-0000-c000-000000000000",
22 | "resourceAccess": [
23 | {
24 | "id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
25 | "type": "Scope",
26 | "x-ms-name": "user.read"
27 | }
28 | ]
29 | }
30 | ]
31 | }
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | buildToolsVersion "28.0.3"
6 | defaultConfig {
7 | applicationId "com.azuresamples.msalandroidapp"
8 | minSdkVersion 21
9 | targetSdkVersion 27
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | implementation 'com.android.volley:volley:1.1.1'
28 | implementation 'com.microsoft.identity.client:msal:0.3.+'
29 |
30 | testImplementation 'junit:junit:4.12'
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | MIT License
3 |
4 | Copyright (c) 2017 Microsoft
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
22 |
31 |
32 |
40 |
41 |
47 |
48 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | languages:
3 | - java
4 | page_type: sample
5 | description: "The MSAL Android preview gives your app the ability to begin using the
6 | Microsoft identity platform."
7 | products:
8 | - azure
9 | - azure-active-directory
10 | - ms-graph
11 | urlFragment: msal-android-app
12 | ---
13 |
14 | # MSAL Android app calling Microsoft Graph
15 |
16 | >[!IMPORTANT]
17 | >DEPRECATED - Please see new Samples for MSAL 1.0
18 |
19 | * [Java Sample](https://github.com/Azure-Samples/ms-identity-android-java)
20 | * [Kotlin Sample](https://github.com/Azure-Samples/ms-identity-android-kotlin)
21 |
22 | | [Getting Started](https://docs.microsoft.com/azure/active-directory/develop/guidedsetups/active-directory-android)| [Library](https://github.com/AzureAD/microsoft-authentication-library-for-android) | [API Reference](http://javadoc.io/doc/com.microsoft.identity.client/msal) | [Support](README.md#community-help-and-support)
23 | | --- | --- | --- | --- |
24 |
25 | 
26 |
27 | The MSAL Android preview gives your app the ability to begin using the
28 | [Microsoft identity platform](https://aka,ms/aaddev) by supporting [Microsoft Azure Active Directory](https://azure.microsoft.com/services/active-directory/) and [Microsoft Accounts](https://account.microsoft.com) in a converged experience using industry standard OAuth2 and OpenID Connect. This sample demonstrates all the normal lifecycles your application should experience, including:
29 |
30 | * Get a token for the Microsoft Graph
31 | * Device-wide SSO and Conditional Access suport through the Auth Broker
32 | * Refresh tokens automatically
33 | * Call the Microsoft Graph
34 | * Sign out the user
35 |
36 | ## Scenario
37 |
38 | This app is a multi-tenant app meaning it can be used by any Azure AD tenant or Microsoft Account. It demonstrates how a developer can build apps to connect with enterprise users and access their Azure + O365 data via the Microsoft Graph. During the auth flow, end users will be required to sign in and consent to the permissions of the application, and in some cases may require an admin to consent to the app. The majority of the logic in this sample shows how to auth an end user and make a basic call to the Microsoft Graph.
39 |
40 | 
41 |
42 | ## Example
43 |
44 | ```Java
45 | // Initialize your app with MSAL
46 | PublicClientApplication pApp = new PublicClientApplication(
47 | this.getApplicationContext(),
48 | R.raw.auth_config);
49 |
50 | // Perform authentication requests
51 | pApp.acquireToken(getActivity(), SCOPES, getAuthInteractiveCallback());
52 |
53 | // ...
54 |
55 | // Get tokens to call APIs like the Microsoft Graph
56 | authenticationResult.getAccessToken();
57 | ```
58 |
59 | ## Register your App
60 |
61 | To begin registering your app, begin at the [Azure portal](https://aka.ms/MobileAppReg)
62 |
63 | To create an app,
64 | 1. Click `New Registration`.
65 |
66 | 2. Name your app, select the audience you're targeting, and click `Register`.
67 | - This sample is focused on `Accounts in any organization and personal Microsoft accounts`, but can be modified to support any audience.
68 | - Do not register a Redirect URI.
69 |
70 | 3. Click `Authentication` > `Add Platform` > `Android`.
71 | - Enter the Package Name from your Android studio project.
72 | - Generate a Signature Hash. Refer to the portal for instructions.
73 |
74 | 4. Hit the `Configured` button. Store the ***MSAL Configuration*** for the next steps.
75 |
76 | ## Steps to Run
77 |
78 | 1. Clone the code.
79 | ```
80 | git clone https://github.com/Azure-Samples/active-directory-android-native-v2
81 | ```
82 | 2. Open Android Studio, and select *open an existing Android Studio project*. Find the cloned project and open it.
83 |
84 | 3. Configure the sample code in `auth_config.JSON` and `AndroidManifest.xml`.
85 | - Copy and paste the ***MSAL Configuration*** JSON from the Azure portal into `auth_config.JSON`.
86 | - Inside the `AndroidManifest.xml`, replace `android:host` and `android:path` with the same info registered above.
87 | - `auth_config.JSON` contains this information as a reference inside the `redirect_uri` field.
88 | - The Signature Hash should NOT be URL encoded in the `AndroidManifest.xml`.
89 |
90 | 5. Select *Build* > *Clean Project*.
91 |
92 | 6. Select *Run* > *Run 'app'*.
93 |
94 | ## Feedback, Community Help, and Support
95 |
96 | We use [Stack Overflow](http://stackoverflow.com/questions/tagged/msal) with the community to
97 | provide support. We highly recommend you ask your questions on Stack Overflow first and browse
98 | existing issues to see if someone has asked your question before.
99 |
100 | If you find and bug or have a feature request, please raise the issue
101 | on [GitHub Issues](../../issues).
102 |
103 | To provide a recommendation, visit
104 | our [User Voice page](https://feedback.azure.com/forums/169401-azure-active-directory).
105 |
106 | ## Contribute
107 |
108 | We enthusiastically welcome contributions and feedback. You can clone the repo and start
109 | contributing now. Read our [Contribution Guide](Contributing.md) for more information.
110 |
111 | This project has adopted the
112 | [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
113 | For more information see
114 | the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
115 | [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
116 |
117 | ## Security Library
118 |
119 | This library controls how users sign-in and access services. We recommend you always take the
120 | latest version of our library in your app when possible. We
121 | use [semantic versioning](http://semver.org) so you can control the risk associated with updating
122 | your app. As an example, always downloading the latest minor version number (e.g. x.*y*.x) ensures
123 | you get the latest security and feature enhanements but our API surface remains the same. You
124 | can always see the latest version and release notes under the Releases tab of GitHub.
125 |
126 | ## Security Reporting
127 |
128 | If you find a security issue with our libraries or services please report it
129 | to [secure@microsoft.com](mailto:secure@microsoft.com) with as much detail as possible. Your
130 | submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty)
131 | program. Please do not post security issues to GitHub Issues or any other public site. We will
132 | contact you shortly upon receiving the information. We encourage you to get notifications of when
133 | security incidents occur by
134 | visiting [this page](https://technet.microsoft.com/en-us/security/dd252948) and subscribing
135 | to Security Advisory Alerts.
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/app/src/main/java/com/azuresamples/msalandroidapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.azuresamples.msalandroidapp;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.TextView;
11 | import android.widget.Toast;
12 | import com.android.volley.*;
13 | import com.android.volley.toolbox.JsonObjectRequest;
14 | import com.android.volley.toolbox.Volley;
15 | import org.json.JSONObject;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.Map;
19 | import com.microsoft.identity.client.*;
20 | import com.microsoft.identity.client.exception.*;
21 |
22 | public class MainActivity extends AppCompatActivity {
23 |
24 | /* Azure AD v2 Configs */
25 | final static String[] SCOPES = {"https://graph.microsoft.com/User.Read"};
26 | final static String MSGRAPH_URL = "https://graph.microsoft.com/v1.0/me";
27 |
28 | /* UI & Debugging Variables */
29 | private static final String TAG = MainActivity.class.getSimpleName();
30 | Button callGraphButton;
31 | Button signOutButton;
32 |
33 | /* Azure AD Variables */
34 | private PublicClientApplication sampleApp;
35 | private IAuthenticationResult authResult;
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.activity_main);
41 |
42 | callGraphButton = findViewById(R.id.callGraph);
43 | signOutButton = findViewById(R.id.clearCache);
44 |
45 | callGraphButton.setOnClickListener(new View.OnClickListener() {
46 | public void onClick(View v) {
47 | onCallGraphClicked();
48 | }
49 | });
50 |
51 | signOutButton.setOnClickListener(new View.OnClickListener() {
52 | public void onClick(View v) {
53 | onSignOutClicked();
54 | }
55 | });
56 |
57 | /* Configure your sample app and save state for this activity */
58 | sampleApp = new PublicClientApplication(
59 | this.getApplicationContext(),
60 | R.raw.auth_config);
61 |
62 |
63 | /* Attempt to get a user and acquireTokenSilent
64 | * If this fails we do an interactive request
65 | */
66 | sampleApp.getAccounts(new PublicClientApplication.AccountsLoadedCallback() {
67 | @Override
68 | public void onAccountsLoaded(final List accounts) {
69 |
70 | if (!accounts.isEmpty()) {
71 | /* This sample doesn't support multi-account scenarios, use the first account */
72 | sampleApp.acquireTokenSilentAsync(SCOPES, accounts.get(0), getAuthSilentCallback());
73 | } else {
74 | /* No accounts or >1 account */
75 | }
76 | }
77 | });
78 | }
79 |
80 | //
81 | // Core Identity methods used by MSAL
82 | // ==================================
83 | // onCallGraphClicked() - attempts to get tokens for graph, if it succeeds calls graph & updates UI
84 | // onSignOutClicked() - Signs account out of the app & updates UI
85 | // callGraphAPI() - called on successful token acquisition which makes an HTTP request to graph
86 | //
87 |
88 | /* Use MSAL to acquireToken for the end-user
89 | * Callback will call Graph api w/ access token & update UI
90 | */
91 | private void onCallGraphClicked() {
92 | sampleApp.acquireToken(getActivity(), SCOPES, getAuthInteractiveCallback());
93 | }
94 |
95 | /* Clears an account's tokens from the cache.
96 | * Logically similar to "sign out" but only signs out of this app.
97 | * User will get interactive SSO if trying to sign back-in.
98 | */
99 | private void onSignOutClicked() {
100 | /* Attempt to get a user and acquireTokenSilent
101 | * If this fails we do an interactive request
102 | */
103 | sampleApp.getAccounts(new PublicClientApplication.AccountsLoadedCallback() {
104 | @Override
105 | public void onAccountsLoaded(final List accounts) {
106 |
107 | if (accounts.isEmpty()) {
108 | /* No accounts to remove */
109 |
110 | } else {
111 | for (final IAccount account : accounts) {
112 | sampleApp.removeAccount(
113 | account,
114 | new PublicClientApplication.AccountsRemovedCallback() {
115 | @Override
116 | public void onAccountsRemoved(Boolean isSuccess) {
117 | if (isSuccess) {
118 | /* successfully removed account */
119 | } else {
120 | /* failed to remove account */
121 | }
122 | }
123 | });
124 | }
125 | }
126 |
127 | updateSignedOutUI();
128 | }
129 | });
130 | }
131 |
132 | /* Use Volley to make an HTTP request to the /me endpoint from MS Graph using an access token */
133 | private void callGraphAPI() {
134 | Log.d(TAG, "Starting volley request to graph");
135 |
136 | /* Make sure we have a token to send to graph */
137 | if (authResult.getAccessToken() == null) {return;}
138 |
139 | RequestQueue queue = Volley.newRequestQueue(this);
140 | JSONObject parameters = new JSONObject();
141 |
142 | try {
143 | parameters.put("key", "value");
144 | } catch (Exception e) {
145 | Log.d(TAG, "Failed to put parameters: " + e.toString());
146 | }
147 | JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, MSGRAPH_URL,
148 | parameters,new Response.Listener() {
149 | @Override
150 | public void onResponse(JSONObject response) {
151 | /* Successfully called graph, process data and send to UI */
152 | Log.d(TAG, "Response: " + response.toString());
153 |
154 | updateGraphUI(response);
155 | }
156 | }, new Response.ErrorListener() {
157 | @Override
158 | public void onErrorResponse(VolleyError error) {
159 | Log.d(TAG, "Error: " + error.toString());
160 | }
161 | }) {
162 | @Override
163 | public Map getHeaders() {
164 | Map headers = new HashMap<>();
165 | headers.put("Authorization", "Bearer " + authResult.getAccessToken());
166 | return headers;
167 | }
168 | };
169 |
170 | Log.d(TAG, "Adding HTTP GET to Queue, Request: " + request.toString());
171 |
172 | request.setRetryPolicy(new DefaultRetryPolicy(
173 | 3000,
174 | DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
175 | DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
176 | queue.add(request);
177 | }
178 |
179 | //
180 | // Helper methods manage UI updates
181 | // ================================
182 | // updateGraphUI() - Sets graph response in UI
183 | // updateSuccessUI() - Updates UI when token acquisition succeeds
184 | // updateSignedOutUI() - Updates UI when app sign out succeeds
185 | //
186 |
187 | /* Sets the graph response */
188 | private void updateGraphUI(JSONObject graphResponse) {
189 | TextView graphText = findViewById(R.id.graphData);
190 | graphText.setText(graphResponse.toString());
191 | }
192 |
193 | /* Set the UI for successful token acquisition data */
194 | private void updateSuccessUI() {
195 | callGraphButton.setVisibility(View.INVISIBLE);
196 | signOutButton.setVisibility(View.VISIBLE);
197 | findViewById(R.id.welcome).setVisibility(View.VISIBLE);
198 | ((TextView) findViewById(R.id.welcome)).setText("Welcome, " +
199 | authResult.getAccount().getUsername());
200 | findViewById(R.id.graphData).setVisibility(View.VISIBLE);
201 | }
202 |
203 | /* Set the UI for signed out account */
204 | private void updateSignedOutUI() {
205 | callGraphButton.setVisibility(View.VISIBLE);
206 | signOutButton.setVisibility(View.INVISIBLE);
207 | findViewById(R.id.welcome).setVisibility(View.INVISIBLE);
208 | findViewById(R.id.graphData).setVisibility(View.INVISIBLE);
209 | ((TextView) findViewById(R.id.graphData)).setText("No Data");
210 |
211 | Toast.makeText(getBaseContext(), "Signed Out!", Toast.LENGTH_SHORT)
212 | .show();
213 | }
214 |
215 | //
216 | // App callbacks for MSAL
217 | // ======================
218 | // getActivity() - returns activity so we can acquireToken within a callback
219 | // getAuthSilentCallback() - callback defined to handle acquireTokenSilent() case
220 | // getAuthInteractiveCallback() - callback defined to handle acquireToken() case
221 | //
222 |
223 | public Activity getActivity() {
224 | return this;
225 | }
226 |
227 | /* Callback used in for silent acquireToken calls.
228 | * Looks if tokens are in the cache (refreshes if necessary and if we don't forceRefresh)
229 | * else errors that we need to do an interactive request.
230 | */
231 | private AuthenticationCallback getAuthSilentCallback() {
232 | return new AuthenticationCallback() {
233 |
234 | @Override
235 | public void onSuccess(IAuthenticationResult authenticationResult) {
236 | /* Successfully got a token, call graph now */
237 | Log.d(TAG, "Successfully authenticated");
238 |
239 | /* Store the authResult */
240 | authResult = authenticationResult;
241 |
242 | /* call graph */
243 | callGraphAPI();
244 |
245 | /* update the UI to post call graph state */
246 | updateSuccessUI();
247 | }
248 |
249 | @Override
250 | public void onError(MsalException exception) {
251 | /* Failed to acquireToken */
252 | Log.d(TAG, "Authentication failed: " + exception.toString());
253 |
254 | if (exception instanceof MsalClientException) {
255 | /* Exception inside MSAL, more info inside MsalError.java */
256 | } else if (exception instanceof MsalServiceException) {
257 | /* Exception when communicating with the STS, likely config issue */
258 | } else if (exception instanceof MsalUiRequiredException) {
259 | /* Tokens expired or no session, retry with interactive */
260 | }
261 | }
262 |
263 | @Override
264 | public void onCancel() {
265 | /* User cancelled the authentication */
266 | Log.d(TAG, "User cancelled login.");
267 | }
268 | };
269 | }
270 |
271 | /* Callback used for interactive request. If succeeds we use the access
272 | * token to call the Microsoft Graph. Does not check cache
273 | */
274 | private AuthenticationCallback getAuthInteractiveCallback() {
275 | return new AuthenticationCallback() {
276 |
277 | @Override
278 | public void onSuccess(IAuthenticationResult authenticationResult) {
279 | /* Successfully got a token, call graph now */
280 | Log.d(TAG, "Successfully authenticated");
281 | Log.d(TAG, "ID Token: " + authenticationResult.getIdToken());
282 |
283 | /* Store the auth result */
284 | authResult = authenticationResult;
285 |
286 | /* call graph */
287 | callGraphAPI();
288 |
289 | /* update the UI to post call graph state */
290 | updateSuccessUI();
291 | }
292 |
293 | @Override
294 | public void onError(MsalException exception) {
295 | /* Failed to acquireToken */
296 | Log.d(TAG, "Authentication failed: " + exception.toString());
297 |
298 | if (exception instanceof MsalClientException) {
299 | /* Exception inside MSAL, more info inside MsalError.java */
300 | } else if (exception instanceof MsalServiceException) {
301 | /* Exception when communicating with the STS, likely config issue */
302 | }
303 | }
304 |
305 | @Override
306 | public void onCancel() {
307 | /* User canceled the authentication */
308 | Log.d(TAG, "User cancelled login.");
309 | }
310 | };
311 | }
312 | }
313 |
--------------------------------------------------------------------------------