Skip to main content

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

About 7 min

Overview

Please note, this tutorial assumes prior knowledge of using Datawiza Access Proxy (DAP). If you're new to DAP, refer to Introduction to Datawiza Access Proxy to familiarize yourself with its functionalities. Our main objective here is to help you modify a couple of lines of your existing code so that you can integrate it with DAP.

Our tutorial is framed around an existing web application "WebApp" which currently relies on username-based authentication. By integrating with DAP, you can modernize its authentication with modern identity providers (IdPs), like Microsoft Entra ID, Azure AD B2C, Okta, Auth0, Ping Identity, Amazon Cognito and others.

We assume that the user has been successfully authenticated by DAP. Following authentication, DAP inserts a specific HTTP header, x-dw-username, in plaintext. Alternatively, it may insert an HTTP header, dw-token, in JWT format. The choice between these options depends on the configured settings of DAP. See the table below for the details of these headers.

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

When the application sees this special header, x-dw-username or dw-token, it should treat the user as an authenticated user. This tutorial provides guidance on how to implement such logic in the application and also the logic of logout. We will address both scenarios, whether the special header isx-dw-username or dw-token.

To enhance security, consider implementing JWT instead of plaintext. With JWT, your custom web application and DAP will share a secret. DAP will utilize this secret to sign the token, and subsequently, you can employ the same secret to verify it.

A Developer Tool

While developing your code, you might be pleased to learn that it's not necessary to keep DAP up and running to transfer headers. In fact, some useful browser extensions, e.g., ModHeader, are available to facilitate header-related development. To learn more about ModHeader and how to use it, please refer to ModHeaderopen in new window.

Here is a screenshot of ModHeader. ModHeader | Custom Web Application SSO MFA Integration

For example, we can open the ModHeader and input the following values.

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 of the Custom App

The process of determining user authentication will need a revision. Previously, confirmation of an active user was carried out by verifying the validity of the current session. Now, we'll adjust this logic to accommodate the new authentication method.

You can retain the initial session check. Nonetheless, if a user does not have a valid session, proceed by confirming the existence of a legitimate header. If a valid header is found, you can confidently consider this user as authenticated. This alternative check using the x-dw-username or dw-token header broadens the scope of user authentication in your application, adding an extra layer of flexibility.

When header x-dw-username is used


// 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 the user session is not valid or headerUsername is not valid
        if (user != null 
+           || headerUsername != null)
        {
            // the request has a valid user session or a valid username header
            // Continue processing
            
        } else
        {
            // Redirect to login page
            filterContext.Result = new RedirectResult("~/Account/LogOn");
        }
    }
}











 



 











When header dw-token is used

public class AuthenticationMiddleware {
    private readonly RequestDelegate _next;

    public AuthenticationMiddleware(RequestDelegate next) {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context) {
        var user = context.Session.GetString("user");
+       var authToken = context.Request.Headers["dw-token"];

        if ((user != null) 
+           || (authToken.Count > 0 && IsValidUserToken(authToken)))
            await _next(context);
        else {
            if (context.Request.Path.Value.EndsWith("login"))
                await _next(context);
            else
                context.Response.Redirect("/login");
        }
    }

+   /**
+   * Use this method to validate authToken in JWT format
+   * @param authToken, a JWT token containing x-dw-username
+   * @return true if the token is valid, false otherwise
+   **/
+   private bool IsValidUserToken(string authToken) {
+       // Your logic to validate/authenticate the token comes here
+       // return true if it is valid, otherwise return false
+       string secret = "Your_Secret_Here";
+       var tokenHandler = new JwtSecurityTokenHandler();
+       var key = System.Text.Encoding.ASCII.GetBytes(secret);
+       try {
+           var tokenValidationParameters = new TokenValidationParameters {
+               ValidateIssuerSigningKey = true,
+               IssuerSigningKey = new SymmetricSecurityKey(key),
+               ValidateIssuer = false,
+               ValidateAudience = false
+           };
+
+           var claimsPrincipal = tokenHandler.ValidateToken(authToken, tokenValidationParameters, out var rawValidatedToken);
+
+           if (((JwtSecurityToken)rawValidatedToken).Claims.Any(x => x.Type == "username")) {
+               // Username exists in the token
+               return true;
+           }
+           else {
+               return false;
+           }
+       }
+       catch (SecurityTokenExpiredException) {
+           // token expired
+           // some code to handle the exception properly
+           return false;
+       }
+       catch (Exception) {
+           // any other exception
+           // some code to handle the exception properly
+           return false;
+       }
+   }
}









 


 









 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Handling The Logout Process of the Custom App

When handling user logout operations, our approach will change somewhat. Earlier, the procedure involved clearing the user's session and then redirecting to the login page. Now, the SSO mechanism necessitates us to do more.

In addition to resetting their session, we'll need to inform and clear the DAP session as well. We achieve this by redirecting the user to a specific path, /datawiza/ab-logout. This extra step ensures that both application and DAP sessions terminate simultaneously, maintaining sync and security between the two systems.

[HttpPost]
public async Task<IActionResult> Logout()
{
    // Clearing authentication cookies
    await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    
    // Clearing session
    HttpContext.Session.Clear();
    
    // Redirecting to the logout success url.
-    return Redirect("/login"); // where to redirect after logout
+    return Redirect("/datawiza/ab-logout"); // redirect to this datawiza endpoint to clear datawiza session
}










 
 

(Optional) Single logout IdP

Occasionally, you may want to single logout IdP after logging out a user from your application. DAP offers the functionality required to accomplish this.

You can find this option in the advanced settings of your application on the Datawiza console. To activate single logout, follow the instructions in the screenshot below and check the Enable Single Logout option.

single logout | Custom Web Application SSO MFA Integration

Remember to save your settings to apply this new logout procedure. This feature secures your system by ensuring all associated sessions are terminated after a user logs out.

(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, navigate to the advanced settings page on the Datawiza console. Here, you can modify the Logout Redirect URI value to your desired destination page.

Please refer to the screenshot below to better understand these steps.

logout redirect uri | Custom Web Application SSO MFA Integration

Don't forget to save your settings once you've altered the URI. This way, you can personalize the logout user experience to better suit your application's needs.

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

In this guide, you've learned how to adjust your custom application's code with minimal code changes to integrate with DAP, enabling modern auth to your custom application.

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.