Skip to content

Commit f8bc455

Browse files
authored
Merge pull request #60 from Azure-Samples/oldalton/shared-device-sample
Updated sample for shared device mode support
2 parents cec6058 + 4a9ed89 commit f8bc455

File tree

5 files changed

+166
-51
lines changed

5 files changed

+166
-51
lines changed

MSALiOS.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@
380380
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
381381
OTHER_LDFLAGS = "$(inherited)";
382382
PRODUCT_BUNDLE_IDENTIFIER = com.microsoft.identitysample.MSALiOS;
383-
PRODUCT_NAME = "$(TARGET_NAME)";
383+
PRODUCT_NAME = MSALiOS;
384384
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
385385
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
386386
SWIFT_VERSION = 4.2;
@@ -401,7 +401,7 @@
401401
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
402402
OTHER_LDFLAGS = "$(inherited)";
403403
PRODUCT_BUNDLE_IDENTIFIER = com.microsoft.identitysample.MSALiOS;
404-
PRODUCT_NAME = "$(TARGET_NAME)";
404+
PRODUCT_NAME = MSALiOS;
405405
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
406406
SWIFT_VERSION = 4.2;
407407
};

MSALiOS/Info.plist

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
</array>
2828
<key>CFBundleVersion</key>
2929
<string>1</string>
30+
<key>LSApplicationQueriesSchemes</key>
31+
<array>
32+
<string>msauth</string>
33+
<string>msauthv2</string>
34+
<string>msauthv3</string>
35+
</array>
3036
<key>LSRequiresIPhoneOS</key>
3137
<true/>
3238
<key>UILaunchStoryboardName</key>
@@ -50,11 +56,5 @@
5056
<string>UIInterfaceOrientationLandscapeLeft</string>
5157
<string>UIInterfaceOrientationLandscapeRight</string>
5258
</array>
53-
<key>LSApplicationQueriesSchemes</key>
54-
<array>
55-
<string>msauth</string>
56-
<string>msauthv2</string>
57-
<string>msauthv3</string>
58-
</array>
5959
</dict>
6060
</plist>

MSALiOS/ViewController.swift

Lines changed: 151 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
4646
var loggingText: UITextView!
4747
var signOutButton: UIButton!
4848
var callGraphButton: UIButton!
49+
var usernameLabel: UILabel!
50+
51+
var currentAccount: MSALAccount?
4952

5053
/**
5154
Setup public client application in viewDidLoad
@@ -62,12 +65,28 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
6265
} catch let error {
6366
self.updateLogging(text: "Unable to create Application Context \(error)")
6467
}
68+
69+
self.loadCurrentAccount()
70+
self.platformViewDidLoadSetup()
71+
}
72+
73+
func platformViewDidLoadSetup() {
74+
75+
NotificationCenter.default.addObserver(self,
76+
selector: #selector(appCameToForeGround(notification:)),
77+
name: UIApplication.willEnterForegroundNotification,
78+
object: nil)
79+
6580
}
6681

6782
override func viewWillAppear(_ animated: Bool) {
6883

6984
super.viewWillAppear(animated)
70-
self.updateSignOutButton(enabled: !self.accessToken.isEmpty)
85+
self.loadCurrentAccount()
86+
}
87+
88+
@objc func appCameToForeGround(notification: Notification) {
89+
self.loadCurrentAccount()
7190
}
7291
}
7392

@@ -103,15 +122,38 @@ extension ViewController {
103122

104123
let msalConfiguration = MSALPublicClientApplicationConfig(clientId: kClientID, redirectUri: nil, authority: authority)
105124
self.applicationContext = try MSALPublicClientApplication(configuration: msalConfiguration)
106-
107-
self.webViewParamaters = MSALWebviewParameters(parentViewController: self)
125+
self.initWebViewParams()
108126
}
109127

110128
func initWebViewParams() {
111129
self.webViewParamaters = MSALWebviewParameters(parentViewController: self)
112130
}
113131
}
114132

133+
// MARK: Shared device
134+
135+
extension ViewController {
136+
137+
@objc func getDeviceMode(_ sender: UIButton) {
138+
139+
if #available(iOS 13.0, *) {
140+
self.applicationContext?.getDeviceInformation(with: nil, completionBlock: { (deviceInformation, error) in
141+
142+
guard let deviceInfo = deviceInformation else {
143+
self.updateLogging(text: "Device info not returned. Error: \(String(describing: error))")
144+
return
145+
}
146+
147+
let isSharedDevice = deviceInfo.deviceMode == .shared
148+
let modeString = isSharedDevice ? "shared" : "private"
149+
self.updateLogging(text: "Received device info. Device is in the \(modeString) mode.")
150+
})
151+
} else {
152+
self.updateLogging(text: "Running on older iOS. GetDeviceInformation API is unavailable.")
153+
}
154+
}
155+
}
156+
115157

116158
// MARK: Acquiring and using token
117159

@@ -123,14 +165,18 @@ extension ViewController {
123165

124166
@objc func callGraphAPI(_ sender: UIButton) {
125167

126-
guard let currentAccount = self.currentAccount() else {
127-
// We check to see if we have a current logged in account.
128-
// If we don't, then we need to sign someone in.
129-
acquireTokenInteractively()
130-
return
168+
self.loadCurrentAccount { (account) in
169+
170+
guard let currentAccount = account else {
171+
172+
// We check to see if we have a current logged in account.
173+
// If we don't, then we need to sign someone in.
174+
self.acquireTokenInteractively()
175+
return
176+
}
177+
178+
self.acquireTokenSilently(currentAccount)
131179
}
132-
133-
acquireTokenSilently(currentAccount)
134180
}
135181

136182
func acquireTokenInteractively() {
@@ -139,7 +185,7 @@ extension ViewController {
139185
guard let webViewParameters = self.webViewParamaters else { return }
140186

141187
let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
142-
parameters.promptType = .selectAccount;
188+
parameters.promptType = .selectAccount
143189

144190
applicationContext.acquireToken(with: parameters) { (result, error) in
145191

@@ -157,7 +203,7 @@ extension ViewController {
157203

158204
self.accessToken = result.accessToken
159205
self.updateLogging(text: "Access token is \(self.accessToken)")
160-
self.updateSignOutButton(enabled: true)
206+
self.updateCurrentAccount(account: result.account)
161207
self.getContentWithToken()
162208
}
163209
}
@@ -262,27 +308,47 @@ extension ViewController {
262308
// MARK: Get account and removing cache
263309

264310
extension ViewController {
265-
func currentAccount() -> MSALAccount? {
266-
267-
guard let applicationContext = self.applicationContext else { return nil }
311+
312+
typealias AccountCompletion = (MSALAccount?) -> Void
313+
314+
func loadCurrentAccount(completion: AccountCompletion? = nil) {
268315

269-
// We retrieve our current account by getting the first account from cache
270-
// In multi-account applications, account should be retrieved by home account identifier or username instead
316+
guard let applicationContext = self.applicationContext else { return }
271317

272-
do {
318+
let msalParameters = MSALParameters()
319+
msalParameters.completionBlockQueue = DispatchQueue.main
320+
321+
// Note that this sample showcases an app that signs in a single account at a time
322+
// If you're building a more complex app that signs in multiple accounts at the same time, you'll need to use a different account retrieval API that specifies account identifier
323+
// For example, see "accountsFromDeviceForParameters:completionBlock:" - https://azuread.github.io/microsoft-authentication-library-for-objc/Classes/MSALPublicClientApplication.html#/c:objc(cs)MSALPublicClientApplication(im)accountsFromDeviceForParameters:completionBlock:
324+
applicationContext.getCurrentAccount(with: msalParameters, completionBlock: { (currentAccount, previousAccount, error) in
273325

274-
let cachedAccounts = try applicationContext.allAccounts()
326+
if let error = error {
327+
self.updateLogging(text: "Couldn't query current account with error: \(error)")
328+
return
329+
}
275330

276-
if !cachedAccounts.isEmpty {
277-
return cachedAccounts.first
331+
if let currentAccount = currentAccount {
332+
333+
self.updateLogging(text: "Found a signed in account \(String(describing: currentAccount.username)). Updating data for that account...")
334+
335+
self.updateCurrentAccount(account: currentAccount)
336+
337+
if let completion = completion {
338+
completion(self.currentAccount)
339+
}
340+
341+
return
278342
}
279343

280-
} catch let error as NSError {
344+
self.updateLogging(text: "Account signed out. Updating UX")
345+
self.accessToken = ""
346+
self.updateCurrentAccount(account: nil)
281347

282-
self.updateLogging(text: "Didn't find any accounts in cache: \(error)")
283-
}
284-
285-
return nil
348+
if let completion = completion {
349+
completion(nil)
350+
}
351+
})
286352
}
287353

288354
/**
@@ -293,7 +359,7 @@ extension ViewController {
293359

294360
guard let applicationContext = self.applicationContext else { return }
295361

296-
guard let account = self.currentAccount() else { return }
362+
guard let account = self.currentAccount else { return }
297363

298364
do {
299365

@@ -303,14 +369,21 @@ extension ViewController {
303369
- account: The account to remove from the cache
304370
*/
305371

306-
try applicationContext.remove(account)
307-
self.updateLogging(text: "")
308-
self.updateSignOutButton(enabled: false)
309-
self.accessToken = ""
372+
let signoutParameters = MSALSignoutParameters(webviewParameters: self.webViewParamaters!)
373+
signoutParameters.signoutFromBrowser = false
310374

311-
} catch let error as NSError {
375+
applicationContext.signout(with: account, signoutParameters: signoutParameters, completionBlock: {(success, error) in
376+
377+
if let error = error {
378+
self.updateLogging(text: "Couldn't sign out account with error: \(error)")
379+
return
380+
}
381+
382+
self.updateLogging(text: "Sign out completed successfully")
383+
self.accessToken = ""
384+
self.updateCurrentAccount(account: nil)
385+
})
312386

313-
self.updateLogging(text: "Received error signing account out: \(error)")
314387
}
315388
}
316389
}
@@ -320,6 +393,20 @@ extension ViewController {
320393
extension ViewController {
321394

322395
func initUI() {
396+
397+
usernameLabel = UILabel()
398+
usernameLabel.translatesAutoresizingMaskIntoConstraints = false
399+
usernameLabel.text = ""
400+
usernameLabel.textColor = .darkGray
401+
usernameLabel.textAlignment = .right
402+
403+
self.view.addSubview(usernameLabel)
404+
405+
usernameLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
406+
usernameLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10.0).isActive = true
407+
usernameLabel.widthAnchor.constraint(equalToConstant: 300.0).isActive = true
408+
usernameLabel.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
409+
323410
// Add call Graph button
324411
callGraphButton = UIButton()
325412
callGraphButton.translatesAutoresizingMaskIntoConstraints = false
@@ -329,7 +416,7 @@ extension ViewController {
329416
self.view.addSubview(callGraphButton)
330417

331418
callGraphButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
332-
callGraphButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
419+
callGraphButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 120.0).isActive = true
333420
callGraphButton.widthAnchor.constraint(equalToConstant: 300.0).isActive = true
334421
callGraphButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
335422

@@ -347,16 +434,28 @@ extension ViewController {
347434
signOutButton.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
348435
signOutButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
349436

437+
let deviceModeButton = UIButton()
438+
deviceModeButton.translatesAutoresizingMaskIntoConstraints = false
439+
deviceModeButton.setTitle("Get device info", for: .normal);
440+
deviceModeButton.setTitleColor(.blue, for: .normal);
441+
deviceModeButton.addTarget(self, action: #selector(getDeviceMode(_:)), for: .touchUpInside)
442+
self.view.addSubview(deviceModeButton)
443+
444+
deviceModeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
445+
deviceModeButton.topAnchor.constraint(equalTo: signOutButton.bottomAnchor, constant: 10.0).isActive = true
446+
deviceModeButton.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
447+
deviceModeButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
448+
350449
// Add logging textfield
351450
loggingText = UITextView()
352451
loggingText.isUserInteractionEnabled = false
353452
loggingText.translatesAutoresizingMaskIntoConstraints = false
354453

355454
self.view.addSubview(loggingText)
356455

357-
loggingText.topAnchor.constraint(equalTo: signOutButton.bottomAnchor, constant: 10.0).isActive = true
456+
loggingText.topAnchor.constraint(equalTo: deviceModeButton.bottomAnchor, constant: 10.0).isActive = true
358457
loggingText.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10.0).isActive = true
359-
loggingText.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 10.0).isActive = true
458+
loggingText.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0).isActive = true
360459
loggingText.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 10.0).isActive = true
361460
}
362461

@@ -380,4 +479,20 @@ extension ViewController {
380479
}
381480
}
382481
}
482+
483+
func updateAccountLabel() {
484+
485+
guard let currentAccount = self.currentAccount else {
486+
self.usernameLabel.text = "Signed out"
487+
return
488+
}
489+
490+
self.usernameLabel.text = currentAccount.username
491+
}
492+
493+
func updateCurrentAccount(account: MSALAccount?) {
494+
self.currentAccount = account
495+
self.updateAccountLabel()
496+
self.updateSignOutButton(enabled: account != nil)
497+
}
383498
}

Podfile.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
PODS:
2-
- MSAL (1.0.7):
3-
- MSAL/app-lib (= 1.0.7)
4-
- MSAL/app-lib (1.0.7)
2+
- MSAL (1.1.0):
3+
- MSAL/app-lib (= 1.1.0)
4+
- MSAL/app-lib (1.1.0)
55

66
DEPENDENCIES:
7-
- MSAL
7+
- MSAL (~> 1.1.0)
88

99
SPEC REPOS:
1010
https://github.com/cocoapods/specs.git:
1111
- MSAL
1212

1313
SPEC CHECKSUMS:
14-
MSAL: e4c1cbcf59e04073b427ce9fbfc0346b54abb62e
14+
MSAL: 917b8c60ac97dcf50ac5ff987f9f971fb665f7c0
1515

16-
PODFILE CHECKSUM: 27b86088d6269d05d5a779778e844591cd997e42
16+
PODFILE CHECKSUM: 472f8eaa19902d9f4ed8556554a7fed88779abfc
1717

1818
COCOAPODS: 1.7.5

podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use_frameworks!
22

33
target 'MSALiOS' do
4-
pod 'MSAL'
4+
pod 'MSAL', '~> 1.1.0'
55
end

0 commit comments

Comments
 (0)