Roel Hendrickx
Roel Hendrickx

Reputation: 23

How to obtain a JWT from ADFS (Windows server 2012R2) using a ClientAssertionCertificate in C#

I'm having trouble obtaining a JWT from ADFS (Windows server 2012R2) using a ClientAssertionCertificate.

It all works fine when I let the user authenticate with his username and password directly into the adfs - login window, but I don't want my application to show the ADFS - login window, because the users of my application won't know the credentials. My application actually IS the user (of an api) so I want to authenticate using a certificate. When i try doing that, I get the message "The authorization server does not support the requested grant_type. The authorization server only supports authorization_code or refresh_token as the grant type.

Does anyone know any workaround or other method to obtain a JWT from ADFS using a certificate? Thx a lot!

My application is a .Net 4.6.1 console application.

This is my code now:

   var certPath = Path.Combine(GetCurrentDirectoryFromExecutingAssembly(), "mycertificate.pfx");
        var certfile = File.OpenRead(certPath);
        var certificateBytes = new byte[certfile.Length];
        certfile.Read(certificateBytes, 0, (int)certfile.Length);
        var cert = new X509Certificate2(
            certificateBytes,
            "mypassword",
            X509KeyStorageFlags.Exportable |
            X509KeyStorageFlags.MachineKeySet |
            X509KeyStorageFlags.PersistKeySet);

        var certificate = new ClientAssertionCertificate("myclientid", cert);

        AuthenticationContext context = new AuthenticationContext("https://sts.example.com/adfs",false);
        AuthenticationResult authenticationResult = await context.AcquireTokenAsync("http://example.com/api", certificate);
        var token = authenticationResult.AccessToken;

Upvotes: 0

Views: 1281

Answers (1)

Tommy
Tommy

Reputation: 36

Have you tried using a WSTrustChannelFactory with CertificateWSTrustBinding? In the RequestSecurityToken you can specify in the TokenType that you want a JWT. The JWT you receive will be a base64 string that you need to decode.

public static async Task<string> GetAccessToken(string authority, string resource, string clientId)
    {


        var certPath = Path.Combine(GetCurrentDirectoryFromExecutingAssembly(), "mycertificate.pfx");
        var certfile = File.OpenRead(certPath);
        var certificateBytes = new byte[certfile.Length];
        certfile.Read(certificateBytes, 0, (int)certfile.Length);
        var cert = new X509Certificate2(
            certificateBytes,
            "PASSWORD",
            X509KeyStorageFlags.Exportable |
            X509KeyStorageFlags.MachineKeySet |
            X509KeyStorageFlags.PersistKeySet);



        var factory = new WSTrustChannelFactory(
            new CertificateWSTrustBinding(
                SecurityMode.TransportWithMessageCredential),
            "https://example.com/adfs/services/trust/13/certificatemixed") {TrustVersion = TrustVersion.WSTrust13};


        if (factory.Credentials != null)
            factory.Credentials.ClientCertificate.Certificate = cert;

        // create token request  
        var rst = new RequestSecurityToken
        {
            RequestType = RequestTypes.Issue,
            KeyType = KeyTypes.Bearer,
            AppliesTo = new EndpointReference("http://example.com/api"),
            KeySizeInBits = 0,
            TokenType = "urn:ietf:params:oauth:token-type:jwt"
        };

        // request token and return
        var genericXmlSecurityToken = factory.CreateChannel().Issue(rst) as GenericXmlSecurityToken;
        return genericXmlSecurityToken != null 
            ? Encoding.UTF8.GetString(Convert.FromBase64String(genericXmlSecurityToken.TokenXml.InnerXml)) 
            : string.Empty;
    }

Upvotes: 2

Related Questions