Skip to content

Commit

Permalink
handle stored request state without returnurl during OWIN error response
Browse files Browse the repository at this point in the history
  • Loading branch information
explunit committed Mar 29, 2018
1 parent 445b2ab commit 609d2b8
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 5 deletions.
14 changes: 9 additions & 5 deletions Sustainsys.Saml2.Owin/Saml2AuthenticationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,18 @@ protected async override Task<AuthenticationTicket> AuthenticateCoreAsync()
private AuthenticationTicket CreateErrorAuthenticationTicket(HttpRequestData httpRequestData, Exception ex)
{
AuthenticationProperties authProperties = null;
if (httpRequestData.StoredRequestState != null)
if (httpRequestData.StoredRequestState?.RelayData != null)
{
authProperties = new AuthenticationProperties(
httpRequestData.StoredRequestState.RelayData);
}
else
{
authProperties = new AuthenticationProperties();
}

if (httpRequestData.StoredRequestState?.ReturnUrl != null)
{
// ReturnUrl is removed from AuthProps dictionary to save space, need to put it back.
authProperties.RedirectUri = httpRequestData.StoredRequestState.ReturnUrl.OriginalString;
}
Expand All @@ -83,10 +90,7 @@ private AuthenticationTicket CreateErrorAuthenticationTicket(HttpRequestData htt

redirectUrl = httpRequestData.ApplicationUrl;
}
authProperties = new AuthenticationProperties
{
RedirectUri = redirectUrl.OriginalString
};
authProperties.RedirectUri = redirectUrl.OriginalString;
}

// The Google middleware adds this, so let's follow that example.
Expand Down
145 changes: 145 additions & 0 deletions Tests/Owin.Tests/Saml2AuthenticationMiddlewareTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,151 @@ public async Task Saml2AuthenticationMiddleware_AcsRedirectsToAuthPropsReturnUri
context.Authentication.AuthenticationResponseGrant.Should().BeNull();
}

[TestMethod]
public async Task Saml2AuthenticationMiddleware_AcsRedirectsToAuthProps_StoredRequestStateWithNoReturnUrl()
{
var context = OwinTestHelpers.CreateOwinContext();
context.Request.Method = "POST";

var authProps = new AuthenticationProperties();
authProps.Dictionary.Add("key1", "value1");

var state = new StoredRequestState(new EntityId("https://idp.example.com"),
null,
new Saml2Id("InResponseToId"),
authProps.Dictionary);

var relayState = SecureKeyGenerator.CreateRelayState();

var cookieData = HttpRequestData.ConvertBinaryData(
CreateAppBuilder().CreateDataProtector(
typeof(Saml2AuthenticationMiddleware).FullName)
.Protect(state.Serialize()));

context.Request.Headers["Cookie"] = $"{StoredRequestState.CookieNameBase}{relayState}={cookieData}";

var response =
@"<saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
ID = """ + MethodBase.GetCurrentMethod().Name + @""" Version=""2.0""
IssueInstant=""2013-01-01T00:00:00Z"">
<saml2:Issuer>
https://idp.example.com
</saml2:Issuer>
<saml2p:Status>
<saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" />
</saml2p:Status>
<saml2:Assertion
Version=""2.0"" ID=""" + MethodBase.GetCurrentMethod().Name + @"_Assertion1""
IssueInstant=""2013-09-25T00:00:00Z"">
<saml2:Issuer>https://idp.example.com</saml2:Issuer>
<saml2:Subject>
<saml2:NameID>SomeUser</saml2:NameID>
<saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" />
</saml2:Subject>
<saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" />
</saml2:Assertion>
</saml2p:Response>";

// No signature, that's an error.
var bodyData = new KeyValuePair<string, string>[] {
new KeyValuePair<string, string>("SAMLResponse",
Convert.ToBase64String(Encoding.UTF8.GetBytes(response))),
new KeyValuePair<string, string>("RelayState",relayState)
};

var encodedBodyData = new FormUrlEncodedContent(bodyData);

context.Request.Body = encodedBodyData.ReadAsStreamAsync().Result;
context.Request.ContentType = encodedBodyData.Headers.ContentType.ToString();
context.Request.Host = new HostString("localhost");
context.Request.Path = new PathString("/Saml2/Acs");

var middleware = new Saml2AuthenticationMiddleware(null, CreateAppBuilder(),
new Saml2AuthenticationOptions(true)
{
SignInAsAuthenticationType = "AuthType"
});

await middleware.Invoke(context);

context.Response.StatusCode.Should().Be(302);
context.Response.Headers["Location"].Should().Be("http://localhost/LoggedIn?error=access_denied");
context.Authentication.AuthenticationResponseGrant.Should().BeNull();
}

[TestMethod]
public async Task Saml2AuthenticationMiddleware_AcsRedirectsToAuthProps_StoredRequestStateWithNoRelayData()
{
var context = OwinTestHelpers.CreateOwinContext();
context.Request.Method = "POST";

var authProps = new AuthenticationProperties();

var state = new StoredRequestState(new EntityId("https://idp.example.com"),
new Uri("http://localhost/PathInRequestState?value=42"),
new Saml2Id("InResponseToId"),
null);

var relayState = SecureKeyGenerator.CreateRelayState();

var cookieData = HttpRequestData.ConvertBinaryData(
CreateAppBuilder().CreateDataProtector(
typeof(Saml2AuthenticationMiddleware).FullName)
.Protect(state.Serialize()));

context.Request.Headers["Cookie"] = $"{StoredRequestState.CookieNameBase}{relayState}={cookieData}";

var response =
@"<saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
ID = """ + MethodBase.GetCurrentMethod().Name + @""" Version=""2.0""
IssueInstant=""2013-01-01T00:00:00Z"">
<saml2:Issuer>
https://idp.example.com
</saml2:Issuer>
<saml2p:Status>
<saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" />
</saml2p:Status>
<saml2:Assertion
Version=""2.0"" ID=""" + MethodBase.GetCurrentMethod().Name + @"_Assertion1""
IssueInstant=""2013-09-25T00:00:00Z"">
<saml2:Issuer>https://idp.example.com</saml2:Issuer>
<saml2:Subject>
<saml2:NameID>SomeUser</saml2:NameID>
<saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" />
</saml2:Subject>
<saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" />
</saml2:Assertion>
</saml2p:Response>";

// No signature, that's an error.
var bodyData = new KeyValuePair<string, string>[] {
new KeyValuePair<string, string>("SAMLResponse",
Convert.ToBase64String(Encoding.UTF8.GetBytes(response))),
new KeyValuePair<string, string>("RelayState",relayState)
};

var encodedBodyData = new FormUrlEncodedContent(bodyData);

context.Request.Body = encodedBodyData.ReadAsStreamAsync().Result;
context.Request.ContentType = encodedBodyData.Headers.ContentType.ToString();
context.Request.Host = new HostString("localhost");
context.Request.Path = new PathString("/Saml2/Acs");

var middleware = new Saml2AuthenticationMiddleware(null, CreateAppBuilder(),
new Saml2AuthenticationOptions(true)
{
SignInAsAuthenticationType = "AuthType"
});

await middleware.Invoke(context);

context.Response.StatusCode.Should().Be(302);
context.Response.Headers["Location"].Should().Be("http://localhost/PathInRequestState?value=42&error=access_denied");
context.Authentication.AuthenticationResponseGrant.Should().BeNull();
}

[TestMethod]
public async Task Saml2AuthenticationMiddleware_AcsWorks()
{
Expand Down

0 comments on commit 609d2b8

Please sign in to comment.