PDC09 and the last 3 months…
December 9th, 2009
Wow, 3 months since my last post… Lots of things happened. We’ve been working with James Conard’s team from Microsoft
DPE on the PDC09 keynote demos, specifically the Platform Converge demo (Doug Pourdy) and the VS2010, AppFabric, NET4, WIF demo (by Cameron Skinner, read more in his post). We also helped delivering the training kits (identity, vs2010, azure, etc.) and the labs that were available on PDC. Tim and Johnny posted more details about this.
Being part of the making of a PDC keynote was very interesting. We had meetings with Partner Architects and Distinguished Engineers of the different Microsoft product groups. You get to learn a lot about politics in those meetings ;).
Once again we worked closely with Vittorio on Windows Identity Foundation content which was RTMed. David and Ryan on Azure and Jonathan Carter on Tailspin among other things. Finally, thanks all the team @ Southworks for the great support pre-PDC.
On a related note, the last month we worked hard with Eugenio, Erwin, the team at patterns & practices, Fede Boerr, Keith, Dominick to deliver a printed preview of the Claims Based Identity & Access Control Guide. Limited copies were distributed on the WIF booth and p&p booth at PDC and the book was very well received. We’ll be soon reaching a milestone and publish the following chapters:
Well, I had to catch-up with the blog… hopefully will keep the pace now.
.NET Service Bus – Remote Desktop over Firewalls!
July 10th, 2009
Today was holiday in Argentina but I had to work on some pending stuff (yeah, lucky me). I didn’t want to travel to the office but I had to access a SQL Server that was hosted at Southworks LAN and we don’t have inbound ports open to connect to our workstations through RDP (port 3389). So…. the .Net Service Bus came to the rescue! Last week David Aiken told me about this cool project hosted on codeplex http://socketshifter.codeplex.com. He told me “these people are streaming video over the service bus”…
So yesterday before leaving the office I opened up the socketshifter server, configured my service bus account and allowed some ports to be redirected. Today I connected from home and here is a nice screenshot of RDP to a Southworks LAN machine (connected to localhost:1000). Isn’t it cool??!!?!
For those of you who are wondering how these work, the code is reeeeallly simple because all of the hardlifting is made by the service bus. There is a client and a server that will redirect a stream of bytes from port a to port b (using plain sockets). The socket shifter client will establish a session to the server via the service bus using the NetTcpRelayBinding. The rest of the story is bytes flowing around
This is the client running on my laptop:
And this is the configuration on the client
<configuration>
<appSettings>
<add key="solutionName" value="southworks-magnolia"/>
<add key="password" value="…."/>
<add key="servicePath" value="sb://southworks-magnolia.servicebus.windows.net/rd"/>
<add key="localPort" value="1000" />
<add key="remoteHost" value="localhost"/>
<add key="remotePort" value="3389"/>
</appSettings>
</configuration>
The server needs to be installed as a Windows Service to be able to redirect 3389. I also tried accessing the web server (IIS7) that is running on the LAN and it worked (need to configure the remoteport to 80). So that means that I can expose a web server that runs on my desktop machine and show stuff to customers without asking IT to move my machine to the DMZ!! Clemens did something similar couple of months ago.
This is a very cool use of the Service Bus. Go ahead and try it yourself! Download the latest source code.
Multi tenant federation with Geneva Framework and Microsoft .NET Services Access Control
April 23rd, 2009
A typical scenario for an ISV that wants to create the "next application in the cloud" will be how to support identity federation with their customers (tenants). A common requirement I’ve heard is:
"I want to enable single sign on and allow enterprises that have their own STS to integrate with us. For companies that don’t have any identity infrastructure in place we want to allow them to login with an ubiquous credential like Windows LiveID. How do we do that without spending three months with a security guru?"
A possible answer is use Microsoft .NET Services Access Control. They enable that scenario in a very straightforward fashion. The following diagram shows a possible architecture that might fulfill the customer requirements. In this picture Southworks is an enterprise that has its own STS and Contoso doesn’t, hence they use Windows LiveID for their users. The good thing about this is that in the middle we have ACS acting as the "normalizer". It will receive tokens from LiveID and Southworks IP STS and will transform them to something Fabrikam knows (Roles, Actions, etc.).
If you are like me, you might be wondering how this all works. Here are the gory details of all the HTTP interactions of a WS-Federation passive profile "dance":
- A user opening a browser
- If he is at Contoso he browses to www.fabrikam-cloudapp.com/contoso (or contoso.fabrikam-cloudapp.com)
- If he is at Southworks he browses to www.fabrikam-cloudapp.com/southworks (or southworks.fabrikam-cloudapp.com)
- At the web site there is an Http Module or ActionFilter in ASP.NET MVC that will read the tenant alias from the route.
- The module will construct the SignInRequestMessage federation message and will redirect to ACS (https://{solution-name}.accesscontrol.windows.net/passivests/{federation-endpoint}).
- If it’s Southworks tenant we have to use the https://{solution-name}.accesscontrol.windows.net/passivests/Federation.aspx endpoint of ACS.
- If it’s Contoso we have to use https://{solution-name}.accesscontrol.windows.net/passivests/LiveFederation.aspx in the endpoint of ACS.
Note: these urls are not in any ACS documentation for now - The homeRealm (whr) parameter will tell ACS which IP STS to use (Contoso = login.live.com, Southworks = login.southworks.net which is a url Southworks provided at provisioning probably).
- Finally, the realm parameter is fabrikam-cloudapp application. This will have to match with the scope you create on ACS (more on that below).
Note: Look at the code at the end of the post to see how homeRealm and realm is used. - ACS will redirect the user to either LiveID or the company IP STS depending on the whr parameter
- The user will login with LiveID cred or with other mechanisms if it’s a Geneva Server (user/pwd, kerberos, certs, cardspace, etc.)
- A token will be issued and will be POSTed to ACS federation endpoint
- [Contoso] In the case of LiveID you don’t have to do anything because ACS setup all the federation for us. LiveID will issue a token with one claim: the WLID email.
- [Southworks] In the case of the company IP STS you will have to configure ACS as a relying party and supply its certificate to encrypt the token in your IP STS. In ACS you will have to create an Issuer and upload the company IP STS certificate. The company STS will issue the name claim and maybe groups or other claims.
- ACS will read the token and apply the claim transformation (more on that below)
- ACS will POST the token to www.fabrikam-cloudapp.com
- Geneva Fx on the website will read the token, generate the ClaimsPrincipal and store it in a cookie for later usage.
- Fabrikam can now authorize access to certain pages by reading the principal! either with IsInRole or for granular checks you could use Actions or a ClaimsAuthorizationManager
This is how ACS is configured for :
A better way to look at the table above is the following diagram:
Here we are using the Geneva Fx manually inside an ASP.NET MVC action filter. We use it manually because the multi tenancy nature that we want to implement does not allow to use the fixed values from the wsFederation configuration section.
1: namespace FabrikamCloudApp.Web.Identity
2: {
3: using System;
4: using System.Globalization;
5: …
6:
7: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
8: public sealed class WSFederationAuthenticationFilterAttribute : ActionFilterAttribute, IExceptionFilter
9: {
10: public override void OnActionExecuting(ActionExecutingContext filterContext)
11: {
12: var principal = Thread.CurrentPrincipal;
13:
14: if (!principal.Identity.IsAuthenticated)
15: {
16: string realm;
17: string homeRealm;
18: string acsEndpoint;
19: SignInRequestMessage message = null;
20:
21: // retrieve tenant from url (http://…/{tenant})
22: var tenant = (string)filterContext.RouteData.Values["tenant"];
23:
24: // lookup in some repository if the tenant setup a federation with its own sts
25: if (IsLiveIDFederation(tenant))
26: {
27: acsEndpoint = "LiveFederation.aspx";
28: homeRealm = "http://login.live.com";
29: }
30: else
31: {
32: acsEndpoint = "Federation.aspx";
33: homeRealm = GetIdentityProviderUrl(tenant);
34: }
35:
36: realm = "http://www.fabrikam-cloudapp.com/";
37: string issuer = "https://{solution-name}.accesscontrol.windows.net/passivests/{federation-endpoint}"
38: .Replace("{solution-name}", Configuration.FabrikamSolutionName)
39: .Replace("{federation-endpoint}", endpoint);
40:
41: message = new SignInRequestMessage(new Uri(issuer), realm);
42: message.Parameters.Add("whr", homeRealm);
43:
44: // redirect to ACS
45: filterContext.Result = new RedirectResult(message.WriteQueryString());
46: }
47: else
48: {
49: // we are back on our site, and we’ve got our token transformed to ClaimsPrincipal
50: var claimsIdentity = principal.Identity as IClaimsIdentity;
51:
52: var tenantClaim = claimsIdentity.Claims.FirstOrDefault(c => c.ClaimType == Configuration.TenantClaimType);
53: if (tenantClaim == null)
54: {
55: throw new HttpException(401, "Access is denied");
56: }
57:
58: var fam = FederatedAuthentication.WSFederationAuthenticationModule;
59: if (fam.CanReadSignInResponse(HttpContext.Current.Request, true))
60: {
61: SecurityToken token = fam.GetSecurityToken(HttpContext.Current.Request);
62: SessionSecurityToken sessionToken = new SessionSecurityToken(principal as IClaimsPrincipal, token);
63: fam.SetPrincipalAndWriteSessionToken(sessionToken, true);
64:
65: filterContext.Result = new RedirectToRouteResult(
66: "DefaultRoute",
67: new RouteValueDictionary(new { tenant = tenantClaim.Value }));
68: }
69: }
70: }
71:
72: }
73: }
And just put this attribute on your entry point Controller:
1: public class HomeController
2: {
3: [WsFederationAuthenticationFilter]
4: public void Index()
5: {
6: }
7: }
I can tell my customer "sure, we can implement this. It’s only 73 lines of code and some configuration here and there"
Happy Federation!
IssueTracker Azure Edition - a Cloud Application
February 13th, 2009
Couple of weeks ago Ryan Dunn announced Azure Issue Tracker. From this post:
"This sample application is a simple issue tracking service and website that pulls together a couple of the Azure services: SQL Data Services and .NET Access Control Service."![]()
I’ve been working with Ryan and other guys at DPE and Southworks to put together this sample before PDC. With all the back and forth (the .NET services were not working as reliable as they work now) we were not able to pull it through at that time. Well, it’s now live and you can download the source code. Some of its features:
- [Identity] .NET Services Access Control as a relying party and claims transformation STS
- [Identity] Federation against LiveID and claim mapping between email -> tasks. I hinted the implementation in these post.
- [Identity] Claims aware application and service layer (by doing identity delegation with ActAs)
- [Data] Storage on SDS using the flexible schema to extend the data model of the issue
- [General] Multi tenancy at all levels (identity, data, programming model)
- [General] Clean separation of concerns using ASP.NET MVC, Geneva Framework, WCF and WF.
This is the standard edition. The enterprise edition is coming with features related to manageability (Management API, Powershell CmdLets, MMC, SCOM, etc.) and identity federations against third party STS. Stay tuned!
Windows Azure @ PDC Buenos Aires
November 18th, 2008
It’s been two weeks already that we’ve got back from LA after attending PDC. Lots of things announced there.
Microsoft Argentina organized the local-mini version of PDC. I will be there showing Windows Azure with Edgardo.
The talk will be mainly demos (as usual
and explain some concepts around Windows Azure.
You can register here (it seems it’s all booked though): http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032394696&Culture=es-AR
Other southies will be presenting as well at this event:
- Martin Salias will be talking about the future of the languages (F# and C#)
- Paulo Arancibia and Julian Dominguez will be showing how to develop data driven apps using the new DataGrid and Ribbon controls of WPF.
See you there…
Azure Services Platform - Passive Federation & Access Control #2
November 9th, 2008
In the previous post I introduced a scenario where you can use .NET Services Access Control and Windows LiveID to delegate authentication and authorization. In this post we will go through the different pieces needed in the application to perform authorization checks. First thing will be configure the passive federation using Geneva on the application and later we will create an ASP.NET MVC action filter to perform the access check against the incoming claims.
Note: all the code showed here is using Microsoft Identity Framework "Zermatt" Beta 1. The new Geneva Framework might have some changes.
Configuring passive federation on the website
Configure passive federation on the website is about defining which SAML token version we will accept and the certificate we will use to decrypt the incoming token. The following configuration uses Zermatt Beta 1, so this probably changes on Geneva.
<microsoft.identityModel> <tokenHandlers> <remove type="Microsoft.IdentityModel.Tokens.Saml11.Saml11TokenHandler, Microsoft.IdentityModel, Version=0.4.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <add type="Microsoft.IdentityModel.Tokens.Saml11.Saml11TokenHandler, Microsoft.IdentityModel, Version=0.4.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> <samlSecurityTokenRequirement> <allowedAudienceUris> <add value="http://localhost/YourApp/" /> </allowedAudienceUris> </samlSecurityTokenRequirement> </add> </tokenHandlers> <federatedAuthentication enabled="true"> </federatedAuthentication> <serviceCertificate> <certificateReference findValue="01 20 …" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" /> </serviceCertificate> </microsoft.identityModel>
When the user click on the sign in button, the link will point to to the .NET Services Access Control passive STS url. The following method uses Geneva to generate this WS-Federation url.
private static string GetFederationUrl(string realm, string issuer, string homeRealm, string returnUrl) { FederatedAuthenticationModule fam = new FederatedAuthenticationModule(); fam.Realm = realm; fam.Issuer = issuer; fam.Reply = returnUrl; SignInRequestMessage signInMsg = fam.CreateSignInRequest(); signInMsg.Parameters.Add("whr", homeRealm); string url = signInMsg.WriteQueryString(); return url; }
The following code and configuration will give you an idea of the url that is being built. Pay attention to this url because a small change might break the whole thing.
string url = GetFederationUrl(ConfigurationManager.AppSettings["AccessControlRealm"], ConfigurationManager.AppSettings["AccessControlIssuer"], ConfigurationManager.AppSettings["AccessControlHomeRealm"], replyTo);
<!– Windows Azure Federation –> <add key="AccessControlRealm" value="http://localhost/YourApp/"/> <!– should match to a scope –> <add key="AccessControlIssuer" value="https://accesscontrol.windows.net/passivests/yoursolution/LiveFederation.aspx"/> <add key="AccessControlDefaultReply" value="http://localhost/YourApp" /> <add key="AccessControlHomeRealm" value="http://login.live.com" />
The AccessControlRealm config is important because it will match the scope on your solution. You will have to configure the scope to encrypt with the public key of your website certificate and create the claim mappings from Windows LiveID to your well known claims. If you don’t have the scope created or configured to output at least one claim you will get a 403 Forbidden on the .NET Services Access Control STS.
Performing access check in the web site
Now that we have everything configured and the token should be coming back to our website, it’s time to do the access check. By using Geneva, the token will be transformed to a Principal object and it will be accessed through the ClaimsPrincipal static class. On the other hand, ASP.NET MVC allow us to plug into the action execution pipeline and get access to the context data like route values. The following code shows an ActionFilterAttribute that will grab the claims from the the Geneva ClaimsPrincipal and will call a strategy class that will perform the access check. If the access check is not successful, the filter will render a NotAuthorized view.
namespace YourApp.Identity { using System; … public class ClaimAuthorizationRouteFilterAttribute : ActionFilterAttribute { public ClaimAuthorizationRouteFilterAttribute(string[] operations) { this.Operations = operations; } public string[] Operations { get; set; } public override void OnActionExecuting(ActionExecutingContext context) { var identity = ClaimsPrincipal.Current.Identity as IClaimsIdentity; var claims = identity.Claims.ToArray(); var routeData = context.RouteData.Values.ToArray(); var strategy = CreateAuthorizationStrategy(); var executionContext = new ExecutionContext() { ClaimsNeeded = Operations, OperationContextData = routeData, }; if (!strategy.IsAuthorizedFor(executionContext, claims)) { context.Result = new ViewResult { ViewName = "NotAuthorized" }; } base.OnActionExecuting(context); } } }
Finally, the following code shows an implemented strategy for a multi tenant application that manage projects.
namespace YourApp.Identity { using System.Linq; using System; public class StandardAuthorizationStrategy : IAuthorizationStrategy { private const string ProjectClaimType = "urn:Project"; private const string TenantClaimType = "urn:Tenant"; private const string OperationClaimType = "urn:Operation"; public bool IsAuthorizedFor(ExecutionContext context, Microsoft.IdentityModel.Claims.Claim[] claims) { bool authorized = true; var tenantClaim = claims.SingleOrDefault(c => c.ClaimType == TenantClaimType); var operationClaims = claims.Where(c => c.ClaimType == OperationClaimType); var projectClaims = claims.Where(c => c.ClaimType == ProjectClaimType); var tenant = context.OperationContextData["Tenant"].ToString(); var project = context.OperationContextData["Project"].ToString(); if (!string.IsNullOrEmpty(tenant)) { authorized &= tenantClaim.Value.Equals("*", StringComparison.OrdinalIgnoreCase) || tenantClaim.Value.Equals(tenant, StringComparison.OrdinalIgnoreCase); } if (!string.IsNullOrEmpty(project)) { authorized &= projectClaims.Where( p => p.Value.Equals("*", StringComparison.OrdinalIgnoreCase)).Count() > 0 || projectClaims.Where( p => p.Value.Equals(project, StringComparison.OrdinalIgnoreCase)).Count() > 0; } if (context.Operations != null) { bool temp = true; foreach (string op in context.ClaimsNeeded) { temp &= operationClaims.Where(o => o.Value.Equals(op, StringComparison.OrdinalIgnoreCase)).Count() > 0 || operationClaims.Where(o => o.Value.Equals("*", StringComparison.OrdinalIgnoreCase)).Count() > 0; } authorized &= temp; } return authorized; } } }
The only thing left is to put an attribute above the action. The following attribute specifies that the New action will be executed if the incoming token contains the following "urn:Operation" claims.
public class ProjectsController : Controller { [ClaimAuthorizationRouteFilter(new string[] { "AddUser", "AddUsersToProject", "CreateProject" })] public ActionResult New() { …. } … }
So if a user browses to: http://yourapp/Contoso/Projecsts/New, the filter will call the strategy that will check:
- if the user contains a tenant claim with the value "Contoso" (taken from the route data)
- if the user contains three operation claims: AddUser, AddUsersToProject and CreateProject
And if a user browses to: http://yourapp/Contoso/Projecsts/some-project/Edit, the filter will call the strategy that will check:
- if the user contains a "tenant" claim with the value "Contoso" (taken from the route data)
- if the user contains the "operation" claims specified in the Edit action
- if the user contains a "project" claim with the value "some-project"
Azure Services Platform - Passive Federation & Access Control #1
November 7th, 2008
The last couple of months together with other people at Southworks we’ve been working with the DPE team on samples, demos, hands on labs for PDC all related to the cloud computing services Microsoft announced at PDC, the Azure Services Platform.
During the week, I attended Kim Cameron’s and Vittorio Bertocci session where they talked about identity federation and claim based architecture using "Geneva" Server, Microsoft Federation Gateway, "Geneva" Framework (previously known as Zermatt) and the .NET Services Access Control. I enjoyed watching Vittorio during the session.
Other interesting things we did in the identity arena with Ryan Dunn is use the .NET Services Access Control and Windows Live ID to delegate authentication and authorization to the cloud. In this post I will introduce the scenario where you can federate your application against .NET Services Access Control which indeed federates against Windows LiveID. This will allow users of your application to log in to your application using their Windows LiveID account and then use .NET Services Access Control to transform the email claim to a set of claims to perform authorization access checks.
Identity + Access Control using Windows Live ID + .NET Services Access Control
Windows Live ID can authenticate users of your web site and then use .NET Services Access Control to map claims between the Live ID (email) and some other claim (like role, operation, task). The image below shows a claim mapping that you would create in your .NET Services account.
The output claims could be used later in the application to perform access check against resources or modify the UI according to the incoming claims. The flow is governed by the WS-Federation protocol as shown below:
In a nutshell, the browser will click on the Sign In link on the website and it will be redirected to the token issuer, in this case the .NET Services Access Control passive STS. The home realm on the url will be login.live.com and the .NET Services STS trust on Windows LiveID tokens. The user will log in on Windows LiveID and it will send the token back to the .NET Services STS. Finally the claim mapping will occur and the token will come back to the website with the authorization claims.
In the following post I will show how to configure your application to read the incoming token claims and do access check over page urls.
Azure Services Kit available
October 30th, 2008
The work we did through the last couple of months is materialized now. James Conard and the DPE team (Nigel Watling, Ryan Dunn, Vittorio Bertocci, Drew Robbins, et al) were able to hit the road before anyone else at Microsoft by releasing the Azure Services Training Kit.
I was walking through the Hands On Labs lounge today, watching at people doing lines to do the labs.
The kit includes labs on:
- SQL Data Services
- Windows Azure
- .NET Services Service Bus
- .NET Services Access Control
- .NET Services Workflow
You will need an invitation code, but you will be able to read the document and code to get lots of information.