Leandro Boffi

Syndicate content
Thoughts on cloud computing, software architecture and stuff...
Updated: 1 hour 52 min ago

Building Elastic and Resilient Cloud Applications

Wed, 2012-05-02 21:40

During the last months I’ve been collaborating as advisor with the Microsoft Patterns and Practices Team in a very interesting project. They worked on an integration pack for Windows Azure and 534475_3170485054475_1035934230_32904838_2136027959_nEnterprise library.

One of the outcomes of that work is a book called “Building Elastic and Resilient Cloud Applications”. This book provides background information on autoscaling and transient fault handling which makes it useful even if you don’t want to use the Application Blocks.

The P&P guys sent me a copy of the book as a gift and they mentioned my name on the list of advisors. I am very proud and thankful of have participated in this project.

You can find the details on MSDN clicking here.

Categories: Blogs

Requesting a Token from ADFS 2.0 using WS-Trust with Username and Password

Fri, 2012-04-13 16:22

In a previous post I showed how to request tokens to ADFS using WS-Trust based on the identity of the user that requests the token.

Due to I’ve received a lot of requests on the subject, here’s the code to do the same but using username and password, I mean request tokens from ADFS 2.0 using username and password based identity.

var stsEndpoint = "https://[server]/adfs/services/trust/13/UsernameMixed"; var relayPartyUri = "https://localhost:8080/WebApp"; var factory = new WSTrustChannelFactory( new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress(stsEndpoint)); factory.TrustVersion = TrustVersion.WSTrust13; // Username and Password here... factory.Credentials.UserName.UserName = user; factory.Credentials.UserName.Password = password; var rst = new RequestSecurityToken { RequestType = RequestTypes.Issue, AppliesTo = new EndpointAddress(relayPartyUri), KeyType = KeyTypes.Bearer, }; var channel = factory.CreateChannel(); SecurityToken token = channel.Issue(rst);

I hope you find it useful!

Categories: Blogs

Request a token from ADFS using WS-Trust from iOS, Objective-C, IPhone, IPad, Android, Java, Node.js or any platform or language

Tue, 2012-02-21 05:56

This is not just a SEO friendly name, in this post I want to show you a very easy way of providing Active Directory authentication in your apps, no matter the platform or language that you use, the only requirement is to be able to make an http post.

Request for a Security Token

To talk with ADFS we must be able to speak WS-Trust protocol, on the .NET platform this is a very easy thing to do thanks to WCF and Windows Identity Foundation frameworks, but regardless the platform make a WS-Trust call is not so hard.

The first thing that we need to know is that WS-Trust protocol defines an standard way of requesting security tokens, based on an XML structure known as Request Security Token or RST, this is an example of that structure:

<trust:RequestSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> <a:EndpointReference> <a:Address>https://yourcompany.com</a:Address> </a:EndpointReference> </wsp:AppliesTo> <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType> <trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType> <trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust:TokenType> </trust:RequestSecurityToken>

Focusing on the basics, there is a couple of fields that are important to us, inside of the RequestSecurityToken element you will find the AppliesTo tag where, using the WS-Addressing standard, we define the scope to which the token is valid, in this case: https://yourcompany.com.

RequestType specifies the action that you want to execute, in our case “Issue”, this means that we want that the (Security Token Service) STS issue a new token, but another option could be renewed an already issued token, in that case the RequestType would be “Renew”.

Finally, the TokenType specifies the type of the token that you want, in our case we are asking for a token based on the SAML 2.0 format.

Doesn’t looks very hard, isn’t? but where do we say who we are? well, one detail that adds a bit of complexity is the fact that all the WS-* protocols stack is build on top of SOAP, so we need to speak SOAP in order to send the token request. Once more, speak SOAP is not so hard, SOAP is also XML-Based, I’m not going to explain the whole SOAP protocol, but you can find the format for a soap message here: http://www.w3.org/2003/05/soap-envelope/

In our case, to talk with ADFS from a native client we going to use username and password security, so this is how the SOAP message will looks like: (I’ve cut some arguments to improve the presentation)

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="..."> <s:Header> <a:Action s:mustUnderstand="1"> http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue </a:Action> <a:To s:mustUnderstand="1">https://yourcompany.com/adfs/services/trust/13/UsernameMixed</a:To> <o:Security s:mustUnderstand="1" mlns:o="..."> <o:UsernameToken u:Id="uuid-6a13a244-dac6-42c1-84c5-cbb345b0c4c4-1"> <o:Username>Leandro Boffi</o:Username> <o:Password Type="...">P@ssw0rd!</o:Password> </o:UsernameToken> </o:Security> </s:Header> <s:Body> <trust:RequestSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> <a:EndpointReference> <a:Address>https://yourcompany.com</a:Address> </a:EndpointReference> </wsp:AppliesTo> <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType> <trust:RequestType> http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue </trust:Requesthttps://yourcompany.com/adfs/services/trust/13/UsernameMixedType> <trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust:TokenType> </trust:RequestSecurityToken> </s:Body> </s:Envelope>

To quickly understand the format, the SOAP envelop has two main tags: header and body. The body of our message contains the RST (Request for Security Token) message that we created before. In the header we can find context parameters like, the Uri of the service endpoint (To), the name of the action exposed in that endpoint that you want to execute (Action), remember that in SOAP you can have multiple actions in a single endpoint, and who we are (Security), in this case username and password.

To use UserName and Password authentication we need to look for the action Issue in the endpoint https://yourcompany.com/adfs/services/trust/13/UsernameMixed, so make sure that this endpoint is enabled on ADFS configuration.

Once we have the SOAP message, we just need to send it to the server using a regular HTTP POST, this is an example of how to do it on .NET, but it can be applied to any platform or language:

var client = new WebClient(); client.Headers.Add("Content-Type", "application/soap+xml; charset=utf-8"); var result = client.UploadString( address: "https://yourcompany.com/adfs/services/trust/13/UsernameMixed", method: "POST", data: soapMessage);

Make sure that you specify the Content-Type header to “application/soap+xml; charset=utf-8”, what you finally need to send to the server is this:

POST /adfs/services/trust/13/UsernameMixed HTTP/1.1

Connection: Keep-Alive

Content-Length: 1862

Content-Type: application/soap+xml; charset=utf-8

Accept-Encoding: gzip, deflate

Expect: 100-continue

Host: localhost

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">….</s:Envelope>

I’ve added other headers to be consistent with the HTTP Protocol, but for ADFS just the Content-Type is required.

The Answer: Request Security Token Response

If your credentials were valid, and the scope Uri is the right one, you will get a SOAP response from ADFS. In the body of that message you will get something like this:

<trust:RequestSecurityTokenResponseCollection xmlns:trust="..."> <trust:RequestSecurityTokenResponse> <trust:Lifetime>...</trust:Lifetime> <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">...</wsp:AppliesTo> <trust:RequestedSecurityToken> <Assertion ID="_fcf06a39-c495-4074-8f22-4a7df6e26513" IssueInstant="2012-02-21T04:27:24.771Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"> <Issuer>http://yourcompany.com/adfs/services/trust</Issuer> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <ds:Reference URI="#_fcf06a39-c495-4074-8f22-4a7df6e26513"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>...</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>...</ds:SignatureValue> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <ds:X509Data> <ds:X509Certificate>...</ds:X509Certificate> </ds:X509Data> </KeyInfo> </ds:Signature> <Subject> <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <SubjectConfirmationData NotOnOrAfter="2012-02-21T04:32:24.771Z"/> </SubjectConfirmation> </Subject> <Conditions NotBefore="2012-02-21T04:27:24.756Z" NotOnOrAfter="2012-02-21T05:27:24.756Z"> <AudienceRestriction> <Audience>https://yourcompany.com/</Audience> </AudienceRestriction> </Conditions> <AttributeStatement> <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"> <AttributeValue>Leandro Boffi</AttributeValue> </Attribute> <Attribute Name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role"> <AttributeValue>Administrator</AttributeValue> <AttributeValue>Mobile User</AttributeValue> </Attribute> </AttributeStatement> <AuthnStatement AuthnInstant="2012-02-21T04:27:24.724Z"> <AuthnContext> <AuthnContextClassRef> urn:oasis:names:tc:SAML:2.0:ac:classes:Password </AuthnContextClassRef> </AuthnContext> </AuthnStatement> </Assertion> </trust:RequestedSecurityToken> <trust:RequestedAttachedReference>...</trust:RequestedAttachedReference> <trust:RequestedUnattachedReference>...</trust:RequestedUnattachedReference> <trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust:TokenType> <trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType> <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType> </trust:RequestSecurityTokenResponse> </trust:RequestSecurityTokenResponseCollection>

This format is also specified in the WS-Trust protocol as “Request Security Token Response” or RSTR, but for you the most important section of the response is in:

RequestSecurityToeknResponseCollection/RequestSecurityToeknResponse/RequestedSecurityToken

The content of that tag is the security token, in our case a SAML 2.0 token:

<Assertion ID="_fcf06a39-c495-4074-8f22-4a7df6e26513" IssueInstant="2012-02-21T04:27:24.771Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"> <Issuer>http://yourcompany.com/adfs/services/trust</Issuer> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <ds:Reference URI="#_fcf06a39-c495-4074-8f22-4a7df6e26513"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>...</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>...</ds:SignatureValue> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <ds:X509Data> <ds:X509Certificate>...</ds:X509Certificate> </ds:X509Data> </KeyInfo> </ds:Signature> <Subject> <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <SubjectConfirmationData NotOnOrAfter="2012-02-21T04:32:24.771Z"/> </SubjectConfirmation> </Subject> <Conditions NotBefore="2012-02-21T04:27:24.756Z" NotOnOrAfter="2012-02-21T05:27:24.756Z"> <AudienceRestriction> <Audience>https://yourcompany.com/</Audience> </AudienceRestriction> </Conditions> <AttributeStatement> <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"> <AttributeValue>Leandro Boffi</AttributeValue> </Attribute> <Attribute Name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role"> <AttributeValue>Administrator</AttributeValue> <AttributeValue>Mobile User</AttributeValue> </Attribute> </AttributeStatement> <AuthnStatement AuthnInstant="2012-02-21T04:27:24.724Z"> <AuthnContext> <AuthnContextClassRef> urn:oasis:names:tc:SAML:2.0:ac:classes:Password </AuthnContextClassRef> </AuthnContext> </AuthnStatement> </Assertion>

Once we extract the token from the response, everything gets simpler: Inside of the AttributeStatment section you will have a list of Attribute, this are the claims, information of the user, for example in this token we have three different claims:

  • Type: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
  • Value: Leandro Boffi
  • Type: http://schemas.microsoft.com/ws/2008/06/identity/claims/role
  • Value: Administrator
  • Type: http://schemas.microsoft.com/ws/2008/06/identity/claims/role
  • Value: Mobile User

You can use those claims to perform authorization in your application, but also if your app needs to call webservices that rely on your ADFS, you will need to send the entire token in each request that you made to those services (I’ll explain this scenario in a future post).

Security Features

The token has some security features with which we can get us to make our application more secure. I’m not going to explain all the features in this post, but for example, if we want we can verify that no body has modified the token, because it is signed by the issuer (in our case, ADFS). You can find the signature on Assertion/Signature/SignatureValue. This signature is also based on a standard called XML Signature, you can find the specification here: http://www.w3.org/Signature/.

Also another very important feature is the fact that the token has a limited life time, to avoid that somebody use an old token, you can find that in the Assertion/Conditions/NotBefore and NotOnOrAfter.

Conclusion

Integrate the identity of our apps to Active Directory, no matter the platform or the language is possible due to ADFS is based on WS-Trust an standard protocol. If your language do not support WS-Trust natively it requires a bit more of effort, but as we saw in this post it’s not hard at all, you just need an XML template for the SOAP+RST call and an HTTP Post.

Download here the template for doing the SOAP-RST call, just replace the values in brackets with your values and start requesting tokens!

Hope has been useful!

Categories: Blogs

Claims Identity + C# 4.0 Dynamics

Sun, 2012-02-05 21:30

In this post I want to share with you something that I always use when I work with claim-based identity using Windows Identity Foundation.

As you probably know, when you use claim-based identity in a project using WIF you have access to the user claims in the System.Thread.CurrentPrincipal.Identity property, but there is an small problem, it’s type is System.Security.IIdentity and to access to the claims you need to cast that property to Microsoft.IdentityModel.Claims.IClaimsIdentity so you end up with something like this in the middle of your app:

((IClaimsIdentity)Thread.CurrentPrincipal.Identity).Claims

That’s not sexy at all, and it gets worse if you need an specific claim, for example the email address of the user:

var user = ((IClaimsIdentity)Thread.CurrentPrincipal.Identity); var email = user.Claims.FirstOrDefault(x => x.ClaimType.Equals(ClaimTypes.Email, StringComparison.OrdinalIgnoreCase)) .Value;

You can solve this with a simple extension method something like GetClaimValue(string claimType), and you’ll get something like this, but is still not sexy:

var user = ((IClaimsIdentity)Thread.CurrentPrincipal.Identity); var email = user.GetClaimValue(ClaimTypes.Email);

Wouldn’t it be better to have something like this?

var email = user.Email;

Yes, you guessed, using C# 4.0 dynamics and a little of reflection, that’s possible, we simply need to create a class called DynamicIdentity that extends the DynamicObject class, and find in the ClaimTypes (or in any class we want) the value for the type requested using the property name to search. For example, if we write user.Email we are asking for all the  claims defined  with the claim type ClaimTypes.Email (“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress”).

This is how the DynamicIndentity class looks like:

public class DynamicIdentity : DynamicObject { private ClaimTypeResolver claimTypeResolver; private IEnumerable<Claim> claims; public DynamicIdentity(IEnumerable<Claim> claims, ClaimTypeResolver typeResolver) { this.claims = claims; this.claimTypeResolver = typeResolver; } public override bool TryGetMember(GetMemberBinder binder, out object result) { var claimType = this.claimTypeResolver.Resolve(binder.Name); var claims = this.claims.Where(x => x.ClaimType.Equals(claimType, StringComparison.OrdinalIgnoreCase)); if (claims.Count() == 0) { throw new ArgumentException( string.Format("Claim with type '{0}' was not found", claimType)); } if (claims.Count() == 1) { result = claims.First().Value; return true; } result = claims.Select(x => x.Value).ToArray(); return true; } }

The ClaimTypeResolver class is responsible for resolving the Claim Type value based on the property name:

public class ClaimTypeResolver { private Type[] typesDefinition; public ClaimTypeResolver(Type[] typesDefinition) { this.typesDefinition = typesDefinition; } public string Resolve(string friendlyName) { foreach (var type in this.typesDefinition) { var field = type.GetField(friendlyName); if (field != null) { return field.GetRawConstantValue().ToString(); } } throw new ArgumentException( string.Format("Claim Type '{0}' was not found.", friendlyName)); } }

Notice that it receives an array with the types that contains the constants for your claim types.

Once we have that, we just need to create and extension method of the IPrincipal class that returns this dynamic object:

public static dynamic AsDynamic(this IPrincipal user) { var claims = ((IClaimsIdentity)user.Identity).Claims.AsEnumerable(); var resolver = new ClaimTypeResolver(new Type[] { typeof(ClaimTypes), typeof(MyCompanyClaimTypes), }); return new DynamicIdentity(claims, resolver); }

That’s all, this is the final experience for the developer:

var user = Thread.CurrentPrincipal.AsDynamic(); Console.WriteLine("Email: {0}", user.Email); foreach (var role in user.Role) { Console.WriteLine("Role: {0}", role); }

Download the code here!

Categories: Blogs