Skip to content

Commit

Permalink
Preserve Owin AuthenticationProperties for Idp call.
Browse files Browse the repository at this point in the history
- Fixes Sustainsys#127.
- Uses present in memory RequestState to store the properties.
- Still won't work with Discovery Service, added that as Sustainsys#182.
  • Loading branch information
AndersAbel committed Jan 15, 2015
2 parents 2d39ac1 + 31019e1 commit 4d0de88
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,10 @@ protected async override Task<AuthenticationTicket> AuthenticateCoreAsync()
var result = CommandFactory.GetCommand(CommandFactory.AcsCommandName)
.Run(await Context.ToHttpRequestData(), Options);

var properties = new AuthenticationProperties()
{
RedirectUri = result.Location.ToString()
};

var identities = result.Principal.Identities.Select(i =>
new ClaimsIdentity(i, null, Options.SignInAsAuthenticationType, i.NameClaimType, i.RoleClaimType));

return new MultipleIdentityAuthenticationTicket(identities, properties);
return new MultipleIdentityAuthenticationTicket(identities, (AuthenticationProperties)result.RelayData);
}

protected override async Task ApplyResponseChallengeAsync()
Expand All @@ -55,7 +50,8 @@ protected override async Task ApplyResponseChallengeAsync()
idp,
challenge.Properties.RedirectUri,
await Context.ToHttpRequestData(),
Options);
Options,
challenge.Properties);

result.Apply(Context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void KentorAuthServicesAuthenticationMiddleware_CtorSetsDefaultAuthOption

options.SignInAsAuthenticationType.Should().BeNull();

var middleware = new KentorAuthServicesAuthenticationMiddleware(new StubOwinMiddleware(0, null),
var middleware = new KentorAuthServicesAuthenticationMiddleware(new StubOwinMiddleware(0, null),
CreateAppBuilder(), options);

options.SignInAsAuthenticationType.Should().Be(DefaultSignInAsAuthenticationType);
Expand Down Expand Up @@ -95,7 +95,7 @@ public async Task KentorAuthServicesAuthenticationMiddleware_CreatesPostOnAuthCh
new Dictionary<string, string>()
{
{ "idp", "http://localhost:13428/idpMetadata" }
}))),
}))),
CreateAppBuilder(),
new KentorAuthServicesAuthenticationOptions(true));

Expand All @@ -106,7 +106,7 @@ public async Task KentorAuthServicesAuthenticationMiddleware_CreatesPostOnAuthCh
context.Response.StatusCode.Should().Be(200);
context.Response.Body.Seek(0, SeekOrigin.Begin);

using(var reader = new StreamReader(context.Response.Body))
using (var reader = new StreamReader(context.Response.Body))
{
string bodyContent = reader.ReadToEnd();

Expand Down Expand Up @@ -159,7 +159,7 @@ public async Task KentorAuthServicesAuthenticationMiddleware_RedirectoToSecondId
new Dictionary<string, string>()
{
{ "idp", secondEntityId.Id }
}))),
}))),
CreateAppBuilder(), new KentorAuthServicesAuthenticationOptions(true));

var context = OwinTestHelpers.CreateOwinContext();
Expand Down Expand Up @@ -236,17 +236,55 @@ public async Task KentorAuthServicesAuthenticationMiddleware_RedirectRemembersRe
storedAuthnData.ReturnUrl.Should().Be(returnUrl);
}

[TestMethod]
public async Task KentorAuthServicesAuthenicationMiddleware_StoresAuthenticationProperties()
{
var returnUrl = "http://sp.example.com/returnurl";

var prop = new AuthenticationProperties()
{
RedirectUri = returnUrl
};
prop.Dictionary["test"] = "SomeValue";

var middleware = new KentorAuthServicesAuthenticationMiddleware(
new StubOwinMiddleware(401, new AuthenticationResponseChallenge(
new string[] { "KentorAuthServices" }, prop)),
CreateAppBuilder(), new KentorAuthServicesAuthenticationOptions(true));

var context = OwinTestHelpers.CreateOwinContext();

await middleware.Invoke(context);

var requestId = AuthnRequestHelper.GetRequestId(new Uri(context.Response.Headers["Location"]));

StoredRequestState storedAuthnData;
PendingAuthnRequests.TryRemove(new Saml2Id(requestId), out storedAuthnData);

((AuthenticationProperties)storedAuthnData.RelayData).Dictionary["test"].Should().Be("SomeValue");
}

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

var state = new StoredRequestState(new EntityId("https://idp.example.com"),
new Uri("http://localhost/LoggedIn"),
new AuthenticationProperties());

((AuthenticationProperties)state.RelayData).RedirectUri = state.ReturnUrl.OriginalString;
((AuthenticationProperties)state.RelayData).Dictionary["Test"] = "TestValue";

PendingAuthnRequests.Add(new Saml2Id("KentorAuthServicesAuthenticationMiddleware_AcsWorksRequestID"), state);

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 = ""KentorAuthServicesAuthenticationMiddleware_AcsWorks"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"">
ID = ""KentorAuthServicesAuthenticationMiddleware_AcsWorks"" Version=""2.0""
IssueInstant=""2013-01-01T00:00:00Z"" InResponseTo=""KentorAuthServicesAuthenticationMiddleware_AcsWorksRequestID"" >
<saml2:Issuer>
https://idp.example.com
</saml2:Issuer>
Expand Down Expand Up @@ -295,8 +333,14 @@ public async Task KentorAuthServicesAuthenticationMiddleware_AcsWorks()
context.Response.StatusCode.Should().Be(302);
context.Response.Headers["Location"].Should().Be("http://localhost/LoggedIn");

context.Authentication.AuthenticationResponseGrant.Principal.Identities.ShouldBeEquivalentTo(ids,
opt => opt.IgnoringCyclicReferences());
context.Authentication.AuthenticationResponseGrant.Principal.Identities
.ShouldBeEquivalentTo(ids, opt => opt.IgnoringCyclicReferences());

context.Authentication.AuthenticationResponseGrant.Properties.RedirectUri
.Should().Be("http://localhost/LoggedIn");

context.Authentication.AuthenticationResponseGrant.Properties.Dictionary["Test"]
.Should().Be("TestValue");
}

[TestMethod]
Expand All @@ -307,7 +351,7 @@ public async Task KentorAuthServicesAuthenticationMiddleware_MetadataWorks()
context.Request.Path = new PathString("/AuthServices");

var middleware = new KentorAuthServicesAuthenticationMiddleware(
null,
null,
CreateAppBuilder(),
new KentorAuthServicesAuthenticationOptions(true));

Expand All @@ -317,7 +361,7 @@ public async Task KentorAuthServicesAuthenticationMiddleware_MetadataWorks()
context.Response.ContentType.Should().Contain("application/samlmetadata+xml");

var xmlData = XDocument.Load(context.Response.Body);

xmlData.Document.Root.Name.Should().Be(Saml2Namespaces.Saml2Metadata + "EntityDescriptor");
}

Expand Down
3 changes: 2 additions & 1 deletion Kentor.AuthServices.Tests/WebSSO/CommandResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public void CommandResult_Defaults()
Location = (Uri)null,
Principal = (ClaimsPrincipal)null,
ContentType = (string)null,
Content = (string)null
Content = (string)null,
RelayData = (object)null
};

new CommandResult().ShouldBeEquivalentTo(expected);
Expand Down
21 changes: 19 additions & 2 deletions Kentor.AuthServices/IdentityProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,27 @@ public Uri MetadataUrl
/// successful authentication.</param>
/// <param name="authServicesUrls">Urls for AuthServices, used to populate fields
/// in the created AuthnRequest</param>
/// <returns></returns>
/// <returns>AuthnRequest</returns>
public Saml2AuthenticationRequest CreateAuthenticateRequest(
Uri returnUrl,
AuthServicesUrls authServicesUrls)
{
return CreateAuthenticateRequest(returnUrl, authServicesUrls, null);
}

/// <summary>
/// Create an authenticate request aimed for this idp.
/// </summary>
/// <param name="returnUrl">The return url where the browser should be sent after
/// successful authentication.</param>
/// <param name="authServicesUrls">Urls for AuthServices, used to populate fields
/// in the created AuthnRequest</param>
/// <param name="relayData">Aux data that should be preserved across the authentication</param>
/// <returns>AuthnRequest</returns>
public Saml2AuthenticationRequest CreateAuthenticateRequest(
Uri returnUrl,
AuthServicesUrls authServicesUrls,
object relayData)
{
if (authServicesUrls == null)
{
Expand All @@ -208,7 +225,7 @@ public Saml2AuthenticationRequest CreateAuthenticateRequest(
AttributeConsumingServiceIndex = spOptions.AttributeConsumingServices.Any() ? 0 : (int?)null
};

var responseData = new StoredRequestState(EntityId, returnUrl);
var responseData = new StoredRequestState(EntityId, returnUrl, relayData);

PendingAuthnRequests.Add(new Saml2Id(authnRequest.Id), responseData);

Expand Down
18 changes: 18 additions & 0 deletions Kentor.AuthServices/StoredRequestState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ public StoredRequestState(EntityId idp, Uri returnUrl)
ReturnUrl = returnUrl;
}

/// <summary>
/// Creates a PendingAuthnRequestData
/// </summary>
/// <param name="idp">The EntityId of the IDP the request was sent to</param>
/// <param name="returnUrl">The Url to redirect back to after a succesful login</param>
/// <param name="relayData">Aux data that can be stored across the authentication request.</param>
public StoredRequestState(EntityId idp, Uri returnUrl, object relayData)
{
Idp = idp;
ReturnUrl = returnUrl;
RelayData = relayData;
}

/// <summary>
/// The IDP the request was sent to
/// </summary>
Expand All @@ -33,5 +46,10 @@ public StoredRequestState(EntityId idp, Uri returnUrl)
/// The Url to redirect back to after a succesful login
/// </summary>
public Uri ReturnUrl { get; private set; }

/// <summary>
/// Aux data that need to be preserved across the authentication call.
/// </summary>
public object RelayData { get; private set; }
}
}
6 changes: 5 additions & 1 deletion Kentor.AuthServices/WebSSO/AcsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ private static CommandResult ProcessResponse(IOptions options, Saml2Response sam
samlResponse.RequestState != null && samlResponse.RequestState.ReturnUrl != null
? samlResponse.RequestState.ReturnUrl
: options.SPOptions.ReturnUrl,
Principal = principal
Principal = principal,
RelayData =
samlResponse.RequestState == null
? null
: samlResponse.RequestState.RelayData
};
}
}
Expand Down
6 changes: 6 additions & 0 deletions Kentor.AuthServices/WebSSO/CommandResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ public class CommandResult
/// </summary>
public string ContentType { get; set; }

/// <summary>
/// Data relayed from a previous request, such as the Owin Authenciation
/// Properties.
/// </summary>
public object RelayData { get; set; }

/// <summary>
/// Ctor
/// </summary>
Expand Down
5 changes: 3 additions & 2 deletions Kentor.AuthServices/WebSSO/SignInCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public static CommandResult CreateResult(
EntityId idpEntityId,
string returnPath,
HttpRequestData request,
IOptions options)
IOptions options,
object relayData = null)
{
var urls = new AuthServicesUrls(request, options.SPOptions);

Expand Down Expand Up @@ -65,7 +66,7 @@ public static CommandResult CreateResult(
Uri.TryCreate(request.Url, returnPath, out returnUrl);
}

var authnRequest = idp.CreateAuthenticateRequest(returnUrl, urls);
var authnRequest = idp.CreateAuthenticateRequest(returnUrl, urls, relayData);

return idp.Bind(authnRequest);
}
Expand Down

0 comments on commit 4d0de88

Please sign in to comment.