@@ -46,7 +46,7 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
4646
4747 var application : MSALPublicClientApplication !
4848
49- var accessToken = String ( )
49+ var accessToken : String ?
5050
5151 @IBOutlet weak var loggingText : UITextView !
5252 @IBOutlet weak var signoutButton : UIButton !
@@ -62,8 +62,8 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
6262
6363 Initialize a MSALPublicClientApplication with a MSALPublicClientApplicationConfig.
6464 MSALPublicClientApplicationConfig can be initialized with client id, redirect uri and authority.
65- Redirect uri will be constucted automatically in the form of "msal <your-client -id-here>://auth" if not provided.
66-
65+ Redirect uri will be constucted automatically in the form of "msauth. <your-bundle -id-here>://auth" if not provided.
66+ The scheme part, i.e. "msauth.<your-bundle-id-here>", needs to be registered in the info.plist of the project
6767 */
6868
6969 let pcaConfig = MSALPublicClientApplicationConfig ( clientId: kClientID)
@@ -92,12 +92,12 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
9292
9393 */
9494
95- let authority = try self . getAuthority ( policy : self . kSignupOrSigninPolicy)
95+ let authority = try self . getAuthority ( forPolicy : self . kSignupOrSigninPolicy)
9696
9797 /**
98- Acquire a token for a new user using interactive authentication
98+ Acquire a token for a new account using interactive authentication
9999
100- - forScopes : Permissions you want included in the access token received
100+ - scopes : Permissions you want included in the access token received
101101 in the result in the completionBlock. Not all scopes are
102102 gauranteed to be included in the access token returned.
103103 - completionBlock: The completion block that will be called when the authentication
@@ -107,9 +107,9 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
107107 let parameters = MSALInteractiveTokenParameters ( scopes: kScopes)
108108 parameters. authority = authority
109109 application. acquireToken ( with: parameters) { ( result, error) in
110- if error == nil {
111- self . accessToken = ( result? . accessToken) !
112- self . loggingText. text = " Access token is \( self . accessToken) "
110+ if let result = result {
111+ self . accessToken = result. accessToken
112+ self . loggingText. text = " Access token is \( self . accessToken ?? " Empty " ) "
113113 self . signoutButton. isEnabled = true
114114 self . callGraphApiButton. isEnabled = true
115115 self . editProfileButton. isEnabled = true
@@ -137,12 +137,12 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
137137
138138 */
139139
140- let authority = try self . getAuthority ( policy : self . kEditProfilePolicy)
140+ let authority = try self . getAuthority ( forPolicy : self . kEditProfilePolicy)
141141
142142 /**
143143 Acquire a token for a new account using interactive authentication
144144
145- - forScopes : Permissions you want included in the access token received
145+ - scopes : Permissions you want included in the access token received
146146 in the result in the completionBlock. Not all scopes are
147147 gauranteed to be included in the access token returned.
148148 - completionBlock: The completion block that will be called when the authentication
@@ -155,12 +155,10 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
155155 parameters. account = thisAccount
156156
157157 application. acquireToken ( with: parameters) { ( result, error) in
158- if error == nil {
159- self . loggingText. text = " Successfully edited profile "
160-
161-
158+ if let error = error {
159+ self . loggingText. text = " Could not edit profile: \( error) "
162160 } else {
163- self . loggingText. text = " Could not edit profile: \( error ?? " No error informarion " as! Error ) "
161+ self . loggingText. text = " Successfully edited profile"
164162 }
165163 }
166164 } catch {
@@ -181,16 +179,16 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
181179
182180 */
183181
184- let authority = try self . getAuthority ( policy : self . kSignupOrSigninPolicy)
182+ let authority = try self . getAuthority ( forPolicy : self . kSignupOrSigninPolicy)
185183
186184 /**
187185
188- Acquire a token for an existing user silently
186+ Acquire a token for an existing account silently
189187
190- - forScopes : Permissions you want included in the access token received
188+ - scopes : Permissions you want included in the access token received
191189 in the result in the completionBlock. Not all scopes are
192190 gauranteed to be included in the access token returned.
193- - User: A user object that we retrieved from the application object before that the
191+ - account: An account object that we retrieved from the application object before that the
194192 authentication flow will be locked down to.
195193 - completionBlock: The completion block that will be called when the authentication
196194 flow completes, or encounters an error.
@@ -204,60 +202,83 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
204202 let parameters = MSALSilentTokenParameters ( scopes: kScopes, account: thisAccount)
205203 parameters. authority = authority
206204 self . application. acquireTokenSilent ( with: parameters) { ( result, error) in
207- if error == nil {
208- self . accessToken = ( result? . accessToken) !
209- self . loggingText. text = " Refreshing token silently "
210- self . loggingText. text = " Refreshed Access token is \( self . accessToken) "
205+ if let error = error {
211206
212- } else if ( ( error! as NSError ) . code == MSALError . interactionRequired . rawValue ) {
207+ let nsError = error as NSError
213208
214- // Notice we supply the user here. This ensures we acquire token for the same user
215- // as we originally authenticated.
209+ // interactionRequired means we need to ask the user to sign-in. This usually happens
210+ // when the user's Refresh Token is expired or if the user has changed their password
211+ // among other possible reasons.
216212
217- let parameters = MSALInteractiveTokenParameters ( scopes: self . kScopes)
218- parameters. account = thisAccount
219-
220- self . application. acquireToken ( with: parameters) { ( result, error) in
221- if error == nil {
222- self . accessToken = ( result? . accessToken) !
223- self . loggingText. text = " Access token is \( self . accessToken) "
213+ if ( nsError. domain == MSALErrorDomain) {
214+
215+ if ( nsError. code == MSALError . interactionRequired. rawValue) {
216+
217+ // Notice we supply the account here. This ensures we acquire token for the same account
218+ // as we originally authenticated.
224219
225- } else {
226- self . loggingText. text = " Could not acquire new token: \( error ?? " No error informarion " as! Error ) "
220+ let parameters = MSALInteractiveTokenParameters ( scopes: self . kScopes)
221+ parameters. account = thisAccount
222+
223+ self . application. acquireToken ( with: parameters) { ( result, error) in
224+ if let result = result {
225+ self . accessToken = result. accessToken
226+ self . loggingText. text = " Access token is \( self . accessToken ?? " empty " ) "
227+
228+ } else {
229+ self . loggingText. text = " Could not acquire new token: \( error ?? " No error informarion " as! Error ) "
230+ }
231+ }
232+ return
227233 }
228234 }
229- } else {
230- self . loggingText. text = " Could not acquire token: \( error ?? " No error informarion " as! Error ) "
235+
236+ self . loggingText. text = " Could not acquire token: \( error) "
237+ return
231238 }
239+
240+ guard let result = result else {
241+
242+ self . loggingText. text = " Could not acquire token: No result returned "
243+ return
244+ }
245+
246+ self . accessToken = result. accessToken
247+ self . loggingText. text = " Refreshing token silently "
248+ self . loggingText. text = " Refreshed access token is \( self . accessToken ?? " empty " ) "
232249 }
233250 } catch {
234251 self . loggingText. text = " Unable to construct parameters before calling acquire token \( error) "
235252 }
236253 }
237254
238255 @IBAction func callApi( _ sender: UIButton ) {
239-
256+ guard let accessToken = self . accessToken else {
257+ self . loggingText. text = " Operation failed because could not find an access token! "
258+ return
259+ }
240260
241261 let sessionConfig = URLSessionConfiguration . default
242262 let url = URL ( string: self . kGraphURI)
243263 var request = URLRequest ( url: url!)
244- request. setValue ( " Bearer \( self . accessToken) " , forHTTPHeaderField: " Authorization " )
264+ request. setValue ( " Bearer \( accessToken) " , forHTTPHeaderField: " Authorization " )
245265 let urlSession = URLSession ( configuration: sessionConfig, delegate: self , delegateQueue: OperationQueue . main)
246266
247267 urlSession. dataTask ( with: request) { data, response, error in
248-
249- if error == nil {
250- let result = try ? JSONSerialization . jsonObject ( with: data!, options: [ ] )
251- if result != nil {
252- self . loggingText. text = " API response: \( result. debugDescription) "
253- } else {
254- self . loggingText. text = " Nothing returned from API "
255- }
256- } else {
268+ guard let validData = data else {
257269 self . loggingText. text = " Could not call API: \( error ?? " No error informarion " as! Error ) "
270+ return
271+ }
272+
273+ let result = try ? JSONSerialization . jsonObject ( with: validData, options: [ ] )
274+
275+ guard let validResult = result as? [ String : Any ] else {
276+ self . loggingText. text = " Nothing returned from API "
277+ return
258278 }
279+
280+ self . loggingText. text = " API response: \( validResult. debugDescription) "
259281 } . resume ( )
260-
261282 }
262283
263284 @IBAction func signoutButton( _ sender: UIButton ) {
@@ -272,6 +293,8 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
272293
273294 if let accountToRemove = thisAccount {
274295 try application. remove ( accountToRemove)
296+ } else {
297+ self . loggingText. text = " There is no account to signing out! "
275298 }
276299
277300 self . signoutButton. isEnabled = false
@@ -291,19 +314,24 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
291314
292315 override func viewWillAppear( _ animated: Bool ) {
293316
294- if self . accessToken. isEmpty {
317+ if self . accessToken == nil {
295318 signoutButton. isEnabled = false
296319 callGraphApiButton. isEnabled = false
297320 editProfileButton. isEnabled = false
298321 refreshTokenButton. isEnabled = false
299322 }
300323 }
301324
302- func getAccountByPolicy ( withAccounts: [ MSALAccount ] , policy: String ) throws -> MSALAccount ? {
325+ func getAccountByPolicy ( withAccounts accounts : [ MSALAccount ] , policy: String ) throws -> MSALAccount ? {
303326
304- for account in withAccounts {
305- if ( account. homeAccountId != nil && account. homeAccountId!. objectId!. hasSuffix ( policy. lowercased ( ) ) ) {
306- return account
327+ for account in accounts {
328+ // This is a single account sample, so we only check the suffic part of the object id,
329+ // where object id is in the form of <object id>-<policy>.
330+ // For multi-account apps, the whole object id needs to be checked.
331+ if let homeAccountId = account. homeAccountId, let objectId = homeAccountId. objectId {
332+ if objectId. hasSuffix ( policy. lowercased ( ) ) {
333+ return account
334+ }
307335 }
308336 }
309337 return nil
@@ -318,7 +346,7 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
318346 tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
319347 use for the current user flow.
320348 */
321- func getAuthority( policy: String ) throws -> MSALB2CAuthority {
349+ func getAuthority( forPolicy policy: String ) throws -> MSALB2CAuthority {
322350 guard let authorityURL = URL ( string: String ( format: self . kEndpoint, self . kTenantName, policy) ) else {
323351 throw NSError ( domain: " SomeDomain " ,
324352 code: 1 ,
0 commit comments