Skip to main content

Integrate Your Custom Web Application with Datawiza Access Proxy to Enable SSO and MFA

About 8 min

Overview

This tutorial shows how to add modern single sign-on (SSO) and multi-factor authentication (MFA) to your legacy or on-premises application with minimal code changes. Learn more about adding modern SSO and MFA to your legacy apps or on-prem applications with Datawiza.open in new window

This tutorial assumes prior knowledge of Datawiza Access Proxy (DAP). If you're new to DAP, refer to Introduction to Datawiza Access Proxy before continuing. The goal is to walk you through a small set of targeted changes to your existing code so that your application can work with DAP.

This tutorial uses an existing web application "WebApp" that currently relies on session-based authentication. By integrating with DAP, you can modernize its authentication with identity providers (IdPs) such as Microsoft Entra ID, Azure AD B2C, Okta, Auth0, Ping Identity, Amazon Cognito, and others.

Once DAP authenticates a user, it adds one of the following HTTP headers to every downstream request, depending on your DAP configuration.

Header NameFormatValue
x-dw-usernamePlaintextThis user's username in plaintext
dw-tokenJWTA JWT (JSON Web Token) including user's username

Network Security Requirement

This integration relies on your application trusting HTTP headers added by DAP. This trust is only valid when your application is exclusively reachable through DAP — not directly from the internet. If a client can reach your application directly, they can forge any HTTP header, including x-dw-username, and bypass authentication entirely.

Enforce this at the network level: use firewall rules, private networking, or bind your application to localhost so that only DAP can reach it.

When the application receives this header, it should treat the user as authenticated. This tutorial covers how to implement that logic for both options, including the logout flow.

DAP supports two header formats. Choose based on your requirements:

x-dw-username (plaintext)dw-token (JWT)
ComplexityMinimal — read a single headerModerate — validate a signed token
VerificationRelies on network isolation (DAP is the only path in)Application verifies the signature independently, providing defense-in-depth
When to useWhen network access is tightly controlled and simplicity is preferredWhen you want the application to cryptographically verify that the header was issued by DAP

Both options are valid for production use. With dw-token, your application and DAP share a secret — DAP signs the token with it, and your application verifies the signature using the same secret.

Testing Headers During Development

You do not need DAP running to test header-based authentication during development. The ModHeaderopen in new window browser extension lets you inject custom headers into any request directly from your browser, so you can simulate DAP's behavior without setting up the full proxy.

ModHeader | Custom Web Application SSO MFA Integration

Set one of the following header values to simulate an authenticated user:

Header NameValue
x-dw-usernamejohn

Or

Header NameValueNote
dw-tokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJ1c2VybmFtZSI6ImpvaG4ifQ
.xhxiOgF2WzKr7QuxpQgdwZCxRwYUM23gHbA9NtLR3aU
The secret in this example is Your_Secret_Here

Handling The Login Process

Your existing authentication check needs a small update. Previously, the application confirmed a logged-in user by checking for a valid session. Now, it also needs to recognize the DAP header as proof of authentication when no session exists.

When DAP successfully authenticates a user, it adds the header to every request. The recommended pattern is: check the session first; if no session exists, read the DAP header, create a session from it, and continue. This ensures subsequent requests (including AJAX calls) use the session rather than re-validating the header every time.

Authentication flow | Custom Web Application SSO MFA Integration

Header Authentication with x-dw-username


// Implement FilterAttribute and IAuthorizationFilter
public class AuthFilter : ActionFilterAttribute, IAuthorizationFilter
{
    // OnAuthorization runs before the action method is called
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        // Get user from session
        var user = HttpContext.Current.Session["user"];

        // Get username header
+       var headerUsername = HttpContext.Current.Request.Headers["x-dw-username"];

        if (user != null)
        {
            // Valid session, continue processing
+       } else if (headerUsername != null)
+       {
+           // Create a session from the DAP header
+           HttpContext.Current.Session["user"] = headerUsername;
        } else
        {
            // Redirect to login page
            filterContext.Result = new RedirectResult("~/Account/LogOn");
        }
    }
}











 




 
 
 
 







Header Authentication with dw-token

The JWT token issued by DAP contains a username claim with the authenticated user's username. The examples below validate the token, extract the username, and create a session — the same outcome as the plaintext approach above.

Required libraries: Add the JWT library for your language before implementing the examples below.

LanguageLibraryHow to add
Javaio.jsonwebtoken:jjwt-api, jjwt-impl, jjwt-jacksonAdd to pom.xml or build.gradle
PHPfirebase/php-jwtRun composer require firebase/php-jwt
.NETSystem.IdentityModel.Tokens.JwtInstall via NuGet Package Manager

Note: Replace Your_Secret_Here with the secret configured in your DAP instance. In production, load this value from an environment variable or a secrets manager — never hardcode it in source code.

// Implement FilterAttribute and IAuthorizationFilter
public class AuthFilter : ActionFilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var user = HttpContext.Current.Session["user"];
+       var authToken = HttpContext.Current.Request.Headers["dw-token"];

        if (user != null)
        {
            // Valid session, continue processing
+       } else if (authToken != null)
+       {
+           string validatedUsername;
+           if (IsValidUserToken(authToken, out validatedUsername))
+           {
+               // Token is valid, issue a session for the validated username
+               HttpContext.Current.Session["user"] = validatedUsername;
+           }
+           else if (!filterContext.HttpContext.Request.Url.AbsolutePath.EndsWith("login"))
+           {
+               filterContext.Result = new RedirectResult("~/Account/LogOn");
+           }
        } else
        {
            if (!filterContext.HttpContext.Request.Url.AbsolutePath.EndsWith("login"))
                filterContext.Result = new RedirectResult("~/Account/LogOn");
        }
    }

+   // Validates the dw-token JWT and returns the authenticated username via validatedUsername
+   private bool IsValidUserToken(string authToken, out string validatedUsername)
+   {
+       validatedUsername = null;
+       string secret = "Your_Secret_Here";
+       var tokenHandler = new JwtSecurityTokenHandler();
+       // DAP signs the token with this shared secret using HMAC-SHA256
+       SymmetricSecurityKey signingKey = new SymmetricSecurityKey(
+           Encoding.UTF8.GetBytes(secret));
+       try
+       {
+           var tokenValidationParameters = new TokenValidationParameters
+           {
+               ValidateIssuerSigningKey = true,
+               IssuerSigningKey = signingKey,
+               ValidateIssuer = false,
+               ValidateAudience = false,
+               ValidateLifetime = true,
+               RequireExpirationTime = false,
+           };
+           SecurityToken rawValidatedToken;
+           var claimsPrincipal = tokenHandler.ValidateToken(
+               authToken, tokenValidationParameters, out rawValidatedToken);
+           var usernameClaim = ((JwtSecurityToken)rawValidatedToken).Claims
+               .FirstOrDefault(x => x.Type == "username");
+           if (usernameClaim != null)
+           {
+               // username claim exists in the token
+               validatedUsername = usernameClaim.Value;
+               return true;
+           }
+           return false;
+       }
+       catch (Exception ex)
+       {
+           // Signature verification failed, or the token is malformed or expired —
+           // in production, log ex here rather than failing silently, since this
+           // could indicate a tampered token
+           return false;
+       }
+   }
}






 




 
 
 
 
 
 
 
 
 
 
 
 







 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Handling The Logout Process

Logging out requires one additional step compared to your existing flow. Previously, logout meant clearing the session and redirecting to the login page. With DAP, you also need to clear the DAP session.

Do this by redirecting to /datawiza/ab-logout instead of your login page. DAP will clear its own session and then redirect the user onward. This ensures both sessions — application and DAP — are terminated together.

[HttpPost]
public ActionResult Logout()
{
    // Clear the authentication cookie
    FormsAuthentication.SignOut();

    // Clear the session
    Session.Clear();

-   return Redirect("/login"); // where to redirect after logout
+   return Redirect("/datawiza/ab-logout"); // redirect to this endpoint to clear the DAP session
}









 
 

(Optional) Changing the default logout redirect page

By default, DAP redirects the user to its default logout page, /datawiza/login.html. But, you may wish to redirect users to a different page upon logout, possibly something more tailored to your application's design or workflow.

To change the logout redirect, go to the advanced settings page in the Datawiza console and update the Logout Redirect URI to your desired destination, then save.

logout redirect uri | Custom Web Application SSO MFA Integration

(Optional) Single logout IdP

For enhanced security in scenarios like B2C (e.g., using Auth0, Microsoft External ID, or other social logins), you can configure your application to terminate the user's session not only within your app but also at the Identity Provider (IdP). This is known as Single Logout (SLO).

SLO is recommended when you want the logout action to clear the user's central identity session, providing a more secure and seamless end-user experience.

How to Enable Single Logout

  • Navigate to your application's Advanced Settings in the Datawiza console.
  • Locate and check the Enable Single Logout option.
  • Save your configuration.

single logout | Custom Web Application SSO MFA Integration

Note

If you enable the Single Sign Out option, it's crucial to ensure that the Logout Redirect URI matches the settings on your Identity Provider (IdP) side.

The corresponding setting in IdP is frequently labeled as Allowed sign-out URL. However, the terminology may vary across different IdPs. By aligning these settings, you can ensure a smooth and secure login and logout experience for your users. Always verify this match to avoid unnecessary login issues or restrictions.

allow sign-out url | Custom Web Application SSO MFA Integration

Summary

This guide showed how to integrate a legacy application with DAP by making minimal code changes. The core idea behind all the modifications is the same three-step pattern:

  1. Check the session first. If a valid session already exists, continue as normal — nothing changes for returning users.
  2. If no session, check the DAP header. DAP adds either x-dw-username (plaintext) or dw-token (JWT) on every authenticated request. Read that header and validate it — for JWT, verify the signature using the shared secret.
  3. If the header is valid, create a session. Establish a regular application session from the header value so subsequent requests (AJAX calls, resource fetches) use the session rather than re-checking the header every time.

For logout, the only change is redirecting to /datawiza/ab-logout instead of your application's login page, so both the application session and the DAP session are cleared together.

Everything else in your application — authorization logic, business rules, session usage — stays untouched.

If you encounter any issues or have any questions about the process, don't hesitate to reach out to our support team. We're here to help you every step of the way.