OAuth2 and Business Central

Authentication written in block text

Background

I started developing a B2B App for a client which initially needed to integrate with Sage. However, midway through development the client decided to move away from Sage and use Business Central (Dynamics 365), that means OAuth2.

The integration requirements increased significantly; we went from a system needing to occasionally pass some information to Sage to a system which was heavily integrated with BC (Business Central) and required an almost constant connection.

In this post we will ignore the feature creep and other issue and just discuss the integration.

App registration in Business Central

Like most developers, I understood OAuth2, or at least thought I understood it. I’d worked on Apps using OAuth2 and had had to make changes and tweaks, but I’d never had to roll my own client and more importantly connect it to another service. That ended up being the biggest problem.

If you need to integrate with Facebook, GitHub or any of the other large providers, there will be a well-supported package, the league has you sorted for most things. In my case I was connecting to a customer’s instance of BC.

I spent a long time reading all the Business Central documentation and finally came across this, I needed to create an App registration in Azure. Once we had created a valid registration and confirmed it was working (thanks Postman) I needed to get my App to talk to BC, for that I needed an OAuth2 client.

OAuth2 client

I didn’t find a client or integration anywhere that suited my purposes, so I decided to roll my own using the OAuth2 generic provider by the league – yes, they don’t recommend this.

I created a wrapper class with four public methods, hasToken(), token(), refresh() and authorize().

hasToken()

This method does as the name suggests, it lets us know if we have a valid token.

token()

This method returns the token, however, before trying to return the token it calls an internal method called fetchOrRefresh(). This method checks to see if we have a token and whether it is valid. If it has expired, or due to expire, we wait and create a new token using the refresh token. There are several long running tasks, so we decided it was best to ensure we know the life of the token before starting a long running task.

refresh() and authorize()

These are wrapper functions which contain all the code necessary to authorize the client and save a token or generate a new token using the refresh token,

Usage within the App

So far, the integration with BC has been almost flawless. We keep an eye on request limits and catch all API errors. To date, we have never had a problem with the OAuth2 system, every hour a new token is created using the refresh token and it all works wonderfully.

So, how does this work in the code?

If a class needs to connect to Business Central, the first thing we do is check to see if we have an active token.

$oauth = new OAuth();
if ($oauth->hasToken() === false) {
    // Handle error state,
    // Send notification
    // Exit task and retry again
}

Now, assuming we have a token which should always be the case after the initial authorisation, all we need to do it fetch the token and pass it along with our request.

$token = $oauth->token();

if ($token === null) {
    // Handle error state
    // Unable to fetch the token or 
    // Unable to generate a new token using the refresh token
    // Exit task and send notification
}

A failure at this point is unlikely and typically will only happen if a service is down. As previously mentioned, the token() method fetches the token and if necessary, ensures a new token is created, other than an outage it is unlikely this code will fail.

The code above is just a simplified example, in the real app we have much more error checking and state management and throttling. The client can retry any retriable request and see a full log of all interactions between our App and BC.

Conclusion

Was any of this fun?

No. When under serious time constraints working all of this out was a little stressful, especially when I couldn’t find any examples online

Was it worth it?

Yes. My understanding of OAuth2 and integrating with Azure increased no end, I went from thinking I understood OAuth2 to knowing I understood OAuth2.

This is the App that keeps on giving. You might be interested in my next blog post in the series, UI and UX is tough for B2B Apps.

Action class usage in my Laravel Apps

Action class usage in my Laravel Apps

What is an action class?

I define an action class as exactly that – a class that performs a single action. Examples include addUser, deleteUser, assignRole etc.

In my applications I typically make these classes invokable. However, there will be the odd case where I choose to add a method to call, it depends on usage and how the code reads.

Action class structure

The structure of my classes very much depends on the structure of the App itself. Is the App a standard app with its own database or does it pass everything off to a REST API and effectively acts as the front-end?

Action class usage in a typical App

In a typical App my action classes return a Boolean. I keep things simple, the action either succeeds or doesn’t. Error handling is either handled by the controller or internally within the action, it depends on how exceptions are being handled and whether actions need to be retried, this is typically a business decision.

Below is a simple example which sets the salesperson for an order.

class AssignSalesPerson
{
    public function __invoke(
        int $customer_id,
        string $order_reference,
        array $input
    ): bool {
        Validator::make($input, [
            'sales_person_code' => [
                'required',
                Rule::exists(User::class, 'sales_person_code')
            ]
        ])->validate();

        $order = (new Order())->getOrder($customer_id, $reference);
        if ($order === null) {
            return false;
        }

        try {
            DB::transaction(function () use ($order, $input) {
                // Update the order
                $order->sales_person_code = $input['sales_person_code'];
                $order->save();
                
                // Log the order change
                // ...   
            });

            return true;
            
        } catch (\Exception $e) {
            // Log the exception the error
            //...
            return false;
        }
    }
}

Action class usage with a REST API

If the App is a frontend for a REST API, the structure of my action classes changes. Instead of returning a Boolean, they return an integer, specifically the http status code returned from the API request. Additionally, these action classes will extend a base class which will contain several helper methods and properties.

Below is an example of the parent for API action classes.

abstract class Action
{
    // The message to return to the user, this is usually the message
    // from the the API when the request didn't succeed
    protected string $message;

    // Store any validation errors from the API
    // Our App can then pass these on as necessary
    protected array $validation_errors = [];

    // Store any parameters that need to be passed to the view
    // For example the id for a new resource 
    protected array $parameters = [];

    public function getMessage(): string
    {
        return $this->message;
    }

    public function getParameters(): array
    {
        return $this->parameters;
    }

    public function getValidationErrors(): array
    {
        return $this->validation_errors;
    }
}

And this is an example of a class which creates a budget item.

class CreateBudgetItem extends Action
{
    public function __invoke(
        Service $api,
        string $resource_type_id,
        string $resource_id,
        array $input
    ): int {
        // Handle any validation which needs to be 
        // done locally before passing the data to the API

        if (count($this->validation_errors) > 0) {
            return 422;
        }

        // Create the payload to send to the API        
        $payload = [
            ...
        ];

        $create_response = $api->budgetItemCreate(
            $resource_type_id,
            $resource_id,
            $payload
        );

        if ($create_response['status'] === 201) {
            return $create_response['status'];
        }

        if ($create_response['status'] === 422) {
            $this->message = $create_response['content'];
            $this->validation_errors = $create_response['fields'];

            return $create_response['status'];
        }

        // Unexpected failure, store the message
        $this->message = $create_response['content'];
        return $create_response['status'];
    }
}

The names of my classes depend on the complexity of the App. The class names shown above are simply examples, typically I try to organise my code by namespace and keep the names simple. If you see \App\Actions\BudgetItem\Create you know exactly what the action class is doing, there is no way to misinterpret the desired action.

Benefits

There are several benefits to using action classes – I’m going to talk about two for now, in the future I might do a full deep dive into all the positives and negatives.

Encapsulation

The class encapsulates all the code for the function, if you are strict in how you use actions this is a massive plus, there is a single class that performs the action, and it is easily testable.

Reusability

Action classes are reusable, it is easy to call an action class in a console command or job, I’ve several actions that need to be called on command as well as based on a user action. If I didn’t use action classes I would have had to create something similar or worse, duplicate the relevant code.

Continue to evolve

My action classes have evolved over time and I’m sure I will make more tweaks in the future. Much of my client work is creating B2B Apps and typically they require event logging, transactions, retrying. My real-life action classes are much more complex than the examples detailed above.

As much as I love action classes, they are not always the best solution. I try to ensure my action classes stay small. In cases where that is not possible and you still want to use the same idea, invokable controllers can help.

This post is part of a series about my development process. Please check out my typical project setup and my Git workflow.