Geoff's Blog
Geoff Webber-Cross - .Net, Windows 8, Silverlight and WP developer. Software problem solver. My Website Obelisk - WP7 MVVM Tombstone Library
Tuesday, 29 July 2014
Friday, 16 May 2014
Moving away from Blogger
I've been writing in this blog for about 3 years now and it's served me well, but I've decided to move to an Azure-hosted Ghost site which looks clean and is a lot easier to work with. At this point in time there are 63 articles with > 88,000 views. I'm not deleting this blog and not porting the articles over either.
New blog is here (I ported the latest article over as a test):
http://webbercross.azurewebsites.net
New blog is here (I ported the latest article over as a test):
http://webbercross.azurewebsites.net
Sunday, 11 May 2014
Implementing Azure AD Group Authorization
In my last article we talked about
implementing AD single sign-on authentication to an MVC5 website, now we're going to look at adding AD group authorization to a controller with a customised AuthorizeAttribute implementation. Azure AD doesn’t currently
allow addition or custom roles, there are a number of built-in administrator
roles; however we have full control over groups so we can use these for
authorization.
Unfortunately
authorization isn’t as simple as just using the Authorize attribute with a role
as you would with ASP.Net roles, we need to query the Azure AD Graph API to
check if a user is a member of the
group. We’ll add a Sales group to the AzureBakery AD we created in the last article and then implement a
custom AuthorizeAttribute to query the Azure AD Graph API using the Azure AD
Graph client.
This was written for an MVC controller but can be used for a Web API controller and could used with Azure Mobile Services too.
This was written for an MVC controller but can be used for a Web API controller and could used with Azure Mobile Services too.
We’re going to use the
Azure AD PowerShell module to modify the AD Application service principal later
in the procedure, so install this first.
You can download the
module from here:
- · 32-bit version: http://go.microsoft.com/fwlink/p/?linkid=236298
- · 64-bit version: http://go.microsoft.com/fwlink/p/?linkid=236297
I needed to install Microsoft Online Services Sign-In Assistant for IT
Professionals BETA (not RTW): http://www.microsoft.com/en-us/download/confirmation.aspx?id=39267
Creating an AD Group
1.
First go
the AD GROUP workspace in the portal for our Active Directory and click ADD
GROUP on the toolbar:
2.
Enter a
name and description of the group and click the tick button to create it:
3.
Next click
on the newly created group and then ADD MEMBERS on the toolbar:
4.
In the Add
members dialog, click on the AD user we created to add it to the SELECTED list
and click the tick button to confirm:
5.
Now go to
the GROUP CONFIGURE tab and make a note of the OBJECT ID, we’ll need this
later.
6.
Next we
need to create a key for our application to allow us to access the graph API,
so create a new key in the APPLICATION workspace CONFIGURE tab:
7.
Make a
note of this and the CLIENT ID.
8.
We need to
create keys for the local and Azure AD applications.
Modifying the Application Service Principal
We need to modify
our application’s Service Principal so that it has permission to access the
Graph API, in theory this should be done by adjusting the permissions to other
applications section of the APPLICATION CONFIGURATION tab but at the time of
writing this it doesn’t work. Please try it yourself and if it doesn’t work for
you (you will get an unauthorized exception in the AD Graph API Client) use the
following procedure to manually add the service principal to an administrator
role:
1.
Launch the
Azure AD PowerShell Console (from the desktop shortcut if you chose to use it).
2.
First we
need to obtain our AD credentials, so type the following command and enter your
AD user credentials when prompted:
$msolcred = get-credential
This stores the credentials in a variable called $msolcred.
3.
Next we
need to connect the console by typing the following command:
connect-msolservice -credential $msolcred
For a quick test, we can use the get-msoluser command to list AD users.
We should see something like this:
PS C:\WINDOWS\system32> get-msoluser
UserPrincipalName
DisplayName
isLicensed
-----------------
-----------
----------
gwebbercross_outlook.co... Geoff Webber-Cross False
geoff@azurebakery.onmic... Geoff False
4.
Now we
need to get the service principal for our application using the following
command (get the CLIENT ID from CONFIGURE tab of the AD APPLICATION workspace
for the application associated with the website):
$msolServicePrincipal = Get-MsolServicePrincipal -AppPrincipalId YourClientId
5.
We can see
the properties of the service principal
object by outputting it like this:
write-output $msolServicePrincipal
6.
Next we
need to add the service principal to an administrator role like this:
Add-MsolRoleMember
-RoleName “Company Administrator” -RoleMemberObjectId
$msolServicePrincipal.ObjectId –RoleMemberType ServicePrincipal
Implementing AzureAdAuthorizeAttribute
We’re going to
create a class called AzureAdAuthorizeAttribute which can be added to a
controller with either a group name or group ObjectId specified. The ObjectId
implementation is more efficient as it doesn’t require an additional query to
look up the id from the name.
We need to install the Microsoft.Azure.ActiveDirectory.GraphClient and Microsoft.IdentityModel.Clients.ActiveDirectory Microsoft.IdentityModel.Clients.ActiveDirectory NuGet packages by entering the following commands:
- Install-Package Microsoft.Azure.ActiveDirectory.GraphClient
- Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory
This is the complete
code for the attribute, the comments in the code explain what’s going on:
using Microsoft.Azure.ActiveDirectory.GraphClient;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
namespace AdminWebsite.Auth
{
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AzureAdAuthorizeAttribute : AuthorizeAttribute
{
private readonly string _clientId = null;
private readonly string _appKey = null;
private readonly string _graphResourceID = "https://graph.windows.net";
public string AdGroup { get; set; }
public string AdGroupObjectId
{ get; set; }
public AzureAdAuthorizeAttribute()
{
this._clientId = ConfigurationManager.AppSettings["ida:ClientID"];
this._appKey = ConfigurationManager.AppSettings["ida:Password"];
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// First check if
user is authenticated
if (!ClaimsPrincipal.Current.Identity.IsAuthenticated)
return false;
else if (this.AdGroup == null && this.AdGroupObjectId == null) // If there are no groups return here
return base.AuthorizeCore(httpContext);
// Now check if
user is in group by querying Azure AD Graph API using client
bool inGroup = false;
try
{
// Get
information from user claim
string signedInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string tenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
// Get
AuthenticationResult for access token
var clientCred = new ClientCredential(_clientId,
_appKey);
var authContext = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenantId));
var authResult = authContext.AcquireToken(_graphResourceID, clientCred);
// Create graph
connection with our access token and API version
var currentCallContext = new CallContext(authResult.AccessToken,
Guid.NewGuid(), "2013-11-08");
var graphConnection = new GraphConnection(currentCallContext);
// If we don't
have a group id, we can quiery the graph API to find it
if (this.AdGroupObjectId
== null)
{
// Get all groups
var groups = graphConnection.List<Group>(null, null);
if (groups != null && groups.Results != null)
{
// Find group object
var group = groups.Results.SingleOrDefault(r => (r
as Group).DisplayName == this.AdGroup);
// check if user is in group
if (group != null)
this.AdGroupObjectId =
group.ObjectId;
}
}
if (this.AdGroupObjectId
!= null)
inGroup =
graphConnection.IsMemberOf(this.AdGroupObjectId, userObjectId);
}
catch(Exception ex)
{
string message = string.Format("Unable to
authorize AD user: {0} against group: {1}", ClaimsPrincipal.Current.Identity.Name,
this.AdGroup);
throw new Exception(message, ex);
}
return inGroup;
}
}
}
Once we’ve created
this class, we need to add the ida:ClientID and ida:Password settings to the
web.config like this:
<appSettings>
<add key="ida:ClientID" value="d30553b1-21f3-4ee5-bda5-63cf9b2d9861" />
<add key="ida:Password" value="60VVjSMWB5IHNtfIBym9eIv7XXXXXXXXXXXXXXXXXXXXXXXXXX=" />
</appSettings>
Once we’ve done
this, we can simply add the attribute to our controllers to implement Azure AD
Group authorization like this:
namespace AdminWebsite.Controllers
{
[AzureAdAuthorize(AdGroup = "Sales", AdGroupObjectId = "f8a96bf1-c152-41a8-abcdefabcdef")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
So that we can automatically switch the
web.config settings for the Azure web application, we can simply add the following
transform to web.Release.config which will be run during publishing:
<appSettings>
<add key="ida:ClientID" value="123456-58a2-4549-95fc-AABBCCDDee" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
<add key="ida:Password" value="dXqblNwq1y//qOsgI3mD69KfxIFNfXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
</appSettings>
<system.web>
Wednesday, 7 May 2014
Adding Azure AD Single Sign-On to a MVC5 Website
Azure Active Directory Single Sign-On can be used with MVC websites to allow us to create websites with single sign-on authentication for Azure AD users which can be centrally managed in Azure AD.
Visual Studio 2013 makes implementing this really easy and we don't need to touch AD Applications, or web.config in our website.
This article is an extract from a new book I'm writing titled "Learning Microsoft Azure" for Packt Publishing. This website is an admin website for the Sales Business domain of a fictional industrial bakery called Azure Bakery.
I this article, we’re going to create
an Azure Active Directory for the company, then add a user and configure a new MVC5 Administrator website to implement Azure AD Single Sign-On.
Configuring Active Directory
First we need to
create an Active Directory and an initial user account to sign in with
1.
From the
NEW Services menu, select ACTIVE DIRECTORY | DIRECTORY | CUSTOM CREATE:
2.
Fill out
the NAME of the directory, it’s DOMAIN NAME and the COUNTRY OR REGION:
3.
Now from
the Active Directory USERS workspace, click ADD USER from the bottom toolbar to
add a user:
4.
Fill in
the USER NAME. I’ve left TYPE OF USER as New user in your organization,
although you can add an existing Microsoft account or Windows Azure AD
directory:
5.
Next, fill in the user details, select Global
Administrator for the ROLE and click the
next arrow:
6.
Click
create on the next tab to get the temporary password for the user. Make a note
of it and also enter an email to send it to, then click the tick button to
finish:
Configuring an MVC Website for AD Single Sign-On
Next we’ll create a
new MVC website and use the wizard to help us setup AD single sign-on. In
Visual Studio 2012 this was quite tricky to do with a fair amount of manual
configuration in the portal and the website’s web.config, but it’s quite
straight forward in Visual Studio 2013.
1.
In Visual
Studio, add a new ASP.Net Web Application. From the template dialog, select the
MVC template, check Create remote resources under the Windows Azure section and
then click Change Authentication:
2.
Select
Organizational Accounts and enter the AD domain name for the Active Directory
we just created and click OK:
3.
Sign in
using the new Active Directory user, then click OK in the previous dialog (be
careful to change the user to your Azure portal account when prompted to sign
into Azure).
4.
Enter the
Site name, choose Subscription, Region, Database Server (select No database
because we’re using the existing one):
5.
Click OK,
this will now provision the website, setup an AD application and create our MVC
project for us.
6.
We can
test this locally by simply running the website from Visual Studio. You will
get a security warning due to the implementation of a temporary SSL certificate
on your local web server like this (in IE):
7.
Accept the
warning (Continue to this website (not recommended) and you will then see the
AD Login page:
8.
Login with
your new user and the website should load.
Publishing the Website with AD Single Sign-on
When Visual Studio
provisioned our website for us it created an application entry in the AD
APPLICATIONS tab for our local debug configuration:
Rather than changing
the APPLICATION CONFIGURATION for our production website, when we publish the
application, there is an option to Enable Organizational Authentication which
will add a new application entry in AD and rewrite the federation section of
the web.config for us on publish:
<system.identityModel.services>
<federationConfiguration>
<cookieHandler requireSsl="true" />
<wsFederation passiveRedirectEnabled="true" issuer="https://login.windows.net/azurebakery.onmicrosoft.com/wsfed" realm="https://azurebakery.onmicrosoft.com/AdminWebsite" requireHttps="true" />
</federationConfiguration>
</system.identityModel.services>
In the Publish Web
dialog, check Enable Organizational Authentication and enter the AD Domain name.
You will need to include a connection string for your database as the website
will update the database with entries in new IssuingAuthorityKeys and Tenants
tables:
Once published, we
will see a new entry in the AD APPLICATIONS workspace:
This is great as we
don’t need to reconfigure the applications between running locally and
publishing to Azure.
Subscribe to:
Posts (Atom)