It's all about the answers!

Ask a question

Authentication Issue to RTC server (v6.0.2) using c#


Bing Zhu (1112) | asked Jul 25 '17, 2:18 p.m.

 Hi,

When using this guide "Consuming RTC (Rational Team Concert) OSLC APIs using C#: Post 1- Authentication"
 (https://nkumar83.wordpress.com/2013/06/13/consuming-rtc-rational-team-concert-oslc-apis-using-c-post-1-authentication/), But the response header "X-com-ibm-team-repository-web-auth-msg" always returns "authfailed"

I tried both "jts" and "ccm" in "_rtcServerURL", but the result are the same. Here is my code:

public static HttpWebResponse requestSecureDocument(HttpWebRequest _request, string _rtcServerURL, string _userName, string _password)
        {
            //FormBasedAuth Step1: Request the resource and clone the request to be used later
            HttpWebRequest _requestClone = (HttpWebRequest)WebRequest.Create(_rtcServerURL); // WebRequestExtensions.CloneRequest(_request, _request.RequestUri);
           

            //store the response in _docResponse variable
            HttpWebResponse _docResponse = (HttpWebResponse)_request.GetResponse();

            //HttpStatusCode.OK indicates that the request succeeded and that the requested information is in the response.
            if (_docResponse.StatusCode == HttpStatusCode.OK)
            {
                //X-com-ibm-team-repository-web-auth-msg header signifies form based authentication is being used
                string _rtcAuthHeader = _docResponse.Headers["X-com-ibm-team-repository-web-auth-msg"];
                if ((_rtcAuthHeader != null) && _rtcAuthHeader.Equals("authrequired"))
                {
                    _docResponse.GetResponseStream().Flush();
                    _docResponse.Close();

                    //Prepare form for authentication as _rtcAuthHeader = authrequired
                    HttpWebRequest _formPost = (HttpWebRequest)WebRequest.Create(_rtcServerURL + "/j_security_check");
                    _formPost.Method = "POST";
                    _formPost.Timeout = 30000;
                    _formPost.CookieContainer = _request.CookieContainer;
                    _formPost.Accept = "text/xml";
                    _formPost.ContentType = "application/x-www-form-urlencoded";

                    String _authString = "j_username=" + _userName + "&j_password=" + _password; //create authentication string
                    Byte[] _outBuffer = Encoding.UTF8.GetBytes(_authString); //store in byte buffer
                    _formPost.ContentLength = _outBuffer.Length;
                    Stream _str = _formPost.GetRequestStream();
                    _str.Write(_outBuffer, 0, _outBuffer.Length); //update form
                    _str.Close();

                    //FormBasedAuth Step2:submit the login form and get the response from the server
                    HttpWebResponse _formResponse = (HttpWebResponse)_formPost.GetResponse();

                    _rtcAuthHeader = _formResponse.Headers["X-com-ibm-team-repository-web-auth-msg"];
                    //check if authentication has failed
                    if ((_rtcAuthHeader != null) && _rtcAuthHeader.Equals("authfailed"))
                        {
                        //authentication failed. You can write code to handle the authentication failure.
                        //if (DEBUG) Console.WriteLine("Authentication Failure");
                    }
                    else
                    {
                        //login successful
                        _formResponse.GetResponseStream().Flush();
                        _formResponse.Close();
                        //FormBasedAuth Step3: Resend the request for the protected resource.
                        //if (DEBUG) Console.WriteLine(">> Response " + request.RequestUri);
                        return (HttpWebResponse)_requestClone.GetResponse();
                    }
                }
            }
            //already authenticated return original response_docResponse
            return _docResponse;
        }



Comments
Ulf Arne Bister commented Jul 25 '17, 5:47 p.m.

 Is the sample code mentioned here working better for you?
http://blog.boriskuschel.com/2012/02/c-with-visual-studio-and-rational-team.html

5 answers



permanent link
Bing Zhu (1112) | answered Sep 12 '17, 12:34 p.m.

 I was able to use the sample solution from this zip file to successfully access project area on our server. 


I did have to do slight modifications. I converted the console app in zip file into MVC web project to avoid "web proxy" error. I also added "/authenticated/" in the authentication url (so it looks like "/authenticated/j_security_check?j_username={0}&j_password={1}").

Thanks for everyone's help!


Comments
Jeffery Hanson commented Sep 12 '17, 10:37 p.m. | edited Sep 13 '17, 4:06 a.m.
JAZZ DEVELOPER

Bing, Thanks for sharing your successful results!


permanent link
John Vasta (2.6k15) | answered Sep 03 '17, 9:10 a.m.
FORUM MODERATOR / JAZZ DEVELOPER

This line looks wrong to me:

String _authString = "j_username=" + _userName + "&j_password=" + _password; //create authentication string

You don't encode the '&' separator character in form-encoded input. Encoding it makes it part of the value of the "j_username" parameter, instead of indicating the end of the value of that parameter and the start of another one. Try using

String _authString = "j_username=" + _userName + "&j_password=" + _password; //create authentication string

You might find https://jazz.net/wiki/bin/view/Main/NativeClientAuthentication interesting - it describes how to deal with all the authenticaiton protocols that Jazz servers can be configured with.


Comments
Donald Nong commented Sep 03 '17, 7:57 p.m.

Good catch! The blog quoted in the OP shows it as encoded (List 2), but the "&&" operator in the same piece of code is also encoded as "&&", which is very wrong. I believe that is not what the code originally looks like, but a glitch when the code is formatted in an HTML page.


permanent link
Kirk Woods (9158) | answered Sep 01 '17, 12:56 p.m.

I don't know C# but here is the java code I use:

                HttpPost formPost = new HttpPost(jtsURI + "/j_security_check");
                List<NameValuePair> nvps = new ArrayList<NameValuePair>();
                nvps.add(new BasicNameValuePair("j_username", login));
                nvps.add(new BasicNameValuePair("j_password", password));
                formPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
                HttpResponse formResponse = httpClient.execute(formPost);

Note that I am going to JTS and not RTC and as previously mentioned, the username and password are encoded (which you might be doing with Encoding.UTF8.GetBytes, but I am not familiar with C# libraries).  I don't add any headers.


permanent link
Bing Zhu (1112) | answered Jul 26 '17, 9:31 a.m.
edited Jul 26 '17, 9:43 a.m.

 Thanks Don!


Yes, I understand the code. That's where I got the "authfailed" from response when I debug. 

For step 1, The first parameter "_request" passed try to access a secured resource "<hostname>/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/924". That call returns "authfailed". (When I tried this in RestClient in Firefox using the JSESSIONID received from manually login, the call is successful.)

For step 2, The second parameter "_rtcServerURL" is "<hostname>/ccm/authenticated/j_security_check". I did a POST with j_username and j_password as form data, but I always get "authfailed"

I also tried this post request in browser client like Postman and RestClient (FF), but I got a html page says "You have followed a direct link to log in to a Jazz server.  This page has been presented to ensure that a malicious website cannot use cleverly crafted content to circumvent security. Please log in if you would like to access the server."

Anything else I should check? Other headers needed when making this call?

Thanks,


Comments
1
Nate Decker commented Jul 28 '17, 8:44 a.m. | edited Sep 13 '17, 4:06 a.m.

One pitfall that you should watch out for and that you might want to check is if your password uses any characters that need to be HTML encoded. The first time I developed an API application, it worked for me and then I gave it to someone else to use and it locked their account. It worked for me because my password didn't have any unusual characters, but theirs did. My code wasn't HTML encoding the password so I was authenticating fine, but they were not.


permanent link
Donald Nong (14.5k414) | answered Jul 25 '17, 9:26 p.m.

Your code is not that different from that in the blog. You need to understand what your code does and how to debug it. Regardless the programming/scripting language of choice, the process of authentication is very simple.
1. Try to access a protected resource, to verify whether you're already logged in, and get the JSESSIONID at the same time.
2. If not yet logged in, POST to /<app>/authenticated/j_security_check with j_username and j_password for authentication, where <app> can be "jts", "ccm", "qm", "jazz" or whatever the root context of your Jazz application is assigned to.

Depending on what value you pass on to the variable "_rtcServerURL", you may not have the correct URL to POST at all. Please double check.


Comments
Donald Nong commented Jul 26 '17, 10:48 p.m.

You don't need to add any extra headers for the POST request, but make sure you have retained all the cookies and used them with this request. Other than that, I can imagine what can go wrong - as I said, the authentication process is very simple.


Bing Zhu commented Jul 27 '17, 8:57 a.m.

 After the step 1 call, in the response header there is a "Set-Cookie" header. Is that what you referring? Do I need to put that in header when I POST in step 2? If yes, how to do that?


If that is not the cookie you referred, what cookies I need to retain and use?

Thanks a lot!


Donald Nong commented Jul 28 '17, 6:08 a.m.

You should retain all the cookies. I'm not familiar with C# and you probably need to ask such question on Microsoft's developer forum.


Bing Zhu commented Jul 28 '17, 12:30 p.m.

 This is what I added to pass the cookie, still got "authfailed":


//receive the cookie from header in Step 1
var cookie = _docResponse.Headers["Set-Cookie"];
.....
//set the cookie before send post in step 2
_formPost.Headers["Cookie"] = cookie;

Your answer


Register or to post your answer.