You can use external login providers such as ADFS for Active Directory (AD) or social such as Google or Facebook.
You can add links to a third-party service on the login page that can be used for logging in to Litium. If a user logging in with an external provider is not recognised in Litium, a new user account will be created based on the credentials used with the external provider.
To use external login providers on your site you need to do the following:
Install the packages of the third-party providers you want to use through the Nuget Package Manager Console. A full list of providers that Microsoft already built can be found on http://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity. Remember to select the project where the startup class is located before running the commands, otherwise you will not be able to register the provider.
Install-Package Microsoft.Owin.Security.Google
Install-Package Microsoft.Owin.Security.Facebook
Back to the top
2. Register and enable the external login proivders in the IOwinStartupConfiguration interface
In the following example Facebook and Google has been registered as external authentication providers. You can register Microsoft and other providers in the same way. The information that can be claimed from the external login providers is determined by what the users have allowed, and needs to be configured individually for each provider.
- OnAuthenticated: Callback method sent to the external login provider.
- Scope: The information claimed from the external login provider.
// Src\Litium.Accelerator.Mvc\App_Start\SsoSetup.cs
using Litium.Owin.Lifecycle;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security.Facebook;
using Microsoft.Owin.Security.Google;
using Owin;
using System.Configuration;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Litium.Accelerator.Mvc.App_Start
{
internal class SsoSetup : IOwinStartupConfiguration
{
public void Configuration(IAppBuilder app)
{
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable Google third party authentication providers
var googleClientId = ConfigurationManager.AppSettings["GoogleApplicationId"];
var googleSecretKey = ConfigurationManager.AppSettings["GoogleApplicationSecret"];
if (!string.IsNullOrEmpty(googleClientId) && !string.IsNullOrEmpty(googleSecretKey))
{
var google = new GoogleOAuth2AuthenticationOptions()
{
ClientId = googleClientId,
ClientSecret = googleSecretKey,
Provider = new GoogleOAuth2AuthenticationProvider
{
OnAuthenticated = context =>
{
context.Identity.AddClaim(new Claim("GoogleAccessToken", context.AccessToken));
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Name));
context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email));
return Task.CompletedTask;
}
}
};
google.Scope.Add("email");
google.Scope.Add("profile");
app.UseGoogleAuthentication(google);
}
// Enable Facebook third party authentication providers
var facebookAppId = ConfigurationManager.AppSettings["FacebookApplicationId"];
var facebookAppSecret = ConfigurationManager.AppSettings["FacebookApplicationSecret"];
if (!string.IsNullOrWhiteSpace(facebookAppId) && !string.IsNullOrWhiteSpace(facebookAppSecret))
{
var facebookAuthenticationOptions = new FacebookAuthenticationOptions
{
AppId = facebookAppId,
AppSecret = facebookAppSecret,
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = context =>
{
context.Identity.AddClaim(new Claim("FacebookAccessToken", context.AccessToken));
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Name));
context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email));
return Task.CompletedTask;
}
},
Scope = { "email", "public_profile" }
};
app.UseFacebookAuthentication(facebookAuthenticationOptions);
}
}
}
}
Back to the top
Create a controller for the third-party login providers and mark it as a login page.
// Src\Litium.Accelerator.Mvc\Controllers\Login\SsoLoginController.cs
using Litium.ComponentModel;
using Litium.Customers;
using Litium.FieldFramework;
using Litium.Security;
using Litium.Web.Security.Identity;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
namespace Litium.Accelerator.Mvc.Controllers.Login
{
public class SsoLoginController : ControllerBase
{
private readonly AuthenticationService _authenticationService;
private readonly PersonService _personService;
private readonly SecurityContextService _securityContextService;
private readonly FieldTemplateService _templateService;
public SsoLoginController(AuthenticationService authenticationService,
PersonService personService,
SecurityContextService securityContextService,
FieldTemplateService templateService)
{
_authenticationService = authenticationService;
_personService = personService;
_securityContextService = securityContextService;
_templateService = templateService;
}
[HttpGet]
public async Task<ActionResult> Index(string redirectUrl)
{
var owinContext = HttpContext.GetOwinContext();
var userManager = owinContext.Get<ApplicationUserManager>();
var authenticationManager = owinContext.Authentication;
var connectedServices = (User.Identity.IsAuthenticated
? (await userManager.GetLoginsAsync(new Guid(User.Identity.GetUserId())))
: new List<UserLoginInfo>()).Select(x => x.LoginProvider);
var providers = authenticationManager.GetExternalAuthenticationTypes()
.Where(t => !connectedServices.Contains(t.AuthenticationType))
.Select(t => new SsoProviderModel
{
ProviderName = t.AuthenticationType,
Url = Url.Action(nameof(RedirectToProvider), new { provider = t.AuthenticationType, redirectUrl })
}).ToList();
return View(model: new SsoLoginModel { Providers = providers });
}
[HttpGet]
[AllowAnonymous]
public ActionResult RedirectToProvider(string provider, string redirectUrl)
{
var owinContext = HttpContext.GetOwinContext();
var properties = new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(ProviderLogin), new { redirectUrl })
};
HttpContext.SkipAuthorization = true;
owinContext.Authentication.Challenge(properties, provider);
return Content("OK");
}
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult> ProviderLogin(string redirectUrl)
{
var url = redirectUrl.NullIfEmpty() ?? "/";
var owinContext = HttpContext.GetOwinContext();
var userManager = owinContext.Get<ApplicationUserManager>();
var authenticationManager = owinContext.Authentication;
var loginInfo = await authenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return Redirect(url);
}
var identityUser = userManager.Find(loginInfo.Login);
if (identityUser == null)
{
var externalIdentity = await authenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
Guid personSystemId;
if (User.Identity.IsAuthenticated)
{
personSystemId = new Guid(User.Identity.GetUserId());
}
else
{
var email = externalIdentity.FindFirstValue(ClaimTypes.Email);
var person = _personService.Get(email);
if (person == null)
{
var template = _templateService.Get<PersonFieldTemplate>(typeof(CustomerArea), "SystemUserTemplate");
person = new Person(template.SystemId);
var surename = externalIdentity.FindFirstValue(ClaimTypes.Surname);
var givenName = externalIdentity.FindFirstValue(ClaimTypes.GivenName);
if (string.IsNullOrEmpty(surename) && string.IsNullOrEmpty(givenName))
{
var displayName = externalIdentity.FindFirstValue("urn:facebook:name")
?? externalIdentity.FindFirstValue(ClaimTypes.Name);
var displayNamePart = displayName.Split(new[] { '.', ' ' });
if (displayNamePart.Length > 0)
{
givenName = displayNamePart.First();
surename = displayNamePart.Last();
}
}
person.FirstName = givenName;
person.LastName = surename;
person.Email = email;
person.Id = email;
person.LoginCredential.Username = email;
using (_securityContextService.ActAsSystem(extraInfo: "Create person from SSO"))
{
_personService.Create(person);
}
}
personSystemId = person.SystemId;
}
var result = await userManager.AddLoginAsync(personSystemId, loginInfo.Login);
if (!result.Succeeded)
{
return Redirect(url);
}
}
_authenticationService.ExternalSignIn(loginInfo.Login.LoginProvider, loginInfo.Login.ProviderKey);
return Redirect(url);
}
[HttpGet]
public RedirectResult Logout(string redirectUrl = "")
{
if (string.IsNullOrWhiteSpace(redirectUrl))
{
redirectUrl = "~/";
}
_authenticationService.SignOut();
return new RedirectResult(redirectUrl);
}
}
public class SsoLoginModel
{
public List<SsoProviderModel> Providers { get; set; }
}
public class SsoProviderModel
{
public string ProviderName { get; set; }
public string Url { get; set; }
}
}
Then display the links to the external login providers on the login page by adding the following code to the view:
@* Src\Litium.Accelerator.Mvc\Views\SsoLogin\Index.cshtml *@
@using Litium.Web.Mvc;
@using Litium.Studio.Extenssions;
@model Litium.Accelerator.Mvc.Controllers.Login.SsoLoginModel
<div class="full-height">
<div class="form__container">
@if (!User.Identity.IsAuthenticated)
{
foreach (var provider in Model.Providers)
{
<span><a href="@provider.Url" class="form__button form__button--expand">@provider.ProviderName</a></span>
}
}
else
{
<fieldset class="form">
<p>@(string.Format("login.alreadylogin".AsWebSiteString(), User.Identity.Name))</p>
@Html.ActionLink("login.logout".AsWebSiteString(), "Logout", "SsoLogin", null, new { @class = "button submit small" })
</fieldset>
}
</div>
</div>
Update LoginPageSignInUrlResolverDecorator to skip authorization for external login:
// File: Src\Litium.Accelerator.Mvc\Routing\LoginPageSignInUrlResolverDecorator.cs
public bool TryGet(RouteRequestLookupInfo routeRequestLookupInfo, out string redirectUrl)
{
if (HttpContext.Current.SkipAuthorization)
{
redirectUrl = null;
return false;
}
// ... the rest of the method
Back to the top
In general you need to set a re-direct URI and get the client ID and client secret from the external provider. Refer to the documentation of the login provider you will use, for example the following:
- Facebook
- Google
- Microsoft
Back to the top
Change the login page template in back office so that it uses the new controller.
- Click Settings > Websites > Field templates.
- Select your Login page in the list.
- Select the new controller in the Controller drop-down list, Index as the Action and click Save.
Back to the top
- Build the site and go to the start page of the accelerator.
- Click Log in. Verify that the new login page with the external provider is displayed.
- Click the name of the external provider. You will be re-directed to the provider's login page. If you are already logged in on the third-party service you will be immediately re-directed back to the Litium site. If you are not logged in, you will be asked to log in, and then you will be re-directed back to the Litium site.
Troubleshooting
- If the old login page is displayed, try clearing your cache.
- If the external providers are not displayed on the login page, make sure that you have setup the IOwinStartupConfiguration interface correctly.
Back to the top