Yammer Authentication with HttpWebRequest

那年仲夏 提交于 2020-01-28 11:06:28

问题


I am currently working on a project that requires using the Yammer API. The intent is to bypass using a browser, and use HttpWebRequest to do all authentication. Originally, this was working for me, but now I get a 404 error when I try to call GetResponse().

For my URL, I have tried using
https://www.yammer.com/session?client_id={CLIENT_ID}
as well as
https://www.yammer.com/session

using (var stream = webrequest.GetRequestStream())
{
    stream.Write(postdata, 0, postdata.Length);
}

try
{
    webresponse = webrequest.GetResponse() as HttpWebResponse;
}
catch (WebException ex)
{
    webresponse = ex.Response as HttpWebResponse;
}

Have they changed the URL or am I doing something wrong?


回答1:


The following is my code snippet for yammer authentication. Steve Pescha's article - http://blogs.technet.com/b/speschka/archive/2013/10/05/using-the-yammer-api-in-a-net-client-application.aspx explains how to perform Programmatic yammer authentication. I have customized it according to my needs.

public class YammerSession 
{
    #region Variables

    /// <summary>
    /// The client identifier
    /// </summary>
    private readonly string clientId = "XXXXXXXX";

    /// <summary>
    /// client secret
    /// </summary>
    private readonly string clientSecret = "XXXXXXXX";

    /// <summary>
    /// Cookie container that stores yammer authentication information
    /// </summary>
    private CookieContainer cookieContainer = new CookieContainer(2);

    /// <summary>
    /// The user code sent in response to login request
    /// </summary>
    private string userCode;

    /// <summary>
    /// The user email
    /// </summary>
    private string email;

    /// <summary>
    /// The user password
    /// </summary>
    private SecureString password;

    #endregion

    #region Methods

    /// <summary>
    /// Gets the supported yammer version
    /// </summary>
    public static int SupportedVersion
    {
        get
        {
            return 1;
        }
    }

    /// <summary>
    /// Gets the client id.
    /// </summary>
    public string ClientId
    {
        get
        {
            return this.clientId;
        }
    }

    /// <summary>
    /// Gets the authenticity token.
    /// </summary>
    /// <value>
    /// The authenticity token.
    /// </value>
    public string AuthenticityToken { get; private set; }

    /// <summary>
    /// Gets the token.
    /// </summary>
    /// <value>
    /// The token.
    /// </value>
    public string Token { get; private set; }

    /// <summary>
    /// Connects the specified connection.
    /// </summary>
    public override void Connect()
    {
        this.InternalLogin(this.Connection.User, this.Connection.Password);            
    }

    /// <summary>
    /// Disconnects this instance.
    /// </summary>
    public override void Disconnect()
    {            
        // Log out
        this.InternalLogout(this.Connection.Address);            
    }

    /// <summary>
    /// Creates the web request to a service endpoint.
    /// </summary>        
    /// <param name="serviceEndpoint">The service endpoint.</param>
    /// <returns>
    /// A new HTTP web request.
    /// </returns>
    public string GetEndpoint(string serviceEndpoint)
    {            
        // Get the uri
        var requestUri = string.Format("{0}/api/v{1}/{2}", this.Connection.Address, SupportedVersion, serviceEndpoint);

        // return the result
        return requestUri;
    }

    /// <summary>
    /// Connects the specified email.
    /// </summary>
    /// <param name="email">The email.</param>
    /// <param name="password">The password.</param>
    private void InternalLogin(string email, SecureString password)
    {            
        this.email = email;
        this.password = password;

        // Get the user code.
        this.GetUserCode();

        // Now get the bearer token
        this.GetBearerToken(this.userCode);            
    }

    /// <summary>
    /// Gets the user code.
    /// </summary>                        
    private void GetUserCode()
    {            
        string yammerAuthUrl = string.Format("https://www.yammer.com/dialog/oauth?client_id={0}", this.clientId);
        string yammerSessionUrl = string.Format("https://www.yammer.com/session?client_id={0}", this.clientId);

        // The authenticity token
        string authenticityToken = string.Empty;

        // Make a get request to Yammer authentication endpoint and get the response
        using (HttpWebResponse webResponse = this.MakeGetRequestToEndPoint(yammerAuthUrl))
        {
            // Set the cookies
            this.SetCookies(webResponse);

            // Look for authenticity token
            authenticityToken = this.GetAuthenticityToken(SessionHelper.ConvertResponseStreamToString(webResponse, Encoding.UTF8));
        }

        if (!string.IsNullOrEmpty(authenticityToken))
        {
            // Construct the post body with user login information
            string postBody = string.Format(
                        "{0}{1}{2}{3}{4}{5}{6}",
                        "utf8=%E2%9C%93&authenticity_token=",
                        HttpUtility.UrlEncode(authenticityToken),
                        "&network_permalink=&login=",
                        HttpUtility.UrlEncode(this.email),
                        "&password=",
                        HttpUtility.UrlEncode(this.password.ConvertToUnsecureString()),
                        "&remember_me=off");

            // Make the first post for User Code
            HttpWebResponse sessionPostWebResponse = this.MakePostRequestToEndPoint(postBody, yammerSessionUrl);
            string postResults = this.GetResponseAsString(sessionPostWebResponse);

            // Get the next auth token that was returned. This will be used for logout purposes
            this.AuthenticityToken = this.GetAuthenticityToken(postResults);

            using (HttpWebResponse webResponse = this.MakeGetRequestToEndPoint(yammerAuthUrl, true))
            {
                // Now look for the query string and set the user code
                this.userCode = webResponse.ResponseUri.Query;

                // Check whether we are in Authorization Page
                if (this.userCode.IndexOf("?client_id") >= 0)
                {
                    // Construct the yammer network name
                    string yammerNetworkName = webResponse.ResponseUri.AbsolutePath.Substring(0, webResponse.ResponseUri.AbsolutePath.ToLower().IndexOf("dialog/"));

                    // Construct the yammer decision url
                    string yammerUserAuthDecisionUrl = string.Format(
                                    "{0}{1}{2}{3}{4}",
                                    "https://www.yammer.com",
                                    yammerNetworkName,
                                    "oauth2/decision?client_id=",
                                    this.clientId,
                                    "&redirect_uri=https%3A%2F%2Fwww.yammer.com&response_type=code");

                    // Construct the Payload for authorization page
                    string payLoad = "utf8=%E2%9C%93&authenticity_token=" + HttpUtility.UrlEncode(this.AuthenticityToken) + "&allow=Allow";

                    // Authorize the app by posting the request 
                    using (HttpWebResponse decisionPostWebResponse = this.MakePostRequestToEndPoint(payLoad, yammerUserAuthDecisionUrl))
                    {
                        // Reset the user Code
                        this.userCode = decisionPostWebResponse.ResponseUri.Query;
                    }
                }

                // App should have been granted access at this point if it did not already have access. 
                // Now check whether the code is present in the query string
                if (this.userCode.IndexOf("?code=") < 0)
                {
                    throw new ArgumentException(Properties.ErrorMessges.UnableToLogin);
                }

                this.userCode = this.userCode.Replace("?code=", string.Empty);
            }
        }            
    }

    /// <summary>
    /// Get Yammer Authenticity Token
    /// </summary>
    /// <param name="rawHtml">The Yammer response that was got after posting to yammer endpoint</param>
    /// <returns>The Yammer authenticity token</returns>
    private string GetAuthenticityToken(string rawHtml)
    {            
        string result = string.Empty;

        int at = rawHtml.IndexOf("<meta name=\"authenticity_token\" id=\"authenticity_token\"");

        if (at > -1)
        {
            // Get the authenticity token string
            int et = rawHtml.IndexOf("/>", at + 1);
            string tokenText = rawHtml.Substring(at, et - at);

            // Get the token value
            int ts = tokenText.IndexOf("content=");
            int es = tokenText.LastIndexOf("\"");

            result = tokenText.Substring(ts + 9, es - ts - 9);
        }

        return result;
    }

    /// <summary>
    /// Perform a get request to an endpoint and return the Http response
    /// </summary>
    /// <param name="endPoint">The endpoint to make the request</param>
    /// <param name="addCookies">Should cookies be added to the request</param>
    /// <returns>The Http Web Response</returns>
    private HttpWebResponse MakeGetRequestToEndPoint(string endPoint, bool addCookies = false)
    {            
        HttpWebRequest webRequest = WebRequest.CreateHttp(endPoint);

        webRequest.Method = "GET";
        if (addCookies)
        {
            webRequest.CookieContainer = this.cookieContainer;
        }

        return (HttpWebResponse)webRequest.GetResponse();
    }

    /// <summary>
    /// Read the cookies from the web response object and store it in the cookie container instance variable
    /// </summary>
    /// <param name="webResponse">The Web Response object</param>
    private void SetCookies(HttpWebResponse webResponse)
    {            
        const string YAMTRAK_COOKIE = "yamtrak_id";
        const string SESSION_COOKIE = "_workfeed_session_id";
        const string LOGIN_CSRF_TOKEN_COOKIE = "login_csrf_token";

        string cookies = webResponse.Headers["Set-Cookie"];
        if (string.IsNullOrEmpty(cookies))
        {
            this.cookieContainer = new CookieContainer();
        }
        else
        {
            // Split the cookie based on , and ;
            string[] sepChar = new string[2];
            sepChar[0] = ",";
            sepChar[1] = ";";
            string[] cookieArray = cookies.Split(sepChar, StringSplitOptions.None);

            // Declare variables to hold the different types of cookies
            string login_csrf_token = string.Empty;
            string yammerTrakToken = string.Empty;
            string sessionToken = string.Empty;

            // Parse the cookie array and store the cookies in the array
            for (int i = 0; i < cookieArray.Length; i++)
            {
                if (cookieArray[i].IndexOf(YAMTRAK_COOKIE) >= 0)
                {
                    yammerTrakToken = cookieArray[i];
                }
                if (cookieArray[i].IndexOf(SESSION_COOKIE) >= 0)
                {
                    sessionToken = cookieArray[i];
                }
                if (cookieArray[i].IndexOf(LOGIN_CSRF_TOKEN_COOKIE) >= 0)
                {
                    login_csrf_token = cookieArray[i];
                }
            }

            // Create the cookie container
            this.cookieContainer = new CookieContainer();

            // Get the value for each of the cookie and add it to the cookie container                
            if (!string.IsNullOrWhiteSpace(yammerTrakToken))
            {
                yammerTrakToken = yammerTrakToken.Substring(YAMTRAK_COOKIE.Length + 1);
                this.cookieContainer.Add(new Cookie(YAMTRAK_COOKIE, yammerTrakToken, "/", "www.yammer.com"));
            }
            if (!string.IsNullOrWhiteSpace(sessionToken))
            {
                sessionToken = sessionToken.Substring(SESSION_COOKIE.Length + 1);
                this.cookieContainer.Add(new Cookie(SESSION_COOKIE, sessionToken, "/", "www.yammer.com"));
            }
            if (!string.IsNullOrWhiteSpace(login_csrf_token))
            {
                login_csrf_token = login_csrf_token.Substring(LOGIN_CSRF_TOKEN_COOKIE.Length + 1);
                this.cookieContainer.Add(new Cookie(LOGIN_CSRF_TOKEN_COOKIE, login_csrf_token, "/", "www.yammer.com"));
            }
        }            
    }

    /// <summary>
    /// Make a post request to an endpoint and return the result
    /// </summary>
    /// <param name="postBody">The post request payload</param>
    /// <param name="endPoint">The endpoint</param>
    /// <returns>The response got from the server</returns>
    private HttpWebResponse MakePostRequestToEndPoint(string postBody, string endPoint)
    {            
        string responseString = string.Empty;

        HttpWebRequest webRequest = WebRequest.CreateHttp(endPoint);
        webRequest.Method = "POST";
        webRequest.CookieContainer = this.cookieContainer;
        webRequest.ContentType = "application/x-www-form-urlencoded";
        SessionHelper.WritePayLoadToWebRequest(webRequest, postBody);
        HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

        return webResponse;
    }

    /// <summary>
    /// Get Response as string
    /// </summary>
    /// <param name="webResponse">The http web response object</param>
    /// <returns>The web response string</returns>
    /// <remarks>The Http Response object would be disposed after use</remarks>
    private string GetResponseAsString(HttpWebResponse webResponse)
    {            
        string responseString = string.Empty;

        using (webResponse)
        {
            responseString = SessionHelper.ConvertResponseStreamToString(webResponse, Encoding.UTF8);
        }

        return responseString;
    }

    /// <summary>
    /// Gets the user code.
    /// </summary>
    /// <param name="userCode">The user code.</param>
    /// <exception cref="System.TimeoutException">Waiting for login page load.
    /// or
    /// Waiting for post login page load.</exception>
    private void GetBearerToken(string userCode)
    {            
        string formatUri = string.Format("https://www.yammer.com/oauth2/access_token.json?client_id={0}&client_secret={1}&code={2}", this.clientId, this.clientSecret, userCode);

        Uri yammerUri = new Uri(formatUri);

        WebRequest webRequest = WebRequest.Create(yammerUri);
        webRequest.Method = "GET";

        using (WebResponse response = webRequest.GetResponse())
        {
            using (Stream responseStream = response.GetResponseStream())
            {
                DataContractJsonSerializer seralizer = new DataContractJsonSerializer(typeof(Model.JSON.Yammer.AccessTokenResponse));
                Model.JSON.Yammer.AccessTokenResponse accessTokenResponse = (Model.JSON.Yammer.AccessTokenResponse)seralizer.ReadObject(responseStream);

                if (string.IsNullOrWhiteSpace(accessTokenResponse.access_token.token))
                {
                    throw new InvalidOperationException("Unable to extract Yammer.com authorization bearer token.");
                }

                // Set the bearer token
                this.Token = accessTokenResponse.access_token.token;
            }
        }            
    }

    /// <summary>
    /// Internal logout.
    /// </summary>
    /// <param name="address">The address.</param>
    private void InternalLogout(string address)
    {            
        string formatUri = string.Format("{0}/logout?from=nav", address);

        Uri yammerUri = new Uri(formatUri);

        // Create request
        var request = HttpWebRequest.CreateHttp(yammerUri);

        // POST
        request.Method = "POST";

        // Set the cookie container
        request.CookieContainer = this.cookieContainer;

        // Sent the request body            
        request.ContentType = "application/x-www-form-urlencoded";
        string requestBody = string.Format(
            "authenticity_token={0}&_method=delete",
            HttpUtility.UrlEncode(this.AuthenticityToken));
        byte[] requestBodyUTF8 = Encoding.UTF8.GetBytes(requestBody);

        // Set the length before writing the request steam.
        request.ContentLength = requestBody.Length;

        // Write the request stream
        using (var requestStream = request.GetRequestStream())
        {
            using (StreamWriter streamWrite = new StreamWriter(requestStream))
            {
                streamWrite.Write(requestBody);
            }
        }

        // Make the request
        using (var response = request.GetResponse())
        {
            // Always read the response
            using (Stream responseStream = response.GetResponseStream())
            {
            }
        }
    }

    #endregion
}

In the above code replace your client Id, client secret, email and password. Then you can use the connect method to get the bearer token and disconnect to log out of yammer. Recently yammer changed the number of cookies that were passed back and forth and I have fixed the issue



来源:https://stackoverflow.com/questions/25389113/yammer-authentication-with-httpwebrequest

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!