Getting "invalid_grant" while navigation on SugarCRM

Hi.

I browse SugarCRM inside several iFrames in my Web Application. The SessionID is always the same.

When I log in SugarCRM I can see a "/rest/v10/oauth2/token" with my credentials and I get the following reply:

{"access_token":"tokenAccess","expires_in":3600,"token_type":"bearer","scope":null,"refresh_token":"tokenRefresh","refresh_expires_in":1209600,"download_token":"tokenDownload"}

After some minutes, while navigating, I'm redirected to the login page. When that happens I see the following request to "/rest/v10/oauth2/token":

{"grant_type":"refresh_token","client_id":"sugar","client_secret":"","refresh_token":"refreshToken","platform":"base","refresh":true}

But I get the response:

{"error":"invalid_grant","error_message":"Invalid refresh token"}

Inside SugarCRM I have some custom code that invokes WebServices on my side. That WebServices requests tokens to SugarCRM with a different user that is navigating BUT in some cases I invoke "/rest/v10/oauth2/sudo/ with the user that is navigating.

Anyone have an idea of why the "Invalid refresh token"?

Thanks in advance.

 

  • Hi Nuno,

    It isn't that much easy to identify this kind of problems. Can you try to disable your custom code to see if that changes this behaviour? 

    Best Regards
    Tevfik Tümer
    Developer Support Engineer

  • Hi,

    I've commented the code I was suspecting and the problem seems to disappear.

    I wonder if anyone has a suggestion to solve this issue.

    Here is what I do:

    • I'm calling oauth2/token with a special API user to obtain a token
    • I use this token is every REST call
    • There is one place where I need to add a "Call". I need this "Call" to be associated with the user navigating in SugarCRM and not to the "special API user". Therefore, I'm:
      • Calling oauth2/sudo/<navigatingUser> with the oath-token header of the special API user
      • Calling /Calls with the oauth-token header set with the token fetched on the last step.

    This seems to ruin my navigation session.

    Any hint?

    Thanks

  • Hi Nuno,

    I'm not sure about this but sounds like platform issue. 

    Can you provide some code block to me? Mostly assumption causes the problems. Especially how you pass the OAuth-token from one to another one. I believe there you are kicking your first user out. 

    Best Regards
    Tevfik Tümer
    Developer Support Engineer

  • Hi,

    Thanks for replying.

    This is my method to perform the "special API user" authentication:

    public static string Authenticate(string sugarURL, string clientID = null, string clientSecret = null, string username = null, string password = null)
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sugarURL + "oauth2/token");
                request.Method = "POST";
                request.ContentType = "application/json";

                TokenRequest t = new TokenRequest()
                {
                    client_id = clientID,
                    client_secret = clientSecret,
                    username = username,
                    password = password
                };

                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(TokenRequest));
                using (Stream stream = request.GetRequestStream())
                {
                    ser.WriteObject(stream, t);
                }

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                using (StreamReader responseString = new StreamReader(response.GetResponseStream()))
                {
                    ser = new DataContractJsonSerializer(typeof(TokenResponse));
                    TokenResponse r = (TokenResponse)ser.ReadObject(responseString.BaseStream);
                    return r.access_token;
                }
            }

    This is the method to request the user token. It receives a "otherUsername" which is the user navigating SugarCRM and a "authToken" which was obtained previously by calling "Authenticate".

    public static string RequestUserToken(string sugarURL, string otherUsername, string authToken = null, string clientID = null, string clientSecret = null, string username = null, string password = null)
            {
                sugarURL = "https://" + sugarURL + "/rest/v10/";

                if (string.IsNullOrWhiteSpace(authToken))
                {
                    authToken = Authenticate(sugarURL, clientID, clientSecret, username, password);
                }

                //Structure need by SugarCRM to make this request
                sugarURL = sugarURL + "oauth2/sudo/" + otherUsername;
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sugarURL);

                request.Method = "POST";
                request.Headers.Add("oauth-token", authToken);

                TokenRequest t = new TokenRequest()
                {
                    client_id = clientID,
                    client_secret = clientSecret,
                    username = username,
                    password = password
                };

                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(TokenRequest));
                using (Stream stream = request.GetRequestStream())
                {
                    ser.WriteObject(stream, t);
                }

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                using (StreamReader responseString = new StreamReader(response.GetResponseStream()))
                {
                    ser = new DataContractJsonSerializer(typeof(TokenResponse));
                    TokenResponse r = (TokenResponse)ser.ReadObject(responseString.BaseStream);
                    return r.access_token;
                }
            }

    This is the code that adds a call:

    public string AddNewCall(string sugarURL, string callName, string dateStart, string direction, string assignedUsername, 
                                    String username, String password, String key, String secret, string oauthToken = null, string description = "None",
                                    string status = "Scheduled")
            {
                string addCallResult = DoRequest.Call(
                    sugarURL,
                    "Calls",
                    "POST",
                    new Dictionary<string, string>()
                    {
                        { "name", callName },
                        {"description", description },
                        {"date_start", dateStart },
                        {"duration_minutes", durationMinutes },
                        {"direction", direction },
                        {"status", status },
                        {"assigned_user_id", assignedUsername }
                    },
                    oauthToken, key, secret, username, password);

                AddCallResponse response = null;
                using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(addCallResult)))
                {
                    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(AddCallResponse));
                    response = (AddCallResponse)ser.ReadObject(ms);
                }

                return response.id;
            }

    public static string Call(string sugarURL, string url, string method, Dictionary<string, string> arguments, string authToken = null, string clientID = null, string clientSecret = null, string username = null, string password = null)
            {
                sugarURL = "https://" + sugarURL + "/rest/v10/";

                if (string.IsNullOrWhiteSpace(authToken))
                {
                    authToken = Authenticate(sugarURL, clientID, clientSecret, username, password);
                }
               
                if (method == "GET")
                {
                    string queryParameters = "";
                    if (arguments != null)
                    {
                        foreach (string key in arguments.Keys)
                        {
                            queryParameters += WebUtility.UrlEncode(key) + "=" + WebUtility.UrlEncode(arguments[key]) + "&";
                        }
                    }

                    if (!string.IsNullOrWhiteSpace(queryParameters))
                    {
                        queryParameters = "?" + queryParameters.Substring(0, queryParameters.Length-1);
                    }

                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sugarURL + url + queryParameters);
                    request.Method = "GET";
                    request.Headers.Add("oauth-token", authToken);

                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        return reader.ReadToEnd();
                    }
                }

                if(method == "POST")
                {
                    string queryParameters = "";
                    if (arguments != null)
                    {
                        foreach (string key in arguments.Keys)
                        {
                            queryParameters += WebUtility.UrlEncode(key) + "=" + WebUtility.UrlEncode(arguments[key]) + "&";
                        }
                    }

                    if (!string.IsNullOrWhiteSpace(queryParameters))
                    {
                        queryParameters = "?" + queryParameters.Substring(0, queryParameters.Length - 1);
                    }

                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sugarURL + url + "/" + queryParameters);

                    request.Method = "POST";
                    request.Headers.Add("oauth-token", authToken);

                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        return reader.ReadToEnd();
                    }
                }

                if (method == "PUT")
                {
                    string queryParameters = "";
                    if (arguments != null)
                    {
                        foreach (string key in arguments.Keys)
                        {
                            queryParameters += WebUtility.UrlEncode(key) + "=" + WebUtility.UrlEncode(arguments[key]) + "&";
                        }
                    }

                    if (!string.IsNullOrWhiteSpace(queryParameters))
                    {
                        queryParameters = "?" + queryParameters.Substring(0, queryParameters.Length - 1);
                    }

                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sugarURL + url + queryParameters);
                    request.Method = "PUT";
                    request.Headers.Add("oauth-token", authToken);

                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        return reader.ReadToEnd();
                    }
                }

                return null;
            }

    So, basically I have this:

    // key & secret -> OAuth Key 2.0 created in Sugar
    // username & password -> Special API user
    string oauthToken = DoRequest.Authenticate(sugarREST, key, secret, username, password);
    ...
    ...
    ...
    // sugarUsername -> Another user username
    // remaining parameters will be ignored
    sugarToken = DoRequest.RequestUserToken(SugarURL, sugarUsername, oauthToken, key, secret, username, password);
    ...
    ...
    ...
    callID = newCall.AddNewCall(SugarURL, callName, ConstructDateString(DateTime.UtcNow), direction, sugarID, username, password, key, secret, sugarToken, "None", "Scheduled", "0", "None", mediaType);

    Do you see anything wrong?

  • Hi Nuno,

    Here is the first question, looking at your

    public static string RequestUserToken

    Why do you pass your username and password one more time? 

      TokenRequest t = new TokenRequest()
                {
                    client_id = clientID,
                    client_secret = clientSecret,
                    username = username,
                    password = password
                };

    I think this is causing your user to be kicked out. Take a look at /oauth2/sudo/:user_name POST Endpoint. It expects your user to be an Admin and you don't need to pass the username and password. All you need username as a third parameter( /oauth2/sudo/:username)

    Let me know if this helps. 

    Best Regards
    Tevfik Tümer
    Developer Support Engineer

  • OMG!

    I've Code Reviewed this code (I'm not the developer) and I don't know how I've missed this!

    It's working!

    Thanks a lot Tevfik Tümer!

  • Hi Nuno,

    I'm glad it works now

    Best Regards
    Tevfik Tümer
    Developer Support Engineer

  • Hi Nuno,

    Even after changing the code? 

    I don't have any environment for you to check this code. 

    If it would be php, i would check here in my local machine. 

    I will take a look at this later on one more time. See if i can catch another problem. 

    Best Regards
    Tevfik Tümer
    Developer Support Engineer