En la anterior entrada hemos visto como usar WIF para configurar un servicio WCF que delegue todo el proceso de authenticación de los usarios conectados a este servicio. En este sencillo ejemplo, no nos preocupamos de ningún tipo de configuración, dejando los parametros por defecto, de los generados por el asistente FedUtil. A lo largo de esta entrada, veremos como modificar algunos elementos habituales en nuestros Security Token Service. Para ello, partiremos del ejemplo entregado en la entrada 3-1 y sobre él haremos todo el trabajo.
Modificación de la autenticación
Una de las cosas más habituales en cuanto a la configuración de un STS ( lógicamente si este hace las tareas de un Identity Provider) es el cambio del mecanismo de autenticación. Por defecto, cuando creamos un STS por medio de nuestro asistente este nos presenta la configuración por defecto, delegando la autenticación de los tokens en un manejador llamado WindowsUserNameSecurityTokenHandler. Si no queremos utilizar las credenciales Windows como mecanismo de validación de los usuarios tenemos que modificar el “handler” asociado. Esta tarea, requiere de varios pasos, en primer lugar, modificar el binding asociado al endpoint que por defecto presenta el siguiente aspecto.
1 2 3 4 5 6 7 |
<ws2007HttpBinding> <binding name=<span class="str">"ws2007HttpBindingConfiguration"</span>> <security mode=<span class="str">"Message"</span>> <message establishSecurityContext=<span class="str">"false"</span> /> </security> </binding> </ws2007HttpBinding> |
Aunque pueda pensar que nuestro trabajo consiste en delegar en WCF y sus enlaces todo el proceso, en realidad, WIF, puentea toda la infraestructura para delegar el trabajo en su propio stack. Si queremos establecer una autenticación en base a usuario y contraseña, por ejemplo, tendríamos que modificar la configuración anterior incluyendo el atributo clientCredentialType=”UserName”.
1 2 3 4 5 6 7 |
<ws2007HttpBinding> <binding name=<span class="str">"ws2007HttpBindingConfiguration"</span>> <security mode=<span class="str">"Message"</span>> <message establishSecurityContext=<span class="str">"false"</span> clientCredentialType=<span class="str">"UserName"</span> /> </security> </binding> </ws2007HttpBinding> |
Una vez hecho esto, procederemos a indicarle a WIF cual será el manejador de los tokens que lleguen a nuestro STS, para ello, incluiremos la sección de configuración personalizada Microsoft.IdentityModel y estableceremos un “handler” concreto.
1 2 3 4 5 6 7 8 |
<microsoft.identityModel> <service> <securityTokenHandlers> <remove type=<span class="str">"Microsoft.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler,Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"</span>/> <add type=<span class="str">"Helpers.CustomTokenHandler,Helpers"</span>/> </securityTokenHandlers> </service> </microsoft.identityModel> |
Fíjese como en esta sección se ha quitado el manejador encantado de “manejar” identidades windows por un nuevo manejador llamado CustomTokenHandler. Todos estos “manejadores” están incluído dentro de la jerarquía que impone SecurityTokenHandler y que por defecto ,podemos ver en la siguiente imagen extraída de reflector.
Cada uno de los handlers de esta jerarquía nos servirán como base para los distintos mecanismos de autenticación, usuario, kerberos, certificados, tokens saml11 o saml2 etc… Como nuestro propósito es crear un manejador para credenciales de tipo usuario/contraseña tendremos que hacer una implementación personalizada de la clase UserNameSecurityTokenHandler.
Crear un manejador de tokens de seguridad es tan sencillo como sobreescribir dos elementos, una propiedad llamada CanValidateToken y un método de validación de tokens ValidateToken. El siguiente fragmento de código nos muestra una posible implementación de un token de seguridad, lógicamente, es un ejemplo sencillo y no se hace la validación de los tokens contra ningún almacen, dejamos para usted, amigo lector, esta tarea.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<span class="kwrd">public</span> <span class="kwrd">class</span> CustomTokenHandler : Microsoft.IdentityModel.Tokens.UserNameSecurityTokenHandler { <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> CanValidateToken { get { <span class="kwrd">return</span> <span class="kwrd">true</span>; } } <span class="kwrd">public</span> <span class="kwrd">override</span> Microsoft.IdentityModel.Claims.ClaimsIdentityCollection ValidateToken(System.IdentityModel.Tokens.SecurityToken token) { <span class="rem">//validate the token, the SecurityToken can be ( RsaSecurityToken,WindowsSecurityToken,SessionSecurityToken ..... ) but</span> <span class="rem">//for UserNameSecurityTokenHandler the SecurityToken is a UserNameSecurityToken</span> UserNameSecurityToken userNameToken = token <span class="kwrd">as</span> UserNameSecurityToken; <span class="kwrd">if</span> (userNameToken != <span class="kwrd">null</span>) { <span class="rem">//TODO:validate data into a specific store</span> <span class="kwrd">if</span> (userNameToken.UserName.Equals(<span class="str">"unai"</span>, StringComparison.InvariantCultureIgnoreCase)) { <span class="rem">//Create the principal claims identity collection</span> var claims = <span class="kwrd">new</span> ClaimsIdentityCollection(); var claimIdentity = <span class="kwrd">new</span> ClaimsIdentity(); <span class="rem">//assign identity ( this is the input collection of claims in GetOutputClaimsIdentity in SecurityTokenService)</span> claimIdentity.Claims.Add(<span class="kwrd">new</span> Claim(WSIdentityConstants.ClaimTypes.Name, <span class="str">"unai"</span>)); <span class="rem">//return claims</span> claims.Add(claimIdentity); <span class="kwrd">return</span> claims; } <span class="kwrd">else</span> <span class="kwrd">throw</span> <span class="kwrd">new</span> SecurityException(<span class="str">"Invalid user or password"</span>); } <span class="kwrd">else</span> <span class="kwrd">throw</span> <span class="kwrd">new</span> SecurityException(<span class="str">"Invalid token for Custom Token Handler!"</span>); } } |
Con estos pasos, ya tenemos modificado nuestro sistema de autenticación, aunque nos falta un pequeño paso. Este consiste en incluir el certificado a usar para la encriptación de los datos incluidos en el mensaje de validación (user/pwd) igual que con cualquier servicio WCF. Para ello, simplemente incluiremos dentro de nuestro comportamiento una entrada ServiceCredentials.
1 2 3 4 5 6 7 8 9 |
<serviceBehaviors> <behavior name=<span class="str">"ServiceBehavior"</span>> <serviceMetadata httpGetEnabled=<span class="str">"true"</span> /> <serviceDebug includeExceptionDetailInFaults=<span class="str">"false"</span> /> <serviceCredentials> <serviceCertificate findValue=<span class="str">"PORTBLACKCODE"</span> x509FindType=<span class="str">"FindByIssuerName"</span> /> </serviceCredentials> </behavior> </serviceBehaviors> |
Ahora ya tenemos todo el trabajo realizado, solamente nos quedará actualizar nuestros clientes para actualizar la configuración de los enlaces hacia nuestros STS. Desde esta dirección de Sky Drive podrá descargarse el ejemplo modificado, lógicamente tendrá que actualizar la información refererida a los certificados.
Al intentar descargar el ejemplo desde SkyDrive se muestra un mensaje que indica que el elemento ya no está disponible.
un saludo.
Últimamente tengo poco tiempo para postear, mucho menos de lo que debería seguramente, y entre tarera