├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── Podfile ├── QuickStart.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── QuickStart ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── QuickStart.entitlements └── ViewController.swift ├── README.md └── images └── iosintro.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | *.lock 23 | *.xcworkspace 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Azure samples 2 | 3 | Thank you for your interest in contributing to Azure samples! 4 | 5 | ## Ways to contribute 6 | 7 | You can contribute to [Azure samples](https://azure.microsoft.com/documentation/samples/) in a few different ways: 8 | 9 | - Submit feedback on [this sample page](https://azure.microsoft.com/documentation/samples/active-directory-ios/) whether it was helpful or not. 10 | - Submit issues through [issue tracker](https://github.com/Azure-Samples/active-directory-ios/issues) on GitHub. We are actively monitoring the issues and improving our samples. 11 | - If you wish to make code changes to samples, or contribute something new, please follow the [GitHub Forks / Pull requests model](https://help.github.com/articles/fork-a-repo/): Fork the sample repo, make the change and propose it back by submitting a pull request. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'QuickStart' do 4 | use_frameworks! 5 | pod 'ADAL' 6 | end 7 | -------------------------------------------------------------------------------- /QuickStart.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 78880686B80CF8D84046D260 /* Pods_QuickStart.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA7653BB6CB4821B302534A3 /* Pods_QuickStart.framework */; }; 11 | FB1A4AF4214992180062E42D /* ADAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB1A4AF3214992180062E42D /* ADAL.framework */; }; 12 | FBA79EA11E9A038700179A54 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBA79EA01E9A038700179A54 /* AppDelegate.swift */; }; 13 | FBA79EA31E9A038700179A54 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBA79EA21E9A038700179A54 /* ViewController.swift */; }; 14 | FBA79EA61E9A038700179A54 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FBA79EA41E9A038700179A54 /* Main.storyboard */; }; 15 | FBA79EA81E9A038700179A54 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FBA79EA71E9A038700179A54 /* Assets.xcassets */; }; 16 | FBA79EAB1E9A038700179A54 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FBA79EA91E9A038700179A54 /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 4B5985AC4EC72AE16C81F234 /* Pods-QuickStart.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickStart.debug.xcconfig"; path = "Pods/Target Support Files/Pods-QuickStart/Pods-QuickStart.debug.xcconfig"; sourceTree = ""; }; 21 | 83ED874D158D34B63DEB6793 /* Pods-QuickStart.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickStart.release.xcconfig"; path = "Pods/Target Support Files/Pods-QuickStart/Pods-QuickStart.release.xcconfig"; sourceTree = ""; }; 22 | C7CBF881BF504F0A15F7FD5C /* Pods-QuickStart.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickStart.debug.xcconfig"; path = "Pods/Target Support Files/Pods-QuickStart/Pods-QuickStart.debug.xcconfig"; sourceTree = ""; }; 23 | CA7653BB6CB4821B302534A3 /* Pods_QuickStart.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_QuickStart.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | DF06CB9178C218430A5EDADB /* Pods-QuickStart.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickStart.release.xcconfig"; path = "Pods/Target Support Files/Pods-QuickStart/Pods-QuickStart.release.xcconfig"; sourceTree = ""; }; 25 | FB1A4AF3214992180062E42D /* ADAL.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ADAL.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | FBA79E9D1E9A038700179A54 /* QuickStart.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuickStart.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | FBA79EA01E9A038700179A54 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 28 | FBA79EA21E9A038700179A54 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 29 | FBA79EA51E9A038700179A54 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | FBA79EA71E9A038700179A54 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | FBA79EAA1E9A038700179A54 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | FBA79EAC1E9A038700179A54 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | FBBE358D1E9C476300460053 /* QuickStart.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = QuickStart.xcodeproj; sourceTree = ""; }; 34 | FBBE35B41E9DAE6C00460053 /* QuickStart.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QuickStart.entitlements; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | FBA79E9A1E9A038700179A54 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | FB1A4AF4214992180062E42D /* ADAL.framework in Frameworks */, 43 | 78880686B80CF8D84046D260 /* Pods_QuickStart.framework in Frameworks */, 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | AED3371A3BA5BB965217412E /* Frameworks */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | FB1A4AF3214992180062E42D /* ADAL.framework */, 54 | CA7653BB6CB4821B302534A3 /* Pods_QuickStart.framework */, 55 | ); 56 | name = Frameworks; 57 | sourceTree = ""; 58 | }; 59 | C864BFB36A9CE66547F41D83 /* Pods */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | C7CBF881BF504F0A15F7FD5C /* Pods-QuickStart.debug.xcconfig */, 63 | DF06CB9178C218430A5EDADB /* Pods-QuickStart.release.xcconfig */, 64 | 4B5985AC4EC72AE16C81F234 /* Pods-QuickStart.debug.xcconfig */, 65 | 83ED874D158D34B63DEB6793 /* Pods-QuickStart.release.xcconfig */, 66 | ); 67 | name = Pods; 68 | sourceTree = ""; 69 | }; 70 | FB1A4AF5214993550062E42D /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | FBA79E941E9A038700179A54 = { 78 | isa = PBXGroup; 79 | children = ( 80 | FBBE358D1E9C476300460053 /* QuickStart.xcodeproj */, 81 | FBA79E9F1E9A038700179A54 /* QuickStart */, 82 | FBA79E9E1E9A038700179A54 /* Products */, 83 | C864BFB36A9CE66547F41D83 /* Pods */, 84 | AED3371A3BA5BB965217412E /* Frameworks */, 85 | ); 86 | sourceTree = ""; 87 | }; 88 | FBA79E9E1E9A038700179A54 /* Products */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | FBA79E9D1E9A038700179A54 /* QuickStart.app */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | FBA79E9F1E9A038700179A54 /* QuickStart */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | FBBE35B41E9DAE6C00460053 /* QuickStart.entitlements */, 100 | FBA79EA01E9A038700179A54 /* AppDelegate.swift */, 101 | FBA79EA21E9A038700179A54 /* ViewController.swift */, 102 | FBA79EA41E9A038700179A54 /* Main.storyboard */, 103 | FBA79EA71E9A038700179A54 /* Assets.xcassets */, 104 | FBA79EA91E9A038700179A54 /* LaunchScreen.storyboard */, 105 | FBA79EAC1E9A038700179A54 /* Info.plist */, 106 | ); 107 | path = QuickStart; 108 | sourceTree = ""; 109 | }; 110 | /* End PBXGroup section */ 111 | 112 | /* Begin PBXNativeTarget section */ 113 | FBA79E9C1E9A038700179A54 /* QuickStart */ = { 114 | isa = PBXNativeTarget; 115 | buildConfigurationList = FBA79EC51E9A038700179A54 /* Build configuration list for PBXNativeTarget "QuickStart" */; 116 | buildPhases = ( 117 | CA99DF8CB4EFF07F56D45BE6 /* [CP] Check Pods Manifest.lock */, 118 | FBA79E991E9A038700179A54 /* Sources */, 119 | FBA79E9A1E9A038700179A54 /* Frameworks */, 120 | FBA79E9B1E9A038700179A54 /* Resources */, 121 | 62FF8D48DDB503F027F22D2C /* [CP] Embed Pods Frameworks */, 122 | ); 123 | buildRules = ( 124 | ); 125 | dependencies = ( 126 | ); 127 | name = QuickStart; 128 | productName = MSALiOS; 129 | productReference = FBA79E9D1E9A038700179A54 /* QuickStart.app */; 130 | productType = "com.apple.product-type.application"; 131 | }; 132 | /* End PBXNativeTarget section */ 133 | 134 | /* Begin PBXProject section */ 135 | FBA79E951E9A038700179A54 /* Project object */ = { 136 | isa = PBXProject; 137 | attributes = { 138 | LastSwiftUpdateCheck = 0830; 139 | LastUpgradeCheck = 0930; 140 | ORGANIZATIONNAME = Microsoft; 141 | TargetAttributes = { 142 | FBA79E9C1E9A038700179A54 = { 143 | CreatedOnToolsVersion = 8.3; 144 | DevelopmentTeam = QPA5Y64RGS; 145 | LastSwiftMigration = 0930; 146 | ProvisioningStyle = Automatic; 147 | SystemCapabilities = { 148 | com.apple.Keychain = { 149 | enabled = 1; 150 | }; 151 | }; 152 | }; 153 | }; 154 | }; 155 | buildConfigurationList = FBA79E981E9A038700179A54 /* Build configuration list for PBXProject "QuickStart" */; 156 | compatibilityVersion = "Xcode 3.2"; 157 | developmentRegion = English; 158 | hasScannedForEncodings = 0; 159 | knownRegions = ( 160 | en, 161 | Base, 162 | ); 163 | mainGroup = FBA79E941E9A038700179A54; 164 | productRefGroup = FBA79E9E1E9A038700179A54 /* Products */; 165 | projectDirPath = ""; 166 | projectReferences = ( 167 | { 168 | ProductGroup = FB1A4AF5214993550062E42D /* Products */; 169 | ProjectRef = FBBE358D1E9C476300460053 /* QuickStart.xcodeproj */; 170 | }, 171 | ); 172 | projectRoot = ""; 173 | targets = ( 174 | FBA79E9C1E9A038700179A54 /* QuickStart */, 175 | ); 176 | }; 177 | /* End PBXProject section */ 178 | 179 | /* Begin PBXResourcesBuildPhase section */ 180 | FBA79E9B1E9A038700179A54 /* Resources */ = { 181 | isa = PBXResourcesBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | FBA79EAB1E9A038700179A54 /* LaunchScreen.storyboard in Resources */, 185 | FBA79EA81E9A038700179A54 /* Assets.xcassets in Resources */, 186 | FBA79EA61E9A038700179A54 /* Main.storyboard in Resources */, 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | }; 190 | /* End PBXResourcesBuildPhase section */ 191 | 192 | /* Begin PBXShellScriptBuildPhase section */ 193 | 62FF8D48DDB503F027F22D2C /* [CP] Embed Pods Frameworks */ = { 194 | isa = PBXShellScriptBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | ); 198 | inputPaths = ( 199 | "${PODS_ROOT}/Target Support Files/Pods-QuickStart/Pods-QuickStart-frameworks.sh", 200 | "${BUILT_PRODUCTS_DIR}/ADAL/ADAL.framework", 201 | ); 202 | name = "[CP] Embed Pods Frameworks"; 203 | outputPaths = ( 204 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ADAL.framework", 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | shellPath = /bin/sh; 208 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-QuickStart/Pods-QuickStart-frameworks.sh\"\n"; 209 | showEnvVarsInLog = 0; 210 | }; 211 | CA99DF8CB4EFF07F56D45BE6 /* [CP] Check Pods Manifest.lock */ = { 212 | isa = PBXShellScriptBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | ); 216 | inputPaths = ( 217 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 218 | "${PODS_ROOT}/Manifest.lock", 219 | ); 220 | name = "[CP] Check Pods Manifest.lock"; 221 | outputPaths = ( 222 | "$(DERIVED_FILE_DIR)/Pods-QuickStart-checkManifestLockResult.txt", 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | shellPath = /bin/sh; 226 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 227 | showEnvVarsInLog = 0; 228 | }; 229 | /* End PBXShellScriptBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | FBA79E991E9A038700179A54 /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | FBA79EA31E9A038700179A54 /* ViewController.swift in Sources */, 237 | FBA79EA11E9A038700179A54 /* AppDelegate.swift in Sources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | /* End PBXSourcesBuildPhase section */ 242 | 243 | /* Begin PBXVariantGroup section */ 244 | FBA79EA41E9A038700179A54 /* Main.storyboard */ = { 245 | isa = PBXVariantGroup; 246 | children = ( 247 | FBA79EA51E9A038700179A54 /* Base */, 248 | ); 249 | name = Main.storyboard; 250 | sourceTree = ""; 251 | }; 252 | FBA79EA91E9A038700179A54 /* LaunchScreen.storyboard */ = { 253 | isa = PBXVariantGroup; 254 | children = ( 255 | FBA79EAA1E9A038700179A54 /* Base */, 256 | ); 257 | name = LaunchScreen.storyboard; 258 | sourceTree = ""; 259 | }; 260 | /* End PBXVariantGroup section */ 261 | 262 | /* Begin XCBuildConfiguration section */ 263 | FBA79EC31E9A038700179A54 /* Debug */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | ALWAYS_SEARCH_USER_PATHS = NO; 267 | CLANG_ANALYZER_NONNULL = YES; 268 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 269 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 270 | CLANG_CXX_LIBRARY = "libc++"; 271 | CLANG_ENABLE_MODULES = YES; 272 | CLANG_ENABLE_OBJC_ARC = YES; 273 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_COMMA = YES; 276 | CLANG_WARN_CONSTANT_CONVERSION = YES; 277 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 280 | CLANG_WARN_EMPTY_BODY = YES; 281 | CLANG_WARN_ENUM_CONVERSION = YES; 282 | CLANG_WARN_INFINITE_RECURSION = YES; 283 | CLANG_WARN_INT_CONVERSION = YES; 284 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 286 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 287 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 288 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 289 | CLANG_WARN_STRICT_PROTOTYPES = YES; 290 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 291 | CLANG_WARN_UNREACHABLE_CODE = YES; 292 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 293 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 294 | COPY_PHASE_STRIP = NO; 295 | DEBUG_INFORMATION_FORMAT = dwarf; 296 | ENABLE_STRICT_OBJC_MSGSEND = YES; 297 | ENABLE_TESTABILITY = YES; 298 | GCC_C_LANGUAGE_STANDARD = gnu99; 299 | GCC_DYNAMIC_NO_PIC = NO; 300 | GCC_NO_COMMON_BLOCKS = YES; 301 | GCC_OPTIMIZATION_LEVEL = 0; 302 | GCC_PREPROCESSOR_DEFINITIONS = ( 303 | "DEBUG=1", 304 | "$(inherited)", 305 | ); 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 313 | MTL_ENABLE_DEBUG_INFO = YES; 314 | ONLY_ACTIVE_ARCH = YES; 315 | SDKROOT = iphoneos; 316 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 317 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 318 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 319 | TARGETED_DEVICE_FAMILY = "1,2"; 320 | }; 321 | name = Debug; 322 | }; 323 | FBA79EC41E9A038700179A54 /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_ANALYZER_NONNULL = YES; 328 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 330 | CLANG_CXX_LIBRARY = "libc++"; 331 | CLANG_ENABLE_MODULES = YES; 332 | CLANG_ENABLE_OBJC_ARC = YES; 333 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 334 | CLANG_WARN_BOOL_CONVERSION = YES; 335 | CLANG_WARN_COMMA = YES; 336 | CLANG_WARN_CONSTANT_CONVERSION = YES; 337 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 338 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 339 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 340 | CLANG_WARN_EMPTY_BODY = YES; 341 | CLANG_WARN_ENUM_CONVERSION = YES; 342 | CLANG_WARN_INFINITE_RECURSION = YES; 343 | CLANG_WARN_INT_CONVERSION = YES; 344 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 345 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 346 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 347 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 348 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 349 | CLANG_WARN_STRICT_PROTOTYPES = YES; 350 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 351 | CLANG_WARN_UNREACHABLE_CODE = YES; 352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 353 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 354 | COPY_PHASE_STRIP = NO; 355 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 356 | ENABLE_NS_ASSERTIONS = NO; 357 | ENABLE_STRICT_OBJC_MSGSEND = YES; 358 | GCC_C_LANGUAGE_STANDARD = gnu99; 359 | GCC_NO_COMMON_BLOCKS = YES; 360 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 361 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 362 | GCC_WARN_UNDECLARED_SELECTOR = YES; 363 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 364 | GCC_WARN_UNUSED_FUNCTION = YES; 365 | GCC_WARN_UNUSED_VARIABLE = YES; 366 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 367 | MTL_ENABLE_DEBUG_INFO = NO; 368 | SDKROOT = iphoneos; 369 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 370 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 371 | TARGETED_DEVICE_FAMILY = "1,2"; 372 | VALIDATE_PRODUCT = YES; 373 | }; 374 | name = Release; 375 | }; 376 | FBA79EC61E9A038700179A54 /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | baseConfigurationReference = 4B5985AC4EC72AE16C81F234 /* Pods-QuickStart.debug.xcconfig */; 379 | buildSettings = { 380 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 381 | CLANG_ENABLE_MODULES = YES; 382 | CODE_SIGN_ENTITLEMENTS = QuickStart/QuickStart.entitlements; 383 | DEVELOPMENT_TEAM = QPA5Y64RGS; 384 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 385 | INFOPLIST_FILE = "$(SRCROOT)/QuickStart/Info.plist"; 386 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 387 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 388 | OTHER_LDFLAGS = "$(inherited)"; 389 | PRODUCT_BUNDLE_IDENTIFIER = com.microsoft.identity.client.sample.quickstart; 390 | PRODUCT_NAME = "$(TARGET_NAME)"; 391 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 392 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 393 | SWIFT_VERSION = 4.0; 394 | }; 395 | name = Debug; 396 | }; 397 | FBA79EC71E9A038700179A54 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | baseConfigurationReference = 83ED874D158D34B63DEB6793 /* Pods-QuickStart.release.xcconfig */; 400 | buildSettings = { 401 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 402 | CLANG_ENABLE_MODULES = YES; 403 | CODE_SIGN_ENTITLEMENTS = QuickStart/QuickStart.entitlements; 404 | DEVELOPMENT_TEAM = QPA5Y64RGS; 405 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 406 | INFOPLIST_FILE = "$(SRCROOT)/QuickStart/Info.plist"; 407 | IPHONEOS_DEPLOYMENT_TARGET = 11.3; 408 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 409 | OTHER_LDFLAGS = "$(inherited)"; 410 | PRODUCT_BUNDLE_IDENTIFIER = com.microsoft.identity.client.sample.quickstart; 411 | PRODUCT_NAME = "$(TARGET_NAME)"; 412 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 413 | SWIFT_VERSION = 4.0; 414 | }; 415 | name = Release; 416 | }; 417 | /* End XCBuildConfiguration section */ 418 | 419 | /* Begin XCConfigurationList section */ 420 | FBA79E981E9A038700179A54 /* Build configuration list for PBXProject "QuickStart" */ = { 421 | isa = XCConfigurationList; 422 | buildConfigurations = ( 423 | FBA79EC31E9A038700179A54 /* Debug */, 424 | FBA79EC41E9A038700179A54 /* Release */, 425 | ); 426 | defaultConfigurationIsVisible = 0; 427 | defaultConfigurationName = Release; 428 | }; 429 | FBA79EC51E9A038700179A54 /* Build configuration list for PBXNativeTarget "QuickStart" */ = { 430 | isa = XCConfigurationList; 431 | buildConfigurations = ( 432 | FBA79EC61E9A038700179A54 /* Debug */, 433 | FBA79EC71E9A038700179A54 /* Release */, 434 | ); 435 | defaultConfigurationIsVisible = 0; 436 | defaultConfigurationName = Release; 437 | }; 438 | /* End XCConfigurationList section */ 439 | }; 440 | rootObject = FBA79E951E9A038700179A54 /* Project object */; 441 | } 442 | -------------------------------------------------------------------------------- /QuickStart.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /QuickStart/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Microsoft Corporation. 4 | // All rights reserved. 5 | // 6 | // This code is licensed under the MIT License. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files(the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions : 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | //------------------------------------------------------------------------------ 27 | 28 | import UIKit 29 | import ADAL 30 | 31 | 32 | @UIApplicationMain 33 | class AppDelegate: UIResponder, UIApplicationDelegate { 34 | 35 | var window: UIWindow? 36 | 37 | 38 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 39 | 40 | 41 | return true 42 | } 43 | 44 | func applicationWillResignActive(_ application: UIApplication) { 45 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 46 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 47 | } 48 | 49 | func applicationDidEnterBackground(_ application: UIApplication) { 50 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 51 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 52 | } 53 | 54 | func applicationWillEnterForeground(_ application: UIApplication) { 55 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 56 | } 57 | 58 | func applicationDidBecomeActive(_ application: UIApplication) { 59 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 60 | } 61 | 62 | func applicationWillTerminate(_ application: UIApplication) { 63 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 64 | } 65 | 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /QuickStart/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /QuickStart/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /QuickStart/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 48 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /QuickStart/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleURLTypes 20 | 21 | 22 | CFBundleTypeRole 23 | Editor 24 | CFBundleURLName 25 | $(PRODUCT_BUNDLE_IDENTIFIER) 26 | CFBundleURLSchemes 27 | 28 | adal-sample-app 29 | 30 | 31 | 32 | CFBundleVersion 33 | 1 34 | LSRequiresIPhoneOS 35 | 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIMainStoryboardFile 39 | Main 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationPortraitUpsideDown 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | LSApplicationQueriesSchemes 58 | 59 | msauth 60 | msauthv3 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /QuickStart/QuickStart.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | keychain-access-groups 6 | 7 | $(AppIdentifierPrefix)com.microsoft.MSALTestApp 8 | $(AppIdentifierPrefix)com.microsoft.adalcache 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /QuickStart/ViewController.swift: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Microsoft Corporation. 4 | // All rights reserved. 5 | // 6 | // This code is licensed under the MIT License. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files(the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions : 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | //------------------------------------------------------------------------------ 27 | 28 | import UIKit 29 | import ADAL 30 | 31 | /// 😃 A View Controller that will respond to the events of the Storyboard. 32 | 33 | // Note that this is written to be one QuickStart file and meant to demonstrate basic concepts 34 | // with easy to follow flow control and readable syntax. For a sample that demonstrates production 35 | // quality Swift code check out the /Samples folder in the SDK repository. 36 | 37 | class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate { 38 | 39 | // Update the below to your client ID you received in the portal. The below is for running the demo only 40 | let kClientID = "227fe97b-c455-4039-b618-5af215c48d71" 41 | 42 | // These settings you don't need to edit unless you wish to attempt deeper scenarios with the app. 43 | let kGraphURI = "https://graph.microsoft.com" 44 | let kAuthority = "https://login.microsoftonline.com/common" 45 | let kRedirectUri = URL(string: "adal-sample-app://com.microsoft.identity.client.sample.quickstart") 46 | let defaultSession = URLSession(configuration: .default) 47 | 48 | var applicationContext : ADALAuthenticationContext? 49 | var dataTask: URLSessionDataTask? 50 | 51 | @IBOutlet weak var loggingText: UITextView! 52 | @IBOutlet weak var signoutButton: UIButton! 53 | 54 | override func viewDidLoad() { 55 | 56 | /** 57 | Initialize a ADAuthenticationContext with a given authority 58 | 59 | - authority: A URL indicating a directory that ADAL can use to obtain tokens. In Azure AD 60 | it is of the form https://, where is the 61 | directory host (e.g. https://login.microsoftonline.com) and is a 62 | identifier within the directory itself (e.g. a domain associated to the 63 | tenant, such as contoso.onmicrosoft.com, or the GUID representing the 64 | TenantID property of the directory) 65 | - error The error that occurred creating the application object, if any, if you're 66 | not interested in the specific error pass in nil. 67 | */ 68 | 69 | self.applicationContext = ADALAuthenticationContext(authority: kAuthority, error: nil) 70 | self.applicationContext?.credentialsType = AD_CREDENTIALS_AUTO 71 | super.viewDidLoad() 72 | 73 | } 74 | 75 | override func viewWillAppear(_ animated: Bool) { 76 | 77 | super.viewWillAppear(animated) 78 | 79 | if let currentAccount = currentAccount(), 80 | currentAccount.accessToken != nil { 81 | signoutButton.isEnabled = true 82 | } else { 83 | signoutButton.isEnabled = false 84 | } 85 | } 86 | 87 | /** 88 | This button will invoke the authorization flow. 89 | */ 90 | 91 | @IBAction func callGraphButton(_ sender: UIButton) { 92 | 93 | self.callAPI() 94 | 95 | } 96 | 97 | func acquireToken(completion: @escaping (_ success: Bool) -> Void) { 98 | 99 | guard let applicationContext = self.applicationContext else { return } 100 | guard let kRedirectUri = kRedirectUri else { return } 101 | /** 102 | 103 | Acquire a token for an account 104 | 105 | - withResource: The resource you wish to access. This will the Microsoft Graph API for this sample. 106 | - clientId: The clientID of your application, you should get this from the app portal. 107 | - redirectUri: The redirect URI that your application will listen for to get a response of the 108 | Auth code after authentication. Since this a native application where authentication 109 | happens inside the app itself, we can listen on a custom URI that the SDK knows to 110 | look for from within the application process doing authentication. 111 | - completionBlock: The completion block that will be called when the authentication 112 | flow completes, or encounters an error. 113 | */ 114 | 115 | applicationContext.acquireToken(withResource: kGraphURI, clientId: kClientID, redirectUri: kRedirectUri){ (result) in 116 | if (result.status != AD_SUCCEEDED) { 117 | 118 | if let error = result.error { 119 | if error.domain == ADAuthenticationErrorDomain, 120 | error.code == ADALErrorCode.AD_ERROR_UNEXPECTED.rawValue { 121 | self.updateLogging(text: "Unexpected internal error occured: \(error.description))"); 122 | } else { 123 | self.updateLogging(text: error.description) 124 | } 125 | } 126 | completion(false) 127 | } else { 128 | self.updateLogging(text: "Access token is \(String(describing: result.accessToken))") 129 | self.updateSignoutButton(enabled: true) 130 | completion(true) 131 | } 132 | } 133 | } 134 | 135 | func acquireTokenSilently(completion: @escaping (_ success: Bool) -> Void) { 136 | 137 | guard let applicationContext = self.applicationContext else { return } 138 | guard let kRedirectUri = kRedirectUri else { return } 139 | 140 | /** 141 | 142 | Acquire a token for an existing account silently 143 | - withResource: The resource you wish to access. This will the Microsoft Graph API for this sample. 144 | - clientId: The clientID of your application, you should get this from the app portal. 145 | - redirectUri: The redirect URI that your application will listen for to get a response of the 146 | Auth code after authentication. Since this a native application where authentication 147 | happens inside the app itself, we can listen on a custom URI that the SDK knows to 148 | look for from within the application process doing authentication. 149 | - completionBlock: The completion block that will be called when the authentication 150 | flow completes, or encounters an error. 151 | 152 | */ 153 | 154 | 155 | 156 | applicationContext.acquireTokenSilent(withResource: kGraphURI, clientId: kClientID, redirectUri: kRedirectUri) { (result) in 157 | 158 | if (result.status != AD_SUCCEEDED) { 159 | 160 | // USER_INPUT_NEEDED means we need to ask the user to sign-in. This usually happens 161 | // when the user's Refresh Token is expired or if the user has changed their password 162 | // among other possible reasons. 163 | if let error = result.error { 164 | if error.domain == ADAuthenticationErrorDomain, 165 | error.code == ADALErrorCode.AD_ERROR_SERVER_USER_INPUT_NEEDED.rawValue { 166 | 167 | DispatchQueue.main.async { 168 | self.acquireToken() { (success) -> Void in 169 | if success { 170 | completion(true) 171 | } else { 172 | self.updateLogging(text: "After determining we needed user input, could not acquire token: \(error.description)") 173 | completion(false) 174 | } 175 | } 176 | } 177 | } else { 178 | self.updateLogging(text: "Could not acquire token silently: \(error.description)") 179 | completion(false) 180 | } 181 | } 182 | 183 | } else { 184 | self.updateLogging(text: "Refreshed Access token is \(String(describing: result.accessToken))") 185 | self.updateSignoutButton(enabled: true) 186 | completion(true) 187 | } 188 | } 189 | } 190 | 191 | func currentAccount() -> ADALTokenCacheItem? { 192 | 193 | 194 | // We retrieve our current account by getting the last account from cache. This isn't best practice. You should rely 195 | // on AcquireTokenSilent and store the UPN separately in your application. For simplicity of the sample, we just use the cache. 196 | // In multi-account applications, account should be retrieved by home account identifier or username instead 197 | 198 | 199 | guard let cachedTokens = ADALKeychainTokenCache.defaultKeychain().allItems(nil) else { 200 | self.updateLogging(text: "Didn't find a default cache. This is very unusual.") 201 | 202 | return nil 203 | } 204 | 205 | if !(cachedTokens.isEmpty) { 206 | 207 | // In the token cache, refresh tokens and access tokens are separate cache entries. 208 | // Therefore, you need to keep looking until you find an entry with an access token. 209 | for (_, cachedToken) in cachedTokens.enumerated() { 210 | if cachedToken.accessToken != nil { 211 | return cachedToken 212 | } 213 | } 214 | } 215 | 216 | return nil 217 | } 218 | 219 | func updateLogging(text : String) { 220 | 221 | DispatchQueue.main.async { 222 | self.loggingText.text += text + "\n\n" 223 | let bottom = NSMakeRange(self.loggingText.text.count - 1, 1) 224 | self.loggingText.scrollRangeToVisible(bottom) 225 | } 226 | } 227 | 228 | func updateSignoutButton(enabled : Bool) { 229 | 230 | DispatchQueue.main.async { 231 | self.signoutButton.isEnabled = enabled 232 | } 233 | } 234 | 235 | /** 236 | This button will invoke the call to the Microsoft Graph API. It uses the 237 | built in URLSession to create a connection. 238 | */ 239 | 240 | func callAPI(retry: Bool = true) { 241 | 242 | // Specify the Graph API endpoint 243 | let url = URL(string: kGraphURI + "/v1.0/me/") 244 | var request = URLRequest(url: url!) 245 | 246 | guard let accessToken = currentAccount()?.accessToken else { 247 | // We haven't signed in yet, so let's do so now, then retry. 248 | // To ensure we don't prompt the user twice, 249 | // we set retry to false. If acquireToken() has some 250 | // other issue we don't want an infinite loop. 251 | 252 | if retry { 253 | 254 | self.acquireToken() { (success) -> Void in 255 | if success { 256 | self.callAPI(retry: false) 257 | } 258 | } 259 | } else { 260 | self.updateLogging(text: "Couldn't get access token and we were told to not retry.") 261 | } 262 | return 263 | } 264 | 265 | // Set the Authorization header for the request. We use Bearer tokens, so we specify Bearer + the token we got from the result 266 | request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") 267 | 268 | dataTask = defaultSession.dataTask(with: request) { data, response, error in 269 | 270 | if let error = error { 271 | self.updateLogging(text: "Couldn't get graph result: \(error)") 272 | } 273 | 274 | guard let httpResponse = response as? HTTPURLResponse else { 275 | self.updateLogging(text: "Couldn't get graph result") 276 | return 277 | } 278 | 279 | // If we get HTTP 200: Success, go ahead and parse the JSON 280 | if httpResponse.statusCode == 200 { 281 | 282 | guard let result = try? JSONSerialization.jsonObject(with: data!, options: []) else { 283 | self.updateLogging(text: "Couldn't deserialize result JSON") 284 | return 285 | } 286 | 287 | self.updateLogging(text: "Result from Graph: \(result))") 288 | } 289 | 290 | // Sometimes the server API will throw HTTP 401: Unauthorized if it is expired or needs some 291 | // other interaction from the authentication service. You should always refresh the 292 | // token on first failure just to make sure that you cannot recover. 293 | 294 | if httpResponse.statusCode == 401 { 295 | 296 | if retry { 297 | // We will try to refresh the token silently first. This way if there are any 298 | // issues that can be resolved by getting a new access token from the refresh 299 | // token, we avoid prompting the user. If user interaction is required, the 300 | // acquireTokenSilently() will call acquireToken() 301 | 302 | self.acquireTokenSilently() { (success) -> Void in 303 | if success { 304 | self.callAPI(retry: false) 305 | } 306 | } 307 | } else { 308 | self.updateLogging(text: "Couldn't access API with current access token, and we were told to not retry.") 309 | 310 | } 311 | } 312 | } 313 | 314 | dataTask?.resume() 315 | } 316 | 317 | /** 318 | This button will invoke the signout APIs to clear the token cache. 319 | */ 320 | @IBAction func signoutButton(_ sender: UIButton) { 321 | 322 | /** 323 | Removes all tokens from the cache for this application for the current account in use 324 | - account: The account user ID to remove from the cache 325 | */ 326 | 327 | guard let account = currentAccount()?.userInformation?.userId else { 328 | self.updateLogging(text: "Didn't find a logged in account in the cache.") 329 | 330 | return 331 | } 332 | 333 | ADALKeychainTokenCache.defaultKeychain().removeAll(forUserId: account, clientId: kClientID, error: nil) 334 | self.signoutButton.isEnabled = false 335 | self.updateLogging(text: "Removed account for: \(account)" ) 336 | } 337 | } 338 | 339 | 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | Services: active-directory 3 | platforms: iOS 4 | author: brandwe 5 | level: 100 6 | client: iOS Mobile App 7 | service: Microsoft Graph 8 | endpoint: AAD V1 9 | --- 10 | # ADAL Swift Microsoft Graph API Sample 11 | 12 | 13 | | [Getting Started](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-devquickstarts-ios)| [Library](https://github.com/AzureAD/azure-activedirectory-library-for-objc) | [API Reference](http://cocoadocs.org/docsets/ADAL/2.5.1/) | [Support](README.md#community-help-and-support) 14 | | --- | --- | --- | --- | 15 | 16 | > There's a newer version of this sample! Check it out: https://github.com/azure-samples/ms-identity-ios-swift-native 17 | > 18 | > This newer sample takes advantage of the Microsoft identity platform (formerly Azure AD v2.0). 19 | > 20 | > While still in public preview, every component is supported in production environments. 21 | 22 | The ADAL Objective C library gives your app the ability to begin using the 23 | [Microsoft Azure Cloud](https://cloud.microsoft.com) by supporting [Microsoft Azure Active Directory accounts](https://azure.microsoft.com/en-us/services/active-directory/) using industry standard OAuth2 and OpenID Connect. This sample demonstrates all the normal lifecycles your application should experience, including: 24 | 25 | * Get a token for the Microsoft Graph 26 | * Refresh a token 27 | * Call the Microsoft Graph 28 | * Sign out the user 29 | 30 | ## Scenario 31 | 32 | This app can be used for all Azure AD accounts. 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. 33 | 34 | ![Topology](./images/iosintro.png) 35 | 36 | ## Steps to Run 37 | 38 | ### Register & Configure your app 39 | 40 | You will need to have a native client application registered with Microsoft using the 41 | [Azure portal](https://portal.azure.com). 42 | 43 | 1. Getting to app registration 44 | - Navigate to the [App registrations](https://go.microsoft.com/fwlink/?linkid=2083908) experience. 45 | 2. Create the app 46 | - Click ***New registration***. 47 | - Enter an app name in the ***Name*** field. 48 | - In ***Supported account types***, select `Accounts in any origanizational directory and personal Microsoft accounts`. 49 | - In ***Redirect URI (optional)***, select `Public client (mobile & desktop)` and enter `urn:ietf:wg:oauth:2.0:oob`as the URI. 50 | 51 | 3. Configure Microsoft Graph API Access 52 | - Select ***API Permissions*** 53 | - Select ***Add a permission***, and then select ***Microsoft Graph***. 54 | - Select `Delegated permissions` and select the permissions `offline_access`, `openid`, and `profile`. We'll also need `User.Read`, but this is selected by default for us already. 55 | - Hit ***Add permissions*** to save. 56 | 57 | 4. Congrats! Your app is successfully configured. In the next section, you'll need: 58 | - `Application ID` 59 | - `Redirect URI` 60 | - `Entitlements` 61 | - Is required for building your own Xcode project 62 | 63 | ### Get the code 64 | 65 | * `$ git clone git@github.com:Azure-Samples/active-directory-ios.git` 66 | 67 | 1. Download Cocoapods (if you don't already have it) 68 | 69 | CocoaPods is the dependency manager for Swift and Objective-C Cocoa projects. It has thousands of libraries and can help you scale your projects elegantly. To install on OS X 10.9 and greater simply run the following command in your terminal: 70 | 71 | `$ sudo gem install cocoapods` 72 | 73 | 1. Build the sample and pull down ADAL for iOS automatically 74 | 75 | Run the following command in your terminal: 76 | 77 | `$ pod install` 78 | 79 | This will download and build ADAL for iOS for you and configure your Microsoft Tasks.xcodeproj to use the correct dependencies. 80 | 81 | ### Step 4: Run the application in Xcode 82 | 83 | This will download and build ADAL for iOS for you and configure your QuickStart.xcodeproj to use the correct dependencies. 84 | 85 | You should see the following output: 86 | 87 | ``` 88 | $ pod install 89 | Analyzing dependencies 90 | Downloading dependencies 91 | Installing ADAL (2.5.2) 92 | Generating Pods project 93 | Integrating client project 94 | 95 | [!] Please close any current Xcode sessions and use `QuickStart.xcworkspace` for this project from now on. 96 | Sending stats 97 | Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed. 98 | ``` 99 | 100 | 1. Run the application in Xcode 101 | 102 | Launch XCode and load the `QuickStart.xcworkspace` file. The application will run in an emulator as soon as it is loaded. 103 | 104 | 105 | 1. Configure the `ViewController.swift` file with your app information 106 | 107 | You will need to configure your application to work with the Azure AD tenant you've created. 108 | 109 | - In the QuickStart project, open the file `ViewController.swift`. Replace the values of the elements in the section to reflect the values you input into the Azure Portal. Your code will reference these values whenever it uses ADAL. 110 | - The `kClientID` is the clientId of your application you copied from the portal. 111 | - The `kRedirectUri` is the redirect url you registered in the portal. 112 | 113 | If you are adding ADAL to an existing Xcode project or you've created a new one on your own, you'll also need to enable Keychain Sharing. 114 | - In the ```Signing & capabilities``` tap, add ```Keychain Sharing``` if you don't have one 115 | - Add `com.microsoft.adalcache` into ```Keychain Groups``` 116 | 117 | If your app supports `Brokered Authentication`, you will need to update the `CFBundleURLSchemes` name 118 | - In the QuickStart project, open the file `Info.plist`. replace the `CFBundleURLSchemes` with your scheme name 119 | ``` 120 | CFBundleURLTypes 121 | 122 | 123 | CFBundleTypeRole 124 | Editor 125 | CFBundleURLName 126 | $(PRODUCT_BUNDLE_IDENTIFIER) 127 | CFBundleURLSchemes 128 | 129 | adal-sample-app 130 | 131 | 132 | 133 | ``` 134 | 135 | ## Important Info 136 | 137 | 1. Checkout the [ADAL Objective C Wiki](https://github.com/AzureAD/azure-activedirectory-library-for-objc/wiki) for more info on the library mechanics and how to configure new scenarios and capabilities. 138 | 2. In Native scenarios, the app will use an embedded Webview and will not leave the app. The `Redirect URI` can be arbitrary. 139 | 3. Find any problems or have requests? Feel free to create an issue or post on Stackoverflow with 140 | tag `azure-active-directory`. 141 | 142 | ## Feedback, Community Help, and Support 143 | 144 | We use [Stack Overflow](http://stackoverflow.com/questions/tagged/adal) with the community to 145 | provide support. We highly recommend you ask your questions on Stack Overflow first and browse 146 | existing issues to see if someone has asked your question before. 147 | 148 | If you find and bug or have a feature request, please raise the issue 149 | on [GitHub Issues](../../issues). 150 | 151 | To provide a recommendation, visit 152 | our [User Voice page](https://feedback.azure.com/forums/169401-azure-active-directory). 153 | 154 | ## Contribute 155 | 156 | We enthusiastically welcome contributions and feedback. You can clone the repo and start 157 | contributing now. Read our [Contribution Guide](CONTRIBUTING.md) for more information. 158 | 159 | This project has adopted the 160 | [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 161 | For more information see 162 | the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact 163 | [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 164 | 165 | ## Security Library 166 | 167 | This library controls how users sign-in and access services. We recommend you always take the 168 | latest version of our library in your app when possible. We 169 | use [semantic versioning](http://semver.org) so you can control the risk associated with updating 170 | your app. As an example, always downloading the latest minor version number (e.g. x.*y*.x) ensures 171 | you get the latest security and feature enhanements but our API surface remains the same. You 172 | can always see the latest version and release notes under the Releases tab of GitHub. 173 | 174 | ## Security Reporting 175 | 176 | If you find a security issue with our libraries or services please report it 177 | to [secure@microsoft.com](mailto:secure@microsoft.com) with as much detail as possible. Your 178 | submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) 179 | program. Please do not post security issues to GitHub Issues or any other public site. We will 180 | contact you shortly upon receiving the information. We encourage you to get notifications of when 181 | security incidents occur by 182 | visiting [this page](https://technet.microsoft.com/en-us/security/dd252948) and subscribing 183 | to Security Advisory Alerts. 184 | -------------------------------------------------------------------------------- /images/iosintro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/active-directory-ios/65674e109f1624590ae29bbe0cfc5e684a7c3fdf/images/iosintro.png --------------------------------------------------------------------------------