Skip to content

feat(auth): ignore user cancel when session is expired on hostedui sign out #3956

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,49 @@ struct InitiateSignOut: Action {

func execute(withDispatcher dispatcher: EventDispatcher, environment: Environment) async {
logVerbose("\(#fileID) Starting execution", environment: environment)

let updatedSignedInData = await getUpdatedSignedInData(environment: environment)
let event: SignOutEvent
if case .hostedUI(let options) = signedInData.signInMethod,
options.preferPrivateSession == false {
event = SignOutEvent(eventType: .invokeHostedUISignOut(signOutEventData,
signedInData))
updatedSignedInData))
} else if signOutEventData.globalSignOut {
event = SignOutEvent(eventType: .signOutGlobally(signedInData))
event = SignOutEvent(eventType: .signOutGlobally(updatedSignedInData))
} else {
event = SignOutEvent(eventType: .revokeToken(signedInData))
event = SignOutEvent(eventType: .revokeToken(updatedSignedInData))
}
logVerbose("\(#fileID) Sending event \(event.type)", environment: environment)
await dispatcher.send(event)
}

private func getUpdatedSignedInData(
environment: Environment
) async -> SignedInData {
let credentialStoreClient = (environment as? AuthEnvironment)?.credentialsClient
do {
let data = try await credentialStoreClient?.fetchData(
type: .amplifyCredentials
)
guard case .amplifyCredentials(let credentials) = data else {
return signedInData
}

// Update SignedInData based on credential type
switch credentials {
case .userPoolOnly(let updatedSignedInData):
return updatedSignedInData
case .userPoolAndIdentityPool(let updatedSignedInData, _, _):
return updatedSignedInData
case .identityPoolOnly, .identityPoolWithFederation, .noCredentials:
return signedInData
}
} catch {
let logger = (environment as? LoggerProvider)?.logger
logger?.error("Unable to update credentials with error: \(error)")
return signedInData
}
}

}

extension InitiateSignOut: CustomDebugDictionaryConvertible {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ class ShowHostedUISignOut: NSObject, Action {
inPrivate: false,
presentationAnchor: signOutEvent.presentationAnchor)
await sendEvent(with: nil, dispatcher: dispatcher, environment: environment)
} catch HostedUIError.cancelled {
if signInData.isRefreshTokenExpired == true {
self.logVerbose("\(#fileID) Received user cancelled error, but session is expired and continue signing out.", environment: environment)
await sendEvent(with: nil, dispatcher: dispatcher, environment: environment)
} else {
self.logVerbose("\(#fileID) Received error \(HostedUIError.cancelled)", environment: environment)
await sendEvent(with: HostedUIError.cancelled, dispatcher: dispatcher, environment: environment)
}
} catch {
self.logVerbose("\(#fileID) Received error \(error)", environment: environment)
await sendEvent(with: error, dispatcher: dispatcher, environment: environment)
Expand Down Expand Up @@ -101,4 +109,3 @@ extension ShowHostedUISignOut {
debugDictionary.debugDescription
}
}
//#endif
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@

import Foundation
import Amplify
import AWSPluginsCore

class FetchAuthSessionOperationHelper {

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

func fetch(_ authStateMachine: AuthStateMachine,
forceRefresh: Bool = false) async throws -> AuthSession {
Expand Down Expand Up @@ -98,7 +100,7 @@ class FetchAuthSessionOperationHelper {
case .sessionEstablished(let credentials):
return credentials.cognitoSession
case .error(let authorizationError):
return try sessionResultWithError(
return try await sessionResultWithError(
authorizationError,
authenticationState: authenticationState)
default: continue
Expand All @@ -111,7 +113,7 @@ class FetchAuthSessionOperationHelper {
func sessionResultWithError(
_ error: AuthorizationError,
authenticationState: AuthenticationState
) throws -> AuthSession {
) async throws -> AuthSession {
log.verbose("Received fetch auth session error - \(error)")

var isSignedIn = false
Expand All @@ -129,8 +131,10 @@ class FetchAuthSessionOperationHelper {
authError = fetchError.authError
}
case .sessionExpired(let error):
await setRefreshTokenExpiredInSignedInData()
let session = AuthCognitoSignedInSessionHelper.makeExpiredSignedInSession(
underlyingError: error)

return session
default:
break
Expand All @@ -143,6 +147,43 @@ class FetchAuthSessionOperationHelper {
cognitoTokensResult: .failure(authError))
return session
}

func setRefreshTokenExpiredInSignedInData() async {
let credentialStoreClient = (environment as? AuthEnvironment)?.credentialsClient
do {
let data = try await credentialStoreClient?.fetchData(
type: .amplifyCredentials
)
guard case .amplifyCredentials(var credentials) = data else {
return
}

// Update SignedInData based on credential type
switch credentials {
case .userPoolOnly(var signedInData):
signedInData.isRefreshTokenExpired = true
credentials = .userPoolOnly(signedInData: signedInData)

case .userPoolAndIdentityPool(var signedInData, let identityId, let awsCredentials):
signedInData.isRefreshTokenExpired = true
credentials = .userPoolAndIdentityPool(
signedInData: signedInData,
identityID: identityId,
credentials: awsCredentials)

case .identityPoolOnly, .identityPoolWithFederation, .noCredentials:
return
}

try await credentialStoreClient?.storeData(data: .amplifyCredentials(credentials))
} catch KeychainStoreError.itemNotFound {
let logger = (environment as? LoggerProvider)?.logger
logger?.info("No existing credentials found.")
} catch {
let logger = (environment as? LoggerProvider)?.logger
logger?.error("Unable to update credentials with error: \(error)")
}
}
}

extension FetchAuthSessionOperationHelper: DefaultLogger { }
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct SignedInData {
let signInMethod: SignInMethod
let deviceMetadata: DeviceMetadata
let cognitoUserPoolTokens: AWSCognitoUserPoolTokens
var isRefreshTokenExpired: Bool?

init(signedInDate: Date,
signInMethod: SignInMethod,
Expand All @@ -27,6 +28,7 @@ struct SignedInData {
self.signInMethod = signInMethod
self.deviceMetadata = deviceMetadata
self.cognitoUserPoolTokens = cognitoUserPoolTokens
self.isRefreshTokenExpired = false
}
}

Expand All @@ -42,7 +44,8 @@ extension SignedInData: CustomDebugDictionaryConvertible {
"signedInDate": signedInDate,
"signInMethod": signInMethod,
"deviceMetadata": deviceMetadata,
"tokens": cognitoUserPoolTokens
"tokens": cognitoUserPoolTokens,
"refreshTokenExpired": isRefreshTokenExpired ?? false
]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class AWSAuthFetchSessionTask: AuthFetchSessionTask, DefaultLogger {
self.request = request
self.authStateMachine = authStateMachine
self.fetchAuthSessionHelper = FetchAuthSessionOperationHelper()
self.fetchAuthSessionHelper.environment = environment
self.taskHelper = AWSAuthTaskHelper(authStateMachine: authStateMachine)
self.configuration = configuration
self.forceReconfigure = forceReconfigure
Expand Down
Loading