Duo Universal C# ASP NET Web Forms

Duo recently released their new Universal two factor authentication method which changes from an iFrame method to using a redirect. Duo provides a C# example using .NET Core and MVC but if you still have old web forms applications you’ve built and aren’t ready, you can still integrate them with Duo Universal.

First, you’ll need to buil the DuoUniversal.dll binary from the project https://github.com/duosecurity/duo_universal_csharp and add it as a reference to your project. You’ll also need to make sure you’re running .NET Framework 4.7.1 or higher for your project.

Second, add the following items from Nuget to the project

  • System.Text.Json
  • System.Net.Http.Json
  • Microsoft.IdentityModel.Tokens
  • Microsoft.IdentityModel.JsonWebTokens

Now you’re ready to start integrating it into your current project.

  1. On your login page, add Using DuoUniversal
  2. Declare string values for Duo Settings
string ClientId = ""; //Setting from Duo
string ClientSecret = ""; //Setting from Duo
string ApiHost = ""; //Setting from Duo
string RedirectUri = "https://localhost:44310/Redirect.aspx"; //This is the URL you will redirect to after authentication. It does not need to be publically accessible.
  1. Create a new method InitiateDuo
public async void InitiateDuo(string User)
{
	//Create client using config settings
	Client duoClient = new DuoUniversal.ClientBuilder(ClientId, ClientSecret, ApiHost, RedirectUri).Build();
	Session["client"] = duoClient;


	// Check if Duo seems to be healthy and able to service authentications.
	// If Duo were unhealthy, you could possibly send user to an error page, or implement a fail mode
	var isDuoHealthy = await duoClient.DoHealthCheck();

	// Generate a random state value to tie the authentication steps together
	string state = DuoUniversal.Client.GenerateState();
	// Save the state and username in the session for later
	Session["STATE_SESSION_KEY"] = state;
	Session["USERNAME_SESSION_KEY"] = User;

	// Get the URI of the Duo prompt from the client.  This includes an embedded authentication request.
	string promptUri = duoClient.GenerateAuthUri(User, state);

	// Redirect the user's browser to the Duo prompt.
	// The Duo prompt, after authentication, will redirect back to the configured Redirect URI to complete the authentication flow.
	// In this example, that is /duo_callback, which is implemented in Callback.cshtml.cs.
	Response.Redirect(promptUri,false);

}
  1. Add the following code to the portion where you want to trigger the Duo prompt.
InitiateDuo(User); //User should be the username trying to login.
  1. On your login.aspx page, add Async=”true” to the top
<%@ Page Async="true" Language="C#"......
  1. Create a new Redirect page which Duo will transfer the user to after authenticating with two factor. Add add Using DuoUniversal to this page.
  2. Create a new method DUOResponse
 public async void DUOResponse(string sessionState, string sessionUsername, string receievedstate, string receivedcode)
{
	Client duoClient = (Client)Session["client"];

	//Confirm the original state(from the session) matches the state sent by Duo; this helps p
	if (!sessionState.Equals(receievedstate))
	{
		throw new DuoException("Session state did not match the expected state");
	}
	//Session.Clear();

	IdToken token = await duoClient.ExchangeAuthorizationCodeFor2faResult(receivedcode, sessionUsername);


	if (token.AuthResult.Result == "allow")
	{
		Session["authenticated"] = true;
		Response.Redirect("Home.aspx");

	}
}
  1. In the Page_Load method of the Redirect page add the following code
string sessionState = string.Empty;
if (Session["STATE_SESSION_KEY"] != null)
	sessionState = Session["STATE_SESSION_KEY"].ToString();
else
	Response.Redirect("login.aspx?Msg=" + HttpUtility.UrlEncode("Duo Session State Key Missing"));

string sessionUsername = string.Empty;
if (Session["userName"] != null)
	sessionUsername = Session["userName"].ToString();
else
	Response.Redirect("login.aspx?Msg=" + HttpUtility.UrlEncode("Duo Session Username Missing"));

string receievedstate = string.Empty;
if (Request.QueryString["state"] != null)
	receievedstate = Request.QueryString["state"];
else
	Response.Redirect("login.aspx?Msg=" + HttpUtility.UrlEncode("Duo State URL Variable Missing"));

string receivedcode = string.Empty;
if (Request.QueryString["code"] != null)
	receivedcode = Request.QueryString["code"];
else
	Response.Redirect("login.aspx?Msg=" + HttpUtility.UrlEncode("Duo Code URL Variable Missing"));

if (string.IsNullOrEmpty(sessionState) || string.IsNullOrEmpty(sessionUsername)
	|| string.IsNullOrEmpty(receievedstate) || string.IsNullOrEmpty(receivedcode))
{
	Response.Redirect("login.aspx?Msg=" + HttpUtility.UrlEncode("Duo null items"));
	return;
}

DUOResponse(sessionState, sessionUsername, receievedstate, receivedcode);
  1. On your Redirect.aspx page, add Async=”true” to the top
<%@ Page Async="true" Language="C#"......

Build and test. When you test it the first time you will see the old prompt. If everything works, you can go into Duo Admin console and turn on the new prompt.

Duo Universal C# ASP NET Web Forms Read More »