Understanding OAuth Authorization Code

In this post, I write about my first experience with developing an app that implements an integration between BC and an external API. If you’re like me, most of the web services stuff goes straight over your head. Sure, with some sample code and following along with demos in a class, I can get it to work. BUT… just ONE itsy bitsy teeny weeny tiny thingy goes wrong and you’re absolutely dead in the water. Thankfully I have friends who are willing to help, and I want to pass on this knowledge so others don’t have to spend days trying to figure this out.

The Flow Itself

The “Authorization Code Grant Flow” is just one of several OAuth flows, and one of the first to be implemented. Go here if you want to read more technical details, but if this is the first time you’re reading about this I can guarantee you that you will not understand any of it, I know I didn’t 🙂

As with all OAuth flows, to get access to the actual API you must first get an access token. The thing that makes the authorization code grant flow stand out from other OAuth flows is:

  • There is an additional token that you must get before requesting the access token, this token is called the ‘Authorization Code’
  • In order to get this authorization code, a human being must enter the credentials. This flow is specifically designed NOT to provide any automated way to get the tokens

The MOST confusing thing is that although there is supposed to be an industry standard for REST APIs, it seems that each one has a slightly different way of connecting. One API that I worked on, for instance, had yet another token that is used for the API itself. As if this stuff isn’t hard enough to understand, some of them make it even more difficult. Most of them make an honest effort to provide really good documentation and in some cases even support forums.

The essential flow requires three things:

  1. API Credentials. You get these by signing up with the API provider, and they usually consist of a login ID and a ‘secret’, sometimes an additional password. They are meant to authenticate a human being logging into the API
  2. Authorization Code. This code is used to request the ‘Access Token’ that is used to get access to the API itself
  3. Access/Refresh Token. This is usually a set of two tokens. The Access Token is used for access to the API, and it usually has an expiration date/time. The Refresh Token is used to get a fresh Access Token. As long as you have a valid Refresh Token, you will not need to log back into the API

Each API that implements the authorization code grant flow will provide an endpoint for the authorization code, as well as an endpoint for the access and refresh tokens. You’d be surprised at how many ways this “standard” flow can be implemented though, so you’ll have to find the details yourself. Just hope that the API provider has good documentation.

Log In for the Authorization Code

You get credentials from the API provider, usually in the form of an ID and a ‘Secret’, sometimes with an additional password. For the authorization code grant flow, a human being is required to enter those credentials to authenticate the connection. In BC, the only way to get past this stage is by using a standard control add-in called ‘OAuthControlAddIn’. I’ve written another post that explains the details.

The control add-in provides the mechanics behind the login and processing the redirect response that comes back from the authorization endpoint, and it passes the authorization code itself back to AL through an event in the control add-in. You then take this authorization code and pass that to the token endpoint for the final piece.

Request The Access/Refresh Token

The token endpoint is the final step of the authentication process of the Authorization Code Grant Flow. Sometimes there is a separate endpoint for new tokens and another one for refreshing tokens. Other APIs have a single endpoint with two modes. One accepts the authorization code, the other accepts a refresh token, and they both return a new token pair.

The API checks the validity of the refresh token in every single API call. It is up to you to make sure that your token is valid, and the API usually provides a straightforward way for you to keep track of this yourself, by for instance providing the expiration date/time as part of the token response.

Keep Your Tokens Fresh

The Access Token usually has an expiration date/time. Some tokens are valid for a short period like 10 minutes, others have a longer shelf life. It is up to you to develop logic that checks the validity of your current tokens, and to request new tokens when they expire.

As long as your tokens are valid, you should not have to re-enter credentials, and there is no need for a new Authorization Code. The authorization code is only used when authenticating a fresh connection to the API. Once you’re past the authorization code stage, you should be able to keep the tokens fresh without having a human being log back in.

In AL, the most common way to store the tokens is through the isolated storage functionality. You can set the scope of isolated storage for the whole company, so that multiple users can share the API connection.

My Difficult Experience

One of the reasons why my experience was so difficult was the fact that the API that I was working with had a third token called ‘RestToken’. At the time, I was barely understanding these codes and tokens, and then there was this other token that I could not find in the excellent training that I had followed. Lucky for me, I had some help and was able to understand what was causing the confusion.

My guess (and it really is only a guess) why this is the case is that the API was initially developed with just this ‘RestToken’ and that at some point they built a wrapper around the API to comply with the OAuth “standard”.

The point is that each API has its own unique attributes, and its own way of implementing something that is supposed to be “standard”. Slowly but surely I started seeing the elements of what makes the flow work, and had an excellent teacher who showed me a solid way to handle that in AL code. Normally I would share the code in these posts. At the moment though I only have the training material and the client production code, neither one is mine to share. Maybe in the future when I’m less busy I’ll take some time to put some code together.

Let me know if this helps or not, I’d be happy to get your feedback and try to help if you need some.

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.