OAuth Authentication in AL

In this video I demonstrate a AL based solution to do Oauth authentication against Linkedin’s API interface.

https://youtu.be/sHA8JRyhg4Yhttps://youtu.be/sHA8JRyhg4Y

In this video, Erik demonstrates a complete OAuth authentication implementation written in pure AL code for Business Central. Using LinkedIn as the OAuth provider, he walks through the entire authorization code flow — from launching a pop-up login window, to handling the redirect callback, to exchanging the authorization code for an access token. The implementation uses JavaScript user controls, isolated storage, and a timer-based polling mechanism to coordinate the multi-step process.

Overview of the OAuth Flow

Erik begins by noting that this implementation was originally created back in version 13 of Business Central (as evidenced by the platform version in the app manifest). Despite its age, the fundamental OAuth process remains the same. The demo runs on a cloud sandbox because OAuth requires a public endpoint for the redirect callback.

The high-level flow works as follows:

  1. A Business Central page opens and launches a pop-up window pointing to LinkedIn’s authorization endpoint.
  2. The user authenticates with LinkedIn in the pop-up.
  3. LinkedIn redirects the pop-up back to a Business Central page (the “OAuth landing page”), passing an authorization code.
  4. The landing page exchanges that authorization code for an access token by calling LinkedIn’s token endpoint.
  5. The access token is stored in isolated storage, and a timer on the original page detects it and proceeds.

The Login Page

The login page is the starting point. It displays a label telling the user to allow pop-ups from the page, and it contains a JavaScript-based user control. This user control is intentionally small (just one pixel) but provides critical functionality through several events and procedures.

The key function here is LaunchURLInNewWindow, which calls window.open in JavaScript to create a pop-up window. The URL being opened is LinkedIn’s authorization endpoint, constructed with several parameters:

  • response_type=code — requesting an authorization code
  • client_id — the application’s client ID
  • redirect_uri — the Business Central page that LinkedIn should redirect to after authentication (page 50101 on the sandbox)
  • state — a random value used to prevent cross-site request forgery
  • scope — LinkedIn-specific scopes defining what access is being requested

Erik emphasizes that the scope parameter is a great example of how every OAuth implementation differs. A generic OAuth library might only work with the specific provider it was designed for, because providers have different requirements for scopes, parameters, and endpoints.

The Timer Mechanism

After launching the pop-up, the login page stores some information in isolated storage about what to do next, and then starts a JavaScript timer. This is a clever workaround for the asynchronous nature of the OAuth flow:

The timer ticks every second, and on each tick it raises an event back in AL code. The AL event handler checks isolated storage for an OAuth token entry. If the token exists (meaning the redirect page has completed its work), the login page closes and proceeds to the next step. If not, it simply waits for the next tick.

The Redirect (Callback) Page

Page 50101 serves as the OAuth landing page — the redirect URI. When LinkedIn redirects back after successful authentication, this page receives the authorization code and state as URL parameters.

The page uses the same JavaScript user control but triggers a different event called RedirectReceipt. The JavaScript grabs the URL search parameters, looks for an entry called “code,” and if found, fires the event back to AL with both the code and the state. This uses the same URL-parsing technique Erik covered in his “Hack the URL” video.

Once the AL code receives the callback:

  1. State validation: The state value is compared against the one originally sent. If it doesn’t match, the process is aborted — this prevents someone from injecting a fraudulent callback.
  2. Token exchange: The authorization code is sent to LinkedIn’s token endpoint along with the client secret and client ID. This is the second HTTP call in the OAuth flow.
  3. Token extraction: The response JSON is parsed to extract the access token.
  4. Storage: The access token is stored in isolated storage, which triggers the timer on the login page to detect completion.

The Client Credentials

The client ID and client secret are managed through a simple codeunit. In a production implementation, these should be stored securely:

codeunit 50102 "AL Secret"
{
    procedure getclientId(): Text
    begin
        exit('secret santa');
    end;

    procedure getclientsecret(): Text
    begin
        exit('enter your own');
    end;
}

Obviously, the placeholder values here need to be replaced with real credentials from your OAuth provider’s application registration. In a production environment, you would want to use isolated storage or Azure Key Vault rather than hard-coding secrets.

The App Manifest

The app.json file shows this extension’s configuration. Note the platform version 13.0.0.0, which Erik jokes allows you to “carbon date” your AL code:

{
  "id": "c24c8b76-1ae7-4b5f-8ef7-d71571e90f8f",
  "name": "BCOauth2",
  "publisher": "Erik Hougaard",
  "version": "1.0.0.0",
  "brief": "",
  "description": "",
  "privacyStatement": "",
  "EULA": "",
  "help": "",
  "url": "",
  "logo": "",
  "dependencies": [],
  "screenshots": [],
  "platform": "13.0.0.0",
  "application": "13.0.0.0",
  "showMyCode": true,
  "runtime": "2.0"
}

Proving It Works

After the authentication flow completes, Erik demonstrates that the token is valid by querying LinkedIn’s API for his own profile. The response returns his last name, profile picture, and other JSON data — proof that the access token is working correctly.

On a second deployment, the process is even faster because the browser already has the LinkedIn session cached, so the pop-up immediately redirects without requiring the user to type credentials again.

Refresh Tokens

Erik concludes with an important production consideration: rather than just storing the access token, you should also obtain and store the refresh token. Here’s why:

  • Access tokens are short-lived — typically minutes or hours.
  • Refresh tokens are long-lived — Microsoft refresh tokens, for example, live for 90 days if unused, and indefinitely if regularly used.
  • With a refresh token stored, you can programmatically request new access tokens without requiring the user to go through the interactive authentication flow again.

This is especially important for scenarios like automated web service calls, where you don’t want users to have to re-authenticate constantly.

Key Takeaways

This implementation demonstrates that OAuth authentication is entirely achievable in pure AL code, without external libraries or middleware. The core pattern — launch a pop-up for authorization, receive a callback with a code, exchange the code for a token — is universal across OAuth providers. However, each provider has its own quirks in terms of scopes, endpoints, and required parameters, so expect to adapt the implementation for each new provider. The JavaScript user control serves as the bridge between the browser-based OAuth flow and the AL runtime, using URL parsing and timer polling to coordinate the asynchronous process.