Allows external logins to be listed on login page, updates BackOfficeController with actions for invoking them.

This commit is contained in:
Shannon
2015-02-09 12:14:59 +11:00
parent 927add6f44
commit d4b21243ca
11 changed files with 191 additions and 35 deletions

View File

@@ -323,12 +323,12 @@ namespace Umbraco.Core.Security
private static FormsAuthenticationTicket GetAuthTicket(this HttpContextBase http, string cookieName)
{
var allKeys = new List<string>();
var asDictionary = new Dictionary<string, string>();
for (var i = 0; i < http.Request.Cookies.Keys.Count; i++)
{
allKeys.Add(http.Request.Cookies.Keys.Get(i));
var key = http.Request.Cookies.Keys.Get(i);
asDictionary[key] = http.Request.Cookies[key].Value;
}
var asDictionary = allKeys.ToDictionary(key => key, key => http.Request.Cookies[key].Value);
//get the ticket
try

View File

@@ -63,8 +63,9 @@
<Reference Include="Microsoft.Owin">
<HintPath>..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security">
<HintPath>..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
<Reference Include="Microsoft.Owin.Security, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.Security.3.0.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security.Cookies">
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.2.1.0\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>

View File

@@ -13,7 +13,7 @@
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.28" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Cookies" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.OAuth" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net40" />

View File

@@ -16,7 +16,11 @@
}); // weekday[d.getDay()];
$scope.errorMsg = "";
$scope.externalLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLoginsUrl;
$scope.externalLogins = Umbraco.Sys.ServerVariables.externalLogins;
$scope.loginSubmit = function (login, password) {
//if the login and password are not empty we need to automatically

View File

@@ -1,4 +1,4 @@
<form method="POST" name="loginForm" ng-submit="loginSubmit(login, password)" ng-controller="Umbraco.Dialogs.LoginController">
<div ng-controller="Umbraco.Dialogs.LoginController">
<div id="login" class="umb-modalcolumn" ng-class="{'show-validation': loginForm.$invalid}">
<div class="form">
@@ -8,19 +8,37 @@
<localize key="login_instruction">Log in below</localize>
</p>
<div class="control-group" ng-class="{error: loginForm.username.$invalid}">
<input type="text" autofocus ng-model="login" name="username" class="input-xlarge" localize="placeholder" placeholder="@placeholders_username" />
</div>
<form method="POST" name="loginForm" ng-submit="loginSubmit(login, password)">
<div class="control-group" ng-class="{error: loginForm.username.$invalid}">
<input type="text" autofocus ng-model="login" name="username" class="input-xlarge" localize="placeholder" placeholder="@placeholders_username" />
</div>
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
<input type="password" ng-model="password" name="password" class="input-xlarge" localize="placeholder" placeholder="@placeholders_password" />
</div>
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
<input type="password" ng-model="password" name="password" class="input-xlarge" localize="placeholder" placeholder="@placeholders_password" />
</div>
<button type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>
<button type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>
<div class="control-group" ng-show="loginForm.$invalid">
<div class="alert alert-error">{{errorMsg}}</div>
</div>
</form>
<br/><br/>
<form method="POST" name="externalLoginForm" action="{{externalLoginFormAction}}">
<div class="alert alert-success" ng-repeat="login in externalLogins">
<button type="submit" class="btn btn-default" id="{{login.authType}}" name="provider" value="{{login.authType}}"
title="Log in using your {{login.caption}} account">
{{login.authType}}
</button>
</div>
</form>
<div class="control-group" ng-show="loginForm.$invalid">
<div class="alert alert-error">{{errorMsg}}</div>
</div>
</div>
</div>
</form>
</div>

View File

@@ -162,15 +162,20 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb">
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security">
<HintPath>..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
<HintPath>..\packages\Microsoft.Owin.Security.3.0.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Security.Cookies">
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.2.1.0\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security.Google">
<HintPath>..\packages\Microsoft.Owin.Security.Google.3.0.0\lib\net45\Microsoft.Owin.Security.Google.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security.OAuth">
<HintPath>..\packages\Microsoft.Owin.Security.OAuth.2.1.0\lib\net45\Microsoft.Owin.Security.OAuth.dll</HintPath>
</Reference>
@@ -202,6 +207,7 @@
</Reference>
<Reference Include="Owin">
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System">
<Name>System</Name>

View File

@@ -25,8 +25,9 @@
<package id="Microsoft.Net.Http" version="2.2.28" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Cookies" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Google" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.OAuth" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" />

View File

@@ -3,6 +3,9 @@
@using Umbraco.Core
@using ClientDependency.Core
@using ClientDependency.Core.Mvc
@using Microsoft.Owin.Security
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@using Umbraco.Core.IO
@using Umbraco.Web
@using Umbraco.Web.Editors
@@ -23,17 +26,17 @@
<meta name="robots" content="noindex, nofollow">
<title ng-bind="$root.locationTitle">Umbraco</title>
@{ Html.RequiresCss("assets/css/umbraco.css", "Umbraco");}
@{ Html.RequiresCss("tree/treeicons.css", "UmbracoClient");}
@Html.RenderCssHere(
new BasicPath("Umbraco", IOHelper.ResolveUrl(SystemDirectories.Umbraco)),
new BasicPath("UmbracoClient", IOHelper.ResolveUrl(SystemDirectories.UmbracoClient)))
</head>
<body ng-class="{touch:touchDevice,emptySection:emptySection}" ng-controller="Umbraco.MainController" id="umbracoMainPageBody">
<div ng-hide="!authenticated" ng-cloak id="mainwrapper" id="mainwrapper" class="clearfix" ng-click="closeDialogs($event)">
<div ng-hide="!authenticated" ng-cloak id="mainwrapper" id="mainwrapper" class="clearfix" ng-click="closeDialogs($event)">
<umb-navigation></umb-navigation>
@@ -47,18 +50,28 @@
<umb-notifications></umb-notifications>
@{
var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes()
.Select(p => new {authType = p.AuthenticationType, caption = p.Caption,
//TODO: Need to see if this exposes any sensitive data!
properties = p.Properties})
.ToArray();
}
@*
These are the bare minimal server variables that are required for the application to start without being authenticated,
we will load the rest of the server vars after the user is authenticated.
*@
*@
<script type="text/javascript">
var Umbraco = {};
Umbraco.Sys = {};
Umbraco.Sys.ServerVariables = {
"umbracoUrls": {
"umbracoUrls": {
"authenticationApiBaseUrl": "@(Url.GetUmbracoApiServiceBaseUrl<AuthenticationController>(controller => controller.PostLogin(null)))",
"serverVarsJs": '@Url.GetUrlWithCacheBust("ServerVariables", "BackOffice")'
"serverVarsJs": "@Url.GetUrlWithCacheBust("ServerVariables", "BackOffice")",
"externalLoginsUrl": "@(Url.Action("ExternalLogin", "BackOffice", new { area = ViewBag.UmbracoPath }))"
},
"externalLogins": @Html.Raw(JsonConvert.SerializeObject(loginProviders)),
"application": {
"applicationPath" : "@Context.Request.ApplicationPath"
}
@@ -68,7 +81,7 @@
@*And finally we can load in our angular app*@
<script type="text/javascript" src="lib/rgrove-lazyload/lazyload.js"></script>
<script type="text/javascript" src="@Url.GetUrlWithCacheBust("Application", "BackOffice")"></script>
@{
var isDebug = false;
if (Request.RawUrl.IndexOf('?') >= 0)
@@ -78,9 +91,9 @@
if (attempt && attempt.Result)
{
isDebug = true;
}
}
}
}
@if (isDebug)
{

View File

@@ -5,9 +5,13 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Web.UI;
using dotless.Core.Parser.Tree;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
@@ -27,6 +31,9 @@ using Umbraco.Web.PropertyEditors;
using Umbraco.Web.Models;
using Umbraco.Web.WebServices;
using Umbraco.Web.WebApi.Filters;
using System.Web;
using AutoMapper;
using Umbraco.Core.Security;
namespace Umbraco.Web.Editors
{
@@ -37,12 +44,18 @@ namespace Umbraco.Web.Editors
[DisableClientCache]
public class BackOfficeController : UmbracoController
{
protected IOwinContext OwinContext
{
get { return Request.GetOwinContext(); }
}
/// <summary>
/// Render the default view
/// </summary>
/// <returns></returns>
public ActionResult Default()
{
ViewBag.UmbracoPath = GlobalSettings.UmbracoMvcArea;
return View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml");
}
@@ -359,6 +372,62 @@ namespace Umbraco.Web.Editors
return JavaScript(ServerVariablesParser.Parse(result));
}
[HttpPost]
[AllowAnonymous]
public ActionResult ExternalLogin(string provider, string returnUrl = null)
{
if (returnUrl.IsNullOrWhiteSpace())
{
returnUrl = GlobalSettings.Path;
}
// Request a redirect to the external login provider
return new ChallengeResult(provider,
Url.Action("ExternalLoginCallback", "BackOffice", new
{
area = GlobalSettings.UmbracoMvcArea,
ReturnUrl = returnUrl
}));
}
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await OwinContext.Authentication.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
//go home, invalid callback
return RedirectToLocal(returnUrl);
}
//// Sign in the user with this external login provider if the user already has a login
//var user = await UserManager<>.FindAsync(loginInfo.Login);
//if (user != null)
//{
// await SignInAsync(user, isPersistent: false);
// return RedirectToLocal(returnUrl);
//}
//else
//{
// // If the user does not have an account, then prompt the user to create an account
// ViewBag.ReturnUrl = returnUrl;
// ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
// return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
//}
//TODO: until we make a user thingy and make this correctly , we'll just see if this works
var user = Security.GetBackOfficeUser(loginInfo.DefaultUserName);
var ticket = UmbracoContext.Security.PerformLogin(user);
HttpContext.AuthenticateCurrentRequest(ticket, false);
return View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml");
}
/// <summary>
/// Returns the server variables regarding the application state
/// </summary>
@@ -505,5 +574,43 @@ namespace Umbraco.Web.Editors
JsUrl
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return Redirect("/");
}
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri, string userId = null)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
private string LoginProvider { get; set; }
private string RedirectUri { get; set; }
private string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
//Ensure the forms auth module doesn't do a redirect!
context.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
}
}
}

View File

@@ -142,8 +142,12 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security">
<HintPath>..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
<Reference Include="Microsoft.Owin.Host.SystemWeb">
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.Security.3.0.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security.Cookies">
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.2.1.0\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>
@@ -167,7 +171,8 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Owin">
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="System">

View File

@@ -20,7 +20,8 @@
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.28" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Cookies" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.OAuth" version="2.1.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />