Contents tagged with Azure AD

  • Using Device Codes to authenticate Bots with Azure AD

    Tags: Bot Framework, Microsoft Teams, npm, Microsoft Azure, Microsoft Graph, Azure AD

    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.

    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 AuthBot and with node.js there’s the botauth module. There’s also other (a bit weird and specialized) ways of doing this by using the backchannel.

    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 Azure Active Directory Authentication Library (ADAL), specifically using the OAuth 2.0 Device Flow.

    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.

    Creating the bot

    First of all we need to create a bot using the Bot Framework portal. 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 /api/messages. 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.

    Configuring the App platform for the bot

    The bot created in the Bot Framework portal, is essentially an Application in the Microsoft Application Registration Portal. 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 Native Application. You don’t have to configure it or anything, it just needs to have a platform.

    Setting the platform for the App

    In this portal you can also add the delegated permissions for your bot, under Microsoft Graph Permissions. For the purpose of this demo we only need the User.Read permissions.

    Let’s write some code

    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:

    • webpack – bundles our TypeScript files
    • ts-loader – webpack plugin that transpiles TypeScript to JavaScript
    • express – node.js webserver for hosting our Bot end-point
    • adal-node – ADAL node.js implementation
    • @microsoft/microsoft-graph-client – a Microsoft Graph client
    • botbuilder – Bot Framework bot implementation

    All code in this sample are found in this Github repo: https://github.com/wictorwilen/device-code-bot. 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:

    MICROSOFT_APP_ID=fa781336-3114-4aa2-932e-44fec5922cbd
    MICROSOFT_APP_PASSWORD=SDA6asds7aasdSDd7

    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.

    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.

    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)
    }

    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.

    The sign-in dialog will create a new ADAL AuthenticationContext and then use that context to acquire a user code.

    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) => {
            ...
    });

    The result from this operation (IUserCodeResponse) is an object with a set of values, where we in this case should pay attention to:

    • userCode – the code to be used by the user for authentication
    • message – a friendly message containing the verification url and the user code
    • verificationUrl – the url where the end user should use the user code (always aka.ms/devicelogin)

    We use this information to construct a Bot Builder Sign-In Card. And send it back to the user:

    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);

    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.

    In our code we then need to wait for this authorization, authentication and consent to happen. That is done as follows:

    context.acquireTokenWithDeviceCode('https://graph.microsoft.com',
       
    process.env.MICROSOFT_APP_ID, response, 
      (err: any, tokenResponse: adal.IDeviceCodeTokenResponse) => {
        if (err) {
          session.send(DeviceCodeBot.createErrorMessage(err));
          session.beginDialog('/signin')
        } else {
            session.userData.accessToken = tokenResponse.accessToken;
            session.send(`Hello ${tokenResponse.givenName} ${tokenResponse.familyName}`);
            ...
        }
    });	

    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 (IDeviceCodeTokenResponse) 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.

    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.

    const graphClient = MicrosoftGraph.Client.init({
        authProvider: (done: any) => {
            done(null, session.userData.accessToken);
        }
    });
    graphClient.
        api('/me/jobTitle').
        version('beta').
        get((err: any, res: any) => {
            if (err) {
                session.send(DeviceCodeBot.createErrorMessage(err));
            } else {
                session.endDialog(`Oh, so you're a ${res.value}`);
            }
        });
        }
    });

    Run the application

    To run the application first we need to transpile and bundle it using webpack like this:

    npm run-script build

    The we start the express server like this:

    npm run-script run

    To test it locally we need to use the Bot Framework emulator. 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

    Testing the bot with the Bot Framework emulator

    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.

    Consenting the device code bot

    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:

    The device code bot in Skype

    Summary

    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.

  • Configuring Office 365 Groups creation the right way

    Tags: Office 365, Office 365 Groups, Azure AD

    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 John P. White - Disable Office 365 Groups, part 2. Read it! This post however will show you how to do it in a more direct way, using PowerShell.

    Background

    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 blog post on that topic. But this way to do it is outdated. 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.

    How to do it properly

    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.

    Azure AD Settings for Office 365 Groups

    The way to do it is to use PowerShell and essentially follow the official documentation. 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 this Gist.

    Prerequisites

    To be able to run the PowerShell you need to install some stuff

    • The Microsoft Online Services Sign-in assistant
    • 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 here. Do not try to download the higher version with version number 1.1.166.0 - it will not work.

    Now, we got that out of the way, let's get to the fun stuff.

    Scripting FTW

    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.

    # Store the credentials in a variable
    $creds = Get-Credential
    
    # Connect to the Microsoft Online services
    Connect-MsolService -Credential $creds 
    
    

    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.

    # Get tenant setting (misspelled in official docs)
    Get-MsolCompanyInformation | Format-List UsersPermissionToCreateGroupsEnabled
    
    # If false, then use the following
    Set-MsolCompanySettings -UsersPermissionToCreateGroupsEnabled $true
    
    

    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:

    # Retrieve ID of Group that should have the option to create groups
    $group = Get-MsolGroup -SearchString "Group creators" 
    
    

    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:

    # 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()
    
    

    Once we have the settings object, we can start setting properties.

    • EnableGroupCreation - should be set to false. We negate the tenant setting here, and we'll override it soon again for the specific security group
    • GroupCreationAllowedGroupId - this is the Id of the security group that are allowed to create Groups
    • UsageGuidelinesUrl - a URL pointing to your usage guidelines. Optional, but recommended
    • GuestUsageGuidelinesUrl - 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
    • ClassificationList - 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)

    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).

    # 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
    
    

    Now we have the settings and all we need to do is to add them to Azure AD:

    # Add the settings to Azure AD
    New-MsolSettings -SettingsObject $settings
    
    

    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. BUT StaffHub still ignores this setting!!!!! Aaargh!

    Need to update the settings?

    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…

    # 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()
    
    

    Summary

    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.

    The PowerShell is a bit weird though, should have had a review by the PowerShell team before going into the production in my opinion.

  • SharePoint Online and Azure AD Dynamic Groups

    Tags: Azure AD, SharePoint Online, Office 365

    One very common requirement in SharePoint, and other portal solutions for that matter, is to have the possibility to target content to a dynamic audience of users and even secure information based on dynamic rules. Traditionally this has been done with Audiences in SharePoint. Audience is a dynamic set of users that is compiled, usually once a day, and at compile time the rules of the Audience is evaluated. A SharePoint Audience is used to target information, but cannot be used to protect content - ie as a security group.

    The Azure Active Directory released a new feature the other week, called Dynamic Membership, which is a very similar feature to the SharePoint Audience feature. But, does it work with SharePoint Online? Let's have a look!

    Enabling Dynamic Groups in Azure AD

    Delegated Group ManagementFirst of all we need to enable Dynamic Membership in Azure Active Directory. To do this you need to be an Azure AD admin and you must have Azure Active Directory Premium subscription, and also the administrator you're logging in with must have an Azure AD Premium license assigned to him. Once you have the licensing sorted out you need to enable Delegated Group Management. This is done in the Azure Portal under Azure AD > Configure.

    Creating a Dynamic Group

    When you've enabled the Delegated Group Management you can create a new group or configure an existing group in Azure AD. Remember if you change an already existing group to dynamic that group will loose all members. Click on the created or already existing group and choose the Configure tab. On that tab you can enable Dynamic Memberships. When you do that the screen changes into an interface where you can specify the rules; either through a simple guide or using a more advanced syntax.

    In the screenshot below you can see that we have a group called "CVP" (Corporate Vice Presidents) and we would like everyone with the term CVP in their title to be a part of this group. Click Save when you are done with your configuring of the dynamic group.

    Dynamic Memberships

    To create the group we can use most of the Azure AD attributes. Note that the SharePoint Online user profile specific attributes cannot be used, so there are still some reasons to use SharePoint Audiences.

    Group memberships are almost immediate. You might have to wait a minute or two when you do changes. There is no way to force a recalculation of the group (as far as I know).

    Does it work in SharePoint Online?

    The final test - can I now use this dynamic group in SharePoint Online (Office 365). The answer is YES! The newly created dynamic security group is immediately available for usage in SharePoint Online.

    Dynamic Groups in SharePoint Online

    Summary

    Dynamic Groups in Azure AD is a really great feature. We can use it in SharePoint Online, Office 365 and even our custom applications to provide a better way to control security or target information. Although it requires you to have an Azure AD Premium subscription this is just one those small features that should make you consider that upgrade!

About Wictor...

Wictor Wilén is the Nordic Digital Workplace Lead working at Avanade. Wictor has achieved the Microsoft Certified Architect (MCA) - SharePoint 2010, Microsoft Certified Solutions Master (MCSM) - SharePoint  and Microsoft Certified Master (MCM) - SharePoint 2010 certifications. He has also been awarded Microsoft Most Valuable Professional (MVP) for seven consecutive years.

And a word from our sponsors...