How to enable OAuth on WCF Soap Web services

With Microsoft Dynamics 365 Business Central 2022 Wave 1 (Version 20), basic authentication is no longer available for web services. Many applications have been written using Microsoft Communication Foundation (WCF) to talk with NAV/BC via the Soap protocol. This video shows how to enable an existing C# app using .NET Framework 4.7 with OAuth. Check it out:

https://youtu.be/koFSWLgmQsI


In this video, Erik walks through how to enable OAuth authentication on WCF (Windows Communication Foundation) SOAP web services when connecting to Business Central. With Business Central version 20 removing support for basic authentication, existing C# code that relied on username/password credentials needs to be updated. Erik demonstrates a practical approach to injecting an OAuth bearer token into WCF SOAP calls using endpoint behaviors and message inspectors.

The Problem: Basic Authentication Is Gone

If you’ve worked with Dynamics NAV or Business Central over the past decade, you’ve likely used SOAP web services extensively. WCF made this incredibly convenient — you’d point it at a URL, it would generate all the proxy code and plumbing, and you could just call your business logic from C# or any other platform.

On the Business Central side, the setup is straightforward. Here’s a simple codeunit that exposes a Ping function as a web service:

codeunit 50119 "WCF Test"
{
    procedure Ping(parm: Text): Text
    begin
        exit(Parm + ' ' + Parm);
    end;
}

In C#, this turns into a function called Ping that takes a string and returns a string — very easy to work with and very convenient.

However, with Business Central version 20, basic authentication is no longer supported. If you were using username/password authentication (which was the typical approach unless you were using Windows authentication), your code will stop working.

Why There’s No Simple Fix

You might think you can just change two lines of code and switch to a different credential type. Unfortunately, if you look at the available client credential options in WCF, you’ll find:

  • Username — the old basic auth approach (no longer supported)
  • Windows — Windows authentication
  • Certificate
  • Issued Token — this is for a whole token service, which is something completely different from the OAuth token we need

There is no out-of-the-box OAuth option in WCF. There’s no way to simply swap in a bearer token using the built-in credential types. So we need a different approach.

Step 1: Set Up Azure AD Application Authentication

Before writing any code, you need to set up the OAuth infrastructure:

  1. Create an app registration in the Azure Portal
  2. In Business Central, go to Azure Active Directory Applications and register the app’s client ID
  3. Assign the appropriate permissions to the application

Erik’s app registration shows up in Business Central under Azure Active Directory Applications with the appropriate permission sets assigned.

Step 2: Get an OAuth Token

The first piece of C# code you need is a function to obtain an access token. This function makes an HTTP POST to Microsoft’s login endpoint with your client ID, client secret, and the client_credentials grant type:

private static async Task<string> GetToken()
{
    var client = new HttpClient();
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("client_id", "your-client-id"),
        new KeyValuePair<string, string>("client_secret", "your-client-secret"),
        new KeyValuePair<string, string>("grant_type", "client_credentials"),
        new KeyValuePair<string, string>("scope", "https://api.businesscentral.dynamics.com/.default")
    });

    var response = await client.PostAsync(
        "https://login.microsoftonline.com/your-tenant-id/oauth2/v2.0/token", content);
    var responseString = await response.Content.ReadAsStringAsync();

    // Parse the JSON response and extract the access_token
    // Return the token string
}

Some people in the community have pointed out that there are libraries (like MSAL) that handle token acquisition. That’s true, and you’re welcome to use them. But Erik’s approach here is intentionally simple — a straightforward HTTP POST call that makes it clear exactly what’s happening, in just a few lines of code rather than pulling in megabytes of DLLs.

Step 3: Inject the Token into WCF Calls

This is where it gets interesting. Since WCF doesn’t natively support OAuth bearer tokens, we need to use WCF’s extensibility model — specifically endpoint behaviors and message inspectors — to inject the Authorization header into every outgoing request.

The Client Message Inspector

First, we need a class that implements IClientMessageInspector. This interface provides a BeforeSendRequest method that fires before every WCF call, giving us the opportunity to modify the HTTP headers:

public class AddAuthTokenClientMessageInspector : IClientMessageInspector
{
    private readonly string _token;

    public AddAuthTokenClientMessageInspector(string token)
    {
        _token = token;
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        HttpRequestMessageProperty httpRequestMessage;
        object httpRequestMessageObject;

        if (request.Properties.TryGetValue(
            HttpRequestMessageProperty.Name, out httpRequestMessageObject))
        {
            httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
            httpRequestMessage.Headers["Authorization"] = "Bearer " + _token;
        }
        else
        {
            httpRequestMessage = new HttpRequestMessageProperty();
            httpRequestMessage.Headers.Add("Authorization", "Bearer " + _token);
            request.Properties.Add(
                HttpRequestMessageProperty.Name, httpRequestMessage);
        }

        return null;
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        // Nothing needed here
    }
}

The key logic here is: if the request already has an HttpRequestMessageProperty, we simply add the Authorization header to it. If not, we create a new one, add the header, and attach it to the request properties. Either way, the result is that every outgoing SOAP call includes a Bearer token in the Authorization header.

The Endpoint Behavior

Next, we need a class that implements IEndpointBehavior to wire up the message inspector. This is the bridge between the WCF client and our custom inspector:

public class AddAuthTokenEndpointBehavior : IEndpointBehavior
{
    private readonly string _token;

    public AddAuthTokenEndpointBehavior(string token)
    {
        _token = token;
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, 
        ClientRuntime clientRuntime)
    {
        clientRuntime.ClientMessageInspectors.Add(
            new AddAuthTokenClientMessageInspector(_token));
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, 
        BindingParameterCollection bindingParameters) { }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
        EndpointDispatcher endpointDispatcher) { }

    public void Validate(ServiceEndpoint endpoint) { }
}

The IEndpointBehavior interface requires several methods to be implemented, but the only one we care about is ApplyClientBehavior. In that method, we add our custom message inspector to the client runtime’s message inspector collection.

Step 4: Wire It All Together

Now, wherever you create your WCF client, you just need to add two things: get a token and attach the endpoint behavior:

// Create the WCF client as before
var portClient = new YourServicePortClient();

// Get an OAuth token and add the endpoint behavior
portClient.Endpoint.EndpointBehaviors.Add(
    new AddAuthTokenEndpointBehavior(GetToken().Result));

// Now make your calls as usual
var result = portClient.Ping("Hello YouTube");
Console.WriteLine(result); // Output: "Hello YouTube Hello YouTube"

Note that GetToken() is an async function, but since this example targets .NET Framework 4.7 (which doesn’t easily support an async Main method), we use .Result to get the token synchronously.

You can also remove any old client credentials code — once the endpoint behavior is in place, the bearer token handles authentication entirely.

Things to Watch Out For

Depending on how you’ve set up your WCF client, you may need to adjust your configuration:

  • If you’re passing a custom binding and endpoint address to the client constructor, make sure the binding’s security settings don’t conflict with the OAuth approach
  • Your transport layer security settings may need updating
  • If your binding is configured to expect a specific credential type, it might interfere with the endpoint behavior — make sure it’s not making things more difficult for the message inspector to work

Summary

To migrate your existing WCF SOAP web service code from basic authentication to OAuth for Business Central v20 and later, you need to:

  1. Register an Azure AD application and configure it in Business Central under Azure Active Directory Applications with the appropriate permissions
  2. Create a GetToken function that obtains an OAuth access token using client credentials
  3. Create two helper classes: an IClientMessageInspector implementation that injects the bearer token into HTTP headers, and an IEndpointBehavior implementation that wires up the inspector
  4. Patch your existing client code by adding the endpoint behavior to your WCF client after creation

The beauty of this approach is that it’s minimally invasive. Your existing WCF proxy code stays the same — you just add the endpoint behavior after creating the client, and all your SOAP calls automatically include the OAuth bearer token. It’s a clean way to keep your legacy WCF integrations running against modern Business Central environments.