aws-amplify / aws-sdk-ios

AWS SDK for iOS. For more information, see our web site:
https://aws-amplify.github.io/docs
Other
1.68k stars 879 forks source link

Get JWT Token using federatedSignIn #1276

Closed nipuncs closed 5 years ago

nipuncs commented 5 years ago

State your question I am using Cognito as my auth service (federatedSignIn method using Facebook) and I need to access AWS API gateway using Cognito. According to the documentation, I need a JWT token to access the API gateway. Since I am using the federatedSignIn method, I couldn't find a way to get the JWT token. What am I missing here?

Which AWS Services are you utilizing? Cognito

Provide code snippets (if applicable)

    override func viewDidLoad() {
        super.viewDidLoad()

        let loginButton = LoginButton(readPermissions: [ .publicProfile ])
        loginButton.center = view.center

        view.addSubview(loginButton)

        if let accessToken = AccessToken.current {
            getJWT(FBToken:accessToken.authenticationToken)
        }
    }

    public func getJWT(FBToken: String) {

        AWSMobileClient.sharedInstance().initialize { (userState, error) in
            if let userState = userState {
                print("userState at init " + userState.rawValue)
            } else if let error = error {
                print(error.localizedDescription)
            }
        }

        AWSMobileClient.sharedInstance().federatedSignIn(providerName: IdentityProvider.facebook.rawValue, token: FBToken) { (userState, passedError)  in
            if let error = passedError {
                print("Federated Sign In failed: \(error)")
            }else{
                print("Federated sign in succeeded: \(userState)");
                DispatchQueue.main.async {
                    AWSMobileClient.sharedInstance().getIdentityId().continueWith { task in
                        if let error = task.error {
                            print("Get IdentityId error: \(error)")
                        }
                        if let result = task.result {
                            print("Identity id: \(result)")
                            // Need to get JWT here
                        }
                        return nil
                    }
                }
            }
        }
    }

Console Result:

userState at init signedIn
Federated sign in succeeded: Optional(AWSMobileClient.UserState.signedIn)
Identity id: us-east-1:0xxxxxxx....xxxxx

Environment(please complete the following information):

Device Information (please complete the following information):

mutablealligator commented 5 years ago

@nipuncs Thank you for reporting to us. You will need to pass the JWT token that you passed into the federatedSignIn method with the APIGateway client.

Construct the headerParameters map as follows:

let headerParameters = [
            //other headers
            "Authorization" : token
    ]

Construct the AWS ApiGateway request object as follows:

// Construct the request object
         let apiRequest = AWSAPIGatewayRequest(httpMethod: httpMethodName,
                 urlString: URLString,
                 queryParameters: queryStringParameters,
                 headerParameters: headerParameters,
                 httpBody: httpBody)

You can refer to our docs here: https://aws-amplify.github.io/docs/ios/api#cognito-user-pools-authorization

nipuncs commented 5 years ago

@kvasukib Thank you for the quick response. Actually, my issue is not with theAPIGateway, I want to get JWT token via federatedSignIn.

I have managed to get the JWT token via normal signin (using the phone number and password). but when I am trying to get JWT Token via federatedSignIn, it gives me below error.

AWSMobileClient.AWSMobileClientError.notSignedIn(message: "User is not signed in, please sign in to use this API.")

Is AWSMobileClient provides this feature?

Note: The codebase for normal sign in (using the phone number and password) This is working fine

@IBAction func tapLoginByPhone(_ sender: Any) {
        AWSMobileClient.sharedInstance().signIn(username: txtPhone.text!, password: txtPassword.text!) { (signInResult, error) in
            if let error = error  {
                print("\(error)")
            } else if let signInResult = signInResult {
                switch (signInResult.signInState) {
                case .signedIn:
                    print("User is signed in.")
                    DispatchQueue.main.async {
                        self.getPhoneJWT()
                    }
                case .smsMFA:
                    print("SMS message sent to \(signInResult.codeDetails!.destination!)")
                default:
                    print("Sign In needs info which is not et supported.")
                }
            }
        }
    }

    func getPhoneJWT() {
        AWSCognitoUserPoolsSignInProvider.sharedInstance().getUserPool().token().continueWith { (AWSTask) -> Any? in
            if AWSTask.error == nil {
                print("Token \(String(describing: AWSTask.result))")

            }else{
                print(AWSTask.error)
            }
            return nil
        }
    }
rohandubal commented 5 years ago

Hello @nipuncs

The federatedSignIn feature allows you to provide a token from an external provider like Google or Facebook that helps you get AWS Credentials in return. The expectation for federatedSignIn API is that you provide the SDK with the token.

Does this make things clear?

nipuncs commented 5 years ago

Hi @rohandubal, Thank you, I think I understood the scenario. Please verify whether I am correct or wrong.

1. Login via FB using federatedSignIn

@IBAction func tapFBLogin(_ sender: Any) {
        print("###Login Via FB")
        if let accessToken = AccessToken.current {
            facebookLogin(FBToken:accessToken.authenticationToken)
        }
    }

    public func facebookLogin(FBToken: String) {
        AWSMobileClient.sharedInstance().initialize { (userState, error) in
            if let userState = userState {
                print("userState at init " + userState.rawValue)
            } else if let error = error {
                print(error.localizedDescription)
            }
        }

        AWSMobileClient.sharedInstance().federatedSignIn(providerName: IdentityProvider.facebook.rawValue, token: FBToken) { (userState, passedError)  in
            if let error = passedError {
                print("Federated Sign In failed: \(error)")
            }else{
                print("Federated sign in succeeded: \(String(describing: userState))");
            }
        }
    }
  1. Invoke the API

    func doInvokeAPI() {
        // change the method name, or path or the query string parameters here as desired
        let httpMethodName = "GET"
        // change to any valid path you configured in the API
        let URLString = "/test"
        let queryStringParameters = ["param1":"ABC"]
        let headerParameters = [
            "Access-Control-Allow-Origin":"*",
            "Content-Type": "application/json",
            "Accept": "application/json"
        ]
    
        let httpBody = "{}"
    
        // Construct the request object
        let apiRequest = AWSAPIGatewayRequest(httpMethod: httpMethodName,
                                              urlString: URLString,
                                              queryParameters: queryStringParameters,
                                              headerParameters: headerParameters,
                                              httpBody: httpBody)
    
        // Create a service configuration
        let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1,
                                                           credentialsProvider: AWSMobileClient.sharedInstance())
    
        // Initialize the API client using the service configuration
        TESTDEVClient.registerClient(withConfiguration: serviceConfiguration!, forKey: "CloudLogicAPIKey")
    
        // Fetch the Cloud Logic client to be used for invocation
        let invocationClient = TESTDEVClient.client(forKey: "CloudLogicAPIKey")
    
        invocationClient.invoke(apiRequest).continueWith { (task: AWSTask) -> Any? in
            if let error = task.error {
                print("Error occurred: \(error)")
                // Handle error here
                return nil
            }
    
            // Handle successful result here
            let result = task.result!
            let responseString = String(data: result.responseData!, encoding: .utf8)
    
            print("Response: \(String(describing: responseString))")
            print("Response: \(result.statusCode)")
    
            return nil
        }
    }

Is this correct?

Thanks!

rohandubal commented 5 years ago

Yes, the code which you have written is correct and would invoke API Gateway using AWSCredentials that are obtained using Facebook federation.

Please feel free to ask additional questions or close the issue if the confusion is resolved :)

nipuncs commented 5 years ago

Hi all, Thanks, Then this is resolved.

samcomkishan commented 2 years ago

Hello friend,

Please help me to come out of this stuck situation. as I followed the same steps in my code that are in the above thread but I am not getting an Identity id.

AWSMobileClient.sharedInstance().initialize { (userState, error) in
            if let userState = userState {
                print("userState at init " + userState.rawValue)
            } else if let error = error {
                print(error.localizedDescription)
            }
        }
        // FBToken == this is facebook token
        AWSMobileClient.sharedInstance().federatedSignIn(providerName: IdentityProvider.facebook.rawValue, token: FBToken) { (userState, passedError)  in
            if let error = passedError {
                print("Federated Sign In failed: \(error)")
            }else{
                print("Federated sign in succeeded: \(userState)");
                DispatchQueue.main.async {
                    AWSMobileClient.sharedInstance().getIdentityId().continueWith { task in
                        if let error = task.error {
                            print("Get IdentityId error: \(error)")
                        }
                        if let result = task.result {
                            print("Identity id: \(result)")
                            // Need to get JWT here
                        }
                        return nil
                    }
                }
            }
        }

Result :

userState at init signedIn Federated sign in succeeded: Optional(AWSMobileClient.UserState.signedIn) Get IdentityId error: identityIdUnavailable(message: "Fetching identity id on another thread failed. Please retry by calling getIdentityId() method.")