Skip to content

Commit 92bf62f

Browse files
authored
Merge pull request #312 from Esri/v.next
V.next
2 parents fb8b3e4 + 45e66fe commit 92bf62f

File tree

81 files changed

+1704
-1280
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+1704
-1280
lines changed

AuthenticationExample/AuthenticationExample.xcodeproj/project.pbxproj

+16-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@
2121
/* End PBXBuildFile section */
2222

2323
/* Begin PBXFileReference section */
24+
21B31E8B29EF53BE00A40B10 /* AuthenticationExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AuthenticationExample.entitlements; sourceTree = "<group>"; };
2425
88AD13752834355000500B2E /* AuthenticationExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AuthenticationExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
2526
88AD13782834355000500B2E /* AuthenticationApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationApp.swift; sourceTree = "<group>"; };
2627
88AD137A2834355000500B2E /* SigninView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigninView.swift; sourceTree = "<group>"; };
2728
88AD137C2834355100500B2E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
2829
88AD137F2834355100500B2E /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
29-
88AD13862834355C00500B2E /* arcgis-maps-sdk-swift-toolit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "arcgis-maps-sdk-swift-toolit"; path = ..; sourceTree = "<group>"; };
30+
88AD13862834355C00500B2E /* arcgis-maps-sdk-swift-toolkit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "arcgis-maps-sdk-swift-toolkit"; path = ..; sourceTree = "<group>"; };
3031
88AD1387283435EA00500B2E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
3132
88AD138D283443F800500B2E /* MapItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapItemView.swift; sourceTree = "<group>"; };
3233
88D24DEF288F062D007A539C /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
@@ -51,7 +52,7 @@
5152
88AD136C2834355000500B2E = {
5253
isa = PBXGroup;
5354
children = (
54-
88AD13862834355C00500B2E /* arcgis-maps-sdk-swift-toolit */,
55+
88AD13862834355C00500B2E /* arcgis-maps-sdk-swift-toolkit */,
5556
88AD13772834355000500B2E /* AuthenticationExample */,
5657
88AD13762834355000500B2E /* Products */,
5758
88AD13882834366100500B2E /* Frameworks */,
@@ -69,6 +70,7 @@
6970
88AD13772834355000500B2E /* AuthenticationExample */ = {
7071
isa = PBXGroup;
7172
children = (
73+
21B31E8B29EF53BE00A40B10 /* AuthenticationExample.entitlements */,
7274
88AD1387283435EA00500B2E /* Info.plist */,
7375
88AD13782834355000500B2E /* AuthenticationApp.swift */,
7476
88D24DF1288F2FA1007A539C /* HomeView.swift */,
@@ -237,7 +239,7 @@
237239
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
238240
GCC_WARN_UNUSED_FUNCTION = YES;
239241
GCC_WARN_UNUSED_VARIABLE = YES;
240-
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
242+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
241243
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
242244
MTL_FAST_MATH = YES;
243245
ONLY_ACTIVE_ARCH = YES;
@@ -292,7 +294,7 @@
292294
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
293295
GCC_WARN_UNUSED_FUNCTION = YES;
294296
GCC_WARN_UNUSED_VARIABLE = YES;
295-
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
297+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
296298
MTL_ENABLE_DEBUG_INFO = NO;
297299
MTL_FAST_MATH = YES;
298300
SDKROOT = iphoneos;
@@ -307,6 +309,7 @@
307309
buildSettings = {
308310
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
309311
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
312+
CODE_SIGN_ENTITLEMENTS = AuthenticationExample/AuthenticationExample.entitlements;
310313
CODE_SIGN_STYLE = Automatic;
311314
CURRENT_PROJECT_VERSION = 1;
312315
DEVELOPMENT_ASSET_PATHS = "\"AuthenticationExample/Preview Content\"";
@@ -322,9 +325,12 @@
322325
"$(inherited)",
323326
"@executable_path/Frameworks",
324327
);
325-
MARKETING_VERSION = 1.0;
328+
MARKETING_VERSION = 200.1.0;
326329
PRODUCT_BUNDLE_IDENTIFIER = com.esri.Authentication;
327330
PRODUCT_NAME = "$(TARGET_NAME)";
331+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
332+
SUPPORTS_MACCATALYST = YES;
333+
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
328334
SWIFT_EMIT_LOC_STRINGS = YES;
329335
SWIFT_VERSION = 5.0;
330336
TARGETED_DEVICE_FAMILY = "1,2";
@@ -336,6 +342,7 @@
336342
buildSettings = {
337343
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
338344
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
345+
CODE_SIGN_ENTITLEMENTS = AuthenticationExample/AuthenticationExample.entitlements;
339346
CODE_SIGN_STYLE = Automatic;
340347
CURRENT_PROJECT_VERSION = 1;
341348
DEVELOPMENT_ASSET_PATHS = "\"AuthenticationExample/Preview Content\"";
@@ -351,9 +358,12 @@
351358
"$(inherited)",
352359
"@executable_path/Frameworks",
353360
);
354-
MARKETING_VERSION = 1.0;
361+
MARKETING_VERSION = 200.1.0;
355362
PRODUCT_BUNDLE_IDENTIFIER = com.esri.Authentication;
356363
PRODUCT_NAME = "$(TARGET_NAME)";
364+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
365+
SUPPORTS_MACCATALYST = YES;
366+
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
357367
SWIFT_EMIT_LOC_STRINGS = YES;
358368
SWIFT_VERSION = 5.0;
359369
TARGETED_DEVICE_FAMILY = "1,2";

AuthenticationExample/AuthenticationExample/AuthenticationApp.swift

+9-8
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ struct AuthenticationApp: App {
2424
// Create an authenticator.
2525
authenticator = Authenticator(
2626
// If you want to use OAuth, uncomment this code:
27-
//oAuthConfigurations: [.arcgisDotCom]
27+
//oAuthUserConfigurations: [.arcgisDotCom]
2828
)
29-
// Set the challenge handler to be the authenticator we just created.
30-
ArcGISEnvironment.authenticationChallengeHandler = authenticator
29+
// Sets authenticator as ArcGIS and Network challenge handlers to handle authentication
30+
// challenges.
31+
ArcGISEnvironment.authenticationManager.handleChallenges(using: authenticator)
3132
}
3233

3334
var body: some SwiftUI.Scene {
@@ -49,21 +50,21 @@ struct AuthenticationApp: App {
4950
.environmentObject(authenticator)
5051
.task {
5152
isSettingUp = true
52-
// Here we make the authenticator persistent, which means that it will synchronize
53-
// with they keychain for storing credentials.
53+
// Here we setup credential stores to be persistent, which means that it will
54+
// synchronize with the keychain for storing credentials.
5455
// It also means that a user can sign in without having to be prompted for
5556
// credentials. Once credentials are cleared from the stores ("sign-out"),
5657
// then the user will need to be prompted once again.
57-
try? await authenticator.setupPersistentCredentialStorage(access: .whenUnlockedThisDeviceOnly)
58+
try? await ArcGISEnvironment.authenticationManager.setupPersistentCredentialStorage(access: .whenUnlockedThisDeviceOnly)
5859
isSettingUp = false
5960
}
6061
}
6162
}
6263
}
6364

6465
// If you want to use OAuth, you can uncomment this code:
65-
//private extension OAuthConfiguration {
66-
// static let arcgisDotCom = OAuthConfiguration(
66+
//private extension OAuthUserConfiguration {
67+
// static let arcgisDotCom = OAuthUserConfiguration(
6768
// portalURL: .portal,
6869
// clientID: "<#Your client ID goes here#>",
6970
// // Note: You must have the same redirect URL used here
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.security.app-sandbox</key>
6+
<true/>
7+
<key>com.apple.security.network.client</key>
8+
<true/>
9+
</dict>
10+
</plist>

AuthenticationExample/AuthenticationExample/FeaturedMapsView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct FeaturedMapsView: View {
3030
if isLoading {
3131
ProgressView()
3232
} else {
33-
List(featuredItems) { item in
33+
List(featuredItems, id: \.id) { item in
3434
NavigationLink {
3535
MapItemView(map: Map(item: item))
3636
} label: {

AuthenticationExample/AuthenticationExample/ProfileView.swift

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ import ArcGISToolkit
1717

1818
/// A view that displays the profile of a user.
1919
struct ProfileView: View {
20-
/// The authenticator that has been passed through the environment down to the app.
21-
@EnvironmentObject var authenticator: Authenticator
22-
2320
/// The portal that the user is signed in to.
2421
@State var portal: Portal
2522

@@ -61,7 +58,8 @@ struct ProfileView: View {
6158
func signOut() {
6259
isSigningOut = true
6360
Task {
64-
await authenticator.clearCredentialStores()
61+
await ArcGISEnvironment.authenticationManager.revokeOAuthTokens()
62+
await ArcGISEnvironment.authenticationManager.clearCredentialStores()
6563
isSigningOut = false
6664
signOutAction()
6765
}

AuthenticationExample/AuthenticationExample/SignInView.swift

+8-37
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ import ArcGISToolkit
1717
import CryptoKit
1818

1919
/// A view that allows the user to sign in to a portal.
20-
struct SignInView: View {
21-
/// The authenticator which has been passed from the app through the environment.
22-
@EnvironmentObject var authenticator: Authenticator
23-
20+
struct SignInView: View {
2421
/// The error that occurred during an attempt to sign in.
2522
@State var error: Error?
2623

@@ -61,10 +58,10 @@ struct SignInView: View {
6158
return
6259
}
6360

64-
if let arcGISCredential = await ArcGISEnvironment.credentialStore.credential(for: .portal) {
65-
lastSignedInUser = arcGISCredential.username ?? ""
61+
if let arcGISCredential = ArcGISEnvironment.authenticationManager.arcGISCredentialStore.credential(for: .portal) {
62+
lastSignedInUser = arcGISCredential.username
6663
} else {
67-
let networkCredentials = await ArcGISEnvironment.networkCredentialStore.credentials(forHost: URL.portal.host!)
64+
let networkCredentials = await ArcGISEnvironment.authenticationManager.networkCredentialStore.credentials(forHost: URL.portal.host!)
6865
if !networkCredentials.isEmpty {
6966
lastSignedInUser = networkCredentials.compactMap { credential in
7067
switch credential {
@@ -74,6 +71,8 @@ struct SignInView: View {
7471
return ""
7572
case .serverTrust:
7673
return nil
74+
@unknown default:
75+
fatalError("Unknown NetworkCredential")
7776
}
7877
}
7978
.first
@@ -128,41 +127,13 @@ struct SignInView: View {
128127
}
129128
}
130129

131-
private extension ArcGISCredential {
132-
/// The username, if any, associated with this credential.
133-
var username: String? {
134-
get {
135-
switch self {
136-
case .oauth(let credential):
137-
return credential.username
138-
case .token(let credential):
139-
return credential.username
140-
case .staticToken:
141-
return nil
142-
}
143-
}
144-
}
145-
}
146-
147130
private extension Error {
148131
/// Returns a Boolean value indicating whether the error is the result of cancelling an
149132
/// authentication challenge.
150133
var isChallengeCancellationError: Bool {
151134
switch self {
152-
case let error as ArcGISAuthenticationChallenge.Error:
153-
switch error {
154-
case .userCancelled:
155-
return true
156-
default:
157-
return false
158-
}
159-
case let error as OAuthCredential.AuthorizationError:
160-
switch error {
161-
case .userCancelled:
162-
return true
163-
default:
164-
return false
165-
}
135+
case is CancellationError:
136+
return true
166137
case let error as NSError:
167138
return error.domain == NSURLErrorDomain && error.code == -999
168139
default:

AuthenticationExample/AuthenticationExample/UserView.swift

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ extension PortalUser.Role: CustomStringConvertible {
7373
return "Admin"
7474
case .publisher:
7575
return "Publisher"
76+
@unknown default:
77+
fatalError("Unknown PortalUser.Role")
7678
}
7779
}
7880
}

Documentation/Authenticator/README.md

+21-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# Authenticator
22

3-
The `Authenticator` is a configurable object that handles authentication challenges. It will display a user interface when network and ArcGIS authentication challenges occur.
3+
The `Authenticator` is a configurable object that handles authentication challenges. It will display a user interface when network and ArcGIS authentication challenges occur.
44

55
![image](https://user-images.githubusercontent.com/3998072/203615041-c887d5e3-bb64-469a-a76b-126059329e92.png)
66

77
## Features
88

9-
The `Authenticator` has a view modifier that will display a prompt when the `Authenticator` is asked to handle an authentication challenge. This will handle many different types of authentication, for example:
9+
The `Authenticator` has a view modifier that will display a prompt when the `Authenticator` is asked to handle an authentication challenge. This will handle many different types of authentication, for example:
1010
- ArcGIS authentication (token and OAuth)
1111
- Integrated Windows Authentication (IWA)
1212
- Client Certificate (PKI)
@@ -23,7 +23,7 @@ The `Authenticator` can be configured to support securely persisting credentials
2323
@ViewBuilder func authenticator(_ authenticator: Authenticator) -> some View
2424
```
2525

26-
To securely store credentials in the keychain, use the following instance method on `Authenticator`:
26+
To securely store credentials in the keychain, use the following extension method of `AuthenticationManager`:
2727

2828
```swift
2929
/// Sets up new credential stores that will be persisted to the keychain.
@@ -39,6 +39,18 @@ To securely store credentials in the keychain, use the following instance method
3939
) async throws
4040
```
4141

42+
During sign-out, use the following extension methods of `AuthenticationManager`:
43+
44+
```swift
45+
/// Revokes tokens of OAuth user credentials.
46+
func revokeOAuthTokens() async
47+
48+
/// Clears all ArcGIS and network credentials from the respective stores.
49+
/// Note: This sets up new `URLSessions` so that removed network credentials are respected
50+
/// right away.
51+
func clearCredentialStores() async
52+
```
53+
4254
## Behavior:
4355

4456
The Authenticator view modifier will display an alert prompting the user for credentials. If credentials were persisted to the keychain, the Authenticator will use those instead of requiring the user to reenter credentials.
@@ -56,21 +68,22 @@ init() {
5668
// If you want to use OAuth, uncomment this code:
5769
//oAuthConfigurations: [.arcgisDotCom]
5870
)
59-
// Set the challenge handler to be the authenticator we just created.
60-
ArcGISEnvironment.authenticationChallengeHandler = authenticator
71+
// Sets authenticator as ArcGIS and Network challenge handlers to handle authentication
72+
// challenges.
73+
ArcGISEnvironment.authenticationManager.handleChallenges(using: authenticator)
6174
}
6275

6376
var body: some SwiftUI.Scene {
6477
WindowGroup {
6578
HomeView()
6679
.authenticator(authenticator)
6780
.task {
68-
// Here we make the authenticator persistent, which means that it will synchronize
69-
// with the keychain for storing credentials.
81+
// Here we setup credential stores to be persistent, which means that it will
82+
// synchronize with the keychain for storing credentials.
7083
// It also means that a user can sign in without having to be prompted for
7184
// credentials. Once credentials are cleared from the stores ("sign-out"),
7285
// then the user will need to be prompted once again.
73-
try? await authenticator.setupPersistentCredentialStorage(access: .whenUnlockedThisDeviceOnly)
86+
try? await ArcGISEnvironment.authenticationManager.setupPersistentCredentialStorage(access: .whenUnlockedThisDeviceOnly)
7487
}
7588
}
7689
}

0 commit comments

Comments
 (0)