5 Steps to Modernize Large Websites using OAuth
One of the major concerns in software systems is keeping a code base maintainable over time as the amount of logic grows.
In recent years it has been a best practice to break down code into modular components: microservices and micro-UIs. Each component can then be managed by different development teams working in parallel without affecting each other. Each team can operate at its own pace and more easily keep their tools, technologies and capabilities up to date. All of this results in faster and more scalable business delivery.
However, many existing real-world business platforms are not built like this. Instead, they were made in an older website architectural style. A decade or so ago, it was common for these code bases to become very large and difficult to manage, slowing down business delivery.
For companies still operating large websites, there is often a compelling need to modernize systems to enable the above benefits of microservices. Yet it’s not always clear which deployment and security choices to make or how to migrate the existing cookie-based security.
Here, I will provide an overview of some deployment and separation patterns we recommend at Curity when implementing security solutions for web and API components. These techniques are also a great fit when modernizing the architecture of large websites.
Foundation: OAuth and OpenID Connect
Modern application-level components implement security using the OAuth family of specifications, which provides security capabilities for web apps, mobile apps and APIs. This empowers companies with the most cutting-edge options for authenticating users with one or more proofs of their identity. It also helps protect data in APIs according to business rules.
In this post, I won’t go into detail about security standards. Instead, I’ll focus on high-level technical and cultural steps to help break down a large website into smaller parts. This manageable process will avoid a big-bang approach to ensure business continuity.
I will also assume organizations are starting with a large website that uses OAuth-based logins and secure cookies in a basic way but are not taking full advantage of the architecture.
Initial Website Architecture
Consider the following example of a large website dealing with insurance business logic. This website uses the older .NET framework and is deployed to Windows servers. Many web pages are downloaded to the browser by combining HTML and data. Newer code increasingly uses Ajax requests to update pages and make them feel fast and interactive. The web backend must also manage many API routes.
Developers probably know how to refactor the large codebase into multiple apps. However, doing so would also require changing the web backend’s deployment and cookie security. This can feel daunting, since it may require buy-in from operations teams and cause user experience issues like requiring a separate login per app. The business may also have concerns about the impact of ongoing, and often high priority, business objectives.
Therefore, in the following sections, I propose a safe way to manage a gradual modernization, consisting of five main steps.
Step 1: Use an API Gateway Entry Point
The first step of a modernization process should be introducing a reverse proxy or API gateway. This can be used in many security design patterns and is also effective for splitting websites.
In the below diagram, one of the less complex business areas (marketing) has been selected from the example insurance business domain. The marketing app has been split into its own website. A high-performance gateway such as NGINX then becomes the web entry point to form the following end-to-end flow:
This refactor would involve some moderate code changes and should use only “lift and shift” migration since it’s primarily a deployment job. At this stage, the new marketing website should continue to use the same .NET framework technology as the main website.
It’s important to keep in mind that each website must be configured to use the same cookie properties, including cookie names and encryption keys. This will enable the user to sign into one of the apps, and then navigate seamlessly to the other. If OAuth is used, then both websites will use the same OAuth client, containing a different redirect URI (reply URL) for each site. Some examples:
This will require some deployment and cookie investigations, and you will need to ensure that websites continue to return external URLs to the browser.
Once done, the marketing website is assigned to a particular team, which becomes its component owner. They then work on a much smaller codebase that is much easier to manage. In the future, the company can follow the same approach for other business areas, so a process is in place for modularizing the entire website.
Step 2: Separate Web and API Concerns
Technology has evolved since the days of building applications as websites. Companies often want to use frameworks such as React, where developers write code solely with a frontend focus and concentrate on delivering the best possible customer experiences. This works well for business leaders since they usually don’t want web developers working on web backend plumbing.
The team and the business owner may therefore agree to update the marketing website, which was modularized in the previous step, to a single-page application (SPA) architecture. One main area of work will involve migrating data logic from the web backend’s Ajax endpoints to APIs. Another will be changing the UI to use client-side rendering rather than combining HTML with data in a web backend:
The migrations can be done incrementally and safely, a few pages at a time, while the overall app remains a website. This will allow you to avoid a “big bang” upgrade. These API migrations will also provide short-term code-sharing benefits, such as enabling the website functionality to be reused by mobile apps.
Be aggressive and aim for a completed migration, though, since that is a prerequisite to realizing your preferred architecture. Choose a time that works well for the business too, such as when there are no other big frontend priorities for the marketing app. Migrate some difficult pages early on, and document the step-by-step process.
Step 3: Integrate Single-Page Application Security
One of the tricky areas of migrating websites to SPAs is security. Using tokens in the browser opens up more attack vectors, and you must protect against cross-site scripting (XSS) threats. The current SPA security best practice is to continue to use HTTP-only cookies in the same way as websites. Therefore SPAs need an application-level cookie layer.
For OAuth-secured SPAs, the most mainstream way to integrate cookies is via a backend for frontend (BFF). A gateway is also used to separate static content requests from OAuth and API requests. Doing so can enable the best user and developer experience while also ensuring that you can deploy the SPA as static content to any host of your choice.
At Curity, we recommend an API-driven variation of BFF called the token handler pattern. This involves plugging in tested components to deal with OAuth and cookies, avoiding the need to add security plumbing to your application code. In the following flow, an OAuth Agent calls the authorization server and issues cookies on behalf of the SPA. The OAuth Proxy is a gateway plugin that makes web-specific security checks during API requests, and then forwards a JWT access token to the target API:
For newer SPAs, access tokens issued should be designed using the principle of least privilege. Keep access tokens short-lived and also lock them down using scopes and claims. This ensures that an access token issued to the marketing app can only be sent to the marketing API, which can then use the token’s scopes and claims for authorization. This is a more secure design than that used by the large website, whose cookies grant access to many business areas.
During this phase, there will likely be a learning curve, so plan some spikes that de-risk it. First, get deployment of the new components working using a small proof-of-concept (POC) application. Also, ensure that APIs make the same authorization checks as the previous website. Have a look at these API guides for further details on claims-based authorization using JWT access tokens.
Step 4: Mix and Match Web Styles
Modernizing large websites will take time, but the approach proposed here enables you to do so gradually, in between other business objectives. During this period, the gateway allows you to mix and match web architecture styles and expose them on the same base URL when needed.
When integrating newer SPAs with existing large websites, use single sign-on (SSO) so that SPA cookies are not shared with websites. Each new app is then issued with its own least privilege tokens, allowing for the best security. It also enables you to vary the authentication for different sets of users, such as using newer passwordless devices for logins for some apps.
In this example, we can see the familiar marketing app and the main website. They are joined by a newly added advertising app that has a different style since it returns unsecured data and needs to achieve good search engine optimization (SEO). This app, therefore, continues to use server-side rendering (SSR) to return HTML and public data together. It can be exposed on a gateway path that does not require cookies.
Step 5: Modernize Technology
Once the deployment, separation and security work are complete, dedicated teams assigned to a component can perform technology modernizations at a suitable time. For example, this might involve updating APIs to the latest .NET stack, enabling Linux container-based deployment or updating SPAs to use modern frameworks.
Once the web and API concerns are separated, you may no longer need a gateway or cookies for web components. Instead, a content delivery network (CDN) can be used. This delivers static (unsecured) web content and can help to ensure equal web performance for a global user base. Meanwhile, only the API side of the architecture uses a gateway, and token translation is part of its zero-trust implementation.
When you split an app into multiple SPAs solely for code size and productivity reasons, it’s fine to share the same cookie across those apps. This is done by hosting the SPAs using different paths within the same domain. Single sign-on navigation is preferred when crossing business areas to keep token privileges small. This will likely lead to separated web domains per business area.
In this article, I proposed an approach for gradually migrating a large website to a modern componentized architecture. This results in an architecture that scales more effectively as the code and people who work on it grow, leading in more predictable business delivery.
The process began with a focus on separation and deployment. This first enabled the large website to be split into multiple apps, after which web and API concerns were separated. Doing so also provides the best future setup for making full use of OAuth security design patterns, which work best when web (client) and API (resource server) concerns are separated.
At Curity, we know that technology migrations are difficult. If you are new to OAuth, the identity and access management (IAM) primer introduces the security components mentioned in this article. And for developers, we provide guides with many tutorials on web, API and gateway solutions. You can run these end-to-end on a development computer to evaluate designs early in your modernization journey.