Skip to content

Commit

Permalink
Issuer parameter added to the GenerateAuthUri method (#40)
Browse files Browse the repository at this point in the history
* .NET Framework 471 architecture added. Version bump to 1.2.3

* adding issuer parameter to the auth jwt to handle the Epic samlResponse generation

* version bump to 1.2.4

* added unit tests and issuer validation

* end of lines fixed

* extra whitespace removed

* Moving the issuer parameter to the ClientBuilder and renaming it to audienceIssuer
  • Loading branch information
yevgenkre authored Apr 29, 2024
1 parent 7dd4c62 commit dac777a
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 4 deletions.
29 changes: 29 additions & 0 deletions DuoUniversal.Tests/TestGenerateAuthUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,35 @@ public void TestSuccess(string username)
Assert.True(authUri.StartsWith($"https://{API_HOST}"));
}

[Test]
[TestCase(USERNAME)]
[TestCase("I iz a user")]
[TestCase("[email protected]")]
public void TestSuccessWithIssuer(string username)
{
Client clientWithIssuer = new ClientBuilder(CLIENT_ID, CLIENT_SECRET, API_HOST, REDIRECT_URI).UseAudienceIssuer("http://issuer").Build();
string authUri = clientWithIssuer.GenerateAuthUri(username, STATE);
Assert.True(Uri.IsWellFormedUriString(authUri, UriKind.Absolute));
Assert.True(authUri.StartsWith($"https://{API_HOST}"));
}

[Test]
[TestCase(" ")]
public void TestInvalidIssuer(string issuer)
{
Client clientWithIssuer = new ClientBuilder(CLIENT_ID, CLIENT_SECRET, API_HOST, REDIRECT_URI).UseAudienceIssuer(issuer).Build();
Assert.Throws<DuoException>(() => clientWithIssuer.GenerateAuthUri("username", STATE));
}

[Test]
[TestCase(null)]
public void TestNullIssuer(string issuer)
{
Client clientWithIssuer = new ClientBuilder(CLIENT_ID, CLIENT_SECRET, API_HOST, REDIRECT_URI).UseAudienceIssuer(issuer).Build();
string authUri = clientWithIssuer.GenerateAuthUri("username", STATE);
Assert.True(Uri.IsWellFormedUriString(authUri, UriKind.Absolute));
}

[Test]
[TestCase(null)]
[TestCase("")]
Expand Down
32 changes: 29 additions & 3 deletions DuoUniversal/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public class Client

internal bool UseDuoCodeAttribute { get; set; } = false;

internal string AudienceIssuer { get; set; } = null;

internal Client()
{
}
Expand Down Expand Up @@ -83,7 +85,7 @@ public async Task<bool> DoHealthCheck(bool handleException = true)
/// <returns>A URL to redirect the user's browser to</returns>
public string GenerateAuthUri(string username, string state)
{
ValidateAuthUriInputs(username, state);
ValidateAuthUriInputs(username, state, AudienceIssuer);

string authEndpoint = CustomizeApiUri(AUTH_ENDPOINT);

Expand Down Expand Up @@ -163,7 +165,7 @@ private string CustomizeApiUri(string baseUrl)
/// </summary>
/// <param name="username">The username to check</param>
/// <param name="state">The state value to check</param>
private void ValidateAuthUriInputs(string username, string state)
private void ValidateAuthUriInputs(string username, string state, string issuer)
{
if (string.IsNullOrWhiteSpace(username))
{
Expand All @@ -174,6 +176,10 @@ private void ValidateAuthUriInputs(string username, string state)
{
throw new DuoException($"state must be a non-empty string between {MINIMUM_STATE_LENGTH} and {MAXIMUM_STATE_LENGTH}.");
}
if (issuer != null && issuer.Trim().Length == 0)
{
throw new DuoException("issuer can be null, but cannot be an empty string");
}
}

/// <summary>
Expand All @@ -196,6 +202,12 @@ private string GenerateAuthJwt(string username, string state, string authEndpoin
// TODO support nonce
};

// issuer parameter is used for the Epic Hyperdrive integration only
if (AudienceIssuer != null)
{
additionalClaims[Labels.AUDIENCE_ISSUER] = AudienceIssuer;
}

if (UseDuoCodeAttribute)
{
additionalClaims[Labels.USE_DUO_CODE_ATTRIBUTE] = "true";
Expand Down Expand Up @@ -299,6 +311,7 @@ public class ClientBuilder
private bool _sslCertValidation = true;
private X509Certificate2Collection _customRoots = null;
private IWebProxy proxy = null;
private string _audienceIssuer = null;


// For testing only
Expand Down Expand Up @@ -411,6 +424,18 @@ public ClientBuilder UseHttpProxy(IWebProxy proxy)
return this;
}

/// <summary>
/// Set an audienceIssuer value to generate a SAML response for the Epic integration
/// </summary>
/// <param name="audienceIssuer">Specific parameter for the Epic integration for the SAML response generation</param>
/// <returns>The ClientBuilder</returns>
public ClientBuilder UseAudienceIssuer(string audienceIssuer)
{
_audienceIssuer = audienceIssuer;

return this;
}

/// <summary>
/// Build the Client based on the settings provided to the Builder
/// </summary>
Expand All @@ -425,7 +450,8 @@ public Client Build()
ClientSecret = _clientSecret,
ApiHost = _apiHost,
RedirectUri = _redirectUri,
UseDuoCodeAttribute = _useDuoCodeAttribute
UseDuoCodeAttribute = _useDuoCodeAttribute,
AudienceIssuer = _audienceIssuer
};

var httpClient = BuildHttpClient();
Expand Down
2 changes: 1 addition & 1 deletion DuoUniversal/DuoUniversal.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net471</TargetFrameworks>
<PackageId>DuoUniversal</PackageId>
<Version>1.2.3</Version>
<Version>1.2.4</Version>
<Authors>Duo Security</Authors>
<Company>Duo Security</Company>
<Copyright>Cisco Systems, Inc. and/or its affiliates</Copyright>
Expand Down
1 change: 1 addition & 0 deletions DuoUniversal/Labels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ internal class Labels
public const string DUO_UNAME = "duo_uname";
public const string PREFERRED_USERNAME = "preferred_username";
public const string USE_DUO_CODE_ATTRIBUTE = "use_duo_code_attribute";
public const string AUDIENCE_ISSUER = "issuer";
}
}

0 comments on commit dac777a

Please sign in to comment.