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.
- On your login page, add Using DuoUniversal
- 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.
- 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);
}
- 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.
- On your login.aspx page, add Async=”true” to the top
<%@ Page Async="true" Language="C#"......
- Create a new Redirect page which Duo will transfer the user to after authenticating with two factor. Add add Using DuoUniversal to this page.
- Create a new method ProcessDuoResponse
public async void ProcessDuoResponse()
{
//Can use same client or create a new one
Client duoClient = (Client)Session["client"];
//Get Session information from login page
string sessionState = (string)Session["STATE_SESSION_KEY"];
string sessionUsername = (string)Session["USERNAME_SESSION_KEY"];
string receievedstate = Request.QueryString["state"];
string receivedcode = Request.QueryString["code"];
// If either is missing, something is wrong.
if (string.IsNullOrEmpty(sessionState) || string.IsNullOrEmpty(sessionUsername))
{
Response.Redirect("Login.aspx");
throw new DuoException("State or username were missing from your session");
}
//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")
Response.Redirect("LoggedIn.aspx"); //Redirect to the final logged in page
}
- In the Page_Load method add the following code
string sessionState = (string)Session["STATE_SESSION_KEY"];
string sessionUsername = (string)Session["USERNAME_SESSION_KEY"];
string receievedstate = Request.QueryString["state"];
string receivedcode = Request.QueryString["code"];
// If either is missing, something is wrong.
if (string.IsNullOrEmpty(sessionState) || string.IsNullOrEmpty(sessionUsername)
|| string.IsNullOrEmpty(receievedstate) || string.IsNullOrEmpty(receivedcode))
{
//Redirect to login page and throw appropriate error message.
Response.Redirect("Login.aspx");
return;
}
- 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.