Impersonation with Yammer and C#


Over the past couple of weeks I’ve been investigating and learning how to impersonate a user in Yammer. Although there information out there, there was nothing showing from beginning to end how to make impersonation work. So hopefully this will be people’s goto post. J

First of all from the Yammer Office 365 Network, I would like to thank Chad Cooper and Greg Kiel who helped point me in the right direction, and Chris O’Brien for allowing me to use his test Office 365 with Yammer Enterprise network.

Rules for Impersonation

To be able to impersonate within Yammer:

  • The Yammer network needs to be an Enterprise network – which means you need to pay for it, or you have an E3 365 license with a domain name.
  • You require a Yammer App.
  • You need to be a verified admin to create a Yammer App. This is different from just being an Admin of the network. If you are using Office 365 and you are a global administrator, then in your related Enterprise Yammer, you will be a verified admin. In the SharePoint 365 Admin section you need to ensure that the settings are set to use Yammer for social, and not for SP2013 social. If you have users that are added to your Yammer network, not via Office 365, a verified admin can make other people verified admin, by going to the Network Admin settings, then clicking Admins under the Network section on the navigation menu.
  • Not a rule, but a recommendation to use a Service account that’s a verified admin, (i.e,. an account not assigned to a real person), this will ensure you app continues to work if you or the app creator leaves the company.
  • In the External network the verified admin account that creates and authenticates the app, needs to be a user in the External network.
  • The External network must say it’s parent network is your home network. You cannot impersonate people in external networks that the person or network isn’t related to your default home network.041515_2040_impersonati2

Creating the Yammer App.

To create the app in your network, as stated above, you need to be a Verified Admin.

  • Go to the URL https://www.yammer.com/client_applications
  • One the left hand side, click Register New App
  • This will open up a Register New App dialog. Fill out the information.
    • App Name – The app name which will appear in the Yammer’s activity stream
    • Organization – The name of the organization affiliated with your app
    • Support e-mail – Add which users of your app can contact for support
    • WebSite – Your Organization’s website.
    • Redirect URI – Where Yammer will redirect to after the authentication flow is complete. (In my case I used my SharePoint site)
  • Tick the Terms and Conditions and then click Continue.
  • This will then show you your Client ID, Client secret and Expected redirect URL.
  • Next step is to Implement Authentication the app. Ensure you are still signed in as a verified admin Go to https://www.yammer.com/dialog/oauth?client_id= [ :client_id ] &redirect_uri [ :redirect_uri ] replacing the tokens [:client_id] and [:redirect_uri] with values from the last step.
  • Click Allow, and then the site you are redirected to, at the end of the URL you will find ?code=[:code] save this code somewhere for the time being.
  • Using Chrome – Go to https://www.yammer.com/oauth2/access_token.json?client_id= [ :client_id ] &client_secret= [ :client_secret ] &code= [ :code ] replace the tokens [:client_id], [:client_secret] and [:code] with the values from previous steps. The reason to use chrome is because the response is displayed in the browser. Grab the token value. This value is what you will need when you write code to access Yammer. Do not share it with anyone, it is basically the access right to be a verified admin on your Yammer network.

C# code to impersonate a user.

The demo code below will show you how to get a user ID by using their email address, then impersonate their token, so that you can add them to a group. The comments in the code explains what is happening.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OfficeDevPnP.Core.Utilities;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
 
namespace YammerTesting
{
    class Program
    {
        static String GlobalAccessToken = "**************" //Put your token value here, best to store in app config settings.
        static String ClientId = "*************" Put your clientID key of the app here, best to store in app config settings.
 
        static void Main(string[] args)
        {
            AddUserToHomeNetworkGroup("users@emaildomain.com", "5605000");
        }

         //Pass in email address and Group ID, and it will add the user to the group. An action you can only do if you are the actual user.
        private static void AddUserToHomeNetworkGroup(string EmailAddress, string GroupId)
        {
           //Gets the User's ID from Yammer, current running in the App context. If this returns a 404, it means the user email doesn't exist.
            string TargetUserId = MakeYammerRequest(string.Format("https://www.yammer.com/api/v1/users/by_email.json?email={0}", EmailAddress), null, GlobalAccessToken, "id");

          //Passing in the ClientID of the App, the user ID you wish to impersonate and the GlobalAccessToken you will get an impersonation token.
            string impersonationtoken = MakeYammerRequest(string.Format("https://www.yammer.com/api/v1/oauth/tokens.json?consumer_key={0}&user_id={1}", ClientId, TargetUserId), null, GlobalAccessToken, "token");

         //Add User to group with GroupID, this call is made as the User.
         MakeYammerRequest(string.Format("https://www.yammer.com/api/v1/group_memberships.json?group_id={0}", GroupId), "", impersonationtoken, null);
        }
 
       //A method written by <a href="https://twitter.com/stephaneeyskens">@stephaneeyskens</a>;
       //If no data is passed it will perform a GET request, with data it will create a POST request. The key is the value to return within the JSON result.
        private static string MakeYammerRequest(string url, string data, string token, string key)
        {
            HttpWebRequest YammerRequest = HttpWebRequest.Create(url) as HttpWebRequest;
            YammerRequest.Method = "GET";
            YammerRequest.ContentType = "application/json; odata=verbose";
            YammerRequest.Accept = "application/json; odata=verbose";
            YammerRequest.Headers.Add("Authorization", string.Concat("Bearer ", token));
            if (!(data == null))
            {
                YammerRequest.Method = "POST";
                byte[] ByteData = UTF8Encoding.UTF8.GetBytes(data);
                YammerRequest.ContentLength = ByteData.Length;
                Stream YammerRequestStream = YammerRequest.GetRequestStream();
                YammerRequestStream.Write(ByteData, 0, ByteData.Length);
            }

            HttpWebResponse YammerResponse = YammerRequest.GetResponse() as HttpWebResponse;

           //returns a single jArray row
            dynamic resp = JsonConvert.DeserializeObject(
                new StreamReader(YammerRequest.GetResponse().GetResponseStream()).ReadToEnd());

           //Returns the value where the key is…
            return (key != null) ? resp[0][key] : null;
        }
    }
}

External Yammer networks and Impersonation of home users.

With Yammer, you have your default network – let us call that your home network, and you can also create external networks. External networks allow you to invite external users to your Yammer Network, you can also invite all your users from your home network into your external network. However it seems that you cannot impersonate an external user, or impersonate a user in an external network where the parent network of the external network isn’t your home network. I’m happy for someone to correct me, but I don’t think it’s possible, and in the next section after I’ve shown you how to impersonate a home user in an External Yammer network, I will explain why.

The comments in the code explains what is happening.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OfficeDevPnP.Core.Utilities;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace YammerTesting
{
    class Program
    {
        static String GlobalAccessToken = "**************"; //Put your token value here, best to store in app config settings.
        static String ClientId = "*************"; Put your clientID key of the app here, best to store in app config settings.
 
        static void Main(string[] args)
        {
            AddHomeUserToExternalNetworkGroup("users@emaildomain.com", "my external network name", "5605010");
        }        

       //Pass in email address, Group ID and the External network name to add user to the group. An action you can only do if you are the actual user.
        private static void AddHomeUserToExternalNetworkGroup(string EmailAddress, string ExternalNetworkName, string GroupId)
        {
           //Gets the User by email. This will grab the user from the home network, current running in the App context. If this returns a 404, it means the user email doesn't exist.

           string TargetUserId = MakeYammerRequest(string.Format("https://www.yammer.com/api/v1/users/by_email.json?email={0}", EmailAddress), null, GlobalAccessToken, "id");

          //Impersonate the User in the home network.
          string impersonatetoken = MakeYammerRequest(string.Format("https://www.yammer.com/api/v1/oauth/tokens.json?consumer_key={0}&user_id={1}", ClientId, TargetUserId), null, GlobalAccessToken, "token");

            //***Extra Step*****    This call will get all the different accessTokens the impersonated user have, by passing in the ExternalNetworkName, we can return just the token for that network.
             string AccessToken = MakeYammerRequestToGetExternalNetworkToken(impersonatetoken, ExternalNetworkName);
 
           //Now we have the AccessToken for the user in the External Network, we can add them to the group.
            MakeYammerRequest(string.Format("https://www.yammer.com/api/v1/group_memberships.json?group_id={0}", GroupId), "", AccessToken, null);
        }

        //A get request to call /api/v1/oauth/tokens.json and return the given network token.
        private static string MakeYammerRequestToGetExternalNetworkToken(string token, string networkName)
        {
            if (networkName == null)
            {
              throw new ArgumentException("Missing networkName.");
            }

            HttpWebRequest YammerRequest = HttpWebRequest.Create("https://www.yammer.com/api/v1/oauth/tokens.json") as HttpWebRequest;
            YammerRequest.Method = "GET";
            YammerRequest.ContentType = "application/json; odata=verbose";
            YammerRequest.Accept = "application/json; odata=verbose";
            YammerRequest.Headers.Add("Authorization", string.Concat("Bearer ", token));
            HttpWebResponse YammerResponse = YammerRequest.GetResponse() as HttpWebResponse;
            //returns multiple jArray rows, one for each network the user is associated with
            JArray resp = (JArray)JsonConvert.DeserializeObject(new StreamReader(YammerRequest.GetResponse().GetResponseStream()).ReadToEnd());

           //Return the row where it contains the external network name.
            var result = resp.FirstOrDefault(x => x["network_name"].ToString().ToLower() == networkName.ToLower());

            if(result != null)
               return result["Token"].ToString();
 
           return null;
        }

       //A method written by &amp;lt;a href="https://twitter.com/stephaneeyskens"&amp;gt;@stephaneeyskens&amp;lt;/a&amp;gt;
      //If no data is passed it will perform a GET request, with data it will create a POST request. The key is the value to return within the JSON result.
        private static string MakeYammerRequest(string url, string data, string token, string key)
        {
            HttpWebRequest YammerRequest = HttpWebRequest.Create(url) as HttpWebRequest;
            YammerRequest.Method = "GET";
            YammerRequest.ContentType = "application/json; odata=verbose";
            YammerRequest.Accept = "application/json; odata=verbose";
            YammerRequest.Headers.Add("Authorization", string.Concat("Bearer ", token));
            if (!(data == null))
            {
                YammerRequest.Method = "POST";
                byte[] ByteData = UTF8Encoding.UTF8.GetBytes(data);
                YammerRequest.ContentLength = ByteData.Length;
                Stream YammerRequestStream = YammerRequest.GetRequestStream();
                YammerRequestStream.Write(ByteData, 0, ByteData.Length);
            }

            HttpWebResponse YammerResponse = YammerRequest.GetResponse() as HttpWebResponse;
           //returns a single jArray row
            dynamic resp = JsonConvert.DeserializeObject(
                new StreamReader(YammerRequest.GetResponse().GetResponseStream()).ReadToEnd());

           //Returns the value where the key is…
           return (key != null) ? resp[0][key] : null;
        }
    }
}

Why you cannot impersonate an External User in an External Yammer Network

Now this was the reason why I started all this work, and it turns out that I don’t think it is possible.

You can get the UserID from the External Network, by calling the following two calls.

//This will get the access token to make a call in the External Network, it uses the App context to get the token.
string AccessToken = MakeYammerRequestToGetExternalNetworkToken(GlobalAccessToken, ExternalNetworkName);

//Get the UserID in that network, using the access token.
string TargetUserId = MakeYammerRequest(string.Format("https://www.yammer.com/api/v1/users/by_email.json?email={0}", EmailAddress), null, AccessToken, "id");

However your next step, you would think, would be to call the following line of code. However your external user is not part of your home network, therefore you will get a 400 bad request error.

string impertoken = MakeYammerRequest(string.Format("https://www.yammer.com/api/v1/oauth/tokens.json?consumer_key={0}&user_id={1}", ClientId, TargetUserId),
               null,
               GlobalAccessToken,
              "token");

Even if you try and use the AccessToken for the ExternalNetwork you get a 401 unauthorised error, which I think is because the access token isn’t acting on behalf of a verified admin, or the App not running the external network.

I believe the only place you can do this, is in the home network of the user. which if they are an external user to you, the likelihood that you have access to their home yammer network is slim. Even slimmer chance is that you are a verified network admin there, and can create an app, as it’s that’s app token you need to use, and then my original method AddHomeUserToExternalNetworkGroup() to get it to work.

Update: I have had more chance to play about with 2 yammer networks that I’m Verified admin in both and I have found out these rules.

  • You can impersonate a user in your home network.
  • You can impersonate a home user in an external network, only if that external network parent network is your home network.
  • You cannot impersonate an external user in your external network where the parent network is your home network.
  • You cannot impersonate by inviting an external user (who is a verified admin and has the app authorized in their network), to your network and making them a verified admin in your network. It doesn’t matter which app token you use, you will not be able to impersonate external users in external networks, you can only do it from the home network.

References:

http://www.silver-it.com/node/174

https://samlman.wordpress.com/2015/03/02/using-impersonation-with-the-yammer-apis/

https://developer.yammer.com/v1.0/docs/impersonation

Advertisements

3 thoughts on “Impersonation with Yammer and C#

  1. Hi,

    I am getting a bad request 404 on “HttpWebResponse YammerResponse = YammerRequest.GetResponse() as HttpWebResponse;” I dont know what I did wrong.

    I already change the client_id and the provided the GlobalAccessToken.

    help please..

    Thanks,
    Rey

      • Hey Rey,
        Are you trying to impersonate a user in your home yammer network, or an external yammer network that is part of your home network? Also which actual step did you get the error message? When getting the TargetUserID, the ImpersonateToken or the AccessToken? You said you were getting a bad request 404 on your first comment but a 400 on your next comment?

        I would check to ensure that the app token is using a verified admin account, not just an Admin account. If you are trying to do anything in the external network, the verified admin account needs to be a member of it. Double check your API URL string. Look and test out the REST API underneath the link I’m sending you to here https://developer.yammer.com/v1.0/docs/rest-api-rate-limits if you are doing anything different from me.

Comments are closed.