The Journey to Our New Authentication System
By Prafull Mishra
As Gojek grew into Southeast Asia’s SuperApp and started serving large numbers of users, it became obvious that we needed a robust user-management system. This system would be responsible for all user-related scenarios such as account creation, authentication, payment-extension with user identification, etc. If our SuperApp ecosystem was a castle, this system served as its gateway.
For this, we realised we had to do the obvious: maintain a decentralised service responsible for all the above tasks, with high throughput.
Meet GoID
Initially GoID was being used just by Gojek merchants as a separate stand-alone system, independent of the Authentication Service which was used by Gojek’s consumer and driver apps. As we scaled, all services that were responsible for managing users’ accounts and their identity, be it for the Gojek app or any third-party service that uses these details, came under GoID.
Why this and why now?
Here’s everything that GoID brings to the table that our legacy Auth Service could not.
1. Monolithic Authentication
With Auth service, the users of our apps — consumers, drivers and merchants, are all authenticated through different services and protocols. This not only creates a mess while trying to standardise user properties, but also makes it hard to perform operations due to heterogeneous user accounts. With GoID, all user accounts, be it consumer, driver or merchant, will be managed in the same way, under same service, ensuring consistency among different user types, thereby making it easier to run required operations, without the need of handling differences between them.
2. Standalone service
Being a unified service, GoID can easily be made a standalone service, which means, it can be easily decoupled from app-specific services and can be converted into an easily pluggable service. This not only ensures smooth maintenance and integration of services, but also enables any of our new products to use this existing user-framework without the need to create one of its own. Just plug-n-go-play! (Not GoPlay, that’s another one of our services 😉).
3. Decentralised user access management
Every service endpoint that deals with user data, needs to have proper access to perform the required operations. This access level is internally managed by the scope granted by our Auth Services for that operation. However up until now, it wasn’t managed by a single point of control, rather was on a per-service basis. Now, with abstraction of user account management, all this can be done by a single service, resulting in a well-structured, maintainable and testable access management service.
4. Integration with 3rd party services
When Auth Service was made, it was mostly made to identify users exclusively within the ecosystem of our services and products. It wasn’t made to provide this facility to other third party services outside of our ecosystem. Although, with our humongous user base, it became obvious that we could enable other apps to verify a user based on their Gojek account.
This becomes effective by providing certain third party services such as enabling other apps to recognise users using OAuth 2 based APIs provided by Gojek, which would then allow other apps to have social login options that prompts: “Login with Gojek”. This will not only allow a hassle free experience for users logging into other apps, but will also ease the user-acquisition process, without the need to create a user-authentication system from scratch.
5. Bridging the gap with GoPay!
With GoID as a service, third party apps can also integrate GoPay for transaction related services, which will save them the effort of building their own payment services from scratch. The users of these third party apps will feel right at home with a familiar and trusted payment service from Gojek — making it a win for both apps and their users!
The Challenges
With the endgame in mind, it was clear that the benefits were many, and so were the challenges. The migration from Auth Service to GoID meant refactoring existing user tokens, login flows, registering flows, refresh services, etc. and all this, without creating any friction for existing users.
Here are a few challenges we were well aware of:
- All our services should accept user tokens generated both by GoID and Auth Service for user accounts, so that user doesn’t face any hiccups while using the app.
- For phased rollout, we usually go with Firebase configurations, and that depends on user-account. But, this toggle needed to be on the login flow itself, because of which remote configuration was out of question. To achieve this, we depended on the toggle embedded in the response of Auth Service itself, which is based on AppID and version of the app. And if that toggle is received as
true
, then the login request is directed to GoID. - Periodically, access tokens for each Gojek user is refreshed to maintain the security and integrity of the user’s account. We knew there would be cases where user logged in with the Auth Service token, but this refresh needed to happen via GoID. This was a critical step. If it went wrong, the user would get logged out and would have to login again, which is against our user-experience principles. A lot of QA and development efforts were put to make sure things were as smooth as intended.
- Security of user accounts also needed to be tested, as it was a brand new identification system, and thus, had to undergo same security tests on which our existing Auth Service stood, while standing even stronger.
- After the migration step completes, and most Gojek users get onboard GoID’s ship, it would be time to waive off the Auth Service. This was also needed to be done in a seamless way, while redirecting all new accounts to sign up through GoID, to attain the goal of moving 100% of our users to GoID.
The Big Migration
With our aim and obstacles in mind, it was time to step up and sprint. We not only broke this entire migration tasks into per-release scopes, but also made sure that each scope was well-contained in itself, so that by the start of the next release, we could test the previous releases’ tasks in an isolated and independent manner.
We also wanted to minimise the changes that were to be done in an otherwise healthy and working code. We couldn’t afford to jeopardise what we had.
With this in mind, we proceeded with the following steps:
- Addition of new endpoints
While Auth Service operated under the same domain as Gojek’s other APIs, GoID endpoints were located in a different cluster. On Android, the network client required cloning of network client config and to be re-configured with the new URL to be able to make a call to GoID, while on iOS, the new URL was added to the app config and then accessed as per the requirement.
2. Toggle-based redirection
Now that the network layer was sorted out, the next step was to add toggle acknowledgement. Instead of a Firebase config, we relied on the flag sent by existing Auth Services. If the flag indicated us to use GoID instead, then a consecutive call to GoID’s APIs were done. This toggle-based-redirection applies for both Login and Refresh Session APIs.
3. Persisting GoID status & managing existing tokens
If a user logs in via GoID, we can be sure that in case a refresh call is ever required, it will be done via GoID too. Hence, upon login completion, along with other details of the user, this detail was also persisted in the storage. As for the existing user who was already logged in and then updates the app, the toggle-based redirection will work. And if it is redirected, again this GoID status was persisted in the storage.
4. Mapping methods for contracts & error codes
Since both GoID and Auth Service work differently, they have different API contracts too. Some Auth Service APIs gave a result which was only possible by combining responses from two APIs of GoID. Amidst these differences, and our quest for making minimal changes to other sections of the app, we had to make sure that all these changes remained transparent to other parts of our app, which we did by creating mapper functions that mapped the response from GoID endpoints, to that of Auth Service’s. This meant that all other logic could go on without any disambiguation or regression. Similarly, error codes from GoID were mapped to that of Auth Service, so that existing error screens could still be shown correctly, in case any step fails.
5. Maintaining the quality
While coding, this logic in itself was quite a complex task. We still had to make sure that the quality of our code was up to our standards. For this, we not only kept running an existing list of extensive automated tests, but also added a few of our own, to keep the newly added code in check, and… It passed! ✌️
6. The Final step
Once the threshold for number of users intended to jump the GoID wagon was reached, the app needed to be converted to GoID only — thus deprecating the Auth login methods and the toggle-based redirection. All other flows needed to be refactored to eliminate Auth Service out.
We at Gojek value users and their identity. We make sure that the users’ security and privacy remains intact even while migrating them from one identification system to another. Our users’ convenience is what we stand for, while making sure we keep making their life easier.
After all, a #SuperApp’s development and growth is dependent on the user base, as much as its codebase!