Search code examples
asp.net-coreoauth-2.0adfs3.0

.net Core Api authentication with ADFS 2012


I need to configure my .Net Core Web Api (.Net Framework) to use ADFS 3.0 (2012) to validate the Bearer tokens sent by our mobile clients.

I am able to generate the access_token from the ADFS server, and I pass it in the Authorization header.

My problem is in the API: how do I configure it to validate and autorize the user?

I searched in many places and I could not find a definitive method of doing it.

What I tried so far:

Used IdentityServer4 (Failed because it uses JWT and ADFS doesn't offer OpenID Tried UseOpenIdConnectAuthentication (found example at IdentityServer4) a custom Middleware I can't use another method, I need to support oAuth2.

So, how do I do it?

Here is my latest try:

    var connectOptions = new OpenIdConnectOptions
    {
        AuthenticationScheme = "adfs",
        SignInScheme = "idsrv.external", //IdentityServerConstants.ExternalCookieAuthenticationScheme,
        SignOutScheme = "idsrv", //IdentityServerConstants.SignoutScheme,
        AutomaticChallenge = false,
        DisplayName = "ADFS",
        Authority = $"https://{options.AdfsHostName}/adfs/oauth2",
        ClientId = options.ClientID,
        ResponseType = "id_token",
        Scope = { "openid profile" },
        CallbackPath = new PathString("/signin-adfs"),
        SignedOutCallbackPath = new PathString("/signout-callback-adfs"),
        RemoteSignOutPath = new PathString("/signout-adfs"),
        ClaimsIssuer = $"https://{options.AdfsHostName}/adfs/services/trust",
        //TokenValidationParameters = new TokenValidationParameters
        //{
        //    ValidateIssuer = true,
        //    ValidIssuer = $"https://{options.AdfsHostName}/adfs/services/trust"
        //},

    };

    app.UseOpenIdConnectAuthentication(connectOptions);

I get a very quick 401 on every calls, with a valid token. In fact, while I see the connection in the console window, I don't see any other log in the Roslyn console window regarding the security validation.

I'm currently using ASP.Net Core 1.1.X, and if I can I'd avoid moving to .Net Core 2.0, as we are late in the project and it contains many breaking changes...

Feel free to ask for more info, and I'll appreciate all the good advices!


Solution

  • As it turns out, we can use the JwtBearerAuthentication with ADFS 3.0.

    My initial problem with it was that it went to fetch the metadata at /.well-known/openid-configuration, but ADFS 3.0 does not support OpenID and this returns a 404.

    I read in another post (I'll update it when I find it) that if with the right configuration, it won't need to fetch the config. But what configuration?

    Well I found deep in the (MS) code that if one pass an OpenIdConnectConfiguration object to the Configuration property of the JwtBearerOptions, it wont fetch the metadata.

    So here is my code now:

    var rawCertData = Convert.FromBase64String(options.X509SigninCertificate);
    
    X509Certificate2 cert = new X509Certificate2(rawCertData);
    
    SecurityKey signingKey = new X509SecurityKey(cert);
    

    The X509 cert data comes from the supported adfs metadata at this url

    https://Your.ADFS.Site/FederationMetadata/2007-06/FederationMetadata.xml 
    

    It contains this:

    <KeyDescriptor use="signing">
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <X509Data>
                <X509Certificate>SOMEUUENCDODEDSTRING=</X509Certificate>
            </X509Data>
        </KeyInfo>
    </KeyDescriptor>
    

    I simply copied the UUEncoded string in my settings' X509SigninCertificate property.

    var tokenValidationParameters = new TokenValidationParameters
            {
                // The signing key must match!
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,
    
                // Validate the JWT Issuer (iss) claim
                ValidateIssuer = true,
                ValidIssuer = $"https://{options.AdfsHostName}/adfs/services/trust",
    
                // Validate the JWT Audience (aud) claim
                ValidateAudience = true,
                ValidAudience = options.ClientUri, //"https://YOUR-AUDIENCE/",
    
                // Validate the token expiry
                ValidateLifetime = true,
    
    
                // If you want to allow a certain amount of clock drift, set that here:
                ClockSkew = TimeSpan.Zero
            };
    
            var connectOptions = new OpenIdConnectConfiguration
            {
                Issuer = $"https://{options.AdfsHostName}/adfs/services/trust",
            };
    
            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                TokenValidationParameters = tokenValidationParameters,
                Configuration = connectOptions                
            });
    

    The important line here is

    Configuration = connectOptions
    

    By doing this, you tell the validator to not fetch the metadata. Simple as that.

    I was able to validate my token (AUD, ISS and SIGN) and I can use ADFS in my project.