In 2023, Vimcar and Avrios merged to become Shiftmove. Before the merger, both companies offered car fleet management software products. Vimcar focused more on telematics solutions (geolocation, logbook automation) while Avrios offered more of a fully featured admin platform for fleet managers. The complementary nature of the products made a clear business case for a merger that would benefit our customers and secure the future of both companies. How would this look in practice? This was still to be figured out. One thing was clear though: we would combine our user bases and make both Vimcar and Avrios products accessible via a single Shiftmove login.
The unified login would achieve a few things:
- Lay the first technical foundations of a future Shiftmove platform by establishing a shared identity service.
- Provide an extensible solution that enables us to bring more products to the Shiftmove family. This is already happening: Optimum Automotive joined Shiftmove in late 2024.
- Improve security for customers and internal users through multi-factor authentication and other security enhancements.
- Launch the Shiftmove brand.
- Help us invite users from Avrios to the complementary features in Vimcar and vice versa by increasing the visibility of the products on offer.
Vimcar and Avrios’ systems were already running on AWS infrastructure. Besides that, there were some key differences:
Avrios in 2023
- Used AWS Cognito as an identity provider
Cognito handled authentication and issued JSON Web Tokens (JWTs) directly to clients. Clients used these tokens to authorise directly against backend services.
- Included third-party SAML IDP integrations for enterprise login
- Included a subset of users managed by an external software solution, not managed by Avrios
- Distinguished between drivers and system users (fleet managers, admins, etc), treating them as separate entities
Vimcar in 2023
- Used its own internal Identity service as a central identity provider and permission management service
- Already had a single point of entry for authentication (Identity frontend), offering SSO (via OpenID Connect) to all Vimcar applications
On authentication, the Identity service issued access tokens to clients, which were signed identifiers only (opaque tokens/bearer tokens). On request to a backend service, that service requested the associated permissions from the Identity service.
- Did not offer any third-party IDP integrations
- Distinguished between drivers, fleet managers, and internal users only by their permissions (single user entity for all user types)
We evaluated a few providers and solution types, and settled on FusionAuth as the third-party identity provider with the best tradeoff for us in terms of cost, features, and the implementation effort required.
To self-host or not?
This was a relatively easy decision. Since we had decided to go with an external provider, we opted to let them be our security experts by hosting on their servers and making use of their provided (customisable) server-side rendered sites for the frontend.
We made FusionAuth the source of truth for a few key global user fields (user name, password etc) but left responsibility for some others to the Avrios and Vimcar products. FusionAuth would manage only the highest level permission: whether a user was a customer of that product or not, represented by a FusionAuth registration. Anything involving product knowledge would remain in the products, including all fine-grained permissions, user preferences and so on. This would ensure the solution could be extended again in future as the Shiftmove product family continues to grow.
We set up one FusionAuth instance for each of our environments: production, staging, and test. Each instance hosts a single tenant containing all Shiftmove users. Users can be registered to one or more of several applications. For Vimcar only one application was needed to support our existing unified Identity service. On the Avrios side, we have a small number of applications to support the different core user types.
FusionAuth provides a few ways to manage your instances. We chose to use their official Terraform provider, while allowing manual changes via the FusionAuth admin console for testing and experimentation before committing our changes to code.
We wrapped the terraform execution in our CI/CD pipelines on AWS. The build job executes terraform plan and uploads the resulting terraform build artifacts to an S3 bucket. A separate deployment job runs terraform apply.
For local development, we use Docker with docker-compose to run a local user database and FusionAuth instance along with other relevant services. This local instance is configured and provisioned via Terraform in much the same way as the remote instances.
In the first iteration of our Avrios/FusionAuth integration, we continued to use Cognito to manage access to the Avrios system and issue JWTs, while delegating authentication to FusionAuth via OpenID Connect. Keeping Cognito in the solution to begin with was tech debt we took on to enable us to deliver the first unified login more quickly. This would need to be paid off later as we moved to Solution 2 (below).
As a temporary solution to support the initial rollout, this worked ok. There were some downsides however, especially a buggy developer experience and complex local setup. It also offered poor value for money. We were still paying for full use of Cognito but using only a small subset of features.
In our next iteration, the Cognito layer was replaced.
Our current solution on the Avrios side: Cognito is gone and fully replaced with FusionAuth functionality customised by FusionAuth Lambdas. These are executed when issuing a token. A lambda calls the Avrios permissions service to fetch user permissions and adds custom claims to the JWT. The client then uses this enriched JWT to authorise against any Avrios backend service.
Our current solution on the Vimcar side: Vimcar’s Identity service delegates authentication to FusionAuth. It then issues its own bearer tokens which continue to be stored in the Identity database as before. For the frontend apps nothing has changed. They still use these opaque tokens looked up via a session cookie to make requests to the backend. Backend services continue to request permissions from Identity before authorising any requests.
Login is a critical part of the whole Shiftmove ecosystem, but each product had different requirements for the rollout. For Vimcar, we aimed for zero downtime, while in the Avrios system a small downtime was acceptable.
In the Avrios system, we needed to support customers who already had enterprise SSO solutions in place, whereas Vimcar did not yet support this.
First we set up FusionAuth, configured via Terraform and deployed on all environments. Since nobody was using it yet, we could freely iterate on the user flows and new Shiftmove branding during this pre-release phase.
FusionAuth publishes user created and updated events to an SNS topic, exposed through an API Gateway endpoint. Before migrating the first users, we prepared the Vimcar and Avrios systems to process these user events to keep all user profiles in sync after migration.
The migration steps for each product looked a little different:
Vimcar User Migration
- Activate slow migration on FusionAuth for Vimcar.
- Run bulk export & import of Vimcar users into FusionAuth.
- Redeploy Identity to switch Vimcar services / applications to FusionAuth via a feature flag.
- Keep slow migration and monitor for two weeks to catch users created/updated between the user export (step 2) and redeployment of the Identity service (step 3).
Avrios User Migration
- Shut down necessary backend services, and block access to Avrios frontend.
- Run bulk export & import of Avrios users into FusionAuth. Some customers use both products, so we also do conflict resolution during this second import. If a user already exists in FusionAuth as a result of the earlier Vimcar migration, add a registration to the existing user for the relevant Avrios application, instead of creating a new user.
- Redeploy necessary Avrios services to enable FusionAuth.
- Migrate existing SSO integrations.
Rollback
Changes to the necessary Vimcar and Avrios services were hidden behind feature flags to enable rollback in case of any issues during the migrations. Had we needed to roll back, we would have needed to support users who had been created, or who had changed their passwords, since the initial migration, by re-importing their password hashes to our database. In the event, both migrations went smoothly and this wasn’t required.
One of our biggest motivations for moving to FusionAuth was the opportunity to enhance security for both internal users and customers. Our initial rollout included no major security enhancements (you could argue it added more layers and therefore more opportunities for vulnerabilities). But once that was complete we were able to introduce:
- Multi-factor authentication (MFA) via email or authenticator app (we do not support SMS due to the weaker security and need to store users’ phone numbers)
- Option to enforce MFA for certain high-risk user groups such as internal admin users, and to offer this enhancement to customers
- Extended login options with social logins from Google and Apple, and additional enterprise identity providers such as Okta (some of these support login methods that are more secure than username and password, e.g. passkeys)
- Breached password protection (notify users when their password has been breached and prompt them to reset it)
- Passwordless login for a smoother checkout process (users request an email with a “magic link” to log in)
It’s been more than a year since we shipped our first FusionAuth integration. For the most part, the migrations went smoothly and the new system has been reliable. Here are a few of the challenges we faced along the way.
Developer Experience
The biggest challenge for me was working with FusionAuth’s FreeMarker-based server-side rendered frontend. The default FusionAuth solution ships with a dump of frontend code with little organisation or documentation. Without easy access to the server-side Java models it felt pretty opaque to work with at times. We were also moving from working with modern frontend libraries/frameworks (React for Vimcar, Angular for Avrios), with very fast feedback loops, to a very unfamiliar markup with poor IDE tooling and a slow feedback cycle, requiring a terraform deployment on each change. The flip side of this is that we got excellent performance and security out of the box, and we could trust that our local deployments reflected what we would get on the remote instances. Given these big advantages, I still feel we made the right decision by customising the FusionAuth solution (as opposed to building our own frontend using the FusionAuth API). But given the time again I would invest more effort up front to improve the tooling and speed up local deployments.
Early struggles with Terraform
We wrapped our Terraform deployments in CI/CD pipelines. Unlike the AWS-only solutions we were used to, the decoupling between Terraform and the remote FusionAuth server and our own AWS infrastructure meant it was possible to run into stale Terraform state from incomplete/failed deployments. This caused teething troubles early on.
Restrictions on Custom Domains
The initial plan we chose from FusionAuth allowed us only one custom domain (auth.shiftmove.com) and did not support the domains that we wanted to use for our staging and test environments. At first, we attempted to work around this, but this meant that our environments did not properly replicate production, and eventually we hit a blocker that forced us to renegotiate and enable more custom domains. If we had known from the start that the additional custom domains would be a hard requirement, we could have saved ourselves some effort.
Lack of Real-World Examples + Limited Documentation
FusionAuth’s hosted solution was bewildering at first, including many moving parts and unfamiliar technologies for a developer used to working in an AWS + React ecosystem. Early on, I looked for real-world examples of customised FusionAuth templates and configurations, and for relevant blog posts, and found very little. That became the motivation for this blog post. If you are looking to adopt FusionAuth as your IDP in your organisation and you would like more information on any aspects mentioned here, leave a comment below!
.png)
![What is a graph database? [video]](https://news.najib.digital/site/assets/img/broken.gif)
