MovGP0        Über mich        Hilfen        Artikel        Weblinks        Literatur        Zitate        Notizen        Programmierung        MSCert        Physik      


Ticket/Token Formats

Bearbeiten
  • FormsAuthenticationTicket
  • JWT Tokens
    • JSON basiert; Binär- und XML-Serialisierung möglich
    • unterstützt Claims, Signatur, Verschlüsselung
    • Standard bei OAuth
  • SAML Token
    • unterstützt Claims, Signatur, Verschlüsselung
    • SAML-Protokoll wird in WIF nicht unterstützt; es kommen WS-* Protokolle zur Anwendung
    • komplexer Standard. der Stack muss XML Signaturen, XML Verschlüsselung, SOAP, etc. können
  • X.509 Certificate Token
  • Kerberos Ticket
  • etc.

Wichtige Protokolle

Bearbeiten

Konfiguration von web.config

Bearbeiten

Simple configurations can be done using the Federation Utility Wizard (FedUtil.exe). Add STS Reference (STS = Security Token Service) in Visual Studio when WIF SDK is installed.

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection,Microsoft.IdentityModel,Version=3.5.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
        <!-- ... -->
    </configSections>

    <appSettings>
        <add key="FederationMetadataLocation" value="./FederationMetadata.xml">
    </appSettings>

    <system.web>
        <authentication mode="None" />
        <authorization>
            <deny users="?" /><!-- '?' = not authenticated users; '*' = all users  -->
        <authorization>
        <!-- configure access for directiories based on roles here -->
    </system.web>

    <!-- use <httpModules> if IIS version < 7.0 -->
    <system.webServer> 
        <validation validateIntegratedModeConfiguration="false" />
        
        <modules>
            <!-- authenticate user with external token service -->
            <!-- it's events are handeled in global.asax -->
            <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule,Microsoft.IdentityModel,Version=3.5.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />

            <!-- already authenticated sessions -->
            <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule,Microsoft.IdentityModel,Version=3.5.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />

            <!-- checks if user is authorized to access a given resource given his claims -->
            <add name="ClaimsAuthorizationModule" type="Microsoft.IdentityModel.Web.ClaimsAuthorizationModule,,Microsoft.IdentityModel,Version=3.5.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" />
        </modules>
    </system.webServer>

    <!-- configuration for WIF plugins -->
    <microsoft.identityModel>
        <service>
            <!-- configure how to validate tokens -->
            <audienceUris>
                <add value="https://my-sitename/">
            </audienceUris>
            <certificateValidation>
                <certificateValidator ... />
            </certificateValidation>
            
            <!-- supported are 
                * Samt11SecurityTokenHandler, 
                * Saml2SecurityTokenHandler, 
                * X509SecrityTokenHandler, 
                * KerberosSecurityTokenHandler, 
                * WindowsUserNameSecurityTokenHandler, 
                * RsaSecurityTokenHandler, 
                * EncryptedSecurityTokenHandler 
            -->
            <securityTokenHandlers>
                <securityTokenHandlerConfiguration>
                    <!-- concrete configuration depends on handler -->
                    <tokenReplayDetection ... />
                </securityTokenHandlerConfiguration>
            </securityTokenHandlers>
            
            <!-- authentication with external WS-* provider -->
            <federatedAuthentication> 
                <wsFederation passiveRedirectEnabled="true" issuer="https://my-STS-provider.com" realm="https://my-sitename/" requireHttps="true" /> <!-- has many more configuration attributes -->
                <cookieHandler requireHttps="true" />
                <!-- <maximunClockSkew ... /> -->
                <!-- <serviceCertificate ... /> -->
            </federatedAuthentication>
            
            <!-- claims whick the application needs about the user -->
            <applicationService>
                <claimTypeReqired>
                    <claim type="http://schemas.xmlsoap.org/claims/Group" optionalt="true" />
                    <claim type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" optionalt="true" />
                </claimTypeReqired>
            <applicationService>
            
            <!-- thumbprint of the WS-* provider  -->
            <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry,,Microsoft.IdentityModel,Version=3.5.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35">
                <trustedIssuers>
                    <add thumbprint="OE2A9EB75F1AFC321790407FA4B130E0E4E22" name="https://my-STS-provider.com" />
                </trustedIssuers>
            </issuerNameRegistry>
            
            <!-- custom authorization manager -->
            <claimsAuthorizationManager type="MyNamespace.AgeTreshholdClaimsAuthorizationManager">
                <policy resource="/xxx.gif" action="GET">
                    <claim claimType="http://schemas.xmlsoap.org/ws/2005-05-identity/claims/dateofbirth" minAge="21" />
                </policy>
            </claimsAuthorizationManager>
        </service>
    </microsoft.identityModel>

    <assemblies>
        <add assembly="Microsoft.IdentityModel,Version=3.5.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35">
    </assemblies>
</configuration>

Handling Identities with Claims

Bearbeiten
  • IClaimsPrincipal : IPrincipal
  • IClaimsIdentity : IIdentity
  • Claim
IClaimsIdentity = Thread.CurrentPrincipal.Identity as IClaimsIdentity;
var email = identity.Claims.Where(c => c.ClaimType == System.IdentityModel.Claims.ClaimTypes.Email).FirstOrDefault();

Custom ClaimsAuthorizationManager

Bearbeiten
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Configuration;
using System.Xml;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.Configuration;

namespace MyNamespace
{
    public sealed class AgeTreshholdClaimsAuthorizationManager : ClaimsAuthorizationManager
    {
        private static Dictionary<string, int> Policies { get; } = new Dictionary<string, int>();
        
        public AgeTreshholdClaimsAuthorizationManager(object config)
        {
            var nodes = config as XmlNodeList;
            foreach(var node in nodes)
            {
                var reader = XmlTextReader(new StringReader(node.OuterXml));
                reader.MoveToContent();
                var resource = reader.GetAttribute("resource");
                reader.Read();
                var claimType = reader.GetAttribute("claimType");
                
                if(claimType.Equals(ClaimTypes.DateOfBirth, StringComparison.Ordinal))
                {
                    throw new NotSupportedException();
                }
                
                var minAge = reader.GetAttribute("minAge");
                Policies[resource] = int.Parse(minAge);
            }
        }
        
        public override bool CheckAccess(AuthorizationContext context)
        {
            Uri webPage = new Uri(context.Resource.First().Value);
            
            if(!Policies.ContainsKey(webPage.PathAndQuery))
            {
                return true; // resource is not age protecdet
            }
            
            IClaimsIdentity claimsIdentity = context.HttpContext.User.Identity as IClaimsIdentity;
            
            var minAge = Policies[webPage.PathAndQuery];
            var birthday = DateTime.Parse(claimsIdentity.Claims.Where(c => c.ClaimType == ClaimTypes.DateOfBirth).Select(c => c.Value));
            
            return dateTime.Today >= birthday.AddYears(minAge));
        }
    }
}

Attributes

Bearbeiten
[Authorize]
public class HomeController : Controller
{
    [ClassPrincipalPermission(SecurityAction.Demand, Resource = "Home", Operation = "Index")]
    public ActionResutl Index()
    {
        return View();
    }

    public void Logout()
    {
        Uri signoutUrl = new Uri(FederatedAuthentication.WSFederationAuthenticationModule.Issuer);
        Uri replyUrl = new Uri(Url.Action("Index", "Home", null, Request.Url.Scheme)); // redirect to home after logout 
        WSFederationAuthenticationModule.FederatedSignOut(signoutUrl, replyUrl);
    }
}
Important
  • Ensure cross site forgery security for cookie
  • Provide proper Authentication and Authorization provider implementations
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions {
            AuthenticationType = CookieAuthenticationDefaults.AuthenticationType, 
            // set provider for custom authentication code 
            Provider = new CookieAuthenticationProvider {
                OnResponseSignIn = context => {
                    var authenticationService = new MyAuthenticationService(); // TODO: dependency inject this 
                    var newIdentity = authenticationService .Authenticate(new ClaimsPrincipal(context.Identity));
                    context.Identity = newIdentity;
                }
            }
        });

        app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions{
            SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType, 
            MetadataAddress = "https://my-sts-server-address.com/core/wsfed/metadata", 
            Wtrealm = "urn:myapprealm"
        });
    }
}
// example code only 
// use thinktecture attributes or other proper implementation
public class MyAuthorizeAttribute : AuthorizeAttribute 
{
    public string Resource { get; set; }
    public string Operation { get; set; }
    
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var service = new AuthorizationService(); // TODO: inject this 
        service.CheckAccess(Resource, Operation);
    }
}
[Authorize]
public class HomeController : Controller
{
    [CustomAuthorize(Resource = "Home", Operation = "Index")]
    public ActionResult Index()
    {
        return View();
    }

    public void Logout()
    {
        Request.GetOwinContext().Authentication.SignOut(WsFederationAuthenticationDefaults.AuthenticationType);
    }
}
Cleanup Cookies
app.Use(async(context, next) => {
    var query = context.Request.Query;
    var wa = query.Get("wa");
    if(wa == "wssignoutcleanup1.0"){
        context.Authentication.SignOut("Cookies");
    }
    await next();
});

JWT Token

Bearbeiten
Produce
// setting up symmetric encryption
using (var hmac = new HMACSHA256(Convert.FromBase64String("c2hhcmVkIHNlY3JldA==")))
{
    const string signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256";
    const string digestAlgorithm = "http://www.w3.org/2001/04/xmlenc#sha256";

    var signingCredentials = new SigningCredentials(new InMemorySymmetricSecurityKey(hmac.Key), signatureAlgorithm, digestAlgorithm);

    // define the claims
    var claims = new[]
    {
        new Claim(ClaimTypes.Email, "foo@bar.com"),
    };

    var token = new JwtSecurityToken(
        "http://authorizationserver.com/",
        "http://resourceserver.com/",
        claims,
        DateTime.UtcNow, // valid from
        DateTime.UtcNow.AddMinutes(15), // valid till
        signingCredentials);

    return new JwtSecurityTokenHandler().WriteToken(token); // serialization as JSON string
}
Verify
// setting up symmetric encryption
using(var hmac = new HMACSHA256(Convert.FromBase64String("c2hhcmVkIHNlY3JldA==")))
{
    const string signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256";
    const string digestAlgorithm = "http://www.w3.org/2001/04/xmlenc#sha256";

    var signingCredentials = new SigningCredentials(new InMemorySymmetricSecurityKey(hmac.Key), signatureAlgorithm, digestAlgorithm);

    // what to validate
    var validationParams = new TokenValidationParameters
    {
        ValidIssuer = "http://authorizationserver.com/",
        ValidAudience = "http://resourceserver.com/",
        ValidateLifetime = true,
        ValidateAudience = true,
        ValidateIssuer = true,
        RequireExpirationTime = true,
        RequireSignedTokens = true,
        IssuerSigningToken = new BinarySecretSecurityToken(hmac.Key),
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = signingCredentials.SigningKey,
        ValidateActor = false
    };

    SecurityToken validatedToken;
    ClaimsPrincipal principal = new JwtSecurityTokenHandler().ValidateToken(tokenString, validationParams, out validatedToken);

    // ...
}

Internetquellen

Bearbeiten