Skip to content

Commit cbd49ea

Browse files
authored
fix(Auth): Making improvements to federation flow (#2488)
1 parent 7f094a9 commit cbd49ea

File tree

12 files changed

+425
-73
lines changed

12 files changed

+425
-73
lines changed

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/Federation/ClearFederationToIdentityPool.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,18 @@ struct ClearFederationToIdentityPool: Action {
1818

1919
let event: StateMachineEvent
2020
do {
21-
try await credentialStoreClient?.deleteData(type: .amplifyCredentials)
22-
event = AuthenticationEvent.init(eventType: .clearedFederationToIdentityPool)
21+
// Check if we have credentials are of type federation, otherwise throw an error
22+
if case .amplifyCredentials(let credentials) = try await credentialStoreClient?.fetchData(
23+
type: .amplifyCredentials),
24+
case .identityPoolWithFederation = credentials {
25+
26+
try await credentialStoreClient?.deleteData(type: .amplifyCredentials)
27+
event = AuthenticationEvent.init(eventType: .clearedFederationToIdentityPool)
28+
} else {
29+
let error = AuthenticationError.service(message: "Unable to find credentials to clear for federation.")
30+
event = AuthenticationEvent.init(eventType: .error(error))
31+
logError("\(#fileID) Sending event \(event.type) with error \(error)", environment: environment)
32+
}
2333

2434
} catch {
2535
let error = AuthenticationError.unknown(message: "Unable to clear credential store: \(error)")

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/CredentialStorage/AmplifyCredentials+CognitoSession.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,13 @@ extension AmplifyCredentials {
3131
identityIdResult: .success(identityId),
3232
awsCredentialsResult: .success(awsCredentials),
3333
cognitoTokensResult: .failure(
34-
.signedOut("Cognito tokens unavailable when federated to identity pool",
35-
"Clear federation and sign to get user pool tokens.", nil)))
34+
.invalidState(
35+
"Users Federated to Identity Pool do not have User Pool access.",
36+
"To access User Pool data, you must use a Sign In method.",
37+
nil
38+
)
39+
)
40+
)
3641
case .userPoolAndIdentityPool(let signedInData, let identityID, let credentials):
3742
return AWSAuthCognitoSession(
3843
isSignedIn: true,

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/ClearFederationOperationHelper.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,18 @@ struct ClearFederationOperationHelper {
1414

1515
let currentState = await authStateMachine.currentState
1616

17-
if case .configured(let authNState, let authZState) = currentState,
18-
case .federatedToIdentityPool = authNState,
19-
case .sessionEstablished = authZState {
17+
guard case .configured(let authNState, let authZState) = currentState else {
18+
let authError = AuthError.invalidState(
19+
"Clearing of federation failed.",
20+
AuthPluginErrorConstants.invalidStateError, nil)
21+
throw authError
22+
}
23+
24+
switch (authNState, authZState) {
25+
case (.federatedToIdentityPool, .sessionEstablished),
26+
(.error, .error):
2027
try await startClearingFederation(with: authStateMachine)
21-
} else {
28+
default:
2229
let authError = AuthError.invalidState(
2330
"Clearing of federation failed.",
2431
AuthPluginErrorConstants.invalidStateError, nil)
@@ -27,9 +34,9 @@ struct ClearFederationOperationHelper {
2734
}
2835

2936
private func startClearingFederation(with authStateMachine: AuthStateMachine) async throws {
30-
let stateSequences = await authStateMachine.listen()
3137
let event = AuthenticationEvent.init(eventType: .clearFederationToIdentityPool)
3238
await authStateMachine.send(event)
39+
let stateSequences = await authStateMachine.listen()
3340
for await state in stateSequences {
3441
guard case .configured(let authNState, _) = state else {
3542
continue

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Operations/Helpers/FetchAuthSessionOperationHelper.swift

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,34 @@ class FetchAuthSessionOperationHelper {
3131
await authStateMachine.send(event)
3232
return try await listenForSession(authStateMachine: authStateMachine)
3333
case .sessionEstablished(let credentials):
34+
return try await postAuthSessionEvent(
35+
forCredential: credentials,
36+
authStateMachine: authStateMachine,
37+
forceRefresh: forceRefresh)
38+
case .error(let error):
39+
if case .sessionExpired = error {
40+
let session = AuthCognitoSignedInSessionHelper.makeExpiredSignedInSession()
41+
return session
42+
} else if case .sessionError(_, let credentials) = error {
43+
return try await postAuthSessionEvent(
44+
forCredential: credentials,
45+
authStateMachine: authStateMachine,
46+
forceRefresh: forceRefresh)
47+
} else {
48+
let event = AuthorizationEvent(eventType: .fetchUnAuthSession)
49+
await authStateMachine.send(event)
50+
return try await listenForSession(authStateMachine: authStateMachine)
51+
}
52+
53+
default:
54+
return try await listenForSession(authStateMachine: authStateMachine)
55+
}
56+
}
3457

58+
func postAuthSessionEvent(
59+
forCredential credentials: AmplifyCredentials,
60+
authStateMachine: AuthStateMachine,
61+
forceRefresh: Bool) async throws -> AuthSession {
3562
switch credentials {
3663

3764
case .userPoolOnly(signedInData: let data):
@@ -82,34 +109,9 @@ class FetchAuthSessionOperationHelper {
82109
await authStateMachine.send(event)
83110
return try await listenForSession(authStateMachine: authStateMachine)
84111
}
85-
86-
case .error(let error):
87-
if case .sessionExpired = error {
88-
let session = AuthCognitoSignedInSessionHelper.makeExpiredSignedInSession()
89-
return session
90-
} else if case .sessionError(_, let amplifyCredentials) = error {
91-
var event: AuthorizationEvent
92-
// If there was no credentials before, we try to get the unauth session
93-
// else, we try to refresh the credentials.
94-
if case .noCredentials = amplifyCredentials {
95-
event = AuthorizationEvent(eventType: .fetchUnAuthSession)
96-
} else {
97-
event = AuthorizationEvent(eventType: .refreshSession(forceRefresh))
98-
}
99-
await authStateMachine.send(event)
100-
return try await listenForSession(authStateMachine: authStateMachine)
101-
} else {
102-
let event = AuthorizationEvent(eventType: .fetchUnAuthSession)
103-
await authStateMachine.send(event)
104-
return try await listenForSession(authStateMachine: authStateMachine)
105-
}
106-
107-
default:
108-
return try await listenForSession(authStateMachine: authStateMachine)
109112
}
110-
}
111113

112-
func listenForSession(authStateMachine: AuthStateMachine) async throws ->AuthSession {
114+
func listenForSession(authStateMachine: AuthStateMachine) async throws -> AuthSession {
113115

114116
let stateSequences = await authStateMachine.listen()
115117
for await state in stateSequences {

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Errors/FetchSessionError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ extension FetchSessionError: AuthErrorConvertible {
7777
AmplifyErrorMessages.reportBugToAWS())
7878
case .federationNotSupportedDuringRefresh:
7979
return .unknown(
80-
"Federation triggered during refresh session that is not supported. \(AmplifyErrorMessages.reportBugToAWS())")
80+
"Refreshing credentials from federationToIdentityPool is not supported \(AmplifyErrorMessages.reportBugToAWS())")
8181
case .service(let error):
8282
return .service(
8383
"Service error occurred",

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/States/AuthorizationState.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ enum AuthorizationState: State {
1919

2020
case clearingFederation
2121

22-
case federatingToIdentityPool(FetchAuthSessionState,
23-
FederatedToken)
22+
case federatingToIdentityPool(
23+
FetchAuthSessionState,
24+
FederatedToken,
25+
existingCredentials: AmplifyCredentials)
2426

2527
case fetchingUnAuthSession(FetchAuthSessionState)
2628

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/States/DebugInfo/AuthorizationState+Debug.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extension AuthorizationState: CustomDebugDictionaryConvertible {
2424
"refreshState": state.debugDictionary]
2525
case .fetchingUnAuthSession(let state),
2626
.fetchingAuthSessionWithUserPool(let state, _),
27-
.federatingToIdentityPool(let state, _):
27+
.federatingToIdentityPool(let state, _, _):
2828
additionalMetadataDictionary = state.debugDictionary
2929

3030
case .error(let error):

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/Resolvers/Authentication/AuthenticationState+Resolver.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ extension AuthenticationState {
122122
} else if let authZEvent = event.isAuthorizationEvent,
123123
case .startFederationToIdentityPool = authZEvent {
124124
return .init(newState: .federatingToIdentityPool)
125+
} else if let authEvent = event as? AuthenticationEvent,
126+
case .clearFederationToIdentityPool = authEvent.eventType {
127+
return .init(
128+
newState: .clearingFederation,
129+
actions: [
130+
ClearFederationToIdentityPool()
131+
])
125132
} else {
126133
return .from(oldState)
127134
}

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/Resolvers/Authorization/AuthorizationState+Resolver.swift

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ extension AuthorizationState {
4646
federatedToken: federatedToken,
4747
developerProvidedIdentityId: identityId)
4848
return .init(
49-
newState: .federatingToIdentityPool(.notStarted, federatedToken),
49+
newState: .federatingToIdentityPool(
50+
.notStarted,
51+
federatedToken,
52+
existingCredentials: .noCredentials),
5053
actions: [action])
5154
}
5255

@@ -69,7 +72,10 @@ extension AuthorizationState {
6972
federatedToken: federatedToken,
7073
developerProvidedIdentityId: identityId)
7174
return .init(
72-
newState: .federatingToIdentityPool(.notStarted, federatedToken),
75+
newState: .federatingToIdentityPool(
76+
.notStarted,
77+
federatedToken,
78+
existingCredentials: credentials),
7379
actions: [action])
7480
}
7581

@@ -89,7 +95,8 @@ extension AuthorizationState {
8995

9096
return .from(oldState)
9197

92-
case .federatingToIdentityPool(let fetchSessionState, let federatedToken):
98+
case .federatingToIdentityPool(
99+
let fetchSessionState, let federatedToken, let credentials):
93100

94101
if case .fetched(let identityID,
95102
let credentials) = event.isAuthorizationEvent {
@@ -103,7 +110,7 @@ extension AuthorizationState {
103110
}
104111

105112
if case .receivedSessionError(let error) = event.isAuthorizationEvent {
106-
return .init(newState: .error(.sessionError(error, .noCredentials)))
113+
return .init(newState: .error(.sessionError(error, credentials)))
107114
}
108115

109116
if case .throwError(let error) = event.isAuthorizationEvent {
@@ -112,8 +119,12 @@ extension AuthorizationState {
112119

113120
let resolver = FetchAuthSessionState.Resolver()
114121
let resolution = resolver.resolve(oldState: fetchSessionState, byApplying: event)
115-
return .init(newState: .federatingToIdentityPool(resolution.newState, federatedToken),
116-
actions: resolution.actions)
122+
return .init(
123+
newState: .federatingToIdentityPool(
124+
resolution.newState,
125+
federatedToken,
126+
existingCredentials: credentials),
127+
actions: resolution.actions)
117128

118129
case .signingIn:
119130
if let authEvent = event.isAuthenticationEvent {
@@ -247,13 +258,24 @@ extension AuthorizationState {
247258
return .from(oldState)
248259

249260
case .error(let error):
261+
var existingCredentials: AmplifyCredentials = .noCredentials
262+
if case .sessionError(_, let credentials) = error {
263+
existingCredentials = credentials
264+
}
265+
250266
if let authNEvent = event.isAuthenticationEvent {
251-
if case .signInRequested = authNEvent {
267+
268+
switch authNEvent {
269+
case .signInRequested:
252270
return .from(.signingIn)
253-
} else if case .signOutRequested = authNEvent {
271+
case .signOutRequested:
254272
return .from(.signingOut(nil))
255-
} else if case .cancelSignIn = authNEvent {
273+
case .cancelSignIn:
256274
return .from(.configured)
275+
case .clearFederationToIdentityPool:
276+
return .from(.clearingFederation)
277+
default: break
278+
257279
}
258280
}
259281
if case .fetchUnAuthSession = event.isAuthorizationEvent {
@@ -269,7 +291,10 @@ extension AuthorizationState {
269291
federatedToken: federatedToken,
270292
developerProvidedIdentityId: identityId)
271293
return .init(
272-
newState: .federatingToIdentityPool(.notStarted, federatedToken),
294+
newState: .federatingToIdentityPool(
295+
.notStarted,
296+
federatedToken,
297+
existingCredentials: existingCredentials),
273298
actions: [action])
274299
}
275300

AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/HubEventTests/AuthHubEventHandlerTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ class AuthHubEventHandlerTests: XCTestCase {
278278
}
279279
}
280280

281+
_ = try await plugin.federateToIdentityPool(withProviderToken: "something", for: .facebook)
281282
try await plugin.clearFederationToIdentityPool()
282283

283284
await waitForExpectations(timeout: networkTimeout)

0 commit comments

Comments
 (0)