Last active
April 2, 2017 16:13
-
-
Save slaneyrw/921076168c27c6eabd4d9f62b76a0954 to your computer and use it in GitHub Desktop.
Handle context switch from MVC to IdentityServer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using IdentityModel; | |
using IdentityServer3.Core.Extensions; | |
using Microsoft.AspNet.Identity; | |
using Microsoft.Owin.Infrastructure; | |
using System; | |
using System.Collections.Generic; | |
using System.Collections.Specialized; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using System.Web; | |
using System.Web.Mvc; | |
namespace Identity.OpenIdServer.Controllers | |
{ | |
public class LoginController : Controller | |
{ | |
private static Type AuthorizeFormPostResultType = typeof(IdentityServer3.Core.Constants).Assembly.GetType("IdentityServer3.Core.Results.AuthorizeFormPostResult"); | |
private static Type AuthorizeRedirectResultType = typeof(IdentityServer3.Core.Constants).Assembly.GetType("IdentityServer3.Core.Results.AuthorizeRedirectResult"); | |
[HttpGet] | |
[AllowAnonymous] | |
[Route(Startup.RouteRootPath + "/Account/CompleteLoginProcess")] // Need to be "under" the same root path as IDSrvr | |
public async Task<ActionResult> CompleteLoginProcess(string id, bool? cancel) | |
{ | |
var owinContext = this.Request.GetOwinContext(); | |
if ( cancel.HasValue && cancel.Value) | |
{ | |
return await HandleCancelLoginProcess(owinContext, id); | |
} | |
return await HandleSignIn(owinContext); | |
} | |
private async Task<ActionResult> HandleCancelLoginProcess(Microsoft.Owin.IOwinContext owinContext, string id) | |
{ | |
var signInMsg = owinContext.Environment.GetSignInMessage(id); | |
var authParams = new Uri(signInMsg.ReturnUrl).Query.Split('&').ToDictionary( item => item.Split('=')[0], item => HttpUtility.UrlDecode( item.Split('=')[1])); | |
// clean up cookies | |
owinContext.Environment.RemovePartialLoginCookie(); | |
// TODO: Using internal IDSvr infrastructure to post a form back to the RA. Replace when capability becomes available | |
var response = new IdentityServer3.Core.Models.AuthorizeResponse() | |
{ | |
IsError = true, | |
RedirectUri = authParams["redirect_uri"], | |
State = authParams[IdentityServer3.Core.Constants.AuthorizeRequest.State], | |
Error = IdentityServer3.Core.Constants.AuthorizeErrors.AccessDenied, | |
ErrorDescription = "cancelled", | |
Request = new IdentityServer3.Core.Validation.ValidatedAuthorizeRequest | |
{ | |
Options = new IdentityServer3.Core.Configuration.IdentityServerOptions(), | |
ResponseMode = authParams[IdentityServer3.Core.Constants.AuthorizeRequest.ResponseMode] | |
} | |
}; | |
if (response.Request.ResponseMode == IdentityServer3.Core.Constants.ResponseModes.FormPost) | |
return await GenerateFormPostResult(owinContext, response); | |
return await GenerateRedirectResult(owinContext, response); | |
} | |
private async Task<ActionResult> GenerateFormPostResult(Microsoft.Owin.IOwinContext owinContext, IdentityServer3.Core.Models.AuthorizeResponse response) | |
{ | |
var requestMsg = new System.Net.Http.HttpRequestMessage(); | |
requestMsg.Properties.Add("MS_OwinEnvironment", owinContext.Environment); | |
var authFormPostResult = Activator.CreateInstance( | |
AuthorizeFormPostResultType, | |
response, | |
requestMsg | |
); | |
var task = authFormPostResult.GetType() | |
.GetMethod("ExecuteAsync") | |
.Invoke(authFormPostResult, new object[] { System.Threading.CancellationToken.None }) as Task<System.Net.Http.HttpResponseMessage>; | |
var httpResponseMessage = (await task.ConfigureAwait(false)) as System.Net.Http.HttpResponseMessage; | |
foreach (var header in httpResponseMessage.Content.Headers) | |
this.HttpContext.Response.Headers.Add(header.Key, header.Value.FirstOrDefault() ?? ""); | |
return new FileStreamResult(await httpResponseMessage.Content.ReadAsStreamAsync(), "text/html"); | |
} | |
private async Task<ActionResult> GenerateRedirectResult(Microsoft.Owin.IOwinContext owinContext, IdentityServer3.Core.Models.AuthorizeResponse response) | |
{ | |
var authRedirectResult = Activator.CreateInstance( | |
AuthorizeRedirectResultType, | |
response, | |
response.Request.Options | |
); | |
var task = authRedirectResult.GetType() | |
.GetMethod("ExecuteAsync") | |
.Invoke(authRedirectResult, new object[] { System.Threading.CancellationToken.None }) as Task<System.Net.Http.HttpResponseMessage>; | |
var httpResponseMessage = (await task.ConfigureAwait(false)) as System.Net.Http.HttpResponseMessage; | |
return new RedirectResult(httpResponseMessage.Headers.Location.ToString()); | |
} | |
private async Task<ActionResult> HandleSignIn(Microsoft.Owin.IOwinContext owinContext) | |
{ | |
var ticket = await owinContext.Authentication.AuthenticateAsync(Startup.CookieName); | |
if (ticket == null || ticket.Identity == null) | |
{ | |
// TODO Log ? | |
return Redirect(await owinContext.Environment.GetPartialLoginRestartUrlAsync()); | |
} | |
await owinContext.Environment.UpdatePartialLoginClaimsAsync(new System.Security.Claims.Claim[] | |
{ | |
// The following claims are required by IDSrvr to resume a login | |
new System.Security.Claims.Claim("sub", ticket.Identity.GetUserId()), | |
new System.Security.Claims.Claim("name", ticket.Identity.Name), | |
new System.Security.Claims.Claim("amr", "password"), // Authentication mechanism | |
new System.Security.Claims.Claim("idp", "idsvr"), | |
new System.Security.Claims.Claim("auth_time", DateTimeOffset.UtcNow.ToEpochTime().ToString(), "http://www.w3.org/2001/XMLSchema#integer") | |
}); | |
return Redirect(await owinContext.Environment.GetPartialLoginResumeUrlAsync()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment