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

FetchAuthSession error does not provide correct AuthError on refresh token expired #3496

Closed
nonesc opened this issue Feb 5, 2024 · 8 comments
Assignees
Labels
auth Issues related to the Auth category bug Something isn't working

Comments

@nonesc
Copy link

nonesc commented Feb 5, 2024

Describe the bug

My Backend team set up token expiration as followings

  1. Access token: 5 min
  2. Refersh token: 1 hr

FetchAuthSession error does not provide the correct AuthError when refresh token expired

Code I use at the moment

 func fetchToken() async throws -> AmplifyAuthTokens {
        do {
            let session = try await Amplify.Auth.fetchAuthSession(options: .forceRefresh())
            
            if let cognitoTokenProvider = session as? AuthCognitoTokensProvider {
                do {
                    let tokens = try cognitoTokenProvider.getCognitoTokens().get()
                    if BuildConfiguration.isDebug() {
                        print("access: \(tokens.accessToken)")
                        print("idToken: \(tokens.idToken)")
                        print("refreshToken: \(tokens.refreshToken)")
                    }
                    return tokens
                } catch AuthError.sessionExpired {
                    print("Never reach here")
                    throw SpotlikeError.unknown
                } catch {
                    
                    if let authError = error as? AuthError {
                        switch authError {
                        case .configuration(let errorDescription, let recoverySuggestion, let error):
                            print("gggg")

                        case .service(let errorDescription, let recoverySuggestion, let error):
                            print("Reach here instead")

                        case .unknown(let errorDescription, let error):
                            print("gggg")

                        case .validation(let field, let errorDescription, let recoverySuggestion, let error):
                            print("gggg")

                        case .notAuthorized(let errorDescription, let recoverySuggestion, let error):
                            print("gggg")

                        case .invalidState(let errorDescription, let recoverySuggestion, let error):
                            print("gggg")

                        case .signedOut(let errorDescription, let recoverySuggestion, let error):
                            print("gggg")

                        case .sessionExpired(let errorDescription, let recoverySuggestion, let error):
                            print("gggg")
                        }
                    }
                    throw error
                }
            } else {
                throw AmplifyError.unkhown
            }
        } catch {
            throw error
        }
    }

Reference that does not work: #3288

Steps To Reproduce

Steps to reproduce the behavior:
1. Login social providers(google)
2. Wait for 1 hr
3. Try calling FetchAuthSession
4. Received Error `sessionError(AWSCognitoAuthPlugin.FetchSessionError.service(AWSCognitoAuthPlugin.HostedUIError.serviceMessage("invalid_grant ")), userPoolOnly)` instead of `.sessionExpired`

Expected behavior

FetchAuthSession should return AuthError as .sessionExpired

Amplify Framework Version

2.25.6

Amplify Categories

Auth

Dependency manager

Swift PM

Swift version

5

CLI version

BE

Xcode version

15

Relevant log output

<details>
<summary>Log Messages</summary>
Successfully completed execution for Auth.fetchSessionAPI with result:
{
    awsCredentialsError = "AuthError: invalid_grant \nRecovery suggestion: Received an error message from the service";
    cognitoTokensError = "AuthError: invalid_grant \nRecovery suggestion: Received an error message from the service";
    identityIdError = "AuthError: invalid_grant \nRecovery suggestion: Received an error message from the service";
    isSignedIn = true;
    userSubError = "AuthError: invalid_grant \nRecovery suggestion: Received an error message from the service";
}
</details>

Is this a regression?

No

Regression additional context

No response

Platforms

iOS

OS Version

15

Device

iPhone 15

Specific to simulators

No response

Additional context

No response

@harsh62 harsh62 added auth Issues related to the Auth category question General question labels Feb 5, 2024
@harsh62
Copy link
Member

harsh62 commented Feb 5, 2024

@nonesc Thanks for opening the issue.

Could you please provide (code snippets) how you are invoking the signIn and fetchAuthSession API's? Also the steps being used on how the app is being used.

Please also provide amplifyConfiguration.json file redacted all the sensitive information and verbose logs when this error happens.. This would greatly help us debug the issue faster.

@nonesc
Copy link
Author

nonesc commented Feb 5, 2024

Thanks for fast response @harsh62

Step would be

  1. I use socialSignInWithWebUI to invoke webview, so user can sign in.
func socialSignInWithWebUI(provider: AmplifyAuthProvider) async throws -> UserDtoLogin {
       _ = try await AmplifyManager.shared.socialSignInWithWebUI(provider: provider)
       let token = try await AmplifyManager.shared.fetchToken()
       let member = try await userRepo.getMember(payload: UserEndpoint.getMemberSelf(token: token.idToken))
       return member
   }

AmplifyManager.shared.fetchToken()

    func fetchToken() async throws -> AmplifyAuthTokens {
        do {
            let session = try await Amplify.Auth.fetchAuthSession()
            
            if let cognitoTokenProvider = session as? AuthCognitoTokensProvider {
                do {
                    let tokens = try cognitoTokenProvider.getCognitoTokens().get()
                    if BuildConfiguration.isDebug() {
                        print("access: \(tokens.accessToken)")
                        print("idToken: \(tokens.idToken)")
                        print("refreshToken: \(tokens.refreshToken)")
                    }
                    return tokens
                } catch {
                    throw error
                }
            } else {
                throw AmplifyError.unkhown
            }
        } catch {
            throw error
        }
    }
  1. User succesfully login, then wait for 1 hr(current configuration for refresh token to expired)
  2. I have networking class to get token from Amplify and attach it to every payloads that point to my BE services. If you take a look at switch case I'm trying to use AuthError to detect that session is expired. However, the .sessionExpired never get hit.
func adapt(_ urlRequest: URLRequest, for session: Alamofire.Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        Task {
            do {
                let isLogin = await amplifyManager.isLogin()
                if isLogin {
                    var urlRequest = urlRequest
                    let accessToken = try await amplifyManager.fetchToken().idToken
                    urlRequest.setValue("Bearer \(accessToken)",
                                        forHTTPHeaderField: "Authorization")
                    if let defaultHeader = defaultHeader {
                        for (key, value) in defaultHeader {
                            urlRequest.setValue(value, forHTTPHeaderField: key)
                        }
                    }
             
                    completion(.success(urlRequest))
                } else {
                    var urlRequest = urlRequest
                    if let defaultHeader = defaultHeader {
                        for (key, value) in defaultHeader {
                            urlRequest.setValue(value, forHTTPHeaderField: key)
                        }
                    }
                    completion(.success(urlRequest))
                }
            } catch let error {
                guard let authError = error as? AuthError else {
                    completion(.failure(error))
                    return
                }
                switch authError {
                case .signedOut(_, _, let error), .sessionExpired(_, _, let error):
                    self.delegate?.requestRetrierDidFailToRefreshToken(self, withError: error)
                    completion(.failure(error ?? SpotlikeError.unknown))
                default:
                    completion(.failure(error))
                }
                
            }
        }
    }

Here is amplifyConfiguration.json

  "auth": {
    "plugins": {
      "awsCognitoAuthPlugin": {
        "IdentityManager": {
          "Default": {}
        },
        "CognitoUserPool": {
          "Default": {
            "PoolId": "ap-southeast-asdfasdf",
            "AppClientId": "asdljkflkajsdf",
            "Region": "ap-southeast-1"
          }
        },
        "Auth": {
          "Default": {
            "authenticationFlowType": "USER_SRP_AUTH",
            "socialProviders": [],
            "OAuth": {
              "WebDomain": "xxxx-userpool-dev.auth.ap-southeast-1.amazoncognito.com",
              "AppClientId": "asdljkflkajsdf",
              "SignInRedirectURI": "xxx://",
              "SignOutRedirectURI": "xxx://",
              "Scopes": [
                "email",
                "openid",
                "phone",
                "profile"
              ]
            }
          }
        }
      }
    }
  }
}

Please let me know if you need more info. Thank you

@harsh62
Copy link
Member

harsh62 commented Feb 5, 2024

@nonesc This information is good.. Would you also be able to provide verbose logs when this happens? You can enable verbose logs by using Amplify.Logging.logLevel = .verbose before configuring Amplify.

@harsh62
Copy link
Member

harsh62 commented Feb 5, 2024

@nonesc I think I was able to understand whats causing this issue and created a fix for it. Would you be able to pull remove-refresh-hostedui-token-flow branch in the linked PR and test out if it works for you. Once you confirm the fix, I can plan to get the fix in a future release.

@harsh62 harsh62 added bug Something isn't working pending-community-response Issue is pending response from the issue requestor and removed question General question labels Feb 5, 2024
@nonesc
Copy link
Author

nonesc commented Feb 6, 2024

Morning @harsh62, It seems to work on my end. I received the followings after changing framework to remove-refresh-hostedui-token-flow. Great work!. Btw, I plan to release the app integrated with this library soon, wondering if while planing to get the fix. Am I free to continue using this branch?

Successfully completed execution for Auth.fetchSessionAPI with result:
{
    awsCredentialsError = "AuthError: Session expired could not fetch AWS Credentials\nRecovery suggestion: Invoke Auth.signIn to re-authenticate the user\nCaused by:\nNotAuthorizedException(properties: AWSCognitoIdentityProvider.NotAuthorizedException.Properties(message: Optional(\"Refresh Token has expired\")), httpResponse: \nStatus Code: http_status_400 \n Connection: keep-alive, \nx-amzn-ErrorMessage: Refresh Token has expired, \nContent-Type: application/x-amz-json-1.1, \nContent-Length: 73, \nx-amzn-RequestId: 71982eb5-3637-4624-a3b2-fa8b700a60be, \nx-amzn-ErrorType: NotAuthorizedException:, \nDate: Tue, 06 Feb 2024 03:04:39 GMT, message: Optional(\"Refresh Token has expired\"), requestID: nil)";
    cognitoTokensError = "AuthError: Session expired could not fetch cognito tokens\nRecovery suggestion: Invoke Auth.signIn to re-authenticate the user\nCaused by:\nNotAuthorizedException(properties: AWSCognitoIdentityProvider.NotAuthorizedException.Properties(message: Optional(\"Refresh Token has expired\")), httpResponse: \nStatus Code: http_status_400 \n Connection: keep-alive, \nx-amzn-ErrorMessage: Refresh Token has expired, \nContent-Type: application/x-amz-json-1.1, \nContent-Length: 73, \nx-amzn-RequestId: 71982eb5-3637-4624-a3b2-fa8b700a60be, \nx-amzn-ErrorType: NotAuthorizedException:, \nDate: Tue, 06 Feb 2024 03:04:39 GMT, message: Optional(\"Refresh Token has expired\"), requestID: nil)";
    identityIdError = "AuthError: Session expired could not fetch identity id\nRecovery suggestion: Invoke Auth.signIn to re-authenticate the user\nCaused by:\nNotAuthorizedException(properties: AWSCognitoIdentityProvider.NotAuthorizedException.Properties(message: Optional(\"Refresh Token has expired\")), httpResponse: \nStatus Code: http_status_400 \n Connection: keep-alive, \nx-amzn-ErrorMessage: Refresh Token has expired, \nContent-Type: application/x-amz-json-1.1, \nContent-Length: 73, \nx-amzn-RequestId: 71982eb5-3637-4624-a3b2-fa8b700a60be, \nx-amzn-ErrorType: NotAuthorizedException:, \nDate: Tue, 06 Feb 2024 03:04:39 GMT, message: Optional(\"Refresh Token has expired\"), requestID: nil)";
    isSignedIn = true;
    userSubError = "AuthError: Session expired could not fetch cognito tokens\nRecovery suggestion: Invoke Auth.signIn to re-authenticate the user\nCaused by:\nNotAuthorizedException(properties: AWSCognitoIdentityProvider.NotAuthorizedException.Properties(message: Optional(\"Refresh Token has expired\")), httpResponse: \nStatus Code: http_status_400 \n Connection: keep-alive, \nx-amzn-ErrorMessage: Refresh Token has expired, \nContent-Type: application/x-amz-json-1.1, \nContent-Length: 73, \nx-amzn-RequestId: 71982eb5-3637-4624-a3b2-fa8b700a60be, \nx-amzn-ErrorType: NotAuthorizedException:, \nDate: Tue, 06 Feb 2024 03:04:39 GMT, message: Optional(\"Refresh Token has expired\"), requestID: nil)";
}

@github-actions github-actions bot removed the pending-community-response Issue is pending response from the issue requestor label Feb 6, 2024
@harsh62
Copy link
Member

harsh62 commented Feb 6, 2024

Thank you for confirming. I will work with my team to get a release out this week.

@harsh62 harsh62 added the pending-release Code has been merged but pending release Code has been merged but pending release label Feb 6, 2024
@harsh62
Copy link
Member

harsh62 commented Feb 9, 2024

@nonesc The fix has been released. Please use the latest version of Amplify. https://github.com/aws-amplify/amplify-swift/releases/tag/2.26.0. Thanks for your patience on this.

@harsh62 harsh62 closed this as completed Feb 9, 2024
@github-actions github-actions bot removed the pending-release Code has been merged but pending release Code has been merged but pending release label Feb 9, 2024
Copy link
Contributor

github-actions bot commented Feb 9, 2024

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@harsh62 harsh62 self-assigned this Feb 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth Issues related to the Auth category bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants