Choosing the OAuth2 grant flow

The OAuth2 specifications define six different grant types (https://tools.ietf.org/html/rfc6749 and https://tools.ietf.org/html/draft-ietf-oauth-device-flow-15). Each provides the most optimal (from the security point of view) way of obtaining access or (for OIDC) id_tokens given the circumstances of the client application. This blog summarizes the questions that the implementer of the OAuth2 client application needs to ask and how the answers lead to the selection of the appropriate grant.

There are two main questions and several variants on answers to them:

  1. Is there a user involved in the transaction or is this an un-attended client (e.g. a nightly batch run)? If a user is involved either directly interacting with the client or interacting with some other client, which called this application go to #2 below. Otherwise, the only applicable grant is the Client Credentials flow (see exception below). Note that the client app must be a confidential client (see https://tools.ietf.org/html/rfc6749#section-2.1).
  2. If a user is involved, the question to ask is whether the authorization server can use the browser interface to authenticate the user and obtain consent, if needed. There are several possible answers:
    1. Yes, browser can used immediately: this is a web app or a native app, which is able to open control hosting a browser. Use Authorization Code Grant. If this is a native app (public client), use PKCE extensions to the grant.
    2. Yes, browser is available BUT the authorization server cannot take control of it because my code is running in it, e.g. Single Page Application. Use Implicit Flow Grant. (There is a draft proposal to replace this grant type with Authorization Code/PKCE grant. However, most toolkits and many authorization servers do not yet fully support it when used from the browser so at this stage aware of potential change and limitations of Implicit Grant, which lead to these proposals).
    3. No, browser surface is not available to the app but some UI interaction with the user is possible, e.g. the application is running on a device which does not support browser interaction (a thermostat wall device or TV screen with no keyboard input). Use Device Code grant.
    4. No, browser surface is not available and user interaction is not needed because the application already has a token for this user – the application is an API in a multi-tier system, it was called with a token obtained using one of the other grants listed under #2 and now needs to call yet another API. Use the Extension Grant.

There is one other grant defined in the above spec: the Resource Owner Password grant. The only valid use for it is in clients which need a token representing a user but there is no user present when they execute. One example is a nightly batch run doing some Calendar operations for Office 365 delegated users. O365 may require that it be called with a token representing a user rather than application as would be the case if the Client Credentials flow was used (#1 above). Another example is un-attended web application testing. The application needs a user token but since a user is not present at execution it may use the ROP grant to create one and inject it into the test. ROP is inherently insecure, requires hard-coding of passwords, does not support MFA, etc. so it should never be used except for very narrow, specialized scenarios like the two above.

Using Groups in Azure AD B2C

Out-of-the-box AAD B2C does not expose any functionality related to Security Groups. They exist as an entity type and can be accessed via the regular Azure AD portal blade but there are no features for including user group membership in a token issued as a result of a user flow. To use groups you will need to add some custom code through custom (IEF) policies. Here is a description of how I accomplished that.

      <ClaimType Id="groups">
        <DisplayName>Comma delimited list of group names</DisplayName>
        <DataType>stringCollection</DataType>
        <UserInputType>Readonly</UserInputType>
      </ClaimType>
        [HttpGet]
        [Route("Groups")]
        public async Task<ActionResult<RESTResponse>> Groups(string objectId)
        {
            await GetAccessTokenAsync();
            var http = new HttpClient();
            http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _GraphToken);
            var queryString = $"users/{userObjectId}/$links/memberOf?api-version=1.6";
            // GET https://graph.windows.net/myorganization/users/{user_id}/$links/memberOf?api-version
            var resp = await http.GetStringAsync($"https://graph.windows.net/{_settings.domain}.onmicrosoft.com/{queryString}");
            var groupArray = (JArray)JObject.Parse(resp)["value"];
            var groups = new List<string>();
            foreach (JObject g in groupArray)
            {
                var groupUrl = g["url"].Value<string>();
                var groupResp = await http.GetStringAsync($"{groupUrl}?api-version=1.6&$select=displayName");
                var name = JObject.Parse(groupResp)["displayName"].Value<string>();
                result.Add(name);
            }
             return new JsonResult(
                new
                {
                    groups
                });
        }
  • Create a Technical Profile in your Base or Extension xml file to call this function from your user journeys:
        <TechnicalProfile Id="GetUserGroups">
          <DisplayName>Retrieves security groups assigned to the user</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">https://xyz.azurewebsites.net/b2cdata/groups</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">QueryString</Item>
            <Item Key="AllowInsecureAuthInProduction">true</Item>
          </Metadata>
          <InputClaims>
            <InputClaim Required="true" ClaimTypeReferenceId="objectId" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="groups" />
          </OutputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>
  • Call the REST function from your signin user journey in a step just before the step where you send claims (or if you want to be fancy add additional steps to determine whether a token even can be issued based on group membership):
        <OrchestrationStep Order="5" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="GetUserGroups" TechnicalProfileReferenceId="GetUserGroups" />
          </ClaimsExchanges>
        </OrchestrationStep>

Your JWT token should now contain the groups claim, e.g.:

{
  "exp": 1556819742,
  "nbf": 1556816142,
  "ver": "1.0",
  "iss": "https://xyz.b2clogin.com/xyz.onmicrosoft.com/v2.0/",
  "sub": "b7a1cb1e-d8d9-41c3-af94-aac0c9c0387c",
  "aud": "e4535fdc-6ae7-4d65-9c5c-bf79b136f932",
  "acr": "b2c_1a_hmhssignin",
  "groups": [
    "Guests",
    "B2CTest1"
  ],
  "tid": "cf6c572c-c72e-4f31-bd0b-75623d040495"
}

Azure BOTs – getting extra access tokens

Existing docs show how to enable use of OAuth2 in an Azure Bot application to sign-in the user and get an access token to MS Graph for the user. The following describes an approach for getting access tokens to more than one resource, without re-displaying the sign in dialog (using the V2 Azure AD endpoint). In a nutshell, the procedure uses the existing authentication dialog to acquire one token and then the OAuth2 Extension flow (on-behalf-of flow) to acquire additional access tokens with the originally acquired token. The originally acquired token must fulfill two conditions to be useful for such an exchange: it’s audience must be the bot application itself and it’s version must be the same as the version expected by all the other resource servers the bot needs access tokens for.

  1. Use the AAD portal App Registration (Preview) blade to expose a new API Scope on the bot application (already registered in AAD), e.g. ‘https://mybot/all’.
  2. Edit the manifest of your bot in AAD and make sure that the accessTokenAcceptedVersion has the same value (e.g. 2) as all the other API applications you will need tokens for (otherwise, you will get ‘ AADSTS500137: The token issuer doesn’t match the api version ‘ error when trying to do an OBO token exchange).
  3. Modify the OAuth Connection Settings in the Bot Settings blade. Instead of the MS Graph scopes used in the sample, enter the name of your bot’s scope defined above (e.g. https://mybot/all). This will mean that upon authentication your bot will have an access token for itself (the aud claim will point to the bot application) – the ‘original’ token that will be used to get tokens to other resource servers.
  4. Use the original token to acquire additional tokens using the OBO flow. The following MSAL code shows to use an token (the ‘original token’) to get another token for another resource (MS Graph with Mail.Read scope).
var client = new ConfidentialClientApplication(
    "53c0ee74-....c8ed", // bot app id
    "https://login.microsoftonline.com/tenant.onmicrosoft.com", // authority
    "https://mybot", // bot app reply url in AAD, not a resolvable url
    new ClientCredential("<app key>"), // bot app secret in AAD
    null, // user token cache
    null); // app token cache
var newTokens = await client.AcquireTokenOnBehalfOfAsync(
    new string[] { "https://graph.microsoft.com/Mail.Read" }, // scopes for the new resource server
    new UserAssertion(
        token, // the original token obtained through bot auth dialogs
        "urn:ietf:params:oauth:grant-type:jwt-bearer"));

For clarity, this code does not reference configuration settings nor does it cache the acquired tokens.

Federation patterns using Azure AD

Objectives

This post considers scenarios where an application needs to be accessed by users from many sources of authentication. (Office 365, owned and operated by Microsoft but whose use is managed separately by many independent organizations is an example of such a resource). It proposes a framework for determining an optimal solution for the application using Azure AD.

The optimal identity infrastructure architecture minimizes security risks, maximizes utility of authentication while minimizing its cost. In particular, providing single sign-on reduces the risk of user compromising their credentials (because they have to remember several). Also, having the credential managed outside of the application removes from it the cost of its management (storage, associated call center services, etc.).

However, providing SSO to users from many identity directories involves solving at least two technical problems. These are described in more detail below as Issues. The Discovery section describes a taxonomy that seems useful in understanding authentication requirements. These requirements are then used to identify distinct identity directory architectures addressing the cost/benefit objectives described above.

Issues

Providing access to a single application by users authenticated by several directories requires solutions to at least the following two technical issues.  

1.       Home Realm Discovery – there needs to be a way to determine which of the possibly many identity providers a user needs to be routed to in order to authenticate. Assuming the user’s initial, anonymous access arrives at the application, it or some service it uses needs to decide to which authentication server the user should be sent to. The user could be presented with a list of available choices (usually not a good idea) or be asked for some additional information (email address and thus their domain or some pre-defined value in the url) to direct the application to the appropriate authentication server.

2.       Federation setup – establishing trust between the application and potentially many authentication servers. This process usually requires some manual configuration on both FP and IdP servers (configuring application ids, their urls, exchanging secrets) and may require periodic maintenance. Disparities between how different servers implement authentication protocols make this process non-trivial and somewhat error-prone. This process also requires cooperation from the administrators of the end user’s IdP and, dependending on how the application uses resources managed by that IdP may require privileged consent to the IdP itself (e.g. to read additional data).

Discovery

The optimal recommended solution choice is based on answers to questions, listed here with their main options:

1.       Authentication – how are users authenticated?

a.       Users lifecycle is managed by a corporate directory, i.e. their presence in the directory and attributes held about them is managed by some authority who owns the directory

                                                               i.      Using Azure AD

                                                             ii.      Using some other corporate IdP supporting a modern protocol (e.g. OpenID Connect, SAML2)

b.       Users life-cycle is self-managed, i.e. they determine to join or leave in an identity provider and manage their own attribute, usually as social identity (Facebook, Gmail, MSA).

c.       Users do not have either

2.       Authorization – how is permission and level of access determined?

a.       Collaborative use – the owner of the application uses a directory to determine who can access the application and with what privileges. Usually, the applications primary users are employees of one company but some access needs to be provided to selected users from other companies (partners).

b.      Partitioned use – owner of the application controls its configuration and deployment but individual directories subscribe to its use and their owners control access to the application. (multi-tenant applications).)..

c.       Self-asserted – individuals register to use the application or leave its use of their own volition, provide some of their attributes and may decide for themselves when to depart from the use of the application

Scenarios and solutions

The matrix below proposes identity infrastructure architectures for each combination of answers to the questions above:

Authentication →
Authorization ↓
Azure AD Other modern Social provider None
Collaborative 1 – AAD B2B 4 – AAD B2B 7 – AAD with social accts 10 – AAD with MSA or Gmail
Partitioned 2 – AAD Multi-tenant 5 – App & tool (b2C, ADFS) based federation 8 – B2C with custom multi-tenancy 11 – B2C with custom multi-tenancy & local accounts
Self-asserted 3 – AAD Multi-tenant 6 – B2C using IEF to federate 9 – B2C with social accounts 12 – B2C with local accounts

These scenarios, solutions and their main features are described in more detail below:

Ref Scenario Solution Pros Cons
1 Users are in AAD directories and need to collaborate on a resource/application owned by one of these directories Use AAD B2B features to allow federated access of users from one Azure AD tenant to resources managed in another. Out-of-the-box support for HRD and federation setup. Easy to operate and manage.
2 Users are in AAD directories, each of which controls its users access to a 3rd party application. Use AAD multi-tenant application support. Out-of-the-box support for HRD, consent and federation. Application needs to be coded and configured to take advantage of these features (minor effort)
3 Users are in AAD directories and decide for themselves to use a 3rd party application. Application itself controls authorization based on minimal set of attributes provided by the users. As above. The scenario is intended for any user with an AAD account to sign up for use of a 3rd party application As above Application may not require any user attributes beyond those volunteered by the individual users, e.g. it may not require.
4 Users are in a non-AAD directory supporting a modern protocol and need access to a resource whose access is managed by our AAD directory. Use AAD B2B features for non-AAD users. Creates an un-managed AAD tenant for users with corporate email addresses. Built-in HRD. Supports validation of users membership of an external, partner directory. Users need to authenticate using a one time code sent to their corporate email address. No federation to user’s IdP, thus limited SSO for users: corporate authentication is not re-used.
5 Users are in a non-AAD directory supporting a modern protocol. Owners of each directory control access of their users to a 3rd party application. Use B2C or ADFS farm to federate customer IdPs. Application is not burdened with managing federations with multiple providers. All setup, consent, etc. done through the federation server Manual federation setup & custom HRD code. B2C IEF may not scale to more than a handful IdPs. ADFS requires IaaS and does not support OIDC IdPs.
6 Users are in a non-AAD directory supporting a modern protocol. Application itself controls authorization based on minimal set of attributes provided by the users. Use AAD B2C federating multiple providers. Application is not burdened with managing federations with multiple providers. All setup, consent, etc. done through the federation server Manual federation setup & custom HRD code. B2C IEF may not scale to more than a handful IdPs. Requires custom HRD and (except for OIDC IdPs) non-trivial federation setup. The latter is still in Public Preview.
7 Users have social accounts. Their access to the application is controlled by the owner of an AAD. Use AAD B2B features for social accounts. Seamless HRD and federation for MSA/Google ID users. Users with other social accounts are required to setup an MSA account with the other social account’s email address. Collaboration on 3rd party software (e.g. SPO) is restricted.
8 Users have social accounts. From the applications point of view the users belong to distinct groups (tenants), each of which needs to control access to the application for its members. E.g. application for small businesses, whose employees use social identities. Each individual business controls its use of the software. Custom application extensions using B2C (see example in references below). The extension manages data about user groupings and access privilege. Built-in federation and HRD. Capable of supporting groups with few users, common in this space, where users often work in multiple small companies (e.g. vet clinic) but use same social identity for all of them. Requires custom code and data storage to link users to application tenants.
9 Users have social accounts and register to use an application. Core B2C scenario using social providers. Simple federation setup for many social providers, ability to add MFA and custom attributes. Built-in HRD.
10 Users do not have any identity accounts. Owner of an application controls their access to it through the directory. AAD B2B using MSA/Google ID accounts. Built-in federation and HRD, supports MFA. Users of social accounts other than MSA or Google ID need to create an MSA account.
11 Users do not have any identity accounts. Application is used in a multi-tenant way (see 8 above). Same as 8 but using local accounts. As above. Users need to register and manage their own credentials.
12 Users do not have any identity accounts but need to access an application requiring authentication. Same as above (11) but no need for custom code to manage multi-tenancy. As above. As above.

Multi-modal support

Few real-life situations fall neatly into one of the above categories. An application may need to support one of the above scenarios. This may require application level customization and extension to work. For example, https://b2cmultitenant.azurewebsites.net is an application using scenario 8 above (B2C multi-tenant). However, the same app accessed as https://b2cmultitenant.azurewebsites.net/aad supports scenario 2 (AAD Multi-tenant) as well. Application code detects the presence or absence of the url segment and redirects the user accordingly.

 Sharepoint Online is an example of another application which is typically used in the multi-tenant scenario – published by Microsoft but access managed by individual subscribing organizations – is also often used in a collaborative scenario: an organization using SPO invites external users to access it in their subscription.

References

  1. AAD support for multi-tenant apps
  2. AAD and B2B features and this.
  3. AAD B2C
  4. Using B2C for multi-tenant support.
  5. Using Google ID in B2B. and this.

Multi-tenant apps and Azure AD

This is a follow up to my previous blog re multi-tenant applications using B2C. Here I am describing some changes to the original demo app and comparing use of the classic Azure AD multi-tenant features with supporting multi-tenancy using custom features in B2C.

Here is the list of changes to the demo app:

  1. The new version of the demo can operate in two modes: using B2C as token issuer or using Azure AD ‘Classic’ (B2E) as token issuer. In the former mode, it operates as the earlier version using some custom code to provide application multi-tenancy (single B2C tenant, which appears as if segmented into tenants). In the second mode, it uses the standard Azure Multi-tenancy features. Both modes operate from a single deployed service and use url suffix (/b2c or /aad) to distinguish between operating modes.
  2. The B2C operating mode includes a new IdP: the existing Microsoft Corporate Azure AD tenant. This is to demonstrate what’s involved in using AAD ‘Classic’ as a separate tenant in the context of B2C. I was initially hoping to enable the application as multi-tenant in that (Microsoft Corp) tenant and thus dispense with needing to enable the ‘Classic’ operating mode altogether but it turned out AAD B2C policies do not allow me to validate issuers against a dynamic, changing list of issuers.

The main differences are as follows:

Using classic AAD multi-tenant features:

  1. Requires that each customer (clinic in the original blog) has an AAD tenant
  2. Supports customer’s existing access control capabilities: groups, user assignments, conditional access, etc.
  3. Supports access to customer’s other resources (e.g. O365) if consented to by the customer admin

Using AAD B2C:

  1. Supports customers with standard (OIDC, SAML) IdPs, social and local accounts
  2. Able to store additional user attributes in the tenant.
  3. Requires modifications and maintenance of xml policies – not scalable if number of partners with own IdP goes above a couple dozen.
  4. Requires custom code for handling tenancy, user assignment, policies.

 

Developing an Azure AD B2C multi-tenant application

The ‘regular’ Azure AD has build-in support for multi-tenant applications. In that case, a user from any Azure AD tenant can sign in to an application registered in another tenant. The application can then use the user’s security context to give the user a view of data that is specific to that tenant.

The goal of this article is to explore providing similar support using Azure AD B2C with one major difference: instead of using multiple Azure AD tenants, we will use a single B2C tenant and allow all registered users (using social ids or local user ids) to access the application with a ‘tenant’ context of their choice. For example, consider a SaaS application for small doctor clinics. The doctors are not employed by any single clinic. They use their own social ids (live, gmail, etc.) to authenticate and may in fact work for several clinics at different times. In this case, each clinic is a tenant. In B2C terms, where an individual has a single social or local id, we will need to allow each doctor to use the application within the context of a single clinic at any given time but allow them to change that context over time, for example to record their work against different clinics at different times.

To support this, AAD B2C needs to support three user scenarios:

  1. A B2C user wants to create/establish a new tenant (clinic) in the application.
  2. A B2C user  wants to become a registered user of a tenant (doctor joins a clinic).
  3. A B2C user already registered as per (2) above, wants to use the application within the context of a particular tenant (clinic).

This demo application illustrates how the above can be implemented. The application exposes two separate sign-in controls (hyperlinks): one for users who want to create a new tenant (1 above) and another for those who want to use an existing tenant (2 and 3). These could also be exposed as two separate applications: one for taking on new tenants and another for regular use of existing tenants. If a user signs-in using the first control, the application will take the user though logic needed to create a new tenant. At the end of the process, the user will be registered as an administrator of that tenant. Once a tenant is created, the administrator can create redemption codes (secrets), which can then be distributed to potential other users to enable them to sign-up into this tenant. Once a user has sign-up for a tenant, subsequent sign-ins to the system will either allow the user to access that tenant or give the user a list of tenants they can operate within (if they signed up for more than one). From then onwards, a user operates within the application as if it was dedicated to the selected tenant.

Currently, all this logic is implemented as part of the application and kept in a local SQL database. The next step is to move as much of it as possible into the B2C custom policies.

 

The source code for this project is now on https://github.com/mrochon/b2cmultitenantapp

Claims augmentation with OWIN but outside of Startup code

Claims list included in the ClaimsPrincipal usually originate from the security token received by the application as part of user authentication (SAML, OpenIDConnect id token) or access authorization (OAuth2 bearer access token).  However, sometimes there is a need to modify that list with claims derived from other sources:

  1. Attributes retrieved from custom databases
  2. Attributes not initially included in the security token but which can be retrieved from the Security Token Service (e.g. Azure AD via Graph API).

Usually such token augmentation is done as soon as the initial security token is validated, e.g. http://www.cloudidentity.com/blog/2015/08/26/augmenting-the-set-of-incoming-claims-with-the-openid-connect-and-oauth2-middleware-in-katana-3-x/.

In order to accomplish #2 above the application will need an access token to Azure AD (assuming that is the STS used by the application). In that case the earliest event when this can be accomplished in a regular web application using OpenIDConnect authentication is when the authorization code returned together with the id token is converted into an AAD access token, i.e. in the AuthorizationCodeReceived delegate.

Occasionally, however an application may need to modify the claims list much later, in the regular application code rather than Startup.auth.cs. What is important though is that not only the memory list is modified but that the list is then serialized to the authentication cookie for future use if the application is using cookies. Theoretically, that should be possible by adding a new ClaimsIdentity to the ClaimsPrincipal. However, OWIN in ASP.NET 4 will not serialize the new identity. An alternative method is to modify the existing ClaimsIdentity as follows:

var id = (ClaimsIdentity)(ClaimsPrincipal.Current.Identity);

id.AddClaim(new Claim(“Extra”, “Extra claim”));

Request.GetOwinContext().Authentication.SignIn(id);

The new ‘Extra’ claim will now be associated with the current ClaimsPrincipal in all subsequent requests for this user.

Using Azure AD to enable partner access to SharePoint 201x

Introduction

The following summarizes my experience with setting Azure AD as authentication provider for Sharepoint 2013 or 2016. This setup enables access to SharePoint for external users (business partners, customers). While there are other approaches that could be used for this purpose, e.g. an on-premises AD with ADFS, using Azure AD has a number of advantages:

  1. No need to create new identities for these users, which implies costs and risks of having own processes to manage user life-cycle in the directory (e.g. handling lost/forgotten passwords).
  2. No need to create and maintain hardware and network infrastructure to maintain the directory.
  3. External users can use their own corporate or social credentials or manage their own passwords.

Lastly, when the on-premises SharePoint is moved to O365  SharePoint Online, all that is needed is a simple checkbox selection to maintain the external user access to the new environment: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-b2b-o365-external-user.

Architecture

The basic architecture of this approach is shown below.

network-components

The Root Azure AD tenant serves as the primary token supplier to SharePoint. However, using the Azure Ad B2B features, it contains links to other users invited to be guests in that tenant: https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2b-collaboration-overview/. These user’s credentials are maintained in their own host tenants. Users whose organizations already have an Azure AD tenant (e.g. because they are using O365) will use their regular corporate credentials to authenticate. For others, Azure AD will set up a viral, in-managed tenant and ask these users to create a new password. Users with Microsoft Accounts will be able to these to login as well.

ADFS in this scenario serves to convert tokens issued by Azure AD from the SAML2.0 to SAML1.1 format needed by SP. It also normalizes the UPN claim handled differently for work and social accounts in Azure AD.

Setup steps

The sequence of steps needed to set this environment up is described below. It assumes that both an Azure AD tenant (root tenant) and SharePoint installation with AD, ADFS and WAP have been completed. In addition to that, the following set up will be needed:

  1. Configure Azure AD to service token requests from ADFS
  2. Configure ADFS to use Azure AD root tenant to a Claims Provider
  3. Configure SharePoint as Relying Party in ADFS
  4. Configure SharePoint to use Azure AD to provide People Picker data (optional)
  5. Invite external users using the B2B process

Optionally, the user experience can be enhanced by ensuring that on-premises users always use AD rather than being presented with a choice of using AD or Azure AD, and by enabling a People Picker in SharePoint which uses Azure AD as source of user information.

Configure Azure AD to service token requests from ADFS

Use Azure portal (https://portal.azure.com) or PowerShell to add your ADFS as an application. You will need to configure the following parameters:

Application id must be the same as the ADFS’ Federation Service Identifier (see below).

adfs-id

Reply url must be the public address of the ADFS server with adfs/ls suffix, e.g. https://myadfs.contoso.com/adfs/ls

Signin url should be the public address for the SharePoint site. It will be used in the user’s myapps.microsoft.com portal as entry point to SharePoint. This approach works only if there is only one site all users will be accessing. If there are multiple sites, the myapps portal cannot be used.

Configure ADFS to use Azure AD

Use ADFS management console or PowerShell to add Azure AD as a Claims Provider. All that is needed is to provide Claims Provider functionality with the Azure AD Federation Metadata address, e.g. https://login.windowsazure.com/contoso.onmicrosoft.com/federationmetadata/2007-06/federationmetadata.xml.

Configure ADFS to accept all claims from Azure AD

Use the following claim rule to pass all incoming claims from Azure AD to the issuance pipeline:

c:[]  => issue(claim = c);

Configure SharePoint as relying party in ADFS

Use ADFS management console or PowerShell to SharePoint as a Relying Party. The following values will need to be used in this operation (replace contoso with your organization name)

Relying party identifier: urn:sharepoint:contoso

Enable WS-Federation and provide the following passive reply url: https://<address of the SP site>/_trust/

Configure Claim Issuance rules for SharePoint

SharePoint will use UPN as user identifier. Azure AD uses different claim types for users who logged in with work or social addresses. Therefore the following claim issuance rules will be needed:

Convert an incoming name claim (work address user) to UPN:

c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name”]
 => issue(Type = “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”, Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

Convert an incoming email claim (social user) to UPN:

c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress”]
 => issue(Type = “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”, Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType);

Configure ADFS to use AD only for intranet users (optional)

If the ADFS is also used as federation provider to Azure AD (for O365 users for example) configure the following setting using PowerShell:

Set-AdfsRelyingPartyTrust -TargetName <name of Azure AD as RP in ADFS, e.g. O365 Directory> -ClaimsProviderName @(“Active Directory”)

To automatically redirect all users redirected to ADFS by SharePoint, use the following PowerShell command:

Set-AdfsRelyingPartyTrust -TargetName <name of SP as Relying Party in ADFS>  -ClaimsProviderName @(“Contoso AAD”)

Finally, to have all intranet users use AD and not be redirected to Azure AD execute the following command:

Set-AdfsProperties -IntranetUseLocalClaimsProvider $true

Where Contoso AAD is the name of the Azure AD tenant as Claims Provider in ADFS (see earlier step)

Configure SharePoint to use ADFS as federation provider

To configure SharePoint to use ADFS as token provider obtain the public key of the ADFS signing certificate. It can be obtained either directly or by extracting it from the federation metadata document of the ADFS. That file (.cer) is then used in the following PowerShell script:

Add-PSSnapIn Microsoft.SharePoint.PowerShell
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(“c:\adfs cert file.cer”)
New-SPTrustedRootAuthority -Name “ADFS” -Certificate $cert
$map = New-SPClaimTypeMapping -IncomingClaimType “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn” -IncomingClaimTypeDisplayName “UPN” -SameAsIncoming
$realm = “urn:sharepoint:contoso”
$ap = New-SPTrustedIdentityTokenIssuer -Name “ADFS” -Description “SharePoint secured by SAML from AAD via ADFS” -realm $realm -ImportTrustCertificate $cert -ClaimsMappings $map -SignInUrl “https://adfs.contoso.local/adfs/ls/” -ProviderSignoutUrl “https://adfs.contoso.local/adfs/ls/” -IdentifierClaim “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn”

Note that realm name must be exactly as entered earlier in ADFS and the provider sign/in/out url must correspond to the ADFS address. (SP 2013 update 2 needs to be installed before the sign out option shows up; in SP 2016 these options do not have the word ‘Provider’ in their name).

Finally enable use of ADFS in SharePoint through the Central Administration -> Security -> Specify authentication providers and selecting the appropriate zone. You should see the following ui:

sp

Install AzureCP to enable People Picker (optional)

Without any other setup, external users can be given access to individual SP sites by adding their upn to the allowed users lists. To enable a People Picker which allows using the Azure AD tenant to list or search for users, use a custom picker. There is an excellent one available here: https://github.com/Yvand/AzureCP

Invite external users

Use the Azure portal (https://portal.azure.com) to invite individual users from the outside of your organization. You need to have ‘User inviter’ privileges to access this functionality. The following shows the UI of Add user panle, after entering an email address with domain other that the current Azure AD tenant:

newuser

The Azure classic portal (https://manage.windowsazure.com) allows bulk upload of external user email addresses via the Add User functionality. Lastly, there is the B2B invitation API that can be used in an application to have full control over how external users are invited to the root Azure AD tenant.

 

 

 

 

 

Discovering AuthorizeAttribute role names

The AuthorizeAttribute is used in ASP.NET code to decorate controller classes and methods which require authorization, e.g.

[Authorize(Roles =“admin”)]

public class HomeController : Controller

{

Meaning that to call any method in this class, the user needs to have a role claim with the value ‘admin’.

With many controllers and methods the number of roles used and their assignment to methods may become an administrative issue. It may not be easily discoverable what is the complete list of roles the application uses or what is the complete set of methods enabled by a role. A small console application I wrote may thus become handy: https://github.com/mrochon/ASPNETRoleDiscovery.

Use the file-open dialog displayed by the application to open the assembly (dll) of your ASP.NET application. Your assembly must be in the directory to which it was compiled since the console application will need to load other assemblies your application references. It will look for them in the same directory.

The current output of the console application consists of a CSV and Json listing. The CSV file lists all uses of a role, in classes or methods. The Json fragment can be used to modify the web application’s manifest in the Azure AD to enable RBAC for the application. Example of output:

ClassName, MethodName, RoleName
WebApplication2.Controllers.AccountController,SignIn,admin
WebApplication2.Controllers.AccountController,SignIn, abc
WebApplication2.Controllers.HomeController,*,admin
WebApplication2.Controllers.HomeController,About,admin
WebApplication2.Controllers.HomeController,About, clerk
———————————
“appRoles”: [
{
“allowedMemberTypes”: [
“Application”,
“User”
],
“description”: “<some description>”,
“displayName”: “admin”,
“id”: “20d97248-94be-49be-8568-9ad8fd33fe4b”,
“isEnabled”: true,
“value”: “admin”
}
{
“allowedMemberTypes”: [
“Application”,
“User”
],
“description”: “<some description>”,
“displayName”: ” abc”,
“id”: “e0aa5b8c-a7dd-4611-a0e9-27845e95fb1c”,
“isEnabled”: true,
“value”: ” abc”
}
{
“allowedMemberTypes”: [
“Application”,
“User”
],
“description”: “<some description>”,
“displayName”: ” clerk”,
“id”: “374ae2fb-d4e0-4b55-8535-ccbd42d44bb5”,
“isEnabled”: true,
“value”: ” clerk”
}
],