Skip to content

Instantly share code, notes, and snippets.

@slaneyrw
Last active April 2, 2017 16:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slaneyrw/921076168c27c6eabd4d9f62b76a0954 to your computer and use it in GitHub Desktop.
Save slaneyrw/921076168c27c6eabd4d9f62b76a0954 to your computer and use it in GitHub Desktop.
Handle context switch from MVC to IdentityServer
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