Integrating Yammer data within SharePoint web-part using REST API

Background

We were developing a SharePoint application for one of our client and have some web-parts that had to retrieve data from Yammer. As we were developing on SharePoint Online (SPO) using a popular SharePoint Framework (SPFx), so for the most part of our engagement we were developing using a client-side library named React to deliver what is required from us.

In order for us to integrate client’s Yammer data into our web-parts, we were using JavaScript SDK provided by Yammer.

Scenario

We were having around 7-8 different calls to Yammer API in different web-parts to extract data from Yammer on behalf of a logged-in user. Against each API call, a user has to be authenticated before a call to Yammer API has been made and this has to be done without the user being redirected to Yammer for login or presented with a popup or a button to log in first.

If you follow Yammer’s JavaScript SDK instructions, we will not be meeting our client’s requirement of not asking the user to go Yammer first (as this will change their user flow) or a pop-up with login/sign-in dialog.

Approach

After looking on the internet to fulfill above requirements, I could not find anything that serves us. I have found the closest match in PnP sample but it only works if a client has already consented to your Yammer app before. In our case, this isn’t possible as many users will be accessing SharePoint home page for the first them and have never accessed Yammer before.

What we have done is, let our API login calls break into two groups. Randomly one of the calls was chosen to let the user login to Yammer and get access token in the background and cache it with Yammer API and make other API login calls to wait for the first login and then use Yammer API to log in.

Step-1

This function will use standard Yammer API to check login status if successful then it will proceed with issuing API data retrieval calls, but if could not log in the first time; it will wait and check again after every 2 sec until it times out after 30 sec.

  public static loginToYammer(callback: Function, requestLogin = true) {
    SPComponentLoader.loadScript('https://assets.yammer.com/assets/platform_js_sdk.js', { globalExportsName: "yam"}).then(() => {
      const yam = window["yam"];

        yam.getLoginStatus((FirstloginStatusResponse) => {
        if (FirstloginStatusResponse.authResponse) {
          callback(yam);
        }
        else {
          let timerId = setInterval(()=>{
              yam.getLoginStatus((SecondloginStatusResponse) => {
                if (SecondloginStatusResponse.authResponse) {
                  clearInterval(timerId);
                  callback(yam);
                }
              });
          }, 2000);

          setTimeout(() => {
              yam.getLoginStatus((TimeOutloginStatusResponse) => {
                if (TimeOutloginStatusResponse.authResponse) {
                  clearInterval(timerId);
                }
                else {
                  console.error("iFrame - user could not log in to Yammer even after waiting");
                }
              });
          }, 30000);
        }
      });
    });
  }

Step-2

This method will again use the standard Yammer API to check login status; then tries to log in user in the background using an iframe approach as called out in PnP sample; if that approach didn’t work either then it will redirect user to Smart URL in the same window to get user consent for Yammer app with a redirect URI set to home page of  your SharePoint where web-parts with Yammer API are hosted.

  public static logonToYammer(callback: Function, requestLogin = true) {
    SPComponentLoader.loadScript('https://assets.yammer.com/assets/platform_js_sdk.js', { globalExportsName: "yam"}).then(() => {
      const yam = window["yam"];

      yam.getLoginStatus((loginStatusResponse) => {
        if (loginStatusResponse.authResponse) {
          callback(yam);
        }
        else if (requestLogin) {
          this._iframeAuthentication()
              .then((res) => {
                callback(yam);
              })
              .catch((e) => {
                window.location.href="https://www.yammer.com/[your-yammer-network-name]/oauth2/authorize?client_id=[your-yammer-app-client-id]&response_type=token&redirect_uri=[your-sharepoint-home-page-url]";
                console.error("iFrame - user could not log in to Yammer due to error. " + e);
              });
        } else {
          console.error("iFrame - it was not called and user could not log in to Yammer");
        }
      });
    });
  }

The function _iframeAuthentication is copied from PnP sample with some modifications to fit our needs as per the client requirements were developing against.


  private static _iframeAuthentication(): Promise<any> {
      let yam = window["yam"];
      let clientId: string = "[your-yammer-app-client-id]";
      let redirectUri: string = "[your-sharepoint-home-page-url]";
      let domainName: string = "[your-yammer-network-name]";

      return new Promise((resolve, reject) => {
        let iframeId: string = "authIframe";
        let element: HTMLIFrameElement = document.createElement("iframe");

        element.setAttribute("id", iframeId);
        element.setAttribute("style", "display:none");
        document.body.appendChild(element);

        element.addEventListener("load", _ => {
            try {
                let elem: HTMLIFrameElement = document.getElementById(iframeId) as HTMLIFrameElement;
                let token: string = elem.contentWindow.location.hash.split("=")[1];
                yam.platform.setAuthToken(token);
                yam.getLoginStatus((res: any) => {
                    if (res.authResponse) {
                        resolve(res);
                    } else {
                        reject(res);
                    }
                });
            } catch (ex) {
                reject(ex);
            }
        });

        let queryString: string = `client_id=${clientId}&response_type=token&redirect_uri=${redirectUri}`;

       let url: string = `https://www.yammer.com/${domainName}/oauth2/authorize?${queryString}`;

        element.src = url;
      });
    }

Conclusion

This resulted in authenticating Office 365 tenant user within the same window of SharePoint home page with the help of an iframe [case: the user had consented Yammer app before] or getting a Yammer app consent from the Office 365 tenant user without being redirected to Yammer to do OAuth based authentication [case: the user is accessing Yammer integrated web-parts for the 1st time].

We do hope future releases of Yammer API will cater seamless integration among O365 products without having to go through a hassle to get access tokens in a way described in this post.

[Updated] Yammer group and user export via Yammer API to JSON, then converted to CSV

Originally posted on Lucian’s blog at clouduccino.com. Follow Lucian on Twitter @LucianFrango.


Update: awesome pro-tip

Big shout out to Scott Hoag (@ciphertxt on Twitter) with this pro-tip which will save you having to read this blog post. Yes, you don’t know what you don’t know.

Part of a Yammer network merge (which I am writing a blog post about.. WIP), you would lose data, posts, files etc as that can’t come across. You can however do an export of all that data to, depending on how much there is to export, usually a large .zip file. This is where Scott showed me the light. In that export, there are also two .csv files that contain all the user info in the first, and in the second all the group info. Knowing this, run that export process and you probably don’t need to read the rest of this blog post. #FacePalm.

HOWEVER, and that is a big however for a reason. The network export process does not export what members there are in groups in that groups.csv file. So if you want to to export Yammer groups and all their members, the below blog post is one way of doing that process, just a longer way… 


Yammer network merges are not pretty. I’m not taking a stab at you (Yammer developers and Microsoft Office 365 developers), but, I’m taking a stab.

There should be an option to allow at least group and group member data to be brought across when there is a network merge. Fair enough not bringing any data across as that can certainly be a headache with the vast amount of posts, photos, files and various content that consumes a Yammer network.

However, it would be considerably much less painful for customers if at least the groups and all their members could be merged. It would also make my life a little easier not having to do it.

Let me set the stage her and paint you a word picture. I’m no developer. Putting that out there from the start. I am good at problem solving though and I’m a black belt at finding information online (surfing the interwebs). So, after some deliberation, I found the following that might help with gathering group and user data, to be used for Yammer network merges.

Read More

Yammer Activation in All Eligible Tenants

In an effort to drive the collaboration experience and further the adoption of Yammer, Microsoft announced on the 2nd of February that Yammer now meets all of Office 365’s security and compliance requirements and Yammer will be activated across all Office 365 tenants that contain a Yammer subscription. This will be a retrospective activation as well as being enabled by default for any new tenants. The rollout will be in three stages:

  • Wave 1 has commenced as of February 1, 2016 and includes Office 365 customers with a business subscription who purchased fewer than 150 licenses that includes Yammer and who have zero or one custom domain for Yammer.
  • Wave 2 starts on March 1, 2016 and includes Office 365 customers with a business subscription who purchased fewer than 5,000 licenses that includes Yammer. This does not include customers with an education subscription.
  • Wave 3 starts on April 1, 2016 and includes all remaining customers with a business subscription and all customers with an education subscription.

As Yammer is automatically activated, users with a license that includes Yammer can immediately start accessing the service through Office 365 and Yammer features will be available from within other Office 365 apps. The activation process will either create a new Yammer network or connect to an existing Yammer network that has all or a subset of domains managed within the Office 365 tenant.

Microsoft are continuing to work towards deeper integration between Yammer and Office 365 and this change will lay the ground work for new features. One of the exciting new capabilities coming in the first half of this year is integration with Office 365 Groups adding  the ability to initiate Skype for Business calls, access OneDrive for Business files, schedule Outlook calendar meetings and create tasks within Planner, all from within a Yammer group.

I’ve had a number of customers choose not to deploy Yammer to their user bases for one reason of another and it is possible to block this from occurring. As of February 2016, the Yammer license is an option that can be disabled. Previously the Yammer license was embedded and enabled by default into E plans; provisioning of the Yammer service is what activated a users’ ability to get into Yammer. If your business is not yet ready to adopt Yammer, it will now be necessary to revoke the Yammer license via PowerShell for individual users.

For more information, see https://blogs.office.com/2016/02/02/get-ready-for-yammer/

Programmatically interacting with Yammer via PowerShell – Part 2

In my last post I foolishly said that part 2 would be ‘coming in the next few days’. This of course didn’t happen, but I guess it’s better late than never!

In part 1 which is available here, I wrote how it was possible to post to a Yammer group via a *.ps1 using a ‘Yammer Verified Admin’ account. While this worked a treat, it soon became apparent that this approach had limited productivity rewards. Instead, I wanted to create groups and add users to these groups, all while providing minimal inputs.

Firstly, there isn’t a documented create group .json?, but a quick hunt round the tinterweb with Google helped me uncover the groups.json?. This simply needs a name and whether it’s open or closed, open = $false, closed = $true. So building on my example from Part 1, the below code should create a new group…

$clientID = "fvIPx********GoqqV4A"
$clientsecret = "5bYh6vvDTomAJ********RmrX7RzKL0oc0MJobrwnc"
$Token = "AyD********NB65i2LidQ"
$Group = "Posting to Yammer Group"
$GroupType = $True
$CreateGroupUri = "https://www.yammer.com/api/v1/groups.json?name=$Group&private=$GroupType"

    $Headers = @{
        "Accept" = "*/*"
        "Authorization" = "Bearer "+$Token
        "accept-encoding" = "gzip"
        "content-type" = "application/json"
         }

Invoke-WebRequest -Method POST -Uri $CreateGroupUri -Header $Headers
    You’ll noticed I’ve moved away from Invoke-RestMethod to Invoke-WebRequest. This is due to finding a bug where the script would hang and eventually timeout which is detailed in this link.

All going well, you should end up with a new group which has your ‘Yammer Verified Admin’ as the sole member ala…

YammerGroup

Created Yammer Group

Great, but as I’ve just highlighted, there is only one person in that group, and that’s the admin account we’ve been using. To add other Yammer registered users to the group we need to impersonate. This is only possible via a ‘Yammer Verified Admin’ account for obvious chaos avoiding reasons. So firstly you need to grab the token of the user…

$GetUsersUri = "https://www.yammer.com/api/v1/users.json"
$YammerUPN = "dave.young@daveswebsite.com"
$YammerUsers = (Invoke-WebRequest -Uri $GetUsersUri -Method Get -Headers $Headers).content | ConvertFrom-Json

foreach ($YammerUser in $YammerUsers)
    {
    if ($YammerUser.email -eq $YammerUPN)
        {
        $YammerUserId = $YammerUser.id
        }
    }

$GetUserTokUri = “https://www.yammer.com/api/v1/oauth/tokens.json?user_id=$YammerUserId&consumer_key=$clientID"
$YammerUserDave = (Invoke-WebRequest -Uri $GetUserTokUri -Method Get -Headers $Headers).content | ConvertFrom-Json

To step you through the code. I’ve changed the uri to the users.json, provided the UPN of the user that I want to impersonate and I’m using the headers from the previously provided code. I grab all the users into the $YammerUsers variable and then I do a foreach/if to obtain the id of the user. Now we’ve got that we can use the tokens.json to perform a Get request. This will bring you back a lot of information about the user, but most importantly you’ll get the token!

    user_id : 154**24726
    network_id : 20**148
    network_permalink : daveswebsite.com
    network_name : daveswebsite.com
    token : 18Lz3********Nu0JlvXYA
    secret : Wn9ab********kellNnQgvSfbGJjBfRMWZNICW0JTA
    view_members : True
    view_groups : True
    view_messages : True
    view_subscriptions : True
    modify_subscriptions : True
    modify_messages : True
    view_tags : True
    created_at : 2015/06/15 23:59:19 +0000
    authorized_at : 2015/06/15 23:59:19 +0000
    expires_at :

Storing this into the $UserToken variable allows for you to append this to the Authorization within the Headers so you can impersonate/authenticate on behalf of the user. The code looks like…

$UserToken = $YammerUserDave.token
$YammerGroupId = "61***91"

 $UserHeaders = @{
                "Accept" = "*/*"
                "Authorization" = "Bearer "+$UserToken
                "accept-encoding" = "gzip"
                "content-type" = "application/json"
                }

$PostGroupUri = "https://www.yammer.com/api/v1/group_memberships.json?group_id=$YammerGroupId"
$AddYammerUser = Invoke-WebRequest -Uri $PostGroupUri -Method Post -Headers $UserHeaders

So using the group that we created earlier and the correct variables we then successfully add the user to the group…

DaveGroup

Dave in the group

Something to be mindful of, when you pull the groups or the users it will be done in pages of 50. I found using a Do/While worked nicely to build up the variables so they could then be queried, like this…

If ($YammerGroups.Count -eq 50)
    {
    $GroupCycle = 1
    DO
        {
        $GetMoreGroupsUri = "https://www.yammer.com/api/v1/groups.json?page=$GroupCycle"
        $MoreYammerGroups = (Invoke-WebRequest -Uri $GetMoreGroupsUri -Method Get -Headers $AdminHeaders).content | ConvertFrom-Json    
        $YammerGroups += $MoreYammerGroups
        $GroupCycle ++
        $GroupCount = $YammerGroups.Count
        }
    While ($MoreYammerGroups.Count -gt 0)
    }

Once you’ve got your head around this, then the rest of the API/Json’s on the REST API are really quite useful, my only gripe right now is that they are really missing a delete group json – hopefully it’ll be out soon!

Cheers,

Dave

Programmatically interacting with Yammer via PowerShell – Part 1

For my latest project I was asked to automate some Yammer activity. I’m first to concede that I don’t have much of a Dev background, but I instantly fired up PowerShell ISE in tandem with Google only to find…well not a lot! After a couple of weeks fighting with a steep learning curve, I thought it best to blog my findings, it’s good to share ‘n all that!

    It’s worth mentioning at the outset, if you want to test this out you’ll need an E3 Office 365 Trial and a custom domain. It’s possible to trial Yammer, but not with the default *.onmicrosoft.com domain.

First things first, there isn’t a PowerShell Module for Yammer. I suspect it’s on the todo list over in Redmond since their 2012 acquisition. So instead, the REST API is our interaction point. There is some very useful documentation along with examples of the json queries over at the Yammer developer site, linked here.

The site also covers the basics of how to interact using the REST API. Following the instructions, you’ll want to register your own application. This is covered perfectly in the link here.

When registering you’ll need to provide a Expected Redirect. For this I simply put my Yammer site address again https://www.yammer.com/daveswebsite.com. For the purposes of my testing, I’ve not had any issues with this setting. This URL is important and you’ll need it later so make sure to take a note of it. From the registration you should also grab your Client ID & Client secret.

While we’ve got what appears to be the necessary tools to authenticate, we actually need to follow some steps to retrieve our Admin token.

    It is key to point out that I use the Yammer Verified Admin. This will be more critical to follow for Part 2 of my post, but it’s always good to start as you mean to go on!

So The following script will load Internet Explorer and compile the necessary URL. You will of course simply change the entries in the variables with the ones you created during your app registration. I have obfuscated some of the details in my examples, for obvious reasons 🙂

$clientID = "fvIPx********GoqqV4A"
$clientsecret = "5bYh6vvDTomAJ********RmrX7RzKL0oc0MJobrwnc"
$RedirURL = "https://www.yammer.com/daveswebsite.com"

$ie = New-Object -ComObject internetexplorer.application
$ie.Visible = $true
$ie.Navigate2("https://www.yammer.com/dialog/oauth?client_id=$clientID&amp;redirect_uri=$RedirURL")

From the IE Window, you should login with your Yammer Verified Admin and authorise the app. Once logged in, proceed to this additional code…

$UrlTidy = $ie.LocationURL -match 'code=(......................)'; $Authcode = $Matches[1]
$ie = New-Object -ComObject internetexplorer.application
$ie.Visible = $true
$ie.Navigate2("https://www.yammer.com/oauth2/access_token.json?client_id=$clientID&amp;client_secret=$clientsecret&amp;code=$Authcode")

This script simply captures the 302 return and extracts the $Authcode which is required for the token request. It will then launch an additional Internet Explorer session and prompt you to download an access_token.json. Within here you will find your Admin Token which does not expire and can be used for all further admin tasks. I found it useful to load this into a variable using the code below…

$Openjson = $(Get-Content 'C:\Tokens\access_token.json' ) -join "`n" | ConvertFrom-Json
$token = $Openjson.access_token.token

Ok, so we seem to be getting somewhere, but our Yammer page is still looking rather empty! Well now all the prerequistes are complete, we can make our first post. A good example json to use is posting a message, which is detailed in the link here.

I started with this one mainly because all we need is the Group_ID of one of the groups in Yammer and the message body in json format. I created a group manually and then just grabbed the Group_ID from the end of the URL in my browser. I have provided an example below…

$uri = "https://www.yammer.com/api/v1/messages.json"

$Payloadjson = '{
"body": "Posting to Yammer!",
"group_id": 59***60
}'

$Headers = @{
"Accept" = "*/*"
"Authorization" = "Bearer "+$token
"accept-encoding" = "gzip"
"content-type"="application/json"
}

Invoke-RestMethod -Method Post -Uri $uri -Header $Headers -Body $Payloadjson
Yammer Result

Yammer Post

    It’s at this stage you’ll notice that I’ve only used my second cmdlet, Invoke-RestMethod. Both this and ConvertFrom-Json were introduced in PowerShell 3.0 and specifically designed for REST web services like this.

A key point to highlight here is the Authorisation attribute in the $Headers. This is where the $Token is passed to Yammer for authentication. Furthermore, this $Header construct is all you need going forward. It’s simply a case of changing the -Method, the $uri and the $Payload and you can play around with all the different json queries listed on the Yammer Site.

While this was useful for me, it soon became apparent that I wanted to perform actions on behalf of other users. This is something I’ll look to cover in Part 2 of this Blog, coming in the next few days!

Extending Yammer SSO to Support Users Without an Email Address

BY TONY DU, JOEL NEFF

Yammer Enterprise is offered through the Microsoft Office 365 Enterprise plan. Deployment of Yammer Single Sign-On (SSO) for Office 365 users with a valid primary email address is a relative simple and well documented process.

One of our customers had a requirement for Yammer as a social platform, however a large percentage of their workforce are not enabled for email services. In the ‘SSO Implementation FAQ‘ published by Microsoft, it suggests that it is possible to configure SSO support for user accounts that do not have an email address associated with them, however there isn’t any supporting documentation to go with it.

The process outlined here assumes that Yammer SSO has already been enabled for users with a valid primary email address and all user accounts have been configured with a publicly routable UserPrincipalName suffix (UPN) for logon. This blog post provides guidance for extending Yammer SSO to support users without an email address, requiring a custom claim configuration on ADFS and the Office 365 tenant to enable this scenario.

ADFS Configuration

As in the image below, you should have an existing ‘Relying Party Trust’ configuration on ADFS if Yammer SSO is enabled for ordinary email enabled users.

Note: The ‘E-Mail Address’ at right side column for ‘Outgoing Claim Type‘ should be replaced with ‘SAML_SUBJECT’.

In order to extend the support to users without primary email address the ‘samAccountName’ attribute will be used for the claim rule (you could also use the UserPrincipalName). Therefore the following four custom claim rules need to be created and configured on the ‘Issuance Transform Rules‘ tab under the ‘Relying Party Trusts‘ node of the ADFS management console.

1. Remove the existing rule for ‘E-Mail-Addresses‘ under ‘Issuances Transform Rules
2. Add following custom rules in the order specified below to ensure the logic flows

Rule 1: Check for Email Address
– Click on Add Rules and select custom rule
– Insert the following text and save

@RuleName = &quot;Check for Email&quot;
c:[Type == &quot;http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname&quot;, Issuer == &quot;AD AUTHORITY&quot;]
=&gt; add(store = &quot;Active Directory&quot;, types = (&quot;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress&quot;), query = &quot;;mail;{0}&quot;, param = c.Value);

Rule 2: Check for No Email Address
– Click on Add Rules and select custom rule
– Insert the following text and save

@RuleName = &quot;No email&quot;
NOT EXISTS([Type == &quot;http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress&quot;])
=&gt; add(Type = &quot;http://emailCheck&quot;, Value = &quot;NoEmail&quot;);

Rule 3: If No Email Address Exists Use samAccountName Attribute
– Click on Add Rules and select custom rule
– Insert the following text and save

@RuleName = &quot;Send samAccountName for users without email&quot;
c:[Type == &quot;http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname&quot;, Issuer == &quot;AD AUTHORITY&quot;]
&amp;&amp; [Type == &quot;http://emailCheck&quot;, Value == &quot;NoEmail&quot;]
=&gt; issue(store = &quot;Active Directory&quot;, types = (&quot;SAML_SUBJECT&quot;), query = &quot;;samAccountName;{0}&quot;, param = c.Value);

Rule 4: Use Primary Email Address if email address exists
– Click on Add Rules and select custom rule
– Insert the following text and save

@RuleTemplate = &quot;LdapClaims&quot;
@RuleName = &quot;Send email to Yammer&quot;
c:[Type == &quot;http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname&quot;, Issuer == &quot;AD AUTHORITY&quot;]
=&gt; issue(store = &quot;Active Directory&quot;, types = (&quot;SAML_SUBJECT&quot;), query = &quot;;mail;{0}&quot;, param = c.Value);

The custom rules will be listed in order that they were created in as shown below:

Office 365 Tenant Configuration

You will need to raise a support request with Microsoft to set the ‘Allow Fake Email‘ option on the email domain being used for Yammer SSO. For all user accounts without a valid email address the ‘Fake Email: true‘ flag will be set after its authentication by ADFS and the Microsoft Office 365 Support Engineer will be able validate this for you.

Yammer Directory Synchronization Tool

Yammer DirSync is typically used for synchronising user account information between your Active Directory and Office 365 Yammer. Yammer DirSync does not officially support user accounts without a valid primary email address as stated in the Yammer Directory Synchronization FAQ:

As such, the recommended way to do this would be to manually synchronise your user list to Yammer by using a CSV. To automate the synchronisation for user accounts without an email address, custom coding through the Yammer REST API would be required.

As is documented in the Yammer configuration guide, Yammer DirSync only requires the two attributes of GUID and mail set on the user accounts for it to work. As a workaround it would be possible to populate the mail attribute in Active Directory with the ‘fake’ email address for the user accounts you would like to synchronise, however this may not be a suitable approach for every environment.