Skip to content

Commit 1d196d5

Browse files
committed
feat(auth): ignore user cancel when session is expired on hostedui sign out
1 parent 695039d commit 1d196d5

File tree

6 files changed

+98
-11
lines changed

6 files changed

+98
-11
lines changed

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignOut/InitiateSignOut.swift

+32-4
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,49 @@ struct InitiateSignOut: Action {
1717

1818
func execute(withDispatcher dispatcher: EventDispatcher, environment: Environment) async {
1919
logVerbose("\(#fileID) Starting execution", environment: environment)
20-
20+
let updatedSignedInData = await getUpdatedSignedInData(environment: environment)
2121
let event: SignOutEvent
2222
if case .hostedUI(let options) = signedInData.signInMethod,
2323
options.preferPrivateSession == false {
2424
event = SignOutEvent(eventType: .invokeHostedUISignOut(signOutEventData,
25-
signedInData))
25+
updatedSignedInData))
2626
} else if signOutEventData.globalSignOut {
27-
event = SignOutEvent(eventType: .signOutGlobally(signedInData))
27+
event = SignOutEvent(eventType: .signOutGlobally(updatedSignedInData))
2828
} else {
29-
event = SignOutEvent(eventType: .revokeToken(signedInData))
29+
event = SignOutEvent(eventType: .revokeToken(updatedSignedInData))
3030
}
3131
logVerbose("\(#fileID) Sending event \(event.type)", environment: environment)
3232
await dispatcher.send(event)
3333
}
3434

35+
private func getUpdatedSignedInData(
36+
environment: Environment
37+
) async -> SignedInData {
38+
let credentialStoreClient = (environment as? AuthEnvironment)?.credentialsClient
39+
do {
40+
let data = try await credentialStoreClient?.fetchData(
41+
type: .amplifyCredentials
42+
)
43+
guard case .amplifyCredentials(var credentials) = data else {
44+
return signedInData
45+
}
46+
47+
// Update SignedInData based on credential type
48+
switch credentials {
49+
case .userPoolOnly(var updatedSignedInData):
50+
return updatedSignedInData
51+
case .userPoolAndIdentityPool(var updatedSignedInData, _, _):
52+
return updatedSignedInData
53+
case .identityPoolOnly, .identityPoolWithFederation, .noCredentials:
54+
return signedInData
55+
}
56+
} catch {
57+
let logger = (environment as? LoggerProvider)?.logger
58+
logger?.error("Unable to update credentials with error: \(error)")
59+
return signedInData
60+
}
61+
}
62+
3563
}
3664

3765
extension InitiateSignOut: CustomDebugDictionaryConvertible {

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignOut/ShowHostedUISignOut.swift

+11-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,17 @@ class ShowHostedUISignOut: NSObject, Action {
4646
inPrivate: false,
4747
presentationAnchor: signOutEvent.presentationAnchor)
4848
await sendEvent(with: nil, dispatcher: dispatcher, environment: environment)
49-
} catch {
49+
}
50+
catch HostedUIError.cancelled {
51+
if signInData.isRefreshTokenExpired == true {
52+
self.logVerbose("\(#fileID) Received user cancelled error, but session is expired and continue signing out.", environment: environment)
53+
await sendEvent(with: nil, dispatcher: dispatcher, environment: environment)
54+
} else {
55+
self.logVerbose("\(#fileID) Received error \(HostedUIError.cancelled)", environment: environment)
56+
await sendEvent(with: HostedUIError.cancelled, dispatcher: dispatcher, environment: environment)
57+
}
58+
}
59+
catch {
5060
self.logVerbose("\(#fileID) Received error \(error)", environment: environment)
5161
await sendEvent(with: error, dispatcher: dispatcher, environment: environment)
5262
}
@@ -101,4 +111,3 @@ extension ShowHostedUISignOut {
101111
debugDictionary.debugDescription
102112
}
103113
}
104-
//#endif

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/ClientBehavior/AWSCognitoAuthPlugin+ClientBehavior.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ extension AWSCognitoAuthPlugin: AuthCategoryBehavior {
114114
public func fetchAuthSession(options: AuthFetchSessionRequest.Options?) async throws -> AuthSession {
115115
let options = options ?? AuthFetchSessionRequest.Options()
116116
let request = AuthFetchSessionRequest(options: options)
117-
let task = AWSAuthFetchSessionTask(request, authStateMachine: authStateMachine)
117+
let task = AWSAuthFetchSessionTask(
118+
request,
119+
environment: authEnvironment,
120+
authStateMachine: authStateMachine)
118121
return try await taskQueue.sync {
119122
return try await task.value
120123
} as! AuthSession

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

+43-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77

88
import Foundation
99
import Amplify
10+
import AWSPluginsCore
1011

1112
class FetchAuthSessionOperationHelper {
1213

1314
typealias FetchAuthSessionCompletion = (Result<AuthSession, AuthError>) -> Void
15+
var environment: Environment? = nil
1416

1517
func fetch(_ authStateMachine: AuthStateMachine,
1618
forceRefresh: Bool = false) async throws -> AuthSession {
@@ -98,7 +100,7 @@ class FetchAuthSessionOperationHelper {
98100
case .sessionEstablished(let credentials):
99101
return credentials.cognitoSession
100102
case .error(let authorizationError):
101-
return try sessionResultWithError(
103+
return try await sessionResultWithError(
102104
authorizationError,
103105
authenticationState: authenticationState)
104106
default: continue
@@ -111,7 +113,7 @@ class FetchAuthSessionOperationHelper {
111113
func sessionResultWithError(
112114
_ error: AuthorizationError,
113115
authenticationState: AuthenticationState
114-
) throws -> AuthSession {
116+
) async throws -> AuthSession {
115117
log.verbose("Received fetch auth session error - \(error)")
116118

117119
var isSignedIn = false
@@ -129,8 +131,10 @@ class FetchAuthSessionOperationHelper {
129131
authError = fetchError.authError
130132
}
131133
case .sessionExpired(let error):
134+
await setRefreshTokenExpiredInSignedInData()
132135
let session = AuthCognitoSignedInSessionHelper.makeExpiredSignedInSession(
133136
underlyingError: error)
137+
134138
return session
135139
default:
136140
break
@@ -143,6 +147,43 @@ class FetchAuthSessionOperationHelper {
143147
cognitoTokensResult: .failure(authError))
144148
return session
145149
}
150+
151+
func setRefreshTokenExpiredInSignedInData() async {
152+
let credentialStoreClient = (environment as? AuthEnvironment)?.credentialsClient
153+
do {
154+
let data = try await credentialStoreClient?.fetchData(
155+
type: .amplifyCredentials
156+
)
157+
guard case .amplifyCredentials(var credentials) = data else {
158+
return
159+
}
160+
161+
// Update SignedInData based on credential type
162+
switch credentials {
163+
case .userPoolOnly(var signedInData):
164+
signedInData.isRefreshTokenExpired = true
165+
credentials = .userPoolOnly(signedInData: signedInData)
166+
167+
case .userPoolAndIdentityPool(var signedInData, let identityId, let awsCredentials):
168+
signedInData.isRefreshTokenExpired = true
169+
credentials = .userPoolAndIdentityPool(
170+
signedInData: signedInData,
171+
identityID: identityId,
172+
credentials: awsCredentials)
173+
174+
case .identityPoolOnly, .identityPoolWithFederation, .noCredentials:
175+
return
176+
}
177+
178+
try await credentialStoreClient?.storeData(data: .amplifyCredentials(credentials))
179+
} catch KeychainStoreError.itemNotFound {
180+
let logger = (environment as? LoggerProvider)?.logger
181+
logger?.info("No existing credentials found.")
182+
} catch {
183+
let logger = (environment as? LoggerProvider)?.logger
184+
logger?.error("Unable to update credentials with error: \(error)")
185+
}
186+
}
146187
}
147188

148189
extension FetchAuthSessionOperationHelper: DefaultLogger { }

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/CodeGen/Data/SignedInData.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct SignedInData {
1414
let signInMethod: SignInMethod
1515
let deviceMetadata: DeviceMetadata
1616
let cognitoUserPoolTokens: AWSCognitoUserPoolTokens
17+
var isRefreshTokenExpired: Bool?
1718

1819
init(signedInDate: Date,
1920
signInMethod: SignInMethod,
@@ -27,6 +28,7 @@ struct SignedInData {
2728
self.signInMethod = signInMethod
2829
self.deviceMetadata = deviceMetadata
2930
self.cognitoUserPoolTokens = cognitoUserPoolTokens
31+
self.isRefreshTokenExpired = false
3032
}
3133
}
3234

@@ -42,7 +44,8 @@ extension SignedInData: CustomDebugDictionaryConvertible {
4244
"signedInDate": signedInDate,
4345
"signInMethod": signInMethod,
4446
"deviceMetadata": deviceMetadata,
45-
"tokens": cognitoUserPoolTokens
47+
"tokens": cognitoUserPoolTokens,
48+
"refreshTokenExpired": isRefreshTokenExpired ?? false
4649
]
4750
}
4851
}

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Task/AWSAuthFetchSessionTask.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ class AWSAuthFetchSessionTask: AuthFetchSessionTask, DefaultLogger {
1818
HubPayload.EventName.Auth.fetchSessionAPI
1919
}
2020

21-
init(_ request: AuthFetchSessionRequest, authStateMachine: AuthStateMachine) {
21+
init(_ request: AuthFetchSessionRequest,
22+
environment: Environment,
23+
authStateMachine: AuthStateMachine) {
2224
self.request = request
2325
self.authStateMachine = authStateMachine
2426
self.fetchAuthSessionHelper = FetchAuthSessionOperationHelper()
27+
self.fetchAuthSessionHelper.environment = environment
2528
self.taskHelper = AWSAuthTaskHelper(authStateMachine: authStateMachine)
2629
}
2730

0 commit comments

Comments
 (0)