Added basic MSAL UI to the sample, added SPA sample
This commit is contained in:
parent
3828f2d7c4
commit
293976688a
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityOIDCWebApplicationSample.Identity.Models;
|
||||
|
|
@ -9,8 +10,10 @@ using Microsoft.AspNetCore.Authorization;
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.Service;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace IdentityOIDCWebApplicationSample.Identity.Controllers
|
||||
{
|
||||
|
|
@ -41,6 +44,7 @@ namespace IdentityOIDCWebApplicationSample.Identity.Controllers
|
|||
}
|
||||
|
||||
[HttpGet]
|
||||
[MsalFilter]
|
||||
public async Task<IActionResult> Login(string returnUrl = null)
|
||||
{
|
||||
// Clear the existing external cookie to ensure a clean login process
|
||||
|
|
@ -50,6 +54,25 @@ namespace IdentityOIDCWebApplicationSample.Identity.Controllers
|
|||
return View();
|
||||
}
|
||||
|
||||
[AttributeUsage(System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
|
||||
sealed class MsalFilterAttribute : Attribute, IResultFilter
|
||||
{
|
||||
public void OnResultExecuted(ResultExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
if(context.HttpContext.Request.Headers.TryGetValue(HeaderNames.UserAgent, out var header) &&
|
||||
header.Equals("Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Tablet PC 2.0)") &&
|
||||
context.Result is ViewResult view &&
|
||||
view.ViewName == null)
|
||||
{
|
||||
view.ViewName = "Login.MSAL";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,169 @@
|
|||
@using System.Collections.Generic
|
||||
@using System.Linq
|
||||
@using Microsoft.AspNetCore.Http
|
||||
@using Microsoft.AspNetCore.Authentication
|
||||
@using Microsoft.AspNetCore.Authentication.OpenIdConnect
|
||||
@model LoginViewModel
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Log in";
|
||||
Layout = null;
|
||||
}
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>@ViewData["Title"]</title>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 300;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.center {
|
||||
margin: 0px auto;
|
||||
width: 60%;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
label{
|
||||
margin-bottom: .8em;
|
||||
}
|
||||
|
||||
input[type=text], input[type=password] {
|
||||
border: 1px solid #CCC;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-title{
|
||||
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="center">
|
||||
<h1 class="login-title">@ViewData["Title"].</h1>
|
||||
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="login-form">
|
||||
<h2 class="login-option">Use a local account to log in.</h2>
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<p>
|
||||
<span class="form-label">@Html.DisplayNameFor(u => u.Email)</span>
|
||||
<input asp-for="Email" class="form-input" />
|
||||
<span asp-validation-for="Email" class="form-validation"></span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="form-label">@Html.DisplayNameFor(u => u.Password)</span>
|
||||
<input asp-for="Password" class="form-input" />
|
||||
<span asp-validation-for="Password" class="form-validation"></span>
|
||||
</p>
|
||||
<p>
|
||||
<input asp-for="RememberMe" class="form-input" />
|
||||
<span class="form-label">@Html.DisplayNameFor(u => u.RememberMe)</span>
|
||||
</p>
|
||||
<button type="submit" class="btn btn-default">Log in</button>
|
||||
<p>
|
||||
<a asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]">Register as a new user?</a>
|
||||
</p>
|
||||
<p>
|
||||
<a asp-action="ForgotPassword">Forgot your password?</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@*<div class="row">
|
||||
<div class="col-md-8">
|
||||
<section>
|
||||
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
|
||||
<h4>Use a local account to log in.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Password" class="form-control" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<div class="checkbox">
|
||||
<label asp-for="RememberMe">
|
||||
<input asp-for="RememberMe" />
|
||||
@Html.DisplayNameFor(m => m.RememberMe)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button type="submit" class="btn btn-default">Log in</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<a asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]">Register as a new user?</a>
|
||||
</p>
|
||||
<p>
|
||||
<a asp-action="ForgotPassword">Forgot your password?</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<section>
|
||||
<h4>Use another service to log in.</h4>
|
||||
<hr />
|
||||
@{
|
||||
var schemes = await SignInManager.GetExternalAuthenticationSchemesAsync();
|
||||
var loginProviders = schemes.Where(scheme => scheme.DisplayName != OpenIdConnectDefaults.AuthenticationScheme).ToList();
|
||||
if (loginProviders.Count == 0)
|
||||
{
|
||||
<div>
|
||||
<p>
|
||||
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
|
||||
for details on setting up this ASP.NET application to support logging in via external services.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
|
||||
<div>
|
||||
<p>
|
||||
@foreach (var provider in loginProviders)
|
||||
{
|
||||
<button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.Name</button>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
</div>
|
||||
</div>*@
|
||||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using IdentityOIDCWebApplicationSample.Models;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace IdentityOIDCWebApplicationSample.Controllers
|
||||
{
|
||||
|
|
@ -24,9 +25,7 @@ namespace IdentityOIDCWebApplicationSample.Controllers
|
|||
|
||||
public IActionResult Contact()
|
||||
{
|
||||
ViewData["Message"] = "Your contact page.";
|
||||
|
||||
return View();
|
||||
return Ok(new { Message = $"API Called with token {HttpContext.Request.Headers[HeaderNames.Authorization]}" });
|
||||
}
|
||||
|
||||
public IActionResult Error()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Calling a Web API as a user authenticated with Msal.js app</title>
|
||||
<style>
|
||||
.hidden {
|
||||
visibility: hidden
|
||||
}
|
||||
|
||||
.visible {
|
||||
visibility: visible
|
||||
}
|
||||
|
||||
.response {
|
||||
border: solid;
|
||||
border-width: thin;
|
||||
background-color: azure;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- bluebird only needed if this page needs to run on Internet Explorer -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js" class="pre"></script>
|
||||
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/0.1.1/js/msal.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js" class="pre"></script>
|
||||
|
||||
<h2>Getting an access token with Azure AD B2C and calling a Web API</h2>
|
||||
<div>
|
||||
<div id="label">Sign-in with Microsoft Azure AD B2C</div>
|
||||
<button id="auth" onclick="login()">Login</button>
|
||||
<button id="callApiButton" class="hidden" onclick="callApi()">Call Web API</button>
|
||||
</div>
|
||||
|
||||
<pre class="response"></pre>
|
||||
|
||||
<script class="pre">
|
||||
// The current application coordinates were pre-registered in a B2C tenant.
|
||||
var applicationConfig = {
|
||||
clientID: '4c0e3ab6-3bdc-4eca-80ab-89669d974e13',
|
||||
authority: "https://localhost:44324/tfp/IdentityService/signinsignup",
|
||||
b2cScopes: ["https://localhost/DFC7191F-FF74-42B9-A292-08FEA80F5B20/v2.0/spa/read"],
|
||||
webApi: 'https://localhost:44324/Home/Contact',
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
var clientApplication = new Msal.UserAgentApplication(applicationConfig.clientID, applicationConfig.authority, function (errorDesc, token, error, tokenType) {
|
||||
// Called after loginRedirect or acquireTokenPopup
|
||||
});
|
||||
|
||||
function login() {
|
||||
clientApplication.loginPopup(applicationConfig.b2cScopes).then(function (idToken) {
|
||||
clientApplication.acquireTokenSilent(applicationConfig.b2cScopes).then(function (accessToken) {
|
||||
updateUI();
|
||||
}, function (error) {
|
||||
clientApplication.acquireTokenPopup(applicationConfig.b2cScopes).then(function (accessToken) {
|
||||
updateUI();
|
||||
}, function (error) {
|
||||
logMessage("Error acquiring the popup:\n" + error);
|
||||
});
|
||||
})
|
||||
}, function (error) {
|
||||
logMessage("Error during login:\n" + error);
|
||||
});
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
var userName = clientApplication.getUser().name;
|
||||
logMessage("User '" + userName + "' logged-in");
|
||||
var authButton = document.getElementById('auth');
|
||||
authButton.innerHTML = 'logout';
|
||||
authButton.setAttribute('onclick', 'logout();');
|
||||
var label = document.getElementById('label');
|
||||
label.innerText = "Hello " + userName;
|
||||
var callWebApiButton = document.getElementById('callApiButton');
|
||||
callWebApiButton.setAttribute('class', 'visible');
|
||||
}
|
||||
|
||||
function callApi() {
|
||||
clientApplication.acquireTokenSilent(applicationConfig.b2cScopes).then(function (accessToken) {
|
||||
callApiWithAccessToken(accessToken);
|
||||
}, function (error) {
|
||||
clientApplication.acquireTokenPopup(applicationConfig.b2cScopes).then(function (accessToken) {
|
||||
callApiWithAccessToken(accessToken);
|
||||
}, function (error) {
|
||||
logMessage("Error acquiring the access token to call the Web api:\n" + error);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function callApiWithAccessToken(accessToken) {
|
||||
// Call the Web API with the AccessToken
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: applicationConfig.webApi,
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + accessToken,
|
||||
},
|
||||
}).done(function (data) {
|
||||
logMessage("Web APi returned:\n" + JSON.stringify(data));
|
||||
})
|
||||
.fail(function (jqXHR, textStatus) {
|
||||
logMessage("Error calling the Web api:\n" + textStatus);
|
||||
})
|
||||
}
|
||||
|
||||
function logout() {
|
||||
// Removes all sessions, need to call AAD endpoint to do full logout
|
||||
clientApplication.logout();
|
||||
}
|
||||
|
||||
function logMessage(s) {
|
||||
document.body.querySelector('.response').appendChild(document.createTextNode('\n' + s));
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue