Skip to content
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

401 Unauthorized Errors Due to missing token in Amplify iOS SDK (Version 2.33.6) #3926

Open
jerfranco-deloitte opened this issue Dec 2, 2024 · 11 comments
Labels
pending-maintainer-response Issue is pending response from an Amplify team member pending-triage Issue is pending triage question General question

Comments

@jerfranco-deloitte
Copy link

jerfranco-deloitte commented Dec 2, 2024

Describe the bug

We are experiencing intermittent 401 Unauthorized errors in our iOS app using AWS Amplify SDK version 2.33.6. The issue seems to be affecting some users but not all, and we have been unable to replicate the problem internally. The errors appear to be related to missing or invalid tokens, with most server logs indicating missing tokens as the primary cause.

Steps To Reproduce

1. Implement authentication using AWS Amplify and Cognito in an iOS app.
2. Use the fetchAuthSession() method to get tokens which will be use as bearer token for api endpoints.
3. After some time, observe that API requests result in 401 errors due to expired tokens.

Additional note:
One user reported that he/she encountered this issue right after updating the myVicroads app to the latest version. However, the user also said he/she hadn't logged in for a while before updating the app.

Expected behavior

The fetchAuthSession() method should automatically refresh expired tokens or provide valid tokens, preventing 401 errors due to missing or invalid tokens.

Amplify Framework Version

2.33.6

Amplify Categories

Auth

Dependency manager

Swift PM

Swift version

5

CLI version

2

Xcode version

16.1

Relevant log output

<details>
<summary>Log Messages</summary>

Invalid tokens

2024-11-28T00:41:51.994Z  b6d1358e-928f-4e93-bd74-d432af989c25  trce  [Information] Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Bearer was not authenticated. Failure message: IDX10223: Lifetime validation failed. The token is expired. ValidTo: 'System.DateTime', Current time: 'System.DateTime'.

2024-11-28T00:42:16.660Z  b4e2bc2f-f221-47a9-95b7-364a8b2e949f  trce  [Information] Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Failed to validate the token.

Missing tokens

2024-11-28T01:28:45.573Z  2f3cd8e2-e043-4032-82d2-7442cc63335e  trce  [Information] Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Authorization failed. These requirements were not met:

DenyAnonymousAuthorizationRequirement: Requires an authenticated user.

```

Is this a regression?

Yes

Regression additional context

No response

Platforms

iOS

OS Version

18.1, 17.4

Device

iPhone 16, iPhone 12

Specific to simulators

No response

Additional context

  • I have reviewed the changes in version 2.39.0 (#3827), which include fixes related to authentication and session management.
  • The problem might be addressed by these updates, but I am seeking confirmation before upgrading.
  • Attached is a snippet of my AmplifyAuthManager class implementation for reference.
public actor AmplifyAuthManager: AuthManagerProtocol, TokenProvider {
    let auth: AuthCategory
    
    public static let manager = AmplifyAuthManager()
    
    private init() {
        do {
            try Amplify.add(plugin: AWSCognitoAuthPlugin())
            try Amplify.configure()
        } catch {
            print("Failed to initialize Amplify with \(error)")
        }
        
        auth = Amplify.Auth
#if DEBUG
        Amplify.Logging.logLevel = .verbose
#endif
    }
    
    public func signOut() async -> Result<AuthStatus, GenericAPIError> {
        do {
            guard try await auth.fetchAuthSession().isSignedIn else {
                return .success(.signedOut)
            }
            
            let signOutState = await auth.signOut()
            guard let signOutResult = signOutState as? AWSCognitoSignOutResult else {
                return .failure(.other("Sign out failed")) 
            }
            switch signOutResult {
            case .complete:
                return .success(.signedOut)
            case .partial(revokeTokenError: let revokeTokenError,
                          globalSignOutError: _,
                          hostedUIError: _):
                return .failure(.other(String(describing: revokeTokenError)))
            case .failed(let error):
                return .failure(.authError(error.toLoginError()))
            }
        } catch {
            debugPrint("AmplifyAuthManager signOut failed")
            debugPrint(error.localizedDescription)
            return .failure(.other(""))
        }
    }
    
    func fetchAuthSession() async -> Result<AuthStatus, GenericAPIError> {
        do {
            let authSession = try await auth.fetchAuthSession()
            let tokens = try getTokens(authSession as? AuthCognitoTokensProvider)
            if authSession.isSignedIn, let _ = tokens {
                return .success(.signedIn)
            } else {
                return await signOut() 
            }
        } catch let authError as AuthError {
            return .failure(.authError(authError.toLoginError()))
        } catch {
            return .failure(.someThingWentWrong(error))
        }
    }

    private func getTokens(_ tokenProvider: AuthCognitoTokensProvider?) throws -> AuthCognitoTokens? {
        try tokenProvider?.getCognitoTokens().get()
    }
}
@github-actions github-actions bot added pending-triage Issue is pending triage pending-maintainer-response Issue is pending response from an Amplify team member labels Dec 2, 2024
@tylerjroach
Copy link
Member

tylerjroach commented Dec 2, 2024

However, the user also said he/she hadn't logged in for a while before updating the app

How long do you think a while is? Is it possible the refresh token expired? How long are refresh token's set to be valid on your account? Once a refresh token is expired, the user will have to logout and back in.

I do not believe the referenced issue you pointed to would be related.

@tylerjroach tylerjroach added the question General question label Dec 2, 2024
@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 2, 2024
@jerfranco-deloitte
Copy link
Author

@tylerjroach we set the validity of the refresh token to 10 years and the access token to 15 minutes. Our app has only been live for more than a year, so we expect the user to remain logged in even after they update the app.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 4, 2024
@harsh62
Copy link
Member

harsh62 commented Dec 4, 2024

@jerfranco-deloitte Some of the cases where a refresh token could be invalidated are:

  • You're using a different app client ID than the one that issued the refresh token
  • Your device tracking is turned on in Cognito User Pools

I have a few other questions:

  • Can you share you configuration file redacted all the sensitive information? Also are you using clientSecret?
  • Did it start happening suddenly? Did something change recently?
  • Is it happening for certain type of users? Users logging in via email or phone?

One user reported that he/she encountered this issue right after updating the myVicroads app to the latest version. However, the user also said he/she hadn't logged in for a while before updating the app.

Did this also include an update the Amplify library?

Any other info you could provide that could narrow down the investigation?

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 4, 2024
@harish-suthar
Copy link

harish-suthar commented Dec 8, 2024

@jerfranco-deloitte Can you try a small change and see if that works?

func fetchAuthSession() async -> Result<AuthStatus, GenericAPIError> {
  do {
      let authSession = try await auth.fetchAuthSession()
      let tokens = try getTokens(authSession as? AuthCognitoTokensProvider)
      if authSession.isSignedIn, let _ = tokens {
          return .success(.signedIn)
      } else {
          return await signOut() 
      }
  } catch let authError as AuthError {
      return .failure(.authError(authError.toLoginError()))
  } catch {
      return .failure(.someThingWentWrong(error))
  }
}

Just change your above code to something as below and see if that resolved the issue?

func fetchAuthSession() async -> Result<AuthStatus, GenericAPIError> {
  do {
      let authSession = try await auth.fetchAuthSession()
      if authSession.isSignedIn {
          let tokens = try getTokens(authSession as? AuthCognitoTokensProvider)
          if let _ = tokens {
              return .success(.signedIn)
          } else {
              return await signOut() 
          }
      } else {
          return await signOut() 
      }
  } catch let authError as AuthError {
      return .failure(.authError(authError.toLoginError()))
  } catch {
      return .failure(.someThingWentWrong(error))
  }
}

Looking forward to your feedback, thanks.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 8, 2024
@harsh62 harsh62 removed the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 9, 2024
@jerfranco-deloitte
Copy link
Author

@harish-suthar, here are the answers to your questions:

  • You're using a different app client ID than the one that issued the refresh token

We're using the same app client ID

  • Your device tracking is turned on in Cognito User Pools

Device tracking is turned off in Cognito User Pools

  • Can you share you configuration file redacted all the sensitive information? Also are you using clientSecret?

Here's my configuration file:

{
    "UserAgent": "aws-amplify-cli/2.0",
    "Version": "1.0",
    "auth": {
        "plugins": {
            "awsCognitoAuthPlugin": {
                "UserAgent": "aws-amplify-cli/0.1.0",
                "Version": "0.1.0",
                "IdentityManager": {
                    "Default": {}
                },
                "CredentialsProvider": {
                    "CognitoIdentity": {
                        "Default": {
                            "PoolId": "ap-southeast-2:REDACTED",
                            "Region": "ap-southeast-2"
                        }
                    }
                },
                "CognitoUserPool": {
                    "Default": {
                        "PoolId": "ap-southeast-2_REDACTED",
                        "AppClientId": "REDACTED",
                        "Region": "ap-southeast-2",
                    }
                }
            }
        }
    }
}
  • Did it start happening suddenly? Did something change recently?

This issue started to happen after we upgraded to 2.33.6

  • Is it happening for certain type of users? Users logging in via email or phone?

Users can log into the app via email and password, but we still can't determine which type of users are encountering this issue.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 10, 2024
@harsh62
Copy link
Member

harsh62 commented Dec 10, 2024

@jerfranco-deloitte Are you somehow able to grab the verbose logs when this issue happens?

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 10, 2024
@harish-suthar
Copy link

@harsh62 How do you access amplify verbose logs?

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 10, 2024
@harsh62
Copy link
Member

harsh62 commented Dec 10, 2024

You can enable verbose logging to the console by doing this before calling Amplify.configure:

Amplify.Logging.logLevel = .verbose

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 10, 2024
@harish-suthar
Copy link

You can enable verbose logging to the console by doing this before calling Amplify.configure:

Amplify.Logging.logLevel = .verbose

@harsh62 I mean is there any way I can send these verbose logs to a logging service, to see the production logs as this issue is not reproducible easily and we are getting reports of this from production app for some users.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 10, 2024
@harsh62
Copy link
Member

harsh62 commented Dec 10, 2024

You will need to setup the logging category to send the logs to AWSCloudWatch. The setup guide is available here: https://docs.amplify.aws/swift/build-a-backend/add-aws-services/logging/set-up-logging/.

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 10, 2024
@jerfranco-deloitte
Copy link
Author

You will need to setup the logging category to send the logs to AWSCloudWatch. The setup guide is available here: https://docs.amplify.aws/swift/build-a-backend/add-aws-services/logging/set-up-logging/.

@harish-suthar @harsh62 Thanks for the advice. This might help since we can't easily reproduce the production issue in our non-prod environments. @harsh62, do we need to set Amplify.Logging.logLevel = .verbose for this logging setup?

@github-actions github-actions bot added the pending-maintainer-response Issue is pending response from an Amplify team member label Dec 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pending-maintainer-response Issue is pending response from an Amplify team member pending-triage Issue is pending triage question General question
Projects
None yet
Development

No branches or pull requests

4 participants