Open ostanik opened 6 months ago
@ostanik Thanks for opening up the issue. From the logs that you have shared, it don't see anything that should cause session being dumped. What I have observed from the logs is that a logged out user is trying to make fetchAuthSession
requests. I don't see session being dumped.
Furthermore, Amplify never logs out the user by itself, even if the session has expired. The only occasion session is removed from keychain, is when Amplify determines that there was a change in user pool configuration.
To further investigate, I would need more concrete details of session dump that was observed. Either exact flow of the Amplify API calls or verbose logs when the issue occurred.
Lastly, the code snippet that you shared should not cause any issues, when retrieving the session any amount of times.
@harsh62 , Thank you for your feedback. I understand there's no clear indication of session dumping from the logs. However, we're observing sessions disappearing unexpectedly, with users being treated as logged out without any action on their part or an explicit logout call.
Clarifications:
fetchAuthSession
call, which then appears as if a logged-out user is trying to fetch a session.We're also encountering unexpected session expirations, which will be addressed separately.
Given these challenges, any further insights or advice would be greatly appreciated as we aim to resolve this perplexing issue.
Thanks for providing more context. Few more questions based on your answers:
find themselves without session data in the keychain, prompting a fetchAuthSession call, which then appears as if a logged-out user is trying to fetch a session
Authentication Flow: Our flow involves
Would you be able to provide more code snippets and surrounding code that could impact how Amplify works?
The new information you provide, could may be help me with a direction to investigate the issue you are seeing.
Conclusion Basis: The conclusion about session data absence in the keychain was reached through extensive testing with multiple simulators. By debugging the Amplify SDK during a scenario where a user encountered the issue, it was observed that the signedOut
event triggers due to an empty keychain where Amplify expects to find session data.
Keychain Clarification: The reference to "keychain" pertains specifically to the storage managed by Amplify (i.e., AWSCognitoAuthCredentialStore.swift). In this context, the Amplify-managed keychain and the app's keychain effectively serve the same purpose, with Amplify utilizing the app's keychain space for its data storage.
Code Snippet for Auth handling:
nit(_ env: ConfigurationEnvironment) {
Amplify.Logging.logLevel = env == .staging ? .verbose : .none
// This is used only to capture traces of auth events
unsubscribeToken = Amplify.Hub.listen(to: .auth) { payload in
AppLogger.shared.debug("Received \(payload.eventName) event from Amplify: \nData: \(String(describing: payload.data))")
}
}
deinit {
Amplify.Hub.removeListener(unsubscribeToken)
}
/// Start the passwordless authentication process
/// - Parameters:
/// - email: The email address to send the magic link to
/// - authType: The type of authentication to perform
/// > Note: This function will retry at most one time if the first attempt fails with a `invalidState` error.
/// This retry is required in order to "request a new link" feature works properly
func startPasswordlessAuth(email: String, authType: PasswordlessAuthType) -> Future<Void, LoginFeature.RequestLinkError> {
AppLogger.shared.debug("Passwordless Auth started")
return Future { promise in
Task { [weak self] in
var hasRetried = false // Flag to track whether a retry has been attempted
func attemptRequest() async {
do {
guard let self = self else {
AppLogger.shared.debug("Start Passwordless Auth: self is nil")
throw LoginFeature.RequestLinkError.authServiceUnavailable
}
try self.configureAmplifyIfNeeded()
// Forcing email to be lower because the user email was configured to be case sensitive
// and BE is lowering all the email addresses (we hope).
_ = try await self.signin(email: email.lowercased())
AppLogger.shared.debug("Passwordless Auth requested")
promise(.success(()))
} catch let error as AuthError {
if case .invalidState = error, !hasRetried {
AppLogger.shared.debug("Invalid state error occurred, attempting retry")
hasRetried = true
await Amplify.Auth.signOut()
await attemptRequest() // Retry request after sign out
} else {
AppLogger.shared.error("Start Passwordless Auth: \(error.debugDescription)", error: error)
promise(.failure(.other))
}
} catch {
AppLogger.shared.error("Start Passwordless Auth: \(error.localizedDescription)", error: error)
promise(.failure(.other))
}
}
await attemptRequest() // Initial call to the request function
}
}
}
func login(code: String) -> Future<Void, Error> {
AppLogger.shared.debug("Claim login code started")
return Future { promise in
Task { [weak self] in
guard let self else {
AppLogger.shared.debug("Claim login code: self is nil")
return promise(.failure(RequestLinkError.authServiceUnavailable))
}
do {
try self.configureAmplifyIfNeeded()
let signInResult = try await Amplify.Auth.confirmSignIn(challengeResponse: code)
AppLogger.shared.debug("Confirm sign in succeeded. Next step: \(signInResult.nextStep)")
// Fetch session right after login to avoid concurrency issues.
// this is a defensive way of working...Should we remove it?
let session = try await Amplify.Auth.fetchAuthSession()
guard let cognitoTokenProvider = session as? AuthCognitoTokensProvider else {
throw RequestLinkError.other
}
_ = try cognitoTokenProvider.getCognitoTokens().get()
AppLogger.shared.debug("Claim login code success")
promise(.success(()))
} catch let error as AuthError {
AppLogger.shared.error("Claim login code: \(error.debugDescription)", error: error)
promise(.failure(error))
} catch {
AppLogger.shared.error("Claim login code: \(error.localizedDescription)", error: error)
promise(.failure(error))
}
}
}
}
func obtainRenewedCredentialsIfNeeded() async -> Bool {
do {
try self.configureAmplifyIfNeeded()
_ = try await fetchCredentials().eraseToAnyPublisher().async()
AppLogger.shared.debug("Obtain renewed Credentials if needed: success - Has Valid Credentials: \(hasValidCredentials)")
return hasValidCredentials
} catch let error as AuthError {
AppLogger.shared.error("Obtain renewed Credentials if needed: \(error.debugDescription)", error: error)
} catch {
AppLogger.shared.error("Obtain renewed Credentials if needed: \(error.localizedDescription)", error: error)
}
return false
}
func fetchCredentials() -> Future<String, Error> {
self.printKeychainItems()
AppLogger.shared.debug("Fetch Credentials started")
return Future { promise in
Task { [weak self] in
guard let self else {
AppLogger.shared.debug("Fetch Credentials: self is nil")
return promise(.failure(RequestLinkError.authServiceUnavailable))
}
do {
try self.configureAmplifyIfNeeded()
let session = try await Amplify.Auth.fetchAuthSession()
if let cognitoTokenProvider = session as? AuthCognitoTokensProvider {
let tokens = try cognitoTokenProvider.getCognitoTokens().get()
AppLogger.shared.debug("Fetch Credentials success")
self.hasValidCredentials = true
promise(.success((tokens.idToken)))
} else {
throw RequestLinkError.other
}
} catch let error as AuthError {
AppLogger.shared.error("Fetch Credentials failed: \(error.debugDescription)", error: error)
switch error {
case .sessionExpired, .signedOut:
AppLogger.shared.debug("Session expired or user signed out, changing valid credentials to false")
self.hasValidCredentials = false
default: break
}
promise(.failure(error))
} catch let error as ConfigurationError {
AppLogger.shared.error("Fetch credentials failed after retry: \(error.debugDescription)", error: error)
promise(.failure(error))
} catch {
AppLogger.shared.error("Fetch Credentials failed: \(error.localizedDescription)", error: error)
promise(.failure(error))
}
}
}
}
// Change this to be async
func clearCredentials() {
Task { [weak self] in
guard let self else {
AppLogger.shared.debug("Clear Credentials: self is nil")
return
}
AppLogger.shared.debug("Cleaning credentials")
do {
// Performing sign out without configure the SDK will cause a crash.
try self.configureAmplifyIfNeeded()
_ = await Amplify.Auth.signOut()
self.hasValidCredentials = false
} catch let error as ConfigurationError {
AppLogger.shared.error("Sign out error \(error.debugDescription)", error: error)
} catch {
AppLogger.shared.error("Sign out error \(error.localizedDescription)", error: error)
}
}
}
/// This method is used to configure Amplify with the Cognito plugin.
/// Amplify configuration should only be called once otherwise it will thrown an error "amplifyAlreaadyConfigured"
/// But on the catching of this function we are handling this error to return and not rethrows. So it should be safe to call this function multiple times.
private func configureAmplifyIfNeeded() throws {
do {
// Creating plugin network preferences to retry 3 times before fail
let cognitoNetworkPreferences = AWSCognitoNetworkPreferences(
maxRetryCount: 3,
timeoutIntervalForRequest: .seconds(60),
timeoutIntervalForResource: .days(7)
)
try Amplify.add(plugin: AWSCognitoAuthPlugin(networkPreferences: cognitoNetworkPreferences))
let userPoolId = BuildSetting(type: .cognitoUserPoolID).value
let clientId = BuildSetting(type: .cognitoClientID).value
let region = BuildSetting(type: .cognitoRegion).value
let configuration = AmplifyConfiguration(
auth: AuthCategoryConfiguration(
plugins: [
"awsCognitoAuthPlugin": [
"IdentityManager": [
"Default": []
],
"CognitoUserPool": [
"Default": [
"PoolId": .string(userPoolId),
"Region": .string(region),
"AppClientId": .string(clientId)
]
],
"Auth": [
"Default": [
"authenticationFlowType": "CUSTOM_AUTH_WITHOUT_SRP"
]
]
]
]
)
)
try Amplify.configure(configuration)
AppLogger.shared.debug("Amplify is configured")
} catch let error as ConfigurationError {
if case .amplifyAlreadyConfigured = error {
AppLogger.shared.debug("Amplify configuration: amplify already configured skipping")
return
}
// Can we only use the configure amplify method once we are not throwing if already configured?
AppLogger.shared.error("Amplify configuration: \(error.debugDescription)", error: error)
throw error
} catch {
AppLogger.shared.error("Amplify configuration: \(error.localizedDescription)", error: error)
throw error
}
}
private func signin(email: String) async throws -> AuthSignInResult {
let customWithoutSRP = AWSAuthSignInOptions(authFlowType: .customWithoutSRP)
let options = AuthSignInRequest.Options(pluginOptions: customWithoutSRP)
return try await Amplify.Auth.signIn(username: email, options: options)
}
If there are specific areas of this code or the authentication flow you'd like more details on, or if there are specific concerns, I'd be happy to provide further explanations or adjust our approach based on your guidance.
I don't know if this helps but here is one scenario from a real user who was authenticated before but right after an app update, got the error:
# Crashlytics - Custom logs
# Platform: apple
# Version: 1.105.0 (6648)
# Date: Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time)
0 | Thu Feb 29 2024 09:03:56 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:56:684 {REDACT}:35 - Updated user in the local data base
1 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:294 {REDACT}:64 - Starting refresh identity flow
2 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:306 CognitoServiceProd:133 - Fetch Credentials started
3 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:373 CognitoServiceProd:238 - Amplify is configured
4 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:374 CognitoServiceProd:28 - Received Amplify.configured event from Amplify:
Data: nil
5 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:461 CognitoServiceProd:28 - Received Auth.fetchSessionAPI event from Amplify:
Data: Optional(Swift.Result<Amplify.AuthSession, Amplify.AuthError>.success({
awsCredentialsError = "AuthError: Unexpected error occurred with message: Unknown error occurred\nRecovery suggestion: This should not happen. There is a possibility that there is a bug if this error persists. Please take a look at https://github.com/aws-amplify/amplify-swift/issues to see if there are any existing issues that match your scenario, and file an issue with the details of the bug if there isn't.";
cognitoTokensError = "AuthError: Unexpected error occurred with message: Unknown error occurred\nRecovery suggestion: This should not happen. There is a possibility that there is a bug if this error persists. Please take a look at https://github.com/aws-amplify/amplify-swift/issues to see if there are any existing issues that match your scenario, and file an issue with the details of the bug if there isn't.";
identityIdError = "AuthError: Unexpected error occurred with message: Unknown error occurred\nRecovery suggestion: This should not happen. There is a possibility that there is a bug if this error persists. Please take a look at https://github.com/aws-amplify/amplify-swift/issues to see if there are any existing issues that match your scenario, and file an issue with the details of the bug if there isn't.";
isSignedIn = false;
userSubError = "AuthError: Unexpected error occurred with message: Unknown error occurred\nRecovery suggestion: This should not happen. There is a possibility that there is a bug if this error persists. Please take a look at https://github.com/aws-amplify/amplify-swift/issues to see if there are any existing issues that match your scenario, and file an issue with the details of the bug if there isn't.";
}))
6 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:466 CognitoServiceProd:154 - Fetch Credentials failed: unkown error - description: Unknown error occurred: The operation couldn’t be completed. (Amplify.AuthError error 2.)
7 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:804 CognitoServiceProd:133 - Fetch Credentials started
8 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:806 CognitoServiceProd:241 - Amplify configuration: amplify already configured skipping
9 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:811 CognitoServiceProd:154 - Fetch Credentials failed: signedOut error - description: There is no user signed in to retreive cognito tokens, recoverySuggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession: The operation couldn’t be completed. (Amplify.AuthError error 6.)
10 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:811 CognitoServiceProd:28 - Received Auth.fetchSessionAPI event from Amplify:
Data: Optional(Swift.Result<Amplify.AuthSession, Amplify.AuthError>.success({
awsCredentialsError = "AuthError: There is no user signed in to retreive AWS credentials\nRecovery suggestion: Call Auth.signIn to sign in a user or enable unauthenticated access in AWS Cognito Identity Pool\nCaused by:\ninvalidAccountTypeException";
cognitoTokensError = "AuthError: There is no user signed in to retreive cognito tokens\nRecovery suggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession";
identityIdError = "AuthError: There is no user signed in to retreive identity id\nRecovery suggestion: Call Auth.signIn to sign in a user or enable unauthenticated access in AWS Cognito Identity Pool\nCaused by:\ninvalidAccountTypeException";
isSignedIn = false;
userSubError = "AuthError: There is no user signed in to retreive user sub\nRecovery suggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession";
}))
11 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:812 CognitoServiceProd:157 - Session expired or user signed out, changing valid credentials to false
12 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:814 {REDACT}:102 - Get user info succeeded
13 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:814 {REDACT}:139 - The persisted user has differet policy location as the fetched one
14 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:816 {REDACT}:151 - Saving user: {REDACT}
15 | Thu Feb 29 2024 09:03:57 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:57:829 {REDACT}:35 - Updated user in the local data base
16 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:095 {REDACT}:200 - Did receive value on refresh identity, starting initial flow
17 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:099 {REDACT}:226 - User logged in, starting logged in flow
18 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:100 {REDACT}:425 - User is allowed to access the app, continuing flow
19 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:185 CognitoServiceProd:133 - Fetch Credentials started
20 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:186 CognitoServiceProd:241 - Amplify configuration: amplify already configured skipping
21 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:187 CognitoServiceProd:28 - Received Auth.fetchSessionAPI event from Amplify:
Data: Optional(Swift.Result<Amplify.AuthSession, Amplify.AuthError>.success({
awsCredentialsError = "AuthError: There is no user signed in to retreive AWS credentials\nRecovery suggestion: Call Auth.signIn to sign in a user or enable unauthenticated access in AWS Cognito Identity Pool\nCaused by:\ninvalidAccountTypeException";
cognitoTokensError = "AuthError: There is no user signed in to retreive cognito tokens\nRecovery suggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession";
identityIdError = "AuthError: There is no user signed in to retreive identity id\nRecovery suggestion: Call Auth.signIn to sign in a user or enable unauthenticated access in AWS Cognito Identity Pool\nCaused by:\ninvalidAccountTypeException";
isSignedIn = false;
userSubError = "AuthError: There is no user signed in to retreive user sub\nRecovery suggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession";
}))
22 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:187 CognitoServiceProd:154 - Fetch Credentials failed: signedOut error - description: There is no user signed in to retreive cognito tokens, recoverySuggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession: The operation couldn’t be completed. (Amplify.AuthError error 6.)
23 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:188 CognitoServiceProd:157 - Session expired or user signed out, changing valid credentials to false
24 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:211 CognitoServiceProd:133 - Fetch Credentials started
25 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:211 CognitoServiceProd:241 - Amplify configuration: amplify already configured skipping
26 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:213 CognitoServiceProd:154 - Fetch Credentials failed: signedOut error - description: There is no user signed in to retreive cognito tokens, recoverySuggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession: The operation couldn’t be completed. (Amplify.AuthError error 6.)
27 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:213 CognitoServiceProd:28 - Received Auth.fetchSessionAPI event from Amplify:
Data: Optional(Swift.Result<Amplify.AuthSession, Amplify.AuthError>.success({
awsCredentialsError = "AuthError: There is no user signed in to retreive AWS credentials\nRecovery suggestion: Call Auth.signIn to sign in a user or enable unauthenticated access in AWS Cognito Identity Pool\nCaused by:\ninvalidAccountTypeException";
cognitoTokensError = "AuthError: There is no user signed in to retreive cognito tokens\nRecovery suggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession";
identityIdError = "AuthError: There is no user signed in to retreive identity id\nRecovery suggestion: Call Auth.signIn to sign in a user or enable unauthenticated access in AWS Cognito Identity Pool\nCaused by:\ninvalidAccountTypeException";
isSignedIn = false;
userSubError = "AuthError: There is no user signed in to retreive user sub\nRecovery suggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession";
}))
28 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:213 CognitoServiceProd:157 - Session expired or user signed out, changing valid credentials to false
29 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:372 {REDACT}:196 - Refresh identity finished
30 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:841 CognitoServiceProd:241 - Amplify configuration: amplify already configured skipping
31 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:841 CognitoServiceProd:133 - Fetch Credentials started
32 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:841 CognitoServiceProd:241 - Amplify configuration: amplify already configured skipping
33 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:843 CognitoServiceProd:154 - Fetch Credentials failed: signedOut error - description: There is no user signed in to retreive cognito tokens, recoverySuggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession: The operation couldn’t be completed. (Amplify.AuthError error 6.)
34 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:843 CognitoServiceProd:28 - Received Auth.fetchSessionAPI event from Amplify:
Data: Optional(Swift.Result<Amplify.AuthSession, Amplify.AuthError>.success({
awsCredentialsError = "AuthError: There is no user signed in to retreive AWS credentials\nRecovery suggestion: Call Auth.signIn to sign in a user or enable unauthenticated access in AWS Cognito Identity Pool\nCaused by:\ninvalidAccountTypeException";
cognitoTokensError = "AuthError: There is no user signed in to retreive cognito tokens\nRecovery suggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession";
identityIdError = "AuthError: There is no user signed in to retreive identity id\nRecovery suggestion: Call Auth.signIn to sign in a user or enable unauthenticated access in AWS Cognito Identity Pool\nCaused by:\ninvalidAccountTypeException";
isSignedIn = false;
userSubError = "AuthError: There is no user signed in to retreive user sub\nRecovery suggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession";
}))
35 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:844 CognitoServiceProd:157 - Session expired or user signed out, changing valid credentials to false
36 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:845 CognitoServiceProd:125 - Obtain renewed Credentials if needed: signedOut error - description: There is no user signed in to retreive cognito tokens, recoverySuggestion: Call Auth.signIn to sign in a user and then call Auth.fetchSession: The operation couldn’t be completed. (Amplify.AuthError error 6.)
37 | Thu Feb 29 2024 09:03:58 GMT+0000 (Western European Standard Time) | 2024/02/29 09:03:58:899 {REDACT}:125 - Starting reauthentication - {REDACT}
@harsh62 does these log tell you anything? I am just wandering if you are able to spot any problem. Thanks for your help.
@Shilaghae @ostanik
I don't see anything in the logs other than that there no session. It would need to be verbose logs, which could help us in figuring out what is going on.
I also looked at your code, and one thing that popped out was configureAmplifyIfNeeded
method. Ideally you would only need configure Amplify at app launch and then thats it. It would not be a best practice to reconfigure Amplify before each Amplify API call (Although I see that you are catching the error). So I would suggest to move your configuration to app launch and then use the API's without calling configure first.
The other thing that I see is that you are building the configuration directly in the code. While it is possible to build a configuration this way, the recommended way would be to use an amplifyconfiguration.json
file to setup Amplify. Furthermore, are you using this way of setting up Amplify to manage different build environments in the same app?
Lastly, I would like to understand how clearCredentials()
is being used within your app.
@ostanik Also, is this error only happening to users who update the app? If yes, was there a config change that happened between versions?
@harsh62
We added this lazy initialization cause we assumed Amplify.configure
is making a network call, which might be failing. This assumption comes from the error in Crashlytics from the Android side where we saw this error
In case Amplify.configure
for iOS doesn't require network, I think it's fine to configure it at app launch.
We're building amplify configuration at runtime from build settings which depend on selected environment (staging/production).
clearCredentials()
is called when user requests log out from the app or if user is not permitted to use the app anymore. None of these seem to be the reason of logouts we experience.
@ostanik Also, is this error only happening to users who update the app? If yes, was there a config change that happened between versions?
We cannot confirm this error only happening to users who update the app. We have some reports that this error happened after an update, but as far as we know this error happens randomly when user launch the app.
However, we can confirm there was no configuration change recently.
Thank you - We will investigate and post followup questions here.
@victorkifer We are not able to reproduce this issue at our end. Would you be able to assist with any additional details that could help us further investigate the issue?
Hey @harsh62, Answering your questions:
The user cannot switch configurations at runtime. Configurations are defined at compile time. We only define configurations this way to take advantage of our different environments via xconfig
instead of creating scripts to load different JSON files based on the build type.
We use the keychain to store other access tokens or sensitive information. However, when we manipulate the keychain, we are careful to only use our keys (if necessary, to clear the session, for example) instead of dumping all the values that we have. This was my first hypothesis before opening this issue.
We do not share the keychain with a group of apps. Only one app uses the keychain.
I hope this information helps you further investigate the issue.
Please let me know if you have any other questions.
@ostanik Unfortunately I am still not able to repro and not able to find any obvious problems in the code. At this moment, I would like you to reach out to us on discord (https://discord.com/channels/705853757799399426/1019643921137139772) tagging me (my username harsh62
). Once you reach out to us, we can set up a meeting and go through the specifics of the code in more detail.
@harsh62 I can't access the link that you shared.
@ostanik Can you try https://discord.com/invite/amplify?
Hello, this issue sounds like something we're also experiencing, are there any news?
@dandreiolteanu I have been unable to reproduce this issue in a local environment, and also unable to find any obvious code issues with Keychain and State Machine implementation. If you are able to provide any logs or codepaths when you are experiencing this issue, it would be really helpful.
We're also seeing behavior in our app which seems to line up with this issues. I don't have full logs unfortunately, only what I've been able to piece together via analytics.
We have the same testers on iOS and Android, and the code is the same across platforms, but we've only ever seen this issue on iOS.
The issue appears to happen when calling fetchAuthSession right after the device wakes up, and has been asleep for some time.
While that call fails attempting to fetch/refresh the auth token, and with Amplify.Hub.subscribe setup, AuthChannelEventName.SESSION_EXPIRED does not appear to be triggered.
The failure to fetchAuthSession reports: localizedDescription = "The operation couldn’t be completed. (Amplify.AuthError error 1.)" print() = "network(AuthError: Unknown service error occurred Recovery suggestion: See the attached error for mo" (Truncated by analytics)
The only other info I have is a screenshot from a debug build which reports an error of "The network connection was lost." It was attempting to contact a url of "https://cognito-idp.us-west-2.amazonaws.com/".
I don't know how the internal networking is handled, but is the framework attempting to preflight network connectivity before actually making a request?
In general, that goes against the recommended behavior and could lead to an issue where connectivity checking fails because the device radios haven't been powered up yet right after wake.
This may also relate to another closed issue that my coworker pointed out: https://github.com/aws-amplify/amplify-swift/issues/3437
Normally you'd just set waitsForConnectivity and a short timeoutIntervalForResource value for the URLSessionConfiguration and issue any calls without any preflight connectivity checking.
Apologies if this has been investigated previously.
@kparichan Would you be able to open another issue with the relevant details and verbose logs. With the brief description you've give us, your issue seems to be something else, which is different from the session dumping issue that is being discussed here in this one.
Describe the bug
We are encountering issues with Amplify's session management where several users experience their sessions being removed from the keychain following an unexpected error. This results in a 'signedOut' state error when attempting to fetch the auth session from Amplify. The problem does not occur consistently but has been observed under certain conditions.
Steps To Reproduce
Expected behavior
The auth session should be reliably fetched from the keychain without being inadvertently dumped, allowing for uninterrupted user authentication and session management.
Amplify Framework Version
2.26.4
Amplify Categories
Auth
Dependency manager
Swift PM
Swift version
5
CLI version
12.4.0
Xcode version
15.0.1
Relevant log output
Flow:
In some instances, following an unexpected error, the session gets removed from the keychain, and attempting to fetch the auth session results in a 'signedOut' state error.