OAuth JS Login

This post explains just the login part of the “Authorization Code Grant Flow”, one of the ways to get an OAuth access token from an endpoint. It has taken me WAY too much time to get this, and I had to get some help from my good friend AJ to explain it to me. This is one flow that you will not be able to do in a local container, since the callback URL must be accessible in SaaS.

Authorization Code Grant Flow

The Authorization Code Grant Flow is the most rudimentary OAuth flow. This post won’t explain the flow itself; I’ll write about that in other posts (if I ever get the courage to actually publish that) but I needed to get this part down while it’s still fresh in my mind.

The tricky part about this flow is that it requires a human being to enter credentials of the endpoint. With those credentials, you will get what is called the ‘authorization code’. This authorization code is then used to get the access/refresh tokens themselves.

OAuth Control Add-In

In standard Business Central, there is just one way to catch the authorization code, and that is through a standard JavaScript control called ‘OAuthControlAddIn’. This standard controladdin provides two essential things. First it opens a login screen, where a human being can enter the credentials. Second, it catches the authorization code response. The control add-in feeds the code back through the trigger called ‘AuthorizationCodeRetrieved’.

Why Not Catch the HttpResponse Directly?

THAT, my friend, is a GREAT question, and please forgive me if I get the technical details of the answer wrong because I barely understand this part. When the authorization code endpoint returns the response, it contains the redirect URL that you send into the endpoint. The type of the response causes BC to then automatically forward the response to the redirect URL, WITHOUT a way for you to intercept the response itself. In other words… the response that you see is not the initial response itself, but the response to the response from the redirect call, and THAT response does NOT have the authorization code in it.

Using Postman or the REST client, you can turn off the auto redirect, but AL does not have a way to do that. Microsoft has decided to not allow us to intercept the initial response, and the only way to get the actual authorization code is to use the control add-in. It is the add-in that catches the code and provides that through the ‘AuthorizationCodeRetrieved’ trigger.

Only in SaaS

This automatic forwarding of the authorization code response is the reason why you can’t use this flow on a local container. The redirect URL must be available publicly, which requires your current connection to be in SaaS.

The standard redirect URL is ‘https://businesscentral.dynamics.com/oauthlanding.htm’. You can follow this URL and look at the page source code, and you will see the JavaScript logic there that catches the response. Since a local container does not provide this public access, the redirect will always fail, and you will not be able to catch the authorization code locally.

Here’s How It Works

On the page that you want to provide the action to connect to the API, you add the following control to the content area within the layout section of the page. Note the ‘AuthorizationCodeRetrieved’ trigger that calls another funtion called ‘GetNewTokens’, which is where we finally get the access/refresh tokens.

usercontrol(OAuthControl; OAuthControlAddIn)
{
    ApplicationArea = All;

    trigger ControlAddInReady()
    begin
        ControlAddInReady := true;
    end;

    trigger AuthorizationCodeRetrieved(AuthCode: Text)
    begin
        GetNewTokens(AuthCode);
    end;

    trigger AuthorizationErrorOccurred(AuthError: Text; AuthErrorDescription: Text)
    begin
        Error('%1 %2', AuthError, AuthErrorDescription);
    end;
}

To initialize the login procedure, you then call the ‘StartAuthorization’ method of the add-in. You could have a ‘Login’ action with a call to a ‘DoTheLogin function, like this:

local procedure DoTheLogin()
var
    ConnectionEstablishedMsg: Label 'The connection has already been established';
begin
    if (AccessToken = '') and (RefreshToken = '') then
        CurrPage.OAuthControl.StartAuthorization(GetAuthUrl())
    else
        Message(ConnectionEstablishedMsg);
end;

The control addin then fires the AuthorizationCodeRetrieved trigger, with the authorization code as a parameter, which you can then use to get the access/refresh tokens. Now this code IS part of the initial response, but the HttpClient in AL does not allow us to intercept that response without automatically redirecting the response.

It took me SO LONG to understand how this works, and I could never have done it without AJ’s help. Reading this back now I still don’t know if I am getting the details correct, so I don’t blame you for not getting it. Leave me a message or a comment if this helps or not.