<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>The blog of Wictor Wilén</title><link>http://www.wictorwilen.se:80/</link><description></description><item><title>Microsoft Teams Tabs SSO and Microsoft Graph - the "on-behalf-of" blog post</title><link>http://www.wictorwilen.se:80/microsoft-teams-tabs-sso-and-microsoft-graph-the-on-behalf-of-blog-post</link><description>&lt;p&gt;Hey, I'm back. Long time since I did some writing on this blog. But I needed to get this one out. As you all know I'm a huge fan of the Microsoft Teams extensibility model and now with the SSO support for Tabs, it's even easier to create integrated experiences for your end users where they can consume data and information from the Microsoft Graph or LOB systems.&lt;/p&gt;&lt;p&gt;I recently did a small appearance at the &lt;a href="https://www.youtube.com/channel/UC_mKdhw-V6CeCM7gTo_Iy7w"&gt;Microsoft 365 PnP webcast&lt;/a&gt; showcasing how to configure and scaffold a Microsoft Teams project that uses this new SSO Tab feature. You can watch the recording here: &lt;/p&gt;&lt;p&gt;&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/zc9S270c-Dg" frameborder="0" allowfullscreen="" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;p&gt;I've also documented the whole process at the &lt;a href="https://github.com/pnp/generator-teams/wiki/Build-a-Tab-with-SSO-support"&gt;Microsoft Teams Yeoman generator wiki&lt;/a&gt;. &lt;/p&gt;&lt;p&gt;One thing I did not show in that video or in the tutorial is how to access other services. The Tab SSO feature (&lt;strong&gt;microsoftTeams.authentication.getAuthToken&lt;/strong&gt;) in Microsoft Teams gives you a token, but this is just an &lt;strong&gt;identity token&lt;/strong&gt; &lt;strong&gt;that cannot be used as an&lt;/strong&gt; &lt;strong&gt;access token&lt;/strong&gt; to call into the Microsoft Graph - despite we grant permissions on the Tab app in the tutorial.&lt;/p&gt;&lt;p&gt;If we inspect the token we get from Microsoft Teams, using &lt;a href="https://jwt.ms"&gt;https://jwt.ms&lt;/a&gt;, we see the following:&lt;/p&gt;&lt;p&gt;&lt;img width="604" height="627" title="sso-jwt-identity" style="display: inline; background-image: none;" alt="sso-jwt-identity" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Microsoft-Teams-Tabs-SSO-and-Microsoft-G_BA10/sso-jwt-identity_3.jpg" border="0"&gt;&lt;/p&gt;&lt;p&gt;Let's focus on the highlights. &lt;strong&gt;aud&lt;/strong&gt; is the intended audience of the token and in this case it's the Application ID URI of my Tab, as defined in the tutorial. &lt;strong&gt;appid&lt;/strong&gt; is the application id of issuing application - in this case the Microsoft Teams web application. You also see the name of the user to whom the token was issues - and that's what we use when typing out our text in the Tab, and finally we do have the &lt;strong&gt;scp&lt;/strong&gt;/scope we created.&lt;/p&gt;&lt;p&gt;As I said, we cannot use this token to talk to Microsoft Graph or any other services. But how do we do that?&lt;/p&gt;&lt;h2&gt;Exchanging the token using an on-behalf-of flow&lt;/h2&gt;&lt;p&gt;In order to be able to call into Microsoft Graph we need to use the token we have, that ensures our identity and use some server side code to do an &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow"&gt;OAuth 2.0 on-behalf-of flow&lt;/a&gt;&lt;em&gt;&lt;/em&gt;. This means that we take the token we received from Microsoft Teams and send to a service, that accepts the &lt;strong&gt;aud&lt;/strong&gt; claim and then uses this token to exchange that for another token for a set of specified scopes using a client secret. We can then use this newly generated token to call into the services, allowed by the app registration and scopes.&lt;/p&gt;&lt;p&gt;To show you how this is done, I'll use the exact same demo as shown in the linked tutorial from the Yo Teams wiki. Note that this demo is in node.js, using npm packages - but the process is the same for all platforms.&lt;/p&gt;&lt;h2&gt;&lt;/h2&gt;&lt;h2&gt;Preparing the Azure AD Application &lt;/h2&gt;&lt;p&gt;Before we start writing any code let's go to our Azure AD application and do two things;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;First we need to create a client secret which will be used during the on-behalf-of flow (Certificates &amp;amp; Secrets &amp;gt; New Client secret). Store the newly created secret in a secure location&lt;/li&gt;&lt;li&gt;Secondly we will do an admin consent of the permissions grants ( API Permissions &amp;gt; Grant admin consent for &amp;lt;tenant&amp;gt;). In a production scenario you might want to manage this in your application/tab instead and redirect the users/admins to a consent page.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Since we're only going to read the user photo from the user in this demo, there's no need to add additional scopes as we already have added &lt;strong&gt;User.Read&lt;/strong&gt;.&lt;/p&gt;&lt;h2&gt;Building a custom web service for the on-behalf-of-flow&lt;/h2&gt;&lt;p&gt;In my application I will use the &lt;strong&gt;passport&lt;/strong&gt; and &lt;strong&gt;passport-azure-ad&lt;/strong&gt; modules to secure my endpoints and we need to install those modules. We also need the &lt;strong&gt;axios&lt;/strong&gt; and &lt;strong&gt;querystring&lt;/strong&gt; modules for our demo.&lt;/p&gt;&lt;p&gt;&lt;pre class="brush: powershell;"&gt;npm install passport passport-azure-ad --save
npm install @types/passport @types/passport-azure-ad --save-dev
npm install axios querystring --save
&lt;/pre&gt;&lt;p&gt;Once we have this we can define a new Express router that will expose a web api that will accept the identity token, use the token and convert it to an access token that can be used by Microsoft Graph and then finally call into the Graph and return the results.&lt;/p&gt;&lt;p&gt;You will find all the code in this repo and I'll only repeat some of the code in this post: &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo"&gt;https://github.com/wictorwilen/teams-sso-tab-demo&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;The web API&lt;/h3&gt;&lt;p&gt;Under the &lt;strong&gt;./src/app&lt;/strong&gt; folder I create a new folder called &lt;strong&gt;api&lt;/strong&gt; in which I create a new file called &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/master/src/app/api/graphRouter.ts"&gt;graphRouter.ts&lt;/a&gt;. This file exposes one single api (&lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/535ae269d8be3ff9bb211ebf778c1c4fcdd601f5/src/app/api/graphRouter.ts#L60-L90"&gt;GET /photo&lt;/a&gt;). The router itself is also added to &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/535ae269d8be3ff9bb211ebf778c1c4fcdd601f5/src/app/server.ts#L62"&gt;server.ts so it is properly loaded by Express&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;pre class="brush:javascript"&gt; router.get(
  "/photo",
  pass.authenticate("oauth-bearer", { session: false }),
  async (req: express.Request, res: express.Response, next: express.NextFunction) =&amp;gt; {
    ...
});&lt;/pre&gt;&lt;p&gt;As you can see this request is passed through a &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/535ae269d8be3ff9bb211ebf778c1c4fcdd601f5/src/app/api/graphRouter.ts#L62"&gt;passport Express handler&lt;/a&gt;. This handler is configured &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/535ae269d8be3ff9bb211ebf778c1c4fcdd601f5/src/app/api/graphRouter.ts#L11-L25"&gt;further up in the file&lt;/a&gt;. A &lt;em&gt;BearerStrategy&lt;/em&gt; is configured to only accept incoming authorization tokens that has the correct client id and audience (the client id and application id URI for my Tab) - any other tokens will be denied. The handler will also decode the token and add the tenant id, user name and upn to the request object for our convenience.&lt;/p&gt;&lt;p&gt;In order to exchange this identity token for an access token that gives us access to the Microsoft Graph we use a &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/535ae269d8be3ff9bb211ebf778c1c4fcdd601f5/src/app/api/graphRouter.ts#L28-L57"&gt;helper method in the router method&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;pre class="brush:javascript"&gt;const accessToken = await exchangeForToken(user.tid,
  req.header("Authorization")!.replace("Bearer ", "") as string,
  "https://graph.microsoft.com/user.read"]);&lt;/pre&gt;&lt;p&gt;The helper method uses the tenant id, the incoming token as well as an array of scopes to exchange that for a new access token. The helper method itself uses this information together with the client id and&amp;nbsp; the client secret we created to request a new token. You can see the full method on &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/535ae269d8be3ff9bb211ebf778c1c4fcdd601f5/src/app/api/graphRouter.ts#L28-L57"&gt;Github&lt;/a&gt;. Essentially it uses this together with a specific &lt;strong&gt;grant_type&lt;/strong&gt; and &lt;strong&gt;request_token_use=on_behalf_of&lt;/strong&gt; parameters to request the token from a Microsoft Azure AD OAuth v2 endpoint.&lt;/p&gt;&lt;p&gt;The token we receive back should look like below, where you can see that the &lt;strong&gt;aud&lt;/strong&gt; now indicates Microsoft Graph and that we do have the required scopes to read the user photo.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Microsoft-Teams-Tabs-SSO-and-Microsoft-G_BA10/sso-jwt-access_2.jpg"&gt;&lt;img width="604" height="836" title="sso-jwt-access" style="display: inline; background-image: none;" alt="sso-jwt-access" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Microsoft-Teams-Tabs-SSO-and-Microsoft-G_BA10/sso-jwt-access_thumb.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Now all that is left is to fire away the query to the correct Microsoft Graph endpoint and use the access token we received from the helper method.&lt;/p&gt;&lt;p&gt;The Tab should now be able to use this client side and retrieve the photo of the user from Microsoft Graph, without the need of signing in on all platforms (desktop, mobile and web).&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Microsoft-Teams-Tabs-SSO-and-Microsoft-G_BA10/sso-avatar_2.jpg"&gt;&lt;img width="604" height="282" title="sso-avatar" style="display: inline; background-image: none;" alt="sso-avatar" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Microsoft-Teams-Tabs-SSO-and-Microsoft-G_BA10/sso-avatar_thumb.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You can see the the &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/535ae269d8be3ff9bb211ebf778c1c4fcdd601f5/src/app/scripts/ssoDemoTab/SsoDemoTab.tsx#L46-L62"&gt;API call here&lt;/a&gt; and the &lt;a href="https://github.com/wictorwilen/teams-sso-tab-demo/blob/535ae269d8be3ff9bb211ebf778c1c4fcdd601f5/src/app/scripts/ssoDemoTab/SsoDemoTab.tsx#L89"&gt;React implementation here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Good luck have fun!&lt;/strong&gt;&lt;/p&gt;</description><pubDate>Tue, 21 Apr 2020 12:54:08 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/microsoft-teams-tabs-sso-and-microsoft-graph-the-on-behalf-of-blog-post</guid></item><item><title>Version 2.7.0 of the Microsoft Teams Apps generator is now available</title><link>http://www.wictorwilen.se:80/version-2-7-0-of-the-microsoft-teams-apps-generator-is-now-available</link><description>&lt;p&gt;Happy Easter everyone, I have fantastic news. After seven preview versions (and even a skipped version - 2.6) the &lt;a href="https://www.npmjs.com/package/generator-teams"&gt;Microsoft Teams Apps Yeoman generator 2.7.0&lt;/a&gt; is now available for you to use! Just like tons of others do; there's been over 6.000 downloads of the generator, it's generating a handful of new Teams projects every day and it's done from all parts of the world! Join the movement!&lt;/p&gt;&lt;p&gt;&lt;img width="600" height="335" title="YoTeams 2.7.0" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="YoTeams 2.7.0" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/The-Microsoft-Teams-Apps-Yeoman-generato_145C4/YoTeams%202.7.0_3.jpg" border="0"&gt;&lt;/p&gt;&lt;p&gt;As usual it is just a simple npm command to install:&lt;/p&gt;&lt;pre class="brush: powershell;  "&gt;npm install generator-teams --global&lt;/pre&gt;&lt;h2&gt;What's new?&lt;/h2&gt;&lt;p&gt;Version 2.7.0 contains quite a few updates, changes, additions and fixes. For all the details please study the &lt;a href="https://github.com/OfficeDev/generator-teams/blob/master/CHANGELOG.md"&gt;CHANGELOG&lt;/a&gt;. But here is a summary of the more important updates.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Support for Microsoft Bot Framework 4&lt;/strong&gt; - this is one of the more substantial changes. The default scaffolded bot looks very different, using the new Bot Framework 4 and the sample bot created contains some examples for you to build on. Note that the middleware for Microsoft Teams is still in beta.&lt;br&gt;&lt;strong&gt;Message Extensions middleware&lt;/strong&gt; - as the time of publishing this there's no support in the Microsoft Teams Bot middleware for message extensions and to make the code easier to read and extend a dedicated middleware is used to manage message extensions. This middleware is available as a standalone &lt;a href="https://www.npmjs.com/package/botbuilder-teams-messagingextensions"&gt;npm package&lt;/a&gt;. The automatic configuring of the Message Extensions are handled by the &lt;a href="https://www.npmjs.com/package/express-msteams-host"&gt;express-msteams-host&lt;/a&gt; package, and see it's documentation for all the details.&lt;br&gt;&lt;strong&gt;Microsoft Teams schema 1.3&lt;/strong&gt; - the default schema is now 1.3, but also the &lt;em&gt;devPreview&lt;/em&gt; schema is supported for validation.&lt;br&gt;&lt;strong&gt;Restructuring of artefacts&lt;/strong&gt; - from this version and forward each artefact will be scaffolded into its own folder in order to make them easier to manage and find in a larger Microsoft Teams Apps project.&lt;br&gt;&lt;strong&gt;Dynamic generation of the manifest&lt;/strong&gt; - the manifest file created by the generator will now have placeholders for things such as the host name, bot id's and connector id's and more. The value of these placeholders are either defined in the .env file or in the application settings and will be replaced when building; gulp build, gulp serve or in the case of the manifest gulp manifest.&lt;br&gt;&lt;strong&gt;Improved generated code&lt;/strong&gt; - all code is now properly linted and linting support is also added to the generated project. This also resulted in that the default casing of files and class names has changed to support the linting.&lt;br&gt;&lt;strong&gt;Test support&lt;/strong&gt; - when scaffolding a project you now have the option of specifying to include test framework and sample tests (for now only Tabs) using Jest and Enzyme. Huge thanks to &lt;a href="https://twitter.com/cagdasdavulcu"&gt;Çağdaş Davulcu&lt;/a&gt; for helping out with this task.&lt;br&gt;&lt;strong&gt;Bug fixes&lt;/strong&gt; - yes, we found a few bugs and squashed them! &lt;/p&gt;&lt;h3&gt;What about updating?&lt;/h3&gt;&lt;p&gt;Since this is a generator, it generates the skeleton code for your Teams Apps, there's no way to upgrade an already scaffolded project using the generator. If you want the latest and greatest, you should create a new project. Then migrate over your existing customizations. Remember to copy over the application id from your old manifest to the new one.&lt;/p&gt;&lt;h2&gt;What's next?&lt;/h2&gt;&lt;p&gt;All very nice updates and new features! And now we're looking forward and are already planning the next version! What do you want to see?&lt;/p&gt;&lt;p&gt;We've updated the &lt;a href="https://github.com/OfficeDev/generator-teams"&gt;Github repo&lt;/a&gt; with a set of Issue templates to support &lt;a href="https://github.com/OfficeDev/generator-teams/issues/new?assignees=&amp;amp;labels=request%3A+feature&amp;amp;template=feature_request.md&amp;amp;title="&gt;feature suggestions&lt;/a&gt; as well as a &lt;a href="https://github.com/OfficeDev/generator-teams/projects/2"&gt;project board&lt;/a&gt; where we start to manage the features.&lt;/p&gt;&lt;p&gt;Also, if you want to be part of the communication, planning and writing some awesome software - join us on &lt;a href="https://gitter.im/OfficeDev/generator-teams"&gt;Gitter&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href="https://twitter.com/search?q=%23yoteams"&gt;#yoteams&lt;/a&gt;&lt;/p&gt;</description><pubDate>Wed, 17 Apr 2019 22:07:14 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/version-2-7-0-of-the-microsoft-teams-apps-generator-is-now-available</guid></item><item><title>Returning to Vegas for SharePoint Conference 2019</title><link>http://www.wictorwilen.se:80/returning-to-vegas-for-sharepoint-conference-2019</link><description>&lt;p&gt;I’m excited to be returning to Las Vegas in May of 2019 to speak at the &lt;a href="https://sharepointna.com/#!/"&gt;SharePoint Conference 2019&lt;/a&gt; in May 21st to 21rd, at the MGM Grand. &lt;/p&gt;&lt;p&gt;This event is one of the two major events, second one being Microsoft Ignite, that the SharePoint, OneDrive and Yammer product groups are announcing their greatest and latest features and also where you will meet some of the finest speakers and community members of our great SharePoint family.&lt;/p&gt;&lt;p&gt;There will be over 200 session giving you all things you need to adopt, build and manage SharePoint Online and if that is not enough there’s even three days of Workshops, more than 20 of them, with even more deep dives into SharePoint, PowerBI, PowerApps, OneDrive – delivered by amazing speakers. &lt;/p&gt;&lt;h2&gt;Full Page Apps in SharPoint Online&lt;/h2&gt;&lt;p&gt;The session I will be presenting is about how to &lt;strong&gt;Build Full Page Experiences in SharePoint Online&lt;/strong&gt;. We have for a long time build complete and full applications in SharePoint; sometimes as huge Web Parts, sometimes as sweet JavaScript injections and back in the days we could create application pages to create solutions that was a full page experience. In SharePoint Online this functionality has been quite limited, even though i blogged years ago on how to do this using a &lt;a href="http://www.wictorwilen.se/Post/Custom-application-pages-in-the-SharePoint-2010-Sandbox.aspx"&gt;Sandbox solution&lt;/a&gt; (still an article that drives traffic!) . This kind of experience often goes under the name of a SPA (Single-Page-Application). &lt;/p&gt;&lt;p&gt;&lt;img width="644" height="364" title="SPC19 discount code WILEN" style="display: inline; background-image: none;" alt="SPC19 discount code WILEN" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/c9c5205a52bc_93D0/SPC19%20(2)_3.jpg" border="0"&gt;&lt;/p&gt;&lt;p&gt;This session will cover all the different options we have of creating these full page experiences in SharePoint Online, using SharePoint Framework Web Parts, zones and page designs. We will of course, if you know me, spend most of the time looking at the code, but also discuss different strategies on when to use which method. And last but not least, given the announcements on how SharePoint Framework and Microsoft Teams can be used together, this will be a topic that we’ll cover. &lt;/p&gt;&lt;p&gt;It’s been a few years since I presented at the SharePoint conferenes, and it’s always been a blas, remembering one session where we spent an additional 90 minutes of QnA. Bring all of your questions you have on this topic (and of course anything else you think I or other speakers can help with) and get your answers – there’s no better way for you to get value from this conference than getting answers to &lt;em&gt;your &lt;/em&gt;questions.&amp;nbsp; &lt;/p&gt;&lt;p&gt;There is still a few more months to go until May and I’m looking forward to be able to modify the session to incorparate all the latest and greatest features in this area. We’re living in an Evergreen world so this session will be as fresh as possible when delivered – so you have to be there! For the latest updates on the sessions content you can always go to the session page at the SPC19 site: &lt;a title="https://sharepointna.com/#!/session/Build%20Full%20Page%20Experiences%20in%20SharePoint%20Online" href="https://sharepointna.com/#!/session/Build%20Full%20Page%20Experiences%20in%20SharePoint%20Online"&gt;https://sharepointna.com/#!/session/Build%20Full%20Page%20Experiences%20in%20SharePoint%20Online&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;(I know it is currently tagged as an IT-Pro, we’ll fix that, so we can make fun of the IT-pros for realz during the session)&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;Register and save $50&lt;/h2&gt;&lt;p&gt;Talking of being there, if you have yet not signed up for the conference, you should do it right now. And I can save you $50, that you can spend on gifts to your family for the holidays seasons coming up. Go to &lt;a href="https://askwictor.com/SPC19"&gt;https://askwictor.com/SPC19&lt;/a&gt; to register with my discount code &lt;strong&gt;WILEN&lt;/strong&gt; (my awesome last name in all caps – and extra bonus for a correct pronounciation) and you will get a $50 discount. And even better, if you do it before the 15th of January you will get an XBox, a Surface Go or other cool stuff depending on your selected package.&lt;/p&gt;&lt;p&gt;I hope to see you there, in my session! Happy holidays from Sweden, where the snow now has started to fall!&lt;/p&gt;</description><pubDate>Thu, 13 Dec 2018 10:23:00 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/returning-to-vegas-for-sharepoint-conference-2019</guid></item><item><title>Creating a Bot for Microsoft Teams using Microsoft Flow</title><link>http://www.wictorwilen.se:80/creating-a-bot-for-microsoft-teams-using-microsoft-flow</link><description>&lt;p&gt;Imagine you want to create a chat bot for Microsoft Teams in order to automate tasks, enhance the discussion or just feeling lonely and want someone to talk to. There’s many ways of doing this; you can start from scratch building a bot, using the Microsoft Bot framework and/or using the Microsoft Teams Yeoman generator, you can use the Azure Bot Service, you can use the FAQ bots to essentially create a no code solution. &lt;/p&gt;&lt;p&gt;But, what if you need some logic to happen with your bot, what if you’re not that adverse in coding or have all the skills to build something using Microsoft Cognitive Services, such as language understanding and what not. What if you could use Microsoft Flow to automate tasks, initiated from a conversation in Microsoft Teams?&lt;/p&gt;&lt;p&gt;Then this is your lucky day, you can do this almost without any code (more on why a little bit later) and be up and running within minutes – and also without involvement of any admins that needs to approve of your solution!&lt;/p&gt;&lt;h2&gt;&lt;/h2&gt;&lt;h2&gt;The Flow!&lt;/h2&gt;&lt;p&gt;First of all we need a Microsoft Flow to do the stuff we want to do. In this sample I’ll create a bot that I can use in a Teams discussion to create a task in my Microsoft To-Do task lists. The Flow is very simple and consists of one HTTP trigger (When a HTTP Request is recieved) and then a task that add a task to To-Do.&lt;/p&gt;&lt;p&gt;&lt;img width="650" height="1009" title="The Microsoft Flow for the bot" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="The Microsoft Flow for the bot" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_3.png" border="0"&gt;&lt;/p&gt;&lt;p&gt;You should copy and store the URL, generated in the HTTP trigger upon save, as we will need that for our bot in just a minute.&lt;p&gt;For now the Subject is static, but for the Body Content I use the text property of the incoming JSON – that will be sent from the Microsoft Teams client. I use the Dynamic Expression to get text from the Teams message: &lt;pre&gt;triggerBody()['text']&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;NOTE: If you want more help with what properties you can use, such as creating a link to the original message, you can use the HTTP trigger and import a sample paylod found in the &lt;a href="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/outgoingwebhook"&gt;Microsoft Teams documentation&lt;/a&gt;. However currently that page is not up to date with the latest schema, so the best way is to make sure this sample works, and then just take a look at the Flow history and copy-paste the JSON from a successful run.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Creating the Microsoft Teams bot
&lt;/h2&gt;&lt;p&gt;To create the bot in Microsoft Teams we take advantage of the feature called &lt;strong&gt;Outgoing webhook&lt;/strong&gt;. This is a somple way that allows you to send an outgoing message when at-mentioning the bot in a Microsoft Teams team (there’s no personal equivalent at the moment). You can add a new outgoing webhook by going in to the Team settings and then click on the Apps tab. In the lower right corner you will find the link called “Create an outgoing webhook”. Click on that one to create it.&lt;/p&gt;&lt;p&gt;In the dialog that appears you need to fill in the name and description of your outgoing webhook (bot) and optionally upload an icon. What you also need to do is to add the Callback URL. Let’s add our URL generated for the MIcrosoft Flow and give it a shot.&lt;/p&gt;&lt;p&gt;&lt;img width="650" height="596" title="Creating an outgoing webhook" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="Creating an outgoing webhook" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_9.png" border="0"&gt;&lt;/p&gt;&lt;p&gt;When you click on create, you will get another popup with some vital information. It contains a security token, used for validating the message. This is the only time you will see it and if you forget it you have to create a new outgoing webhook. So copy-paste this into a notebook or remember it, you will need it shortly.&lt;/p&gt;&lt;p&gt;&lt;img width="654" height="330" title="Security Token" style="display: inline; background-image: none;" alt="Security Token" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_12.png" border="0"&gt;&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Let’s try this&lt;/h2&gt;&lt;p&gt;So, now we should be able to chat to our outgoing webhook/bot by at-mentioning it in a channel in the Team where we added it.&lt;/p&gt;&lt;p&gt;&lt;img width="650" height="191" title="Talking to the bot?" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="Talking to the bot?" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_15.png" border="0"&gt;&lt;/p&gt;&lt;p&gt;Hmm, that was not what we expected, and this is where a lot of people have stopped trying this approach. It’s not that unexepcted though. Remember I just said the outgoing webhook has a security token, that we should validate, and also the Microsoft Flow does not send any message (JSON) back.&lt;/p&gt;&lt;h2&gt;Azure Functions to the rescue&lt;/h2&gt;&lt;p&gt;In order to get this to work, we need to smack some code up in sort of a proxy, that will sit between the outgoing webhook and the Microsoft Flow. The easiest and probably cheapest way is to use Azure Functions. So let’s do that.&lt;/p&gt;&lt;p&gt;Let’s create a new Azure Function, using the JavaScript runtime stack (if you prefer .net, it’s not my problem and you have to spend the time on your own writing the code). When creating the Azure function I choose to use HTTP Trigger (just as we did for the Microsoft Flow).&lt;/p&gt;&lt;p&gt;&lt;img width="475" height="248" title="Azure Function HTTP Trigger" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="Azure Function HTTP Trigger" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_18.png" border="0"&gt;&lt;/p&gt;&lt;p&gt;In this Azure Function we can now write some code that &lt;/p&gt;&lt;ol&gt;&lt;li&gt;Accepts the webhook coming from Microsoft Teams&lt;/li&gt;&lt;li&gt;Validates the Security Token (HMAC)&lt;/li&gt;&lt;li&gt;Invokes the Microsoft Flow&lt;/li&gt;&lt;li&gt;Sends a response back to the user&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;All the code you need can be found in this Gist: &lt;a title="https://gist.github.com/wictorwilen/8ac6f9c167d70e4774e20f3c39a47a55" href="https://gist.github.com/wictorwilen/8ac6f9c167d70e4774e20f3c39a47a55"&gt;https://gist.github.com/wictorwilen/8ac6f9c167d70e4774e20f3c39a47a55&lt;/a&gt;. You need to do two modifications. &lt;/p&gt;&lt;ul&gt;&lt;li&gt;On line 4 you need to add your Security/HMAC token generated when you created the outgoing webhook&lt;/li&gt;&lt;li&gt;On line 5 you need to add the webhook URL for your Microsoft Flow&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Since this example uses the request npm module, which is not by default installed in an Azure Function you need to go in to the &lt;em&gt;Console&lt;/em&gt; located at the bottom of the Azure Function code editor and run&lt;/p&gt;&lt;pre&gt;npm install request --save&lt;/pre&gt;&lt;p&gt;&lt;img width="600" height="210" title="npm install request --save" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="npm install request --save" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_21.png" border="0"&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Note: for production and real scenarios you might not want to build your Azure function directly in the Web UI and you should have a package.json file so any npm modules are properly restored.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Then, just save the Azure function and go back to Microsoft Teams and the same location where you created your outgoing webhook. Click on the outgoing webhook you created to edit it and then change the URL to the Azure Function URL (found next to the Save and Run buttons in the Azure Function code editor - &lt;em&gt;Get function URL)&lt;/em&gt;.&lt;script src="https://gist.github.com/wictorwilen/8ac6f9c167d70e4774e20f3c39a47a55.js"&gt;&lt;/script&gt;&lt;/p&gt;&lt;h2&gt;Let’s try it again&lt;/h2&gt;&lt;p&gt;Head on back over to Microsoft Teams and into a channel, now try to at-mention the bot again!&lt;/p&gt;&lt;p&gt;&lt;img width="650" height="164" title="It's working!!!" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="It's working!!!" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_24.png" border="0"&gt;&lt;/p&gt;&lt;p&gt;Look at that! It’s working. I can even go over to To-Do and see that I have a new task!&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_26.png"&gt;&lt;img width="424" height="572" title="A task!" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="A task!" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Creating-a-Bot-for-Microsoft-Teams-using_F247/image_thumb_8.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Next steps…&lt;/h2&gt;&lt;p&gt;This was the basics on how to get things working. You can of course make the bot far more advanced by injecting more logic to the Microsoft Flow (obviously) but also into the Azure Function. For instance you might want to invoke different flows, depending on the message. You have full access to the JSON in the Azure Function to do this.&lt;/p&gt;&lt;p&gt;One caveat is that right now you only have access to the message where the bot was at-mentioned. Hopefully Microsoft Flow gets some more Microsoft Teams features so we can extract the full conversation. Meanwhile you can always use the Microsoft Graph, but that involves a few extra steps for app permissions etc.&lt;/p&gt;&lt;h2&gt;Summary&lt;/h2&gt;&lt;p&gt;This approach has already today saved me and my team from having to manually do some steps in our daily routing, by simply at-mentioning a bot in our conversation. What cool stuff are you building with this?&lt;/p&gt;</description><pubDate>Fri, 02 Nov 2018 17:34:44 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/creating-a-bot-for-microsoft-teams-using-microsoft-flow</guid></item><item><title>Announcing Microsoft Teams Apps Yeoman generator 2.5.0</title><link>http://www.wictorwilen.se:80/announcing-microsoft-teams-apps-yeoman-generator-2-5-0</link><description>&lt;p&gt;A long overdue update of the &lt;a href="https://aka.ms/yoteams" target="_blank"&gt;Microsoft Teams Apps Yeoman generator&lt;/a&gt; – we’re now up to version 2.5.0! It’s a fairly substantial update both in the generator and in the generated code – this update will make future updates a lot smoother and will allow for enabling more features going forward. Thanks to all who provided feedback and input and has tested the generator over the last few months. &lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Announcing-Microsoft-Teams-Yeoman-gene.0_C577/generator-teams-2.5.0_2.png"&gt;&lt;img width="654" height="437" title="generator-teams-2.5.0" style="display: inline; background-image: none;" alt="generator-teams-2.5.0" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Announcing-Microsoft-Teams-Yeoman-gene.0_C577/generator-teams-2.5.0_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You can get the latest generator by running&lt;/p&gt;&lt;p&gt;&lt;pre class="brush: bash;"&gt;npm install generator-teams@latest –global&lt;/pre&gt;&lt;h2&gt;Generator updates&lt;/h2&gt;&lt;p&gt;The actual Yeoman generator has been refactored and changed so that it used &lt;a href="https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API" target="_blank"&gt;TypeScript AST&lt;/a&gt; (abstract syntax tree) to dynamically generate some of the TypeScript files. This will open up for adding new artifacts to existing projects without having to do weird string and text file parsing. We’ve also refactored some generated files out to separate npm packages, so that those files and controls can be updated without you having to manually update files or generate new solutions – more about this below.&lt;/p&gt;&lt;h2&gt;Solution updates&lt;/h2&gt;&lt;p&gt;In order to improve the generated app we have moved some of the generated files, controls and base classes into separate npm packages. This allows you to get the latest and greatest in these packages with just a simple npm command – and not having to manually copy and paste.&lt;/p&gt;&lt;p&gt;The generated solution is hosted in a node.js &lt;a href="http://expressjs.com/" target="_blank"&gt;Express server&lt;/a&gt; and the new generated solutions will dynamically discover what components and extensions you have in your solution and automatically register the different routes. All this is done by using &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html" target="_blank"&gt;TypeScript decorators&lt;/a&gt; and Express routes that are defined in the &lt;strong&gt;&lt;a href="https://www.npmjs.com/package/express-msteams-host" target="_blank"&gt;express-msteams-host&lt;/a&gt;&lt;/strong&gt; npm package. Previous versions of the generator had a quite complex server implementation and since it was scaffolded by the generator it was close to impossible to update with new features or fixes. Now all you have to do is decorate your bots, connectors and outgoing webhooks with these decorators and their routes will automatically be picked up and registered. For more information, &lt;a href="https://github.com/wictorwilen/express-msteams-host/blob/master/README.md" target="_blank"&gt;see the documentation&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;A similar approach has been taken to the clients side of the generated solution. A few releases ago the UX was moved to leverage the Microsoft &lt;a href="https://github.com/OfficeDev/msteams-ui-components" target="_blank"&gt;Teams React controls&lt;/a&gt;, and with this version we’ve moved the base page class (previously generated by the generator) into a separate npm package called &lt;strong&gt;&lt;a href="https://www.npmjs.com/package/msteams-react-base-component" target="_blank"&gt;msteams-react-base-component&lt;/a&gt;&lt;/strong&gt;. Similar story here, this will make it way easier for everyone to consume updates to this component without running the generator again.&lt;/p&gt;&lt;p&gt;Of course there are plenty of more stuff that has been fixed or refactored such as; the build pipeline that has been upgraded to Gulp 4 and Webpack 4 and improved logging in the generated solution using the &lt;a href="https://www.npmjs.com/package/debug" target="_blank"&gt;debug module&lt;/a&gt; – and you can read about the other changes in the &lt;a href="https://github.com/OfficeDev/generator-teams/blob/master/CHANGELOG.md" target="_blank"&gt;change log&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;What next?&lt;/h2&gt;&lt;p&gt;It’s exciting times in Microsoft Teams Apps land! We now have a corporate store for Teams Apps, we’ve seen that the SharePoint and Microsoft Teams product groups are having secret meetings to improve the integration between (my favorite) two services. I’m of course looking into this and will share some exciting updates when we close in on more public reveals of features – Microsoft Ignite mayhaps. Oh, if you’re at Microsoft Ignite, try to find me somewhere in a Theater session or lurking in the SharePoint and Microsoft Teams booths if you want to discuss the generator.&lt;/p&gt;&lt;p&gt;&lt;a href="https://twitter.com/search?q=%23yoteams" target="_blank"&gt;#yoteams&lt;/a&gt;&lt;/p&gt;</description><pubDate>Fri, 17 Aug 2018 12:40:52 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/announcing-microsoft-teams-apps-yeoman-generator-2-5-0</guid></item><item><title>SharePoint Framework and Microsoft Graph access – convenient but be VERY careful</title><link>http://www.wictorwilen.se:80/sharepoint-framework-and-microsoft-graph-access-%E2%80%93-convenient-but-be-very-careful</link><description>&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-framework-overview"&gt;SharePoint Framework (SPFx)&lt;/a&gt; is a fantastic development model on top of (modern) SharePoint, for user interface extensibility, and it have evolved tremendously over the last year since it became general available. The framework is based on JavaScript extensibility in a controlled manner, compared to the older JavaScript injection mechanisms we used to extend (classic) SharePoint, that comes with a lot of power.&lt;/p&gt;&lt;p&gt;Using SharePoint Framework our JavaScript has access to the whole DOM in the browser, meaning that we can do essentially what we want with the user interface – however, of course, we shouldn’t, only certain parts of the DOM are allowed/supported for modification. These areas are the custom client-side Web Parts we build (that squared box) or specific place holders (currently only two of them; top and bottom). For me that’s fine (although there’s a need for some more placeholders), but if you want to destroy the UX it is all up to you.&lt;/p&gt;&lt;p&gt;In our client-side solutions we can call out to web services and fetch data and present to the user and even allow the end-user to manipulate this data. For a while now we’ve had limited access to Microsoft Graph, where Microsoft has done the auth plumbing for us, and now in the latest version (&lt;a href="https://github.com/SharePoint/sp-dev-docs/wiki/Release-Notes-for-SPFx-Package-Version-1.4.1"&gt;1.4.1&lt;/a&gt;) a whole new set of API’s to both call Microsoft Graph, with our own specified permission scopes, and even custom web services protected by Azure AD. Very convenient and you can build some fantastic (demo) solutions with this to show the power of UX extensibility in SharePoint Online. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;However – there are some serious security disadvantages that you probably don’t think or even care of if you’re a small business, a happy hacker or just want to build stuff. For me – designing and building solutions for larger enterprises this scares me and my clients…a lot!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img width="640" height="426" title="castle-1461009_640" style="display: inline;" alt="castle-1461009_640" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-and-Microsoft-Graph_93A6/castle-1461009_640_3.jpg"&gt;&lt;/p&gt;&lt;h2&gt;Some perspective&lt;/h2&gt;&lt;p&gt;Let’s take a step back and think about JavaScript injections (essentially SPFx is JavaScript injections – just with a fancier name and in a somewhat controlled way). It’s all very basic things, but from recent “social conversations” it seems like “people” forget. &lt;/p&gt;&lt;p&gt;JavaScript running on a web page, has all the power that an end-user has, one could say even more power, since it can do stuff the user doesn’t see or is aware of. I already mentioned that JavaScript can modify the DOM – like hiding, adding or moving elements around. But it can also execute code, that is not necessarily visible. A good example is for instance to use Microsoft Application Insights to log the behavior of the user or the application – seems like a good thing in most cases (although I don’t think that many users of AppInsights understand how GDPR affects this – but that’s another discussion). We could also use JavaScript to call web services, using the information we have on the page to manipulate the state of the page, and also send data from our page to another page. All without the user noticing it. For good or for bad…let’s come back to the latter in a minute or so.&lt;/p&gt;&lt;h2&gt;No Script sites and the NoScript flag&lt;/h2&gt;&lt;p&gt;Before SharePoint Framework Microsoft introduced “No Script” sites to mitigate the issue with arbitrary JavaScript running in sites and pages. All modern Team sites, based on Office 365 Groups, and OneDrive sites are No Script sites. You can as an admin control the behavior of newly created SharePoint Sites using the settings in the SharePoint admin center (under settings):&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-and-Microsoft-Graph_93A6/image_2.png"&gt;&lt;img width="604" height="99" title="Bad settings for this..." style="display: inline; background-image: none;" alt="Bad settings for this..." src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-and-Microsoft-Graph_93A6/image_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Depending on when your tenant was created (before or after the addition of this setting) your default settings may be different. My recommendation is, of course, to &lt;em&gt;Prevent users from running custom scripts&lt;/em&gt;, to ensure that you don’t get some rogue scripts in there (see below).&lt;/p&gt;&lt;p&gt;This setting can also be set on individual sites using the following SharePoint Online PowerShell command:&lt;/p&gt;&lt;pre class="brush: powershell;  "&gt;Set-SPOsite https://contoso.sharepoint.com/sites/site 
-DenyAddAndCustomizePages 0
&lt;/pre&gt;
&lt;div&gt;
&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;More information here: “&lt;a href="https://support.office.com/en-us/article/Allow-or-prevent-custom-script-1F2C515F-5D7E-448A-9FD7-835DA935584F"&gt;Allow or prevent custom script&lt;/a&gt;”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This setting on a site not only affects JavaScript injections it also prohibits the use of Sandbox solutions and the use of SharePoint Designer – all good things!&lt;/p&gt;&lt;h2&gt;Script Editor Web Part – the wolf in sheep clothes&lt;/h2&gt;&lt;p&gt;“Our favorite” SharePoint extensibility mechanism, specifically for the citizen developers (or whatever you prefer calling them), has been the Script Editor Web Part (SEWP). As an editor of a site in SharePoint we can just drag the SEWP onto a page and add arbitrary scripts to get our job done and we’re done.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The aforementioned No Script setting will make the Script Editor Web Part unavailable on these sites.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The Script Editor Web Part does not exist in modern SharePoint. The whole idea with modern SharePoint and SPFx is that we (admins/editors) should have a controlled and managed way to add customizations to a site – and of course SEWP is on a collision course with that. Having that option would violate the whole idea.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;You can read much more about this in the SharePoint Patterns and Practices article called “&lt;a href="https://github.com/SharePoint/sp-dev-docs/blob/master/docs/spfx/web-parts/guidance/migrate-script-editor-web-part-customizations.md"&gt;Migrate existing Script Editor Web Part customizations to the SharePoint Framework&lt;/a&gt;”.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;But, there is now a &lt;a href="https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-script-editor"&gt;“modern” version of the Script Editor Web Part&lt;/a&gt; available as a part of the SharePoint Patterns and Practices samples repository (which is a bit of a shocker to me). This solution is bypassing the whole idea of SharePoint Framework – controlled and governed JavaScript in SharePoint Online. And of course this is being used by a lot of users/tenants – since it’s simple and it works. If&amp;nbsp; you do use this solution you really should continue reading this…&lt;/p&gt;&lt;h2&gt;SharePoint Framework and Microsoft Graph = power?&lt;/h2&gt;&lt;p&gt;How does this relate to SharePoint Framework then? As I said, with SharePoint Framework we now have a very easy way to access the Microsoft Graph (and other Azure AD secured end-points) with pre-consented permission scopes. As a developer when you build a SharePoint Framework solution you can ask to be granted permissions to the Microsoft Graph and other resources. The admin grants these permissions in the new SharePoint Online admin center under &lt;em&gt;API management. &lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-and-Microsoft-Graph_93A6/image_4.png"&gt;&lt;img width="604" height="239" title="API Management in new SPO admin center" style="display: inline; background-image: none;" alt="API Management in new SPO admin center" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-and-Microsoft-Graph_93A6/image_thumb_1.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;For instance you want to build a Web Part that shows the e-mail or calendar on your portal page, you might want to have access to read and write information to tasks. The possibilities are endless and that is great, or is it?&lt;/p&gt;&lt;p&gt;I think this is a huge area of concern. Imagine these user stories:&lt;/p&gt;&lt;p&gt;“&lt;em&gt;As a user I would like to see my calendar events on my Intranet&lt;/em&gt;” – pretty common request I would say. This requires the SPFx Web Part developer to ask for permissions to read the users calendar.&lt;/p&gt;&lt;p&gt;“As a user I would like to see and be able to update my Planner tasks” – another very common request. This requires the SPFx Web Part developer to ask for Read and Write access to all Groups (that’s just how it is…).&lt;/p&gt;&lt;p&gt;Both these scenarios opens up your SharePoint Online solution for malicious attacks in a very severe way. Of course the actual permission has to be approved by an admin – but how many admins do really understand what’s happening when the business cries “we need this feature”.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Note: this is not just a SharePoint Framework issue, but SPFx makes it so easy that you probably don’t see the forest for the trees. And this is also true for many of these “Intranet-in-a-box” vendors that has made their similar service to access mail/calendars etc from the Graph. It’s still JavaScript and if you allow a single user to add a script it can be misused.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Rogue scripts&lt;/h2&gt;&lt;p&gt;Once you have granted permissions to the Microsoft Graph, by a single request from that fancy calendar Web Part, &lt;u&gt;all other scripts in the whole tenant has those permissions&lt;/u&gt;. So your seemingly harmless Web Part has suddenly exposed your calendar for reading to any other Web Part. Assume that now the admin installs a weather Web Part (downloaded or acquired from a third party). This weather Web Part is now also allowed to read the users e-mail, even though it did not request it. And if that vendor goes rogue or already is, he or she can without the users knowing send all the calendar or e-mail details away to a remote server, while just displaying the weather. This requires some social engineering of course to make the admin install this Web Part. But what about allowing the modern Script Editor Web Part! And you piss an employee off…with just some simple JavaScript knowledge this user can then create a sweet looking cat-of-the-day web part or even a hidden one with the Modern Script Editor Web Part. Then send the boss to that page, and read all the bosses calendar events or e-mails, sending them to some random location somewhere…&lt;/p&gt;&lt;p&gt;&lt;em&gt;You still think this is a good idea?&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;img width="604" height="404" title="activist-anonymous-ddos-attack-38275" style="display: inline; background-image: none;" alt="activist-anonymous-ddos-attack-38275" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-and-Microsoft-Graph_93A6/activist-anonymous-ddos-attack-38275_3.jpg" border="0"&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;And what about the second user story; where we need full read and write to all the groups, just to be able to manipulate tasks in Planner. It’s not much you can do, if you’re building this web part – you are opening up for so many more possibilities for “working with” groups and its associated features. This is not a SharePoint Framework thing, but a drawback in how Microsoft Graph works with permissions and the lack of contextual or fine-grained scopes in Azure AD. Same goes for reading/writing data from SharePoint sites – Azure AD/Microsoft Graph cannot restrict you to a single site or list – you have access to all of them. &lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Remember how SharePoint Add-ins have Site Collection or list scoped permissions. I guess you all remember how we complained back then as well. That was some sweet days and we really want those features back. Well, we still have them – SharePoint add-ins are probably the best way to protect the users and your IP still…&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;As I stated above, of course all SPFx solutions has to be added to the tenant app catalog – but, we also have the option of a site collection scoped catalog. So that’s another vector for insertion of seemingly nice solutions that can take advantage of the permissions you granted on a tenant level.&lt;/p&gt;&lt;h2&gt;The grey area between modern and classic sites&lt;/h2&gt;&lt;p&gt;Currently most SharePoint Online tenants is in a transition period between classic and modern sites. That is, they have built their SharePoint Online environment based on the “classic way” of building stuff, most often requiring script enabled sites. And now they want to transition to modern sites, without these scripting capabilities. Should they just add the new modern Script Editor Web Part or should they turn of scripting for all sites? &lt;/p&gt;&lt;p&gt;If you turn this off I can almost guarantee that a lot of your sites will be useless. And in many cases your whole Intranet – specifically this happens with many of the “Intranet-in-a-box” vendor solutions. So be careful.&lt;/p&gt;&lt;h2&gt;So, what should I do?&lt;/h2&gt;&lt;p&gt;If you still think it is very valuable to build solutions with SPFx and Microsoft Graph the first thing you MUST do is to ensure that there is not a single site in your tenant with scripting enabled. You can do a quick check for this with this sample PowerShell command:&lt;/p&gt;&lt;pre class="brush:powershell"&gt; get-sposite | 
  ?{$_.DenyAddAndCustomizePages -eq 'Disabled'}&lt;/pre&gt;&lt;p&gt;This will list all the site collections which still allow JavaScript execution using the Script Editor Web Part for instance. If there’s a single site in here, stop what you’re doing and don’t even consider granting SPFx any permissions.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;I wish we had this kind of notification and warning in the permission grant page in the new SharePoint Admin center. To make it very obvious for admins.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Secondly, be very thoughtful on what solutions you are installing in your app catalog. Do you know the vendor, do you know their code, is their code hosted in a vendor CDN (warning signs – since they can update this without you knowing) etc? Do you have multiple vendors? Who have access to do this?&lt;/p&gt;&lt;p&gt;So, you really need to do a proper due diligence of the code you let into your SharePoint Online tenant.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A note on the CDN issue; when you add a SPFx solution to the app catalog all “registered” external script locations are listed. But this is not a guarantee. It’s only those that are registered in the manifest. As a developer you can request other resources dynamically without having them show up on this screen.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;Summary&lt;/h2&gt;&lt;p&gt;I hope that this gave you the chills, and that you start reflecting on these seemingly harmless weather web parts that you install. You as an admin, developer or purchaser of SharePoint Online customizations MUST think this over. &lt;/p&gt;&lt;ol&gt;&lt;li&gt;Do we have a plan to move from script enabled sites to ALL sites with no custom scripts enabled&lt;/li&gt;&lt;li&gt;Do we have the knowledge and skill to understand what our developers and vendors are adding to our SharePoint Online tenant&lt;/li&gt;&lt;li&gt;Do we understand the specific permission requirements needed&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I also hope (and know) that the SharePoint Framework product team listens, this is an area which needs to be addressed. We want to build these nicely integrated solutions, but we cannot do it on behalf of security concerns. And it’s NOT about security in SharePoint or SharePoint Framework it is about how web browsers work. What we need is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Visibility – make it visible to the admins what is really happening in their tenant; script enabled sites, SEWP instances etc.&lt;/li&gt;&lt;li&gt;Isolation – we need to be able to isolate our web parts, so that they and their permissions scopes cannot be intercepted or misused. In the web world I guess that Iframes is the only solution&lt;/li&gt;&lt;li&gt;Granularity - Azure AD permission granularity – it’s not sustainable to give your applications these broad permissions (Group Write All). I want to give my app access to write in one Planner plan only and not in all groups.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Thanks for reaching the end! I oversimplified some parts, but if you have any concerns, questions or issues with my thinking – don’t hesitate to debate, confront, question or agree with this post.&lt;/p&gt;</description><pubDate>Tue, 03 Apr 2018 11:18:59 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/sharepoint-framework-and-microsoft-graph-access-%E2%80%93-convenient-but-be-very-careful</guid></item><item><title>Finally! Proper custom themes in SharePoint Online!</title><link>http://www.wictorwilen.se:80/finally-proper-custom-themes-in-sharepoint-online</link><description>&lt;p&gt;Microsoft Ignite is just around the corner and the sheer number of new announcements for SharePoint and SharePoint Online has been almost overwhelming. The team is making such a tremendous job right now!&lt;/p&gt;&lt;p&gt;One of my favorite features, that I have requested both privately and openly with Microsoft, is the ability to have custom themes for SharePoint. Yes, we had the old “look and feel” thing, custom CSS thing, Office 365 suite bar branding, but there has never been a good way of using this in Modern sites or even the possibility to turn of the default themes. And now, last week, Microsoft announced a new set of features that can do all of this for us – create custom themes, a nice theme designer and the ability to hide the default themes.&lt;/p&gt;&lt;p&gt;Let’s go through how this works (note that the feature is not currently available in all tenants)…&lt;/p&gt;&lt;h3&gt;How to add your own themes&lt;/h3&gt;&lt;p&gt;By default in Modern sites (Teams, Communications and Hub sites) in SharePoint Online you are given a set of default themes (Blue, Orange, Red, Purple, Green and Gray), which can be changed through the cog wheel settings menu in the suite bar and then choose &lt;em&gt;Change the look&lt;/em&gt;.&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Finally-Proper-custom-themes-in-SharePoi_CFDD/image_2.png"&gt;&lt;img width="604" height="369" title="Default Themes" style="display: inline; background-image: none;" alt="Default Themes" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Finally-Proper-custom-themes-in-SharePoi_CFDD/image_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;In order to create your own Theme, you go to the theme builder at &lt;a href="http://aka.ms/spthemebuilder"&gt;aka.ms/spthemebuilder&lt;/a&gt;. Using this tool you can create a theme visually and then get a set of snippets to be used in PowerShell to add the theme to your tenant.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Finally-Proper-custom-themes-in-SharePoi_CFDD/SNAGHTML83ccfb6.png"&gt;&lt;img width="600" height="502" title="The Theme Builder" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="The Theme Builder" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Finally-Proper-custom-themes-in-SharePoi_CFDD/SNAGHTML83ccfb6_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Once you have created your theme, all you need to do is to fire up your SharePoint Online PowerShell window and start writing some PowerShell. Make sure that you have the latest version of the &lt;a href="https://www.microsoft.com/en-us/download/details.aspx?id=35588"&gt;SharePoint Online Management Shell&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;First of all you need to connect to your SPO tenant:&lt;/p&gt;
&lt;pre class="brush: powershell; toolbar: false"&gt;Connect-SPOService -Url https://contoso-admin.sharepoint.com
&lt;/pre&gt;
&lt;p&gt;While the theme builder has a great feature that allows you to export the PowerShell settings required to create your theme, it does not really work (at least not in the builder and the shell versions that exists at the time of writing this blog post). The theme builder PowerShell generates a Hashtable but the PowerShell command requires a Dictionary object, so here's a quick way to do that conversion (until they fix the builder and/or the cmdlet).
&lt;/p&gt;
&lt;pre class="brush: powershell; toolbar: false"&gt;$builder = [past the PowerShell code from the builder here]
$theme = New-Object "System.Collections.Generic.Dictionary``2[System.String,System.String]"
$builder.Keys | %{$theme.Add($_, $builder[$_])}
&lt;/pre&gt;

&lt;p&gt;Now, that we have a PowerShell variable with our Theme we can use the &lt;strong&gt;Add-SPOTheme&lt;/strong&gt; cmdlet to add our theme, like this:&lt;/p&gt;
&lt;pre class="brush: powershell; toolbar: false"&gt;Add-SPOTheme -Name "Contoso Purple" -Palette $theme -IsInverted:$false&lt;/pre&gt;

&lt;p&gt;And voilá! We have a new custom theme available:&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Finally-Proper-custom-themes-in-SharePoi_CFDD/image_4.png"&gt;&lt;img width="604" height="369" title="Oh, looky - a custom theme" style="display: inline; background-image: none;" alt="Oh, looky - a custom theme" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Finally-Proper-custom-themes-in-SharePoi_CFDD/image_thumb_1.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;We can in the same way add more themes, and when we don’t want them anymore we can use the &lt;strong&gt;Remove-SPOTheme&lt;/strong&gt; cmdlet. There’s also a &lt;strong&gt;Get-SPOTheme&lt;/strong&gt; cmdlet that allows you to get a theme by name, unfortunately it is not possible to use that cmdlet without any parameters and list all available ones (feedback SP Team, feedback).&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;IsInverted&lt;/strong&gt; flag is used for dark theme (true) and light theme (false9, so SharePoint knows when to render light text on top of dark and vice versa.&lt;/p&gt;&lt;h2&gt;Hide the default ones&lt;/h2&gt;&lt;p&gt;An almost as cool feature is that you can actually hide the default themes. Using the &lt;strong&gt;Set-HideDefaultThemes&lt;/strong&gt; cmdlet you can turn the default themes on or off (oh, and I don’t know why this cmdlet is not prefixed with SPO!?)&lt;/p&gt;&lt;pre class="brush: powershell; toolbar: false"&gt;Set-HideDefaultThemes -HideDefaultThemes:$true
&lt;/pre&gt;&lt;p&gt;And now you should only see your themes:&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Finally-Proper-custom-themes-in-SharePoi_CFDD/image_6.png"&gt;&lt;img width="604" height="369" title="No default stinkin stuff here..." style="display: inline; background-image: none;" alt="No default stinkin stuff here..." src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Finally-Proper-custom-themes-in-SharePoi_CFDD/image_thumb_2.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If you want the default ones back you just fire off this:&lt;/p&gt;
&lt;pre class="brush: powershell; toolbar: false"&gt;Set-HideDefaultThemes -HideDefaultThemes:$false
&lt;/pre&gt;&lt;h2&gt;More options&lt;/h2&gt;&lt;p&gt;[Added] &lt;a href="https://twitter.com/vesajuvonen"&gt;Vesa Juvonen&lt;/a&gt; pointed out so correctly that you can do this programmatically as well. You can check the &lt;a href="https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-theming/sharepoint-site-theming-overview"&gt;full documentation&lt;/a&gt; of this feature here with &lt;a href="https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-theming/sharepoint-site-theming-rest-api"&gt;REST&lt;/a&gt; and &lt;a href="https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-theming/sharepoint-site-theming-csom"&gt;CSOM&lt;/a&gt; options for ya devs.&lt;/p&gt;&lt;h2&gt;Summary&lt;/h2&gt;&lt;p&gt;The new themes features in SharePoint Online will make it easier to have a consistent look and feel in all your Modern SharePoint sites, and will be a feature that your communications and marketing departments will love.&lt;/p&gt;</description><pubDate>Mon, 02 Oct 2017 13:32:00 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/finally-proper-custom-themes-in-sharepoint-online</guid></item><item><title>Using Device Codes to authenticate Bots with Azure AD</title><link>http://www.wictorwilen.se:80/using-device-codes-to-authenticate-bots-with-azure-ad</link><description>&lt;p&gt;I’ve been building chat-bots for a while now and I’m seeing more and more requests of building these bots for enterprises. For bots targeted at the enterprise, perhaps being hosted in Microsoft Teams, one of the first requirements is that they should get data from their internal systems and most specifically from Office 365, through the Microsoft Graph. The problem here is that we need to authenticate and authorize the user, through Microsoft Azure AD, to be able to access these resources. A Microsoft Bot Framework bot, does not inherit the credentials or security tickets from the application the bot is being invoked from, so we need handle this ourselves. For instance, even though you have logged in to Microsoft Teams, or Skype for Business or your Intranet – your security token cannot (and should not) be passed to the Bot.&lt;/p&gt;&lt;p&gt;This is not mission impossible, and there are multiple ways of implementing this. For instance if you’re building Bot Framework bots using .NET you can use the &lt;a href="https://github.com/MicrosoftDX/AuthBot"&gt;AuthBot&lt;/a&gt; and with node.js there’s the &lt;a href="https://github.com/MicrosoftDX/botauth"&gt;botauth&lt;/a&gt; module. There’s also other (a bit weird and specialized) ways of doing this by using the backchannel. &lt;/p&gt;&lt;p align="left"&gt;All of these are custom implementations with either sending an already existing access token to the bot or using home brewed magic number generators. But, there’s a much simpler way of doing this – using the native and built-in features of the &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-libraries"&gt;Azure Active Directory Authentication Library (ADAL)&lt;/a&gt;, specifically using the &lt;a href="https://tools.ietf.org/html/draft-ietf-oauth-device-flow-06"&gt;OAuth 2.0 Device Flow&lt;/a&gt;. &lt;/p&gt;&lt;p align="left"&gt;In this post I will demonstrate how to create a bot from scratch and use the device flow to sign in and get data from Microsoft Graph. It will all be built using node.js and TypeScript – but the procedure is the same for any kind of environment.&lt;/p&gt;&lt;h2 align="left"&gt;Creating the bot&lt;/h2&gt;&lt;p align="left"&gt;First of all we need to create a bot using the &lt;a href="https://dev.botframework.com/"&gt;Bot Framework portal&lt;/a&gt;. Give the bot a name, handle, description and specify the messaging endpoint. You can use localhost for testing but in the end you should have a publically available URL to be able to use it in the different Bot channels. In this sample we need to make sure that the messaging endpoint ends with &lt;code&gt;/api/messages&lt;/code&gt;. Then you need to create a Microsoft App ID and a password – just follow the wizard and copy and take a note of the ID and specifically the password – you will only see it once. Once you’re done, save your bot.&lt;/p&gt;&lt;h2 align="left"&gt;Configuring the App platform for the bot&lt;/h2&gt;&lt;p align="left"&gt;The bot created in the Bot Framework portal, is essentially an Application in the &lt;a href="https://apps.dev.microsoft.com/"&gt;Microsoft Application Registration Portal&lt;/a&gt;. In order to use this Application ID with Azure AD and Microsoft Graph, we need to log in to that portal and find our newly registered bot and then add a platform for it. In this case let’s add a &lt;em&gt;Native Application&lt;/em&gt;. You don’t have to configure it or anything, it just needs to have a platform. &lt;/p&gt;&lt;p align="left"&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Using-Device-Codes-to-authenticate-Bots-_BEAC/device-code-bot-2_LI_2.jpg"&gt;&lt;img width="504" height="308" title="Setting the platform for the App" style="display: inline; background-image: none;" alt="Setting the platform for the App" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Using-Device-Codes-to-authenticate-Bots-_BEAC/device-code-bot-2_LI_thumb.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p align="left"&gt;In this portal you can also add the delegated permissions for your bot, under &lt;em&gt;Microsoft Graph Permissions. &lt;/em&gt;For the purpose of this demo we only need the &lt;strong&gt;User.Read&lt;/strong&gt; permissions.&lt;/p&gt;&lt;h2 align="left"&gt;Let’s write some code&lt;/h2&gt;&lt;p align="left"&gt;Next step is to actually start writing some code. This will be done in node.js, using TypeScript and a set of node modules. The most important node modules used in this demo are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;strong&gt;webpack&lt;/strong&gt; – bundles our TypeScript files&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;strong&gt;ts-loader&lt;/strong&gt; – webpack plugin that transpiles TypeScript to JavaScript&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;strong&gt;express&lt;/strong&gt; – node.js webserver for hosting our Bot end-point&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;strong&gt;adal-node&lt;/strong&gt; – ADAL node.js implementation&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;strong&gt;@microsoft/microsoft-graph-client&lt;/strong&gt; – a Microsoft Graph client&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;strong&gt;botbuilder&lt;/strong&gt; – Bot Framework bot implementation&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p align="left"&gt;All code in this sample are found in this Github repo: &lt;a title="https://github.com/wictorwilen/device-code-bot" href="https://github.com/wictorwilen/device-code-bot"&gt;https://github.com/wictorwilen/device-code-bot&lt;/a&gt;. To use it, just clone the repo, run npm install. Then to be able to run it locally or debug it you can add a file called .env and in that file add your Application ID and password as follows:&lt;/p&gt;&lt;pre class="brush: shell; toolbar: false"&gt;MICROSOFT_APP_ID=fa781336-3114-4aa2-932e-44fec5922cbd
MICROSOFT_APP_PASSWORD=SDA6asds7aasdSDd7&lt;/pre&gt;&lt;p&gt;The hosting of the bot, using express, is defined in the /src/server.ts file. For this demo this file contains nothing specific, part from starting the implementation of the bot – which is defined in /src/devicecodebot.ts.&lt;/p&gt;&lt;p&gt;In the bot implementation you will find a constructor for the bot that creates two dialogs; the default dialog and a dialog for sign-ins. It will also initialize the ADAL cache.&lt;/p&gt;&lt;pre class="brush: javascript; toolbar: false"&gt;constructor(connector: builder.ChatConnector) {
    this.Connector = connector;
    this.cache = new adal.MemoryCache()

    this.universalBot = new builder.UniversalBot(this.Connector);
    this.universalBot.dialog('/', this.defaultDialog);
    this.universalBot.dialog('/signin', this.signInDialog)
}&lt;/pre&gt;&lt;p&gt;The implementation of the default dialog is very simple. It will just check if we have already logged in, but in this demo we will not set that value, so a login flow will always be started by starting the sign-in dialog.&lt;/p&gt;&lt;p&gt;The sign-in dialog will create a new ADAL AuthenticationContext and then use that context to acquire a user code.&lt;/p&gt;&lt;pre class="brush: javascript; toolbar: false"&gt;var context = new AuthenticationContext('https://login.microsoftonline.com/common', 
  null, this.cache);
    context.acquireUserCode('https://graph.microsoft.com', 
      process.env.MICROSOFT_APP_ID, '', 
      (err: any, response: adal.IUserCodeResponse) =&amp;gt; {
        ...
});&lt;/pre&gt;&lt;p&gt;The result from this operation (IUserCodeResponse) is an object with a set of values, where we in this case should pay attention to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;userCode&lt;/strong&gt; – the code to be used by the user for authentication&lt;/li&gt;&lt;li&gt;&lt;strong&gt;message&lt;/strong&gt; – a friendly message containing the verification url and the user code&lt;/li&gt;&lt;li&gt;&lt;strong&gt;verificationUrl&lt;/strong&gt; – the url where the end user should use the user code (always aka.ms/devicelogin)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;We use this information to construct a Bot Builder Sign-In Card. And send it back to the user:&lt;/p&gt;&lt;pre class="brush: javascript; toolbar: false"&gt;var dialog = new builder.SigninCard(session);
dialog.text(response.message);
dialog.button('Click here', response.verificationUrl);
var msg = new builder.Message();
msg.addAttachment(dialog);
session.send(msg);&lt;/pre&gt;&lt;p&gt;This allows us to from Bot Framework channel invoke the authorization flow for the bot. The end-user should click on the button, which opens a web browser (to aka.ms/devicelogin) and that page will ask for the user code. After the user entered the user code, the user will be asked to authenticate and if it is the first time also consent to the permissions asked for by the bot.&lt;/p&gt;&lt;p&gt;In our code we then need to wait for this authorization, authentication and consent to happen. That is done as follows:&lt;/p&gt;&lt;pre class="brush: javascript; toolbar: false"&gt;context.acquireTokenWithDeviceCode('https://graph.microsoft.com',
   
process.env.MICROSOFT_APP_ID, response, 
  (err: any, tokenResponse: adal.IDeviceCodeTokenResponse) =&amp;gt; {
    if (err) {
      session.send(DeviceCodeBot.createErrorMessage(err));
      session.beginDialog('/signin')
    } else {
        session.userData.accessToken = tokenResponse.accessToken;
        session.send(`Hello ${tokenResponse.givenName} ${tokenResponse.familyName}`);
        ...
    }
});	&lt;/pre&gt;&lt;p&gt;The result from this operation can of course fail and we need to handle that, in this case just sending the error as a message and restart the sign-in flow. If successful we will get all the data we need to continue (&lt;strong&gt;IDeviceCodeTokenResponse&lt;/strong&gt;) such as access-token, refresh-token, user-id, etc. In a real world scenario you should of course store the refresh token, in case the access token times out. And it is also here that we potentially tells our bot that the user is signed in redirects subsequent dialogs to what we want to do.&lt;/p&gt;&lt;p&gt;Now we can use this access token to grab some stuff from the Microsoft Graph. The following code, with a very simplistic approach, where wo do not handle timed out access tokens, we just grab the title of the user and sends it back to the user.&lt;/p&gt;&lt;pre class="brush: javascript; toolbar: false"&gt;const graphClient = MicrosoftGraph.Client.init({
    authProvider: (done: any) =&amp;gt; {
        done(null, session.userData.accessToken);
    }
});
graphClient.
    api('/me/jobTitle').
    version('beta').
    get((err: any, res: any) =&amp;gt; {
        if (err) {
            session.send(DeviceCodeBot.createErrorMessage(err));
        } else {
            session.endDialog(`Oh, so you're a ${res.value}`);
        }
    });
    }
});&lt;/pre&gt;&lt;h2&gt;Run the application&lt;/h2&gt;&lt;p&gt;To run the application first we need to transpile and bundle it using webpack like this:&lt;/p&gt;&lt;pre class="brush: shell; toolbar: false"&gt;npm run-script build&lt;/pre&gt;&lt;p&gt;The we start the express server like this:&lt;/p&gt;&lt;pre class="brush: shell; toolbar: false"&gt;npm run-script run&lt;/pre&gt;&lt;p&gt;To test it locally we need to use the &lt;a href="https://docs.microsoft.com/en-us/bot-framework/debug-bots-emulator"&gt;Bot Framework emulator&lt;/a&gt;. Download it, run it and configure it to run at http://localhost:3007/api/messages. Type anything in the emulator to start the sign-in experience&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Using-Device-Codes-to-authenticate-Bots-_BEAC/device-code-bot-3.png"&gt;&lt;img width="504" height="435" title="Testing the bot with the Bot Framework emulator" style="display: inline; background-image: none;" alt="Testing the bot with the Bot Framework emulator" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Using-Device-Codes-to-authenticate-Bots-_BEAC/device-code-bot-3_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;As soon as you’ve written something the Sign-In card will be displayed. When you click on the button a browser window will open and you will be asked to type the code. When you’ve done that you will be asked to sign-in and consent. And shortly after that the bot will come alive again and type the users name and if all works well, also the job title of the user.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Using-Device-Codes-to-authenticate-Bots-_BEAC/device-code-bot-1.png"&gt;&lt;img width="504" height="528" title="Consenting the device code bot" style="display: inline; background-image: none;" alt="Consenting the device code bot" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Using-Device-Codes-to-authenticate-Bots-_BEAC/device-code-bot-1_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If you decide to publish your bot (for instance to Azure, all the necessary files are in the Github repo to Git publish it to Azure) you can also use the bot in other channels, for instance Skype:&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Using-Device-Codes-to-authenticate-Bots-_BEAC/device-code-bot-4.png"&gt;&lt;img width="504" height="394" title="The device code bot in Skype" style="display: inline; background-image: none;" alt="The device code bot in Skype" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Using-Device-Codes-to-authenticate-Bots-_BEAC/device-code-bot-4_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Summary&lt;/h2&gt;&lt;p&gt;As you’ve now seen. It is very easy to create a simple and elegant sign-in flow for your bots, without sacrificing any security, and all using standard features of ADAL and OAuth. This will nicely work with any Azure AD accounts, with MFA or not.&lt;/p&gt;</description><pubDate>Sun, 03 Sep 2017 13:05:24 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/using-device-codes-to-authenticate-bots-with-azure-ad</guid></item><item><title>Re-awarded as Microsoft MVP for the 8th year</title><link>http://www.wictorwilen.se:80/re-awarded-as-microsoft-mvp-for-the-8th-year</link><description>&lt;p&gt;Today is the day where the Microsoft community officially award the community with the Microsoft MVP award. We will all receive some new friends and old friends and also we loose some dear friends (that we of course hope to see back into the program again). I’ve been fortunate to be re-awarded with the Office Servers and Services Microsoft MVP award, for the 8th consecutive year. Thank you Microsoft!&lt;/p&gt;&lt;p&gt;&lt;img width="650" height="337" title="MVP Award e-mail" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="MVP Award e-mail" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Reawarded-as-Microsoft-MVP-for-the-8th-y_F8F2/SNAGHTML6bbfc35_1.png" border="0"&gt;&lt;/p&gt;&lt;p&gt;As usual, being an MVP is not something you can take for granted&amp;nbsp; and you have to work hard to stay in the program. But all you can do is have fun and share your joy with the rest of the Microsoft community. I really would like to thank my peer MVPs, the community and the Microsoft product teams – specifically the SharePoint and Teams teams. I’ve had tons of fun this year and I’m looking forward to an exciting new year ahead.&lt;/p&gt;</description><pubDate>Sat, 01 Jul 2017 15:48:20 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/re-awarded-as-microsoft-mvp-for-the-8th-year</guid></item><item><title>yo teams have a new home, and officially backed by Microsoft</title><link>http://www.wictorwilen.se:80/yo-teams-have-a-new-home-and-officially-backed-by-microsoft</link><description>&lt;p&gt;A couple of months back I started creating a &lt;a href="http://yeoman.io/"&gt;Yeoman generator&lt;/a&gt; to make it easier for me to &lt;a href="http://www.wictorwilen.se/yo_teams-tab"&gt;scaffold, build and deploy the Microsoft Teams extensions&lt;/a&gt; (now apps). I’ve received very good feedback on it and had some very nice contributions to the project, which was hosted on my &lt;a href="https://github.com/wictorwilen"&gt;public Github account&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To really make this available for everyone to use I’ve been discussing this project with the Microsoft Teams team about having it “officially backed” by the &lt;em&gt;real&lt;/em&gt; team and nut just me as an individual. After some interesting discussions the Microsoft Teams generator now have a new home.&lt;/p&gt;&lt;p&gt;The Microsoft Teams Yeoman generator are now transferred to the &lt;a href="https://github.com/OfficeDev"&gt;OfficeDev organization on Github&lt;/a&gt; and lives in this repository: &lt;a title="https://github.com/OfficeDev/generator-teams" href="https://github.com/OfficeDev/generator-teams"&gt;https://github.com/OfficeDev/generator-teams&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I think this is great and it will allow more organizations to actually use the generator. We’ve switched to &lt;a href="https://github.com/OfficeDev/generator-teams/blob/master/LICENSE.md"&gt;MIT licensing&lt;/a&gt; and we added some contribution guidelines to be able to do this move. The rest is intact. All the old links to the repo will now redirect to the new one and you still use npm to install it in the same way.&lt;/p&gt;&lt;p&gt;We have some nice updates coming shortly to it, of which some you can see in the &lt;a href="https://github.com/OfficeDev/generator-teams/tree/preview"&gt;preview branch&lt;/a&gt;, that uses all the latest and greatest features of Microsoft Teams Apps.&lt;/p&gt;&lt;p&gt;A big thank you to &lt;a href="https://twitter.com/bill_bliss"&gt;Bill Bliss&lt;/a&gt; who set things in motion and did all the heavy lifting, and of course to all the contributors to the generator and to the great Microsoft Teams team!&lt;/p&gt;&lt;p&gt;#yoteams&lt;/p&gt;</description><pubDate>Sat, 01 Jul 2017 09:26:00 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/yo-teams-have-a-new-home-and-officially-backed-by-microsoft</guid></item><item><title>How to generate SharePoint Framework bundles for multiple tenants</title><link>http://www.wictorwilen.se:80/how-to-generate-sharepoint-framework-bundles-for-multiple-tenants</link><description>&lt;p&gt;If you are an ISV or SI with multiple clients and are interested in building SharePoint Framework (SPFx) solutions that you would like to re-use you will face a huge issue when it comes to reference SharePoint JavaScript files and reference your SharePoint Framework bundles. All these URL's are hardcoded into your solution configuration files and requires you to update these files and rebuild for each and every client environment. And not only that even in your own development team this will cause issues if you don't have a shared development environment.&lt;/p&gt; &lt;p&gt;This causes a lot of issues and headaches. Each and every developer needs to update the configuration files in the SharePoint Framework - meaning that they will check-out the files and then eventually check them back in with their specific tenant information, which will break the solution for another developer. Same goes if you want to deploy a solution to another client; you check the files out update with the new client information and the more clients you have the worse it gets. &lt;/p&gt; &lt;p&gt;The SharePoint Framework is essentially built so that you should NOT reference any SharePoint JavaScript files (think CSOM/JSOM) and always host your bundled SPFx files in a public CDN. In practice this doesn't work. There are tons of features in JSOM that you would like to use, such as managed metadata. Also very few clients really want their JavaScripts to be hosted in a location they don't own or have control of.&lt;/p&gt; &lt;p&gt;So, SharePoint Framework as of now is very limited and it is a mess for you as a developer, SI or ISV. I know, that's exactly where I've been, until now!&lt;/p&gt; &lt;h2&gt;Introducing the spfx-build-url-rewrite node package&lt;/h2&gt; &lt;p&gt;To sort this issue out I've built a node.js package called &lt;a href="https://www.npmjs.com/package/spfx-build-url-rewrite" target="_blank"&gt;spfx-build-url-rewrite&lt;/a&gt; that helps you re-write those URLs at build time. All it requires is that you in your config files use a specific URL that the package knows about (currently it's contoso.sharepoint.com - I know, I'll make it configurable/better later) and when building you specify the URL you want to replace it with, and voila - you can now automate builds for any number of clients/environments.&lt;/p&gt; &lt;h3&gt;How it works&lt;/h3&gt; &lt;p&gt;First of all you need to install the node module into your SPFx solution using npm:&lt;/p&gt;&lt;pre class="brush: shell; toolbar: false"&gt;npm install spfx-build-url-rewrite --save&lt;/pre&gt;
&lt;p&gt;Then you need to modify the gulpfile.js to use this module. Just before the initialize method you need to add two lines so it looks like this:&lt;/p&gt;&lt;pre class="brush: javascript; toolbar: false"&gt;const rewrite = require('spfx-build-url-rewrite');
rewrite.config(build);

build.initialize(gulp);&lt;/pre&gt;
&lt;p&gt;Whenever you want to reference a script inside SharePoint, such as the JSOM files or you want the SPFx CDN to be in SharePoint you modify the &lt;strong&gt;config.json&lt;/strong&gt; or &lt;strong&gt;write-manifest.json&lt;/strong&gt; files to use https://contoso.sharepoint.com instead of your tenant URL.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;config.json&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/How-to-generate-SharePoint-Framework-bun_F55E/image_2.png"&gt;&lt;img title="externals in config.json" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="externals in config.json" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/How-to-generate-SharePoint-Framework-bun_F55E/image_thumb.png" width="604" height="315"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;write-manifest.json&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/How-to-generate-SharePoint-Framework-bun_F55E/image_4.png"&gt;&lt;img title="cdn base path in write-manifest.json" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="cdn base path in write-manifest.json" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/How-to-generate-SharePoint-Framework-bun_F55E/image_thumb_1.png" width="604" height="72"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now when you build the solution you append the argument &lt;strong&gt;--target-cdn &amp;lt;url&amp;gt;&lt;/strong&gt; to replace the URLs in your solution, as follows:&lt;/p&gt;&lt;pre class="brush: shell; toolbar: false"&gt;gulp build --target-cdn https://fabrikam.sharepoint.com
gulp bundle --target-cdn https://fabrikam.sharepoint.com
gulp package-solution&lt;/pre&gt;
&lt;p&gt;If you don't want to specify this for each and every command you can create an empty file called &lt;strong&gt;.env&lt;/strong&gt; and specify the substitution URL in it like this:&lt;/p&gt;&lt;pre class="brush: shell; toolbar: false"&gt;TargetCdn=https://fabrikam.sharepoint.com&lt;/pre&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;I hope this small node package makes your life easier, it sure makes mine! If you have any feedback please use the &lt;a href="https://github.com/wictorwilen/spfx-build-url-rewrite" target="_blank"&gt;Github repository&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;And as a final note, even though it is supported to extend the build pipeline of SPFx this is possibly in the grey zone - but it works…on my machine.&lt;/p&gt;</description><pubDate>Tue, 18 Apr 2017 16:00:49 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/how-to-generate-sharepoint-framework-bundles-for-multiple-tenants</guid></item><item><title>yo teams: a full Microsoft Teams extensibility Yeoman generator</title><link>http://www.wictorwilen.se:80/yo-teams-a-full-microsoft-teams-extensibility-yeoman-generator</link><description>&lt;p&gt;A couple of weeks back I &lt;a href="http://www.wictorwilen.se/yo_teams-tab" target="_blank"&gt;published a Yeoman generator to build Tabs for Microsoft Teams&lt;/a&gt;. Since then I've continued to add stuff to it as the Teams team has continued to add features to their extensibility story. So, this generator is not only for creating Tabs, but now also for adding Bots and Custom Bots to Microsoft Teams. With that I decided to rename the generator to &lt;strong&gt;yo teams&lt;/strong&gt; (generator name is &lt;strong&gt;generator-teams&lt;/strong&gt;). &lt;/p&gt; &lt;p&gt;I'm very thankful to the over 600 downloads within less of a month, and all the positive feedback, the issues and PR created. Keep it coming.&lt;/p&gt; &lt;h2&gt;What's new?&lt;/h2&gt; &lt;p&gt;The two big new features of the generator is the ability to add either a reference to an existing bot that you want to use (your own, or any bot in the Bot &lt;a href="https://bots.botframework.com/" target="_blank"&gt;Framework directory&lt;/a&gt;) or to create a bot from scratch, using the &lt;a href="https://docs.botframework.com/en-us/" target="_blank"&gt;Bot Framework&lt;/a&gt;. You can also add a &lt;em&gt;custom bot&lt;/em&gt;, which is a Microsoft Teams specific webhook that acts like a bot, which can be used by specific Teams only and you don't have to add it to the Bot Framework - perfect for those internal smart bots you want to build.&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/yo-teams--_DC08/image_2.png"&gt;&lt;img title="yo teams" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="yo teams" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/yo-teams--_DC08/image_thumb.png" width="604" height="394"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The source code has also gone through some heavy refactoring with sub-generators and all. There's more to come…&lt;/p&gt; &lt;p&gt;Both the Bot and Custom bot uses the JavaScript/TypeScript implementations of the Bot Framework and you have some boilerplate code to get started, including readme files of the essentials. &lt;/p&gt; &lt;p&gt;If you have any issues, or feedback, or problem or just feeling chatty, then use the &lt;a href="https://github.com/wictorwilen/generator-teams" target="_blank"&gt;Issues list on Github&lt;/a&gt;.&lt;/p&gt; &lt;h2&gt;What do I need to do?&lt;/h2&gt; &lt;p&gt;If you already used the old generator, uninstall it with &lt;strong&gt;npm uninstall generator-teams-tab --global&lt;/strong&gt; and then install the new generator with &lt;strong&gt;npm install generator-teams --global&lt;/strong&gt;. All your current solutions will work, but I recommend you to, if you feel like it, to "move" all your code over to a newly created project.&lt;/p&gt; &lt;p&gt;The old npm package is deprecated and you will get a warning if you try to install it.&lt;/p&gt; &lt;p&gt;The Github repository has been renamed, but the old one will still redirect you to the correct location. You can find it here: &lt;a title="https://github.com/wictorwilen/generator-teams" href="https://github.com/wictorwilen/generator-teams"&gt;https://github.com/wictorwilen/generator-teams&lt;/a&gt;&lt;/p&gt;</description><pubDate>Mon, 27 Mar 2017 22:53:33 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/yo-teams-a-full-microsoft-teams-extensibility-yeoman-generator</guid></item><item><title>Congratulations to the Microsoft Teams team on an excellent delivery</title><link>http://www.wictorwilen.se:80/congratulations-to-the-Microsoft-Teams-team</link><description>&lt;p&gt;A big round of applause for &lt;a href="http://www.microsoft.com" target="_blank"&gt;Microsoft&lt;/a&gt; and the team behind &lt;a href="https://teams.microsoft.com" target="_blank"&gt;Microsoft Teams&lt;/a&gt; for &lt;a href="https://blogs.office.com/2017/03/14/microsoft-teams-rolls-out-to-office-365-customers-worldwide/"&gt;now being general available (GA) worldwide&lt;/a&gt;. Today, they lit up the Teams icon in the Office 365 waffle for all tenants (unless your admins are being boring and has turned it off).&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Congratulations-to-the-Microsoft-Teams-t_D8BA/image_5.png"&gt;&lt;img title="image" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" border="0" alt="image" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Congratulations-to-the-Microsoft-Teams-t_D8BA/image_thumb_1.png" width="604" height="360" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It's been awesome to be a part of this preview journey, which started last summer. &lt;a href="http://www.avanade.com" target="_blank"&gt;Avanade&lt;/a&gt; was selected as one of the TAP members, in a preview program shrouded in a secrecy I've not seen at Microsoft before. Our IT department slowly trickled it out, so that we had a chance of learning how Microsoft Teams could fit into our organization and our way of working. A big thanks to &lt;a href="https://twitter.com/MakeYourselfNrd" target="_blank"&gt;David&lt;/a&gt; who have mastered the preview program internally.&lt;/p&gt;
&lt;p&gt;When Microsoft Teams was unveiled to the public, back in November, we did our first &lt;a href="http://blog.avanade.com/avanade-insights/collaboration/microsoft-teams-supercharges-collaboration-for-millennials-to-boomers/" target="_blank"&gt;point-of-view&lt;/a&gt; based on our experience so far. Since then I would say that the way we work has changed dramatically. Many of our teams and interest groups has quickly adopted this new chat-based workspace - not just for chatting but as the preferred channel for communication and collaboration. It fits our style of work perfectly, given how spread out our teams are and the different time zones we're working in.&lt;/p&gt;
&lt;p&gt;Personally I've been way more effective in my work since we started to adopt Microsoft Teams. The number of unnecessary e-mails has gone done dramatically, my inbox is not flooded with simple questions, or links, or things that can more easily be expressed through a chat. One thing that has surprised me is how much more we use the ad-hoc chats compared to what I've expected, we don't all have to be online at the same time - you can easily go back and see what's been discussed while you were away or in a meeting. Sharing of files and notes is so much easier now and it allows us to have an ongoing discussion about them.&lt;/p&gt;
&lt;p&gt;It's been a blast discussing Microsoft Teams with my clients. And I'm thrilled that some of them now are leading with "Teams first" - that is you create a Team, not a SharePoint team site, not an Office 365 Group. You get them for free with Teams anyways. This will change the way collaboration is done for enterprises going forward, without doing trade offs for compliance, governance and security.&lt;/p&gt;
&lt;p&gt;This is the first release of many to come. And they number of features that has popped up over the last few months are incredible. And I'm sure we will see some more productivity enhancers going forward.&lt;/p&gt;
&lt;p&gt;Once again, thank you to the team behind Microsoft Teams.&lt;/p&gt;</description><pubDate>Tue, 14 Mar 2017 15:32:17 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/congratulations-to-the-Microsoft-Teams-team</guid></item><item><title>yo teams-tab: A Microsoft Teams Tabs Yeoman generator</title><link>http://www.wictorwilen.se:80/yo_teams-tab</link><description>&lt;p&gt;I'm happy to announce that today at &lt;a href="http://www.spsevents.org/city/Munich/Munich2017" target="_blank"&gt;SharePoint Saturday Munich&lt;/a&gt; I presented a new &lt;a href="http://yeoman.io/" target="_blank"&gt;Yeoman generator&lt;/a&gt; for building &lt;a href="https://dev.office.com/microsoft-teams#tabs-detail" target="_blank"&gt;Microsoft Teams Tabs&lt;/a&gt; projects. Tabs in Microsoft Teams is a great way to extend the user interface and to do integrations to other systems and provide visualizations. Tabs are based on a JavaScript framework, a set of web pages and a manifest describing the Tab. It requires a set of manual steps to both build out the pages, configuring CSS, hooking up the JavaScripts, deploying it all to a web site hosted in the cloud, writing the manifest, packaging the manifest into a zip file and more.&lt;/p&gt;
&lt;p&gt;With the Teams Tab generator you can in an easy manner scaffold out the project and get a build and deployment pipeline, and be up and running in a few minutes.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/yo-teams-tab_8BFC/image_2.png"&gt;&lt;img width="600" height="362" title="yo teams-tab" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" alt="yo teams-tab" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/yo-teams-tab_8BFC/image_thumb.png" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The project that will be created is a TypeScript based project with a set of Gulp tasks to build the project and package the manifest, and optionally a built-in Express server to host the web sites and configuration so that you can with a simple command deploy your project to an Azure Web App.&lt;/p&gt;
&lt;h2&gt;How to get it&lt;/h2&gt;
&lt;p&gt;The generator is published as an &lt;a href="https://www.npmjs.com/package/generator-teams-tab" target="_blank"&gt;npm package&lt;/a&gt; and you use npm to install it. The following command will install it as a global package for you to scaffold your Teams tabs.&lt;/p&gt;
&lt;pre class="brush:bash"&gt;npm install generator-teams-tab --global&lt;/pre&gt;
&lt;h2&gt;How to use it&lt;/h2&gt;
&lt;p&gt;To create a new Teams Tab all you need to do is open up a command prompt and use Yeoman to create the project. The generator will ask you a set of questions and your project will configured based on those.&lt;/p&gt;
&lt;pre class="brush:bash"&gt;yo teams-tab&lt;/pre&gt;
&lt;h2&gt;How to use the project&lt;/h2&gt;
&lt;p&gt;The project contains all source code you need to build and deploy tabs. Use Visual Studio Code or whatever text editor you prefer. The source code is divided into two areas. The first one being the actual tabs (pages and scripts), located in &lt;strong&gt;./src/app&lt;/strong&gt; You will find one folder called &lt;strong&gt;web&lt;/strong&gt; which contains the web pages required for a tab; such as the actual tab page, the configure page and remove page. In the &lt;strong&gt;scripts&lt;/strong&gt; folder you will have the TypeScripts file in which you build the logic for your tabs. For instance the actual main tab page, &lt;strong&gt;tab.html&lt;/strong&gt;, has a corresponding &lt;strong&gt;tab.ts&lt;/strong&gt; TypeScript file. You'll get it&amp;hellip;&lt;/p&gt;
&lt;p&gt;In the &lt;strong&gt;./src/app&lt;/strong&gt; folder there is also a TypeScript file called &lt;strong&gt;server.ts&lt;/strong&gt;. Note, this file only exists if you answers yes to the question on using Express to host the Tab. This file is the server side node.js web server. If you need to modify the paths or want it to do fancier stuff than just client-side scripting this is where you start hacking.&lt;/p&gt;
&lt;p&gt;There's also a folder calle &lt;strong&gt;./src/manifest&lt;/strong&gt; which contains the &lt;a href="https://msdn.microsoft.com/en-us/microsoft-teams/schema" target="_blank"&gt;Tab manifest&lt;/a&gt;. A json file you might want to configure. And that folder also contains the two images you need to have for a Tab.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/yo-teams-tab_8BFC/image_4.png"&gt;&lt;img width="600" height="452" title="The tab project" style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;" alt="The tab project" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/yo-teams-tab_8BFC/image_thumb_1.png" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;How to build it&lt;/h2&gt;
&lt;p&gt;You build the tab by using a simple Gulp task that will transpile and bundle your TypeScript into JavaScript and sets up the web server. Just use the following command to build it&lt;/p&gt;
&lt;pre class="brush:bash"&gt;gulp build&lt;/pre&gt;
&lt;p&gt;Once you've built it you can follow the instructions in the &lt;strong&gt;README.md&lt;/strong&gt; file to deploy it to an Azure Web App.&lt;/p&gt;
&lt;p&gt;The manifest for the tab is created by using another Gulp task:&lt;/p&gt;
&lt;pre class="brush:bash"&gt;gulp manifest&lt;/pre&gt;
&lt;p&gt;This task will create a zip file that you use to upload to your Teams team and it references the specified web site hosting the tabs. The file being created is located at &lt;strong&gt;./package/tab.zip&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;This is great, but I want to&amp;hellip;&lt;/h2&gt;
&lt;p&gt;I know, you want to have more stuff in the generator. It's all available on Github for you to grab and hopefully come back with suggestions. Go git it here: &lt;a title="https://github.com/wictorwilen/generator-teams-tab" href="https://github.com/wictorwilen/generator-teams-tab"&gt;https://github.com/wictorwilen/generator-teams-tab&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I'm looking forward to feedback and I'll keep updating the generator in line with what the Teams team are doing with their JavaScript framework, which is currently at 0.4.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;glhf&lt;/em&gt;&lt;/p&gt;</description><pubDate>Sat, 04 Mar 2017 13:45:25 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/yo_teams-tab</guid></item><item><title>SharePoint Framework has now reached General Availability - such a great journey</title><link>http://www.wictorwilen.se:80/sharepoint-framework-has-now-reached-general-availability-such-a-great-journey</link><description>&lt;p&gt;Let me start with congratulating the SharePoint Framework team on an amazing job and an amazing journey reaching this &lt;a href="https://blogs.office.com/2017/02/23/sharepoint-framework-reaches-general-availability-build-and-deploy-engaging-web-parts-today/" target="_blank"&gt;GA milestone&lt;/a&gt;.&lt;/p&gt; &lt;blockquote class="twitter-tweet" data-lang="en"&gt; &lt;p lang="en" dir="ltr"&gt;A Big Thanks from the team here in Redmond to everyone who helped us to get to GA! &lt;a href="https://twitter.com/hashtag/SPFx?src=hash"&gt;#SPFx&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/SharePoint?src=hash"&gt;#SharePoint&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/SharePointFramework?src=hash"&gt;#SharePointFramework&lt;/a&gt; &lt;a href="https://t.co/czo2Duon7z"&gt;pic.twitter.com/czo2Duon7z&lt;/a&gt;&lt;/p&gt;— Chakkaradeep (@chakkaradeep) &lt;a href="https://twitter.com/chakkaradeep/status/834916771245109248"&gt;February 24, 2017&lt;/a&gt;&lt;/blockquote&gt;&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt; &lt;p&gt;The SharePoint Framework plays a significant part of the SharePoint future, yes - this is only the first version with a lot of &lt;a href="https://dev.office.com/sharepoint/docs/spfx/roadmap" target="_blank"&gt;new features on the way&lt;/a&gt;, and it is a part of the new SharePoint wave. I've haven't seen this interest in SharePoint for many years and I'm glad I'm still in this business. Delivering top notch collaboration solutions for our clients at Avanade. The SharePoint Framework will make it easier for us to customize SharePoint and it will also bring a lot more value for our clients in the end allowing them to stay evergreen and not being tied into "workarounds" and pesky SharePoint Designer hacks or arbitrary JavaScript snippets.&lt;/p&gt; &lt;p&gt;I'm extremely glad that I've been a part of this journey, seeing the team making the awesome stuff they've done. For me it started back in the fall of 2015 when we we're shown some very early ideas on where to go next and also some whiteboard sessions where we had an open and frank discussion about what the requirements were from the field. This openness is something that I think has made the difference this time and made the SharePoint Framework into what it is. All discussions was kept very secret and I'll tell you it was hard not to cry out to everyone how excited I was on the progress.&lt;/p&gt; &lt;p&gt;Early 2016 I was part of the first DevKitchen, where we had the opportunity to use SharePoint Framework for the first time. The team had only in a couple of months created something that actually worked! It was very satisfying to build that first web part (I do think that I was the first outside of Microsoft that actually built a client-side Web Part!).&amp;nbsp; The framework had its quirks and issues back then, but they kept the speed up and delivered. A few more DevKitchens were hosted and finally in May they revealed the SharePoint Framework to the public.&lt;/p&gt; &lt;p&gt;Just after the summer everyone could get their hands on the first public release of the SharePoint Framework and the SPFx Team opened the floodgates of feedback through their Github repository. It has been fantastic to see all the support, wishes, bugfixes, samples and documentation that the public (and specifically Waldek) has produced to support the SharePoint Framework. And how fast and agile the team has responded to all the requests and issues. This is how Microsoft should build more stuff! &lt;/p&gt; &lt;p&gt;I've tried to do my best giving feedback as a consultant, developer and things my clients need. I'm particular proud of the &lt;a href="https://dev.office.com/sharepoint/docs/spfx/enterprise-guidance" target="_blank"&gt;enterprise guidance documentation&lt;/a&gt; that I've helped with. I absolutely love being part of this community.&lt;/p&gt; &lt;p&gt;Now, we're here, within a few weeks all tenants in Office 365 should be able to use the SharePoint Framework to build great stuff and awesome client-side Web Parts. We're already in the midst of porting our solutions to take advantage of the SharePoint Framework and getting it in the hands of our clients.&lt;/p&gt; &lt;p&gt;Thank you to the SharePoint Framework Team - I'm so looking forward to what's happening next. See you in a few weeks in Redmond ;-).&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Let's make SharePoint great again!&lt;/p&gt;&lt;/blockquote&gt;</description><pubDate>Fri, 24 Feb 2017 15:24:00 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/sharepoint-framework-has-now-reached-general-availability-such-a-great-journey</guid></item><item><title>Configuring Office 365 Groups creation the right way</title><link>http://www.wictorwilen.se:80/configuring-office-365-groups-creation-the-right-way</link><description>&lt;p&gt;Over the last few days the issue on how to prevent users to create Office 365 Groups has popped up in all sorts of conversations. This blog post will show you how to do it in the correct way, and serve as a future reference. I'm not the only one who have blogged about this, it's in many places including official documentation. But in many places both scripts and some caveats are either wrong or outdated. One post covers this topic really well, and in a good and correct way and it's this post by &lt;a href="https://twitter.com/diverdown1964" target="_blank"&gt;John P. White&lt;/a&gt; - &lt;a href="https://whitepages.unlimitedviz.com/2017/01/disable-office-365-groups-2/" target="_blank"&gt;Disable Office 365 Groups, part 2&lt;/a&gt;. Read it! This post however will show you how to do it in a more direct way, using PowerShell.&lt;/p&gt; &lt;h2&gt;Background&lt;/h2&gt; &lt;p&gt;We used to prevent end-users from creating Office 365 Groups (from now on referred to as only Groups) using an OWA Mailbox policy. Even I have a &lt;a href="http://www.wictorwilen.se/office-365-groups-for-admins-enable-and-disable-user-creation-of-groups" target="_blank"&gt;blog post on that topic&lt;/a&gt;. But this way to do it &lt;strong&gt;is outdated&lt;/strong&gt;. That mailbox policy only applies to Groups being created from OWA (Outlook Web Access, Outlook on the web…whatever) and Outlook. It did not prevent people from creating Groups using Microsoft Teams, Planner, StaffHub, PowerBI, Dynamics 365 and what not.&lt;/p&gt; &lt;h2&gt;How to do it properly&lt;/h2&gt; &lt;p&gt;Instead of continuing to building the settings on the Mailbox policy setting, this setting has now moved to Azure AD. You can even see it in the "new" Azure Portal, although it doesn't really reflect the real settings and not all settings.&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Configuring-Office-365-Groups-creation-t_DA52/SNAGHTMLbb0f83.png"&gt;&lt;img title="Azure AD Settings for Office 365 Groups" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="Azure AD Settings for Office 365 Groups" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/Configuring-Office-365-Groups-creation-t_DA52/SNAGHTMLbb0f83_thumb.png" width="404" height="332"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The way to do it is to use PowerShell and essentially follow &lt;a href="https://support.office.com/en-us/article/Manage-Office-365-Group-creation-4c46c8cb-17d0-44b5-9776-005fced8e618?ui=en-US&amp;amp;rs=en-US&amp;amp;ad=US&amp;amp;fromAR=1" target="_blank"&gt;the official documentation&lt;/a&gt;. The problem with that article however is that it contains a few errors, is not updated, has some weird scripts and is just to darn long to read through. So, here's a my PowerShell for this. You can find the complete script in &lt;a href="https://gist.github.com/wictorwilen/04b4475269ff95bd274de4665ea02122" target="_blank"&gt;this Gist.&lt;/a&gt;&lt;/p&gt; &lt;h3&gt;&lt;/h3&gt; &lt;h3&gt;Prerequisites&lt;/h3&gt; &lt;p&gt;To be able to run the PowerShell you need to install some stuff&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="http://go.microsoft.com/fwlink/p/?LinkId=286152" target="_blank"&gt;The Microsoft Online Services Sign-in assistant&lt;/a&gt;  &lt;li&gt;The Windows Azure Active Directory Module for PowerShell - and here's a big thing. You MUST (at the time of writing) only use the preview version, with version number 1.1.130.0-preview found &lt;a href="http://connect.microsoft.com/site1164/Downloads/DownloadDetails.aspx?DownloadID=59185" target="_blank"&gt;here&lt;/a&gt;. Do not try to download the higher version with version number 1.1.166.0 - it will not work.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Now, we got that out of the way, let's get to the fun stuff.&lt;/p&gt; &lt;h3&gt;&lt;/h3&gt; &lt;h3&gt;Scripting FTW&lt;/h3&gt; &lt;p&gt;First we need to log in to our tenant using an admin account. I prefer to use a the Get-Credential method over the dialog option, makes everything more smoother.&lt;/p&gt; &lt;p&gt;&lt;pre class="brush:javascript"&gt;# Store the credentials in a variable
$creds = Get-Credential

# Connect to the Microsoft Online services
Connect-MsolService -Credential $creds 

&lt;/pre&gt;
&lt;p&gt;The next thing is to make sure that users are allowed to create Groups, we'll limit it later. Make sure you use the script below and not the one in the official article as they have spelling errors on the variable.&lt;/p&gt;
&lt;p&gt;&lt;pre class="brush:javascript"&gt;# Get tenant setting (misspelled in official docs)
Get-MsolCompanyInformation | Format-List UsersPermissionToCreateGroupsEnabled

# If false, then use the following
Set-MsolCompanySettings -UsersPermissionToCreateGroupsEnabled $true

&lt;/pre&gt;
&lt;p&gt;To limit the users allowed to create Groups we need to have a security group with members in Azure AD. And we need the Id of that group, so we'll grab it with some PowerShell:&lt;/p&gt;
&lt;p&gt;&lt;pre class="brush:javascript"&gt;# Retrieve ID of Group that should have the option to create groups
$group = Get-MsolGroup -SearchString "Group creators" 

&lt;/pre&gt;
&lt;p&gt;The settings we need to set are contained in an Azure AD object, created from a template. We retrieve that template using the following command and create our settings object like this:&lt;/p&gt;
&lt;p&gt;&lt;pre class="brush:javascript"&gt;# Retrieve the Group.Unified settings template (assuming you have not done this before)
$template = Get-MsolAllSettingTemplate | Where-Object {$_.DisplayName -eq "Group.Unified"}

# Create the settings object from the template
$settings = $template.CreateSettingsObject()

&lt;/pre&gt;
&lt;p&gt;Once we have the settings object, we can start setting properties.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;EnableGroupCreation&lt;/strong&gt; - should be set to false. We negate the tenant setting here, and we'll override it soon again for the specific security group 
&lt;li&gt;&lt;strong&gt;GroupCreationAllowedGroupId&lt;/strong&gt; - this is the Id of the security group that are allowed to create Groups 
&lt;li&gt;&lt;strong&gt;UsageGuidelinesUrl&lt;/strong&gt; - a URL pointing to your usage guidelines. Optional, but recommended 
&lt;li&gt;&lt;strong&gt;GuestUsageGuidelinesUrl&lt;/strong&gt; - a URL pointing to usage guidelines for external users. This link will be shown in the external sharing e-mails and should of course be on a public available location. Optional, but recommended 
&lt;li&gt;&lt;strong&gt;ClassificationList&lt;/strong&gt; - a comma separated list with your classification labels. Optional. Currently the first one in the list will be the default one. (does not work in all tenants at the time of writing)&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;There's some more properties that you can take a look at, and over the last few weeks even some more popped up (without any documentation).&lt;/p&gt;
&lt;p&gt;&lt;pre class="brush:javascript"&gt;# Use this settings object to prevent others than specified group to create Groups
$settings["EnableGroupCreation"] = $false
$settings["GroupCreationAllowedGroupId"] = $group.ObjectId

# (optional) Add a link to the Group usage guidelines
$settings["UsageGuidelinesUrl"] = 
  "https://contoso.sharepoint.com/Pages/GroupUsageGuidelines.aspx"

# (optional) Add a link to Guest usage guidelines
$settings["GuestUsageGuidelinesUrl"] = 
  "http://contoso.com/usageguidelines"

# (optional) Add classifications to be used for Groups
$settings["ClassificationList"] = "Public,Internal,Top Secret"

# Verify
$settings.Values

&lt;/pre&gt;
&lt;p&gt;Now we have the settings and all we need to do is to add them to Azure AD:&lt;/p&gt;
&lt;p&gt;&lt;pre class="brush:javascript"&gt;# Add the settings to Azure AD
New-MsolSettings -SettingsObject $settings

&lt;/pre&gt;
&lt;p&gt;And from now on, only members of the security group can create Office 365 Groups using all endpoints such as Planner, Teams, PowerBI, Microsoft Graph REST etc. &lt;font color="#ff0000"&gt;BUT StaffHub still ignores this setting!!!!! Aaargh!&lt;/font&gt;&lt;/p&gt;
&lt;h3&gt;Need to update the settings?&lt;/h3&gt;
&lt;p&gt;If you need to update the settings, or there are new properties that you want to configure, then use the PowerShell below. The one(s) in the official documentation is really weird written…&lt;/p&gt;
&lt;p&gt;&lt;pre class="brush:javascript"&gt;# Retrieve settings
$settings = Get-MsolAllSettings | Where-Object {$_.DisplayName -eq "Group.Unified"}

# Check the values
$settings.Values

# Update a property
$settings["GuestUsageGuidelinesUrl"] = "http://www.wictorwilen.se"

# Save the updates
Set-MsolSettings -SettingId $settings.ObjectId -SettingsValue $settings.GetSettingsValue()

&lt;/pre&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;That's it. It's not rocket science. Looking forward to further settings and also a proper UI in the Azure portal for the lazy people.&lt;/p&gt;
&lt;p&gt;The PowerShell is a bit weird though, should have had a review by the PowerShell team before going into the production in my opinion. &lt;/p&gt;</description><pubDate>Wed, 25 Jan 2017 15:08:00 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/configuring-office-365-groups-creation-the-right-way</guid></item><item><title>SharePoint Framework: how to properly dynamically populate dropdown property pane fields</title><link>http://www.wictorwilen.se:80/sharepoint-framework-how-to-properly-dynamically-populate-dropdown-property-pane-fields</link><description>&lt;p&gt;One of the key parts of SharePoint Web Parts is the ability to have them configurable using the Web Part properties. This story is still true with client-side Web Parts in the new SharePoint Framework. In this post I will show you one of the more common scenarios; how to populate drop downs (and other fields) in the property pane dynamically. But also show you how what's wrong with the current implementation.&lt;/p&gt; &lt;h2&gt;Client-side Web Part Property Pane basics&lt;/h2&gt; &lt;p&gt;The Property Pane in client-side Web Parts are defined by the &lt;strong&gt;propertyPaneSettings&lt;/strong&gt; method. It's a method that returns an &lt;strong&gt;IPropertyPaneSettings&lt;/strong&gt; object representing the pages, groups and fields. This method is invoked whenever you click to edit the Web Part. The method is "synchronous" meaning that you should return a static set of pages, groups and fields - there's no option to wait for it to load (&lt;a href="https://github.com/SharePoint/sp-dev-docs/issues/227" target="_blank"&gt;SPFx issue #127&lt;/a&gt;).&lt;/p&gt; &lt;h2&gt;Defining the Dropdown&lt;/h2&gt; &lt;p&gt;For a Dropdown we use the &lt;strong&gt;PropertyPaneDropdown&lt;/strong&gt; field and specify for instance it like this, in the &lt;strong&gt;propertyPaneSettings&lt;/strong&gt; method.&lt;/p&gt;&lt;pre class="brush:javascript"&gt;PropertyPaneDropdown('listName', {
  label: strings.DescriptionFieldLabel,
  options: this._options
})&lt;/pre&gt;
&lt;p&gt;In this case the options property references a local variable defined as below:&lt;/p&gt;&lt;pre class="brush:javascript"&gt;private _options: IPropertyPaneDropdownOption[];&lt;/pre&gt;
&lt;h2&gt;Loading the data…&lt;/h2&gt;
&lt;p&gt;But when do we load this data into the local variable. We do have a couple of options to do this, but there really is only one viable option at the moment and that is to use the &lt;strong&gt;onInit&amp;lt;T&amp;gt;()&lt;/strong&gt; method. This method always runs when the Web Part is loaded or is added to a page, and it only runs once and it is the only option we have if we want to load something using a promise and make sure to have it loaded before we have the chance of rendering the property pane. The &lt;strong&gt;onInit&lt;/strong&gt; method returns a Promise and that allows us to actually block the loading of the Web Part until we have the data. &lt;/p&gt;
&lt;p&gt;To load data, mock or live, I've created a simple list data provider, that you can find in the following Gist. Pay attention to the fact that I have set a delay on the mock loading to 5 seconds, this is to simulate a slow request and show you how bad this current implementation actually is. &lt;a title="https://gist.github.com/wictorwilen/0bf866ee4fda2f9611f43ee17b9127d5" href="https://gist.github.com/wictorwilen/0bf866ee4fda2f9611f43ee17b9127d5"&gt;https://gist.github.com/wictorwilen/0bf866ee4fda2f9611f43ee17b9127d5&lt;/a&gt;&lt;/p&gt;&lt;script src="https://gist.github.com/wictorwilen/0bf866ee4fda2f9611f43ee17b9127d5.js"&gt;&lt;/script&gt;
&lt;p&gt;Then I implement the &lt;strong&gt;onInit&lt;/strong&gt; method as follows:&lt;/p&gt;&lt;pre class="brush:javascript"&gt;public onInit&amp;lt;T&amp;gt;(): Promise&amp;lt;T&amp;gt; {
  let dataService = (this.context.environment.type === EnvironmentType.Test || this.context.environment.type === EnvironmentType.Local) ?
    new MockListsService() :
    new ListsService(this.context);

  this._options = [];

  return new Promise&amp;lt;T&amp;gt;((resolve: (args: T) =&amp;gt; void, reject: (error: Error) =&amp;gt; void) =&amp;gt; {
    dataService.getListNames().then(lists =&amp;gt; lists.forEach(list =&amp;gt; {
      this._options.push(&amp;lt;IPropertyPaneDropdownOption&amp;gt;{
        text: list,
        key: list
      });
      resolve(undefined);
    }))
  });&lt;/pre&gt;
&lt;p&gt;First of all I create my data service object (mock or real one). Then I create a new Promise that uses my list service and once the list data is read it populates the local variable holding the data for our dropdown and finally I resolve the promise.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font color="#434343"&gt;There are some articles and Github repos that uses another approach, they just return a resolved promise and let the call to the external data service run in the background. If that background request takes to long time (the simulated 5 seconds for instance) or fails then the user might have time to open the property pane and see an empty dropdown. &lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Now when the web part is added to a page or a page a loaded with that web part the &lt;strong&gt;onInit&lt;/strong&gt; method will "block" the web part from rendering until it has the data for the property pane. Well, that's not good, but that's how it is at the moment. There is however a possibility that we might be able to use &lt;strong&gt;displayMode&lt;/strong&gt; property of the web part and only do this when we are in edit mode. I have not been able to verify this as the workbench always are in edit mode and none of my tenants actually loads the client side web parts in the modern pages - I'll get back on this.&lt;/p&gt;
&lt;p&gt;Another annoying thing is that since this is blocking the rendering, not even showing a spinning wheel or something, the user experience is quite bad if you have long running calls since nothing is shown (&lt;a href="https://github.com/SharePoint/sp-dev-docs/issues/228" target="_blank"&gt;SPFx issue #228&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;I've now shown you how to pre-load data for your property pane fields, yes same approach works for other field types than dropdowns as well. But is this the proper way then? Nope, it isn't but I have great hopes that this will be fixed in a better manner (see linked issues). If not we still have the option of creating completely custom property pane fields that allows us to do basically whatever we want.&lt;/p&gt;</description><pubDate>Thu, 06 Oct 2016 20:37:00 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/sharepoint-framework-how-to-properly-dynamically-populate-dropdown-property-pane-fields</guid></item><item><title>SharePoint Online CDN features announced in preview</title><link>http://www.wictorwilen.se:80/sharepoint-online-cdn-features-announced-in-preview</link><description>&lt;p&gt;Today, Mr Vesa, &lt;a href="http://dev.office.com/blogs/office-365-public-cdn-developer-preview-release" target="_blank"&gt;announced the availability of the (long awaited) CDN features for SharePoint Online&lt;/a&gt;. The SharePoint Online CDN features allows you to turn one or more libraries in your SharePoint tenant into a repository for assets that you want to store in a CDN for performance reasons and geo-distribution reasons. &lt;/p&gt; &lt;p&gt;&lt;img title="SPO Public CDN" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="SPO Public CDN" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Online-CDN-features-announced_133B8/spo-cdn_3.png" width="604" height="404"&gt;&lt;/p&gt; &lt;h2&gt;How to set things up&lt;/h2&gt; &lt;p&gt;I'm not going to rehash everything that is outlined in the announcement post, but rather highlight a few important things.&lt;/p&gt; &lt;p&gt;To get started and turn one of your libraries into a CDN enabled library you need to install the latest SharePoint Online PowerShell cmdlets to get access to some new and updated PowerShell cmdlets. Once you have that all you need to do is first enable it on your tenant and then add libraries for cdnization:&lt;/p&gt;&lt;pre class="brush:powershell"&gt;$creds = Get-Credential
Connect-SPOService -Url https://contoso-admin.sharepoint.com -Credential $creds
Set-SPOTenant -PublicCdnEnabled $true
&lt;/pre&gt;
&lt;p&gt;Once this is done then you choose what libraries to use as the source for your CDN. You can point directly to a library or a subfolder in a library.&lt;/p&gt;&lt;pre class="brush:powershell"&gt;New-SPOPublicCdnOrigin -Url https://contoso.sharepoint.com/SiteAssets/
&lt;/pre&gt;
&lt;p&gt;When you do this you get a big and yellow warning stating that everything you add in this folder from now on will be pushed to the CDN, and that CDN is publicly available and not governed by all the nice policies we have in Office 365. So, don't put your annual report drafts in there!&lt;/p&gt;
&lt;p&gt;Note, it might take a few minutes until it is all ready for usage after adding or changing the origins. 15-20 minutes is not uncommon.&lt;/p&gt;
&lt;p&gt;You can always retrieve all your CDN origins by using the following command:&lt;/p&gt;&lt;pre class="brush:powershell"&gt;Get-SPOPublicCdnOrigins
&lt;/pre&gt;
&lt;p&gt;This command will list the ID of each CDN origin as well as the origin. The ID is very important, because you need that when you construct the URL to the CDN. It's probably one or more Guids in that ID…&lt;/p&gt;
&lt;h2&gt;How to access the files in the CDN&lt;/h2&gt;
&lt;p&gt;To get access to the files in the CDN you need to construct a path using the ID for the CDN origin like this:&lt;/p&gt;&lt;pre&gt;https://publiccdn.sharepointonline.com/&lt;b&gt;tenant&lt;/b&gt;.sharepoint.com/&lt;b&gt;ID&lt;/b&gt;
&lt;/pre&gt;
&lt;p&gt;For instance &lt;/p&gt;&lt;pre&gt;https://publiccdn.sharepointonline.com/contoso.sharepoint.com/1544004027c884490c55638fcb53de1f5d4897c5ea7513c1c0849dd7d8f4314cbda99429
&lt;/pre&gt;
&lt;p&gt;Yes, it's a bit long but that's how it is.&lt;/p&gt;
&lt;p&gt;If you now try to access an image or script in this CDN by just typing the URL in the browser, you will get an error stating "Invalid referer". This is by design and you need to add a HTTP Header to see the image/script, the &lt;em&gt;Referer&lt;/em&gt; header with the value of the URL of your tenant (see announcement blog). In cases where you embed the image in a page or load the script from a page, this is done automatically for you by the browser. An even easier way is to just create a page and insert a Picture from Address, and paste your CDN Url.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;There's a couple of caveats to the CDN as of now. &lt;strike&gt;One thing I had hoped was that you could use image renditions in combination with this, which would have been AWESOME. Nope, you can't. Vesa, can you add that to the todo list?&lt;/strike&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#ff0000"&gt;[Update 2016-09-29] The image renditions are partly supported, you can now use &lt;strong&gt;width&lt;/strong&gt;, &lt;strong&gt;height&lt;/strong&gt; and &lt;strong&gt;cropMode&lt;/strong&gt; (&lt;strong&gt;fit&lt;/strong&gt; for instance) as query string parameters for the image. But not &lt;strong&gt;RenditionId&lt;/strong&gt; - once that can be done this feature is rock solid!&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#ff0000"&gt;[Update 2016-09-29] CORS support has been enabled! Thanks you to the product group for fast and quick turnaround for these kind of features!&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;strike&gt;Another quite important thing is that the CDN isn't CORS enabled. They do a check for the referer, see above, so that the request are coming from the correct tenant. But if you try to use jQuery or some other mechanism to load scripts or assets from the CDN location you get &lt;/strike&gt;&lt;/p&gt;&lt;pre&gt;&lt;strike&gt;No 'Access-Control-Allow-Origin' header is present on the &lt;br&gt;requested resource. Origin 'https://tenant.sharepoint.com' is &lt;br&gt;therefore not allowed access. &lt;br&gt;The response had HTTP status code 406.&lt;/strike&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strike&gt;I wish they added the possibility to allow CORS requests from the tenant, just as they only allow request refering from the tenant&lt;/strike&gt;.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;I think this feature is awesome and I have already upgraded a few tenants and customers to use this new feature for some embedded resources. Oh, did I say this is free of charge and a part of your Office 365 subscription. Free beer is good beer, despite in preview!&lt;/p&gt;</description><pubDate>Thu, 22 Sep 2016 20:36:00 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/sharepoint-online-cdn-features-announced-in-preview</guid></item><item><title>SharePoint Framework Nuggets: working with GUIDs</title><link>http://www.wictorwilen.se:80/sharepoint-framework-nuggets-working-with-guids</link><description>&lt;p&gt;SharePoint developers - we do like GUIDs, don't we. We all read &lt;a href="https://www.ietf.org/rfc/rfc4122.txt" target="_blank"&gt;RFC4122&lt;/a&gt; both once and twice. And now with SharePoint Framework and the goal to embrace all them Macintosh and open source people - they gotta have their fair share of GUIDs.&lt;/p&gt; &lt;p&gt;And to aid with that the SharePoint Framework got some really nice GUID features, although a bit unpolished as you might notice - but this is all preview bits at the time of writing.&lt;/p&gt; &lt;p&gt;&lt;img title="SharePoint Framework Nuggets - GUIDs" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="SharePoint Framework Nuggets - GUIDs" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-Nuggets_1067E/SharePoint%20Framework%20Nuggets%20-%20GUIDs_3.png" width="608" height="306"&gt;&lt;/p&gt; &lt;h2&gt;The Guid class&lt;/h2&gt; &lt;p&gt;First of all we must have the option to create GUIDs. There might be a situation that you need to store something really, really unique - to help with the we do have the &lt;strong&gt;Guid&lt;/strong&gt; class in the &lt;strong&gt;@microsoft/client-sp-base module&lt;/strong&gt;. This class both allows you to create new Guids, parse Guids and validate Guids. &lt;/p&gt; &lt;p&gt;This is how you import the Guid class:&lt;/p&gt;&lt;pre class="brush:javascript"&gt;import { Guid } from '@microsoft/sp-client-base';&lt;/pre&gt;
&lt;p&gt;Then to create a Guid, you use the code below. The &lt;strong&gt;newGuid&lt;/strong&gt; method can take an optional parameter if you need to create a custom random generator. By default it generates a version 4 UUID, also known as a pseudo random UUID, which can be generated in a browser. I suggest you skip passing in custom random generators.&lt;/p&gt;&lt;pre class="brush: javascript"&gt;var guid: Guid = Guid.newGuid();&lt;/pre&gt;
&lt;p&gt;What if someone gives a "GUID" to you, can you trust it is a proper Guid, no you can't you need to validate it. That can be done using the &lt;strong&gt;isValid&lt;/strong&gt; method, which takes a string as in input.&lt;/p&gt;&lt;pre class="brush: javascript"&gt;if( Guid.isValid(' f0a1f189-dae4-49f5-8846-3a17429fd52') ) { alert('Valid') };&lt;/pre&gt;
&lt;p&gt;Then we finally also have a method to parse a string potentially containing a Guid, &lt;strong&gt;tryParse&lt;/strong&gt;. The method will either return a &lt;strong&gt;Guid&lt;/strong&gt; object or &lt;strong&gt;undefined&lt;/strong&gt;.&lt;/p&gt;&lt;pre class="brush: javascript"&gt;var guid: Guid = Guid.tryParse('f0a1f189-dae4-49f5-8846-3a17429fd52');&lt;/pre&gt;
&lt;h2&gt;The GuidHelpers class (@internal)&lt;/h2&gt;
&lt;p&gt;&lt;font color="#ff0000"&gt;[Update 2016-09-20] - The GuidHelpers class is marked internal and will be removed in future builds of the SharePoint Framework.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;There's also another class in SharePoint Framework for Guids, the &lt;strong&gt;GuidHelpers &lt;/strong&gt;class. This class lives in the &lt;strong&gt;@microsoft/sp-client-preview&lt;/strong&gt; module. It has a set of similar function to generate and validate Guids, but it does not work with Guid objects, it uses strings instead. For instance the &lt;strong&gt;GuidHelpers.generateGuid()&lt;/strong&gt; returns a string. Another issue is that the GuidHelpers.isValid() does not validate Guids the same way as the Guid.isValid does (issue &lt;a href="https://github.com/SharePoint/sp-dev-docs/issues/201" target="_blank"&gt;#201&lt;/a&gt; in the SPFx Repo). &lt;/p&gt;
&lt;p&gt;&lt;img title="GUID validation issues" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="GUID validation issues" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-Nuggets_1067E/image_3.png" width="343" height="301"&gt;&lt;/p&gt;
&lt;p&gt;At the moment (drop 3 of SPFx) I would stay away from the GuidHelpers class.&lt;/p&gt;
&lt;p&gt;As usual there's some code samples of this to be found in this Github repo: &lt;a title="https://github.com/wictorwilen/spfx-nuggets" href="https://github.com/wictorwilen/spfx-nuggets"&gt;https://github.com/wictorwilen/spfx-nuggets&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Happy GUIDing!&lt;/p&gt;</description><pubDate>Mon, 19 Sep 2016 17:50:00 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/sharepoint-framework-nuggets-working-with-guids</guid></item><item><title>SharePoint Framework Nuggets: logging like a pro</title><link>http://www.wictorwilen.se:80/sharepoint-framework-nuggets-logging-like-a-pro</link><description>&lt;p&gt;I guess that almost every application or solution you ever built has contained some portions of a logging mechanism. And how many of you have written your own - yup, all of you! But what about the SharePoint Framework - yes, it has built-in logging!&lt;/p&gt; &lt;p&gt;&lt;img title="SharePoint Framework Nuggets - logging like a pro" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="SharePoint Framework Nuggets - logging like a pro" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-Nuggets-logging-lik_C525/SharePoint%20Framework%20Nuggets%20-%20logging%20like%20a%20pro_3.png" width="608" height="306"&gt;&lt;/p&gt; &lt;h2&gt;How to log in the SharePoint Framework&lt;/h2&gt; &lt;p&gt;Logging is a very convenient and easy way to keep track of events happening, instead of having breakpoints, or in JavaScript even worse - alerts. The SharePoint Framework (SPFx) has as all decent frameworks a built-in logging mechanism, albeit very simple, but still yet valuable. It's contained in the &lt;strong&gt;@microsoft/sp-client-base&lt;/strong&gt; module and the class is called &lt;strong&gt;Log&lt;/strong&gt;. To use it in your SharePoint Framework solutions you need to import it as follows:&lt;/p&gt;&lt;pre class="brush:javascript"&gt;import {
  Log
} from '@microsoft/sp-client-base';&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;Log&lt;/strong&gt; class contains four static methods for logging:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;info&lt;/strong&gt; 
&lt;li&gt;&lt;strong&gt;warn&lt;/strong&gt; 
&lt;li&gt;&lt;strong&gt;error&lt;/strong&gt; 
&lt;li&gt;&lt;strong&gt;verbose&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;The names are exactly what you expect, the info is used for information, warn for warnings, error for errors and verbose for when you just have to much on your mind and want to spit it out.&lt;/p&gt;
&lt;p&gt;In the SharePoint Framework implementation all logging is done to the JavaScript console and you can see the logging using the developer tools of your favorite browser. It can take some searching to find them as SPFx spits out quite a lot of logging by itself.&lt;/p&gt;
&lt;p&gt;All static methods have the same signature, except the error method - they take three arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;source&lt;/strong&gt;: the source of the logging information (max 20 characters), such as method or class name 
&lt;li&gt;&lt;strong&gt;message&lt;/strong&gt;: the actual message to log (max 100 characters) 
&lt;li&gt;&lt;strong&gt;scope&lt;/strong&gt;: an optional service scope (more on this one later)&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;error&lt;/strong&gt; method takes an &lt;strong&gt;Error&lt;/strong&gt; object instead of the &lt;strong&gt;message&lt;/strong&gt; string, otherwise they are the same.&lt;/p&gt;
&lt;p&gt;This is how it could be used in client side web part:&lt;/p&gt;&lt;pre class="brush:javascript"&gt;public render(): void {
  this.context.statusRenderer.clearError(this.domElement);
  this.context.statusRenderer.displayLoadingIndicator(this.domElement, strings.Loading);
  Log.verbose('SpFxNuggets', 'Invoking render');

  this._webInfoProvider.getWebInfo().then((webInfo: IWebInfo) =&amp;gt; {
    if (this.properties.fail) {
      throw new Error('Mayday');
    }
    Log.info('SpFxNuggets', 'Service OK', this.context.serviceScope);
    this.context.statusRenderer.clearLoadingIndicator(this.domElement);
    this.context.domElement.innerHTML = `&amp;lt;h1&amp;gt;${webInfo.title}&amp;lt;/h1&amp;gt;`;

  }).catch((err) =&amp;gt; {
    Log.error('SpFxNuggets', err);
    this.context.statusRenderer.clearLoadingIndicator(this.domElement);
    this.context.statusRenderer.renderError(this.domElement, err);
  });
}
&lt;/pre&gt;
&lt;p&gt;In the example above I use the &lt;strong&gt;verbose&lt;/strong&gt; method to log that we're in the render method. Just verbose information. I also added logging to when the service returns (the promise is fulfilled) to log information that it has returned. In that &lt;strong&gt;info&lt;/strong&gt; method I use the service scope of the web part, and when using the service scope, it replaces my source with the real name (JavaScript name) of the web part. Finally I log the error in the catch method. The image below shows the console output.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-Nuggets-logging-lik_C525/image_2.png"&gt;&lt;img title="Console output" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="Console output" src="http://www.wictorwilen.se/Media/Default/Open-Live-Writer/SharePoint-Framework-Nuggets-logging-lik_C525/image_thumb.png" width="604" height="80"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As usual you can find all the source code in this repository: &lt;a title="https://github.com/wictorwilen/spfx-nuggets" href="https://github.com/wictorwilen/spfx-nuggets"&gt;https://github.com/wictorwilen/spfx-nuggets&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Happy logging!&lt;/p&gt;</description><pubDate>Mon, 19 Sep 2016 12:19:51 GMT</pubDate><guid isPermaLink="true">http://www.wictorwilen.se:80/sharepoint-framework-nuggets-logging-like-a-pro</guid></item></channel></rss>