diff --git a/Directory.Build.props b/Directory.Build.props index 60875f4..a9a9d73 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,9 +6,8 @@ - $(MSBuildThisFileDirectory).artifacts\$(MSBuildProjectName) - $(BaseProjectArtifactPath)\bin\ - $(BaseProjectArtifactPath)\obj\ + true + $(MSBuildThisFileDirectory)_artifacts diff --git a/Directory.Packages.props b/Directory.Packages.props index e2dc5b0..c13396d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,10 +5,10 @@ + - diff --git a/src/WebApp/Program.cs b/src/WebApp/Program.cs index 8d92004..19a2def 100644 --- a/src/WebApp/Program.cs +++ b/src/WebApp/Program.cs @@ -1,7 +1,8 @@ using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; -using Microsoft.Identity.Web; -using Microsoft.Identity.Web.UI; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.Tokens; using ServerCheckupLibrary.Checks; using ServerCheckupLibrary.Hubs; using WebApp.Platform; @@ -32,10 +33,29 @@ } else { - // When running on the server, require an Azure AD login account (configured in the app settings file). + // When running on the server, require an OIDC login provider (configured in the appsettings file). builder.Services - .AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApp(builder.Configuration); + .AddAuthentication(options => + { + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddOpenIdConnect(configureOptions: options => + { + var configSection = builder.Configuration.GetSection("OIDC"); + + options.Authority = configSection["Authority"]; + options.ClientId = configSection["ClientId"]; + options.ClientSecret = configSection["ClientSecret"]; + options.CallbackPath = configSection["CallbackPath"]; + + options.Scope.Add("email"); + options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.ResponseType = OpenIdConnectResponseType.Code; + options.MapInboundClaims = false; + options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "email" }; + }); } builder.Services.AddAuthorization(); @@ -47,7 +67,7 @@ if (!isDevelopment) builder.Services.AddHsts(opts => opts.MaxAge = TimeSpan.FromDays(730)); // Configure the UI. -builder.Services.AddRazorPages().AddMicrosoftIdentityUI(); +builder.Services.AddRazorPages(); builder.Services.AddWebOptimizer(minifyJavaScript: !isDevelopment); // Add HttpClient (used by CheckExternalService) diff --git a/src/WebApp/WebApp.csproj b/src/WebApp/WebApp.csproj index 589f63e..8f7c0b4 100644 --- a/src/WebApp/WebApp.csproj +++ b/src/WebApp/WebApp.csproj @@ -6,7 +6,7 @@ - + @@ -15,10 +15,6 @@ - - appsettings.json - Never - Never Never diff --git a/src/WebApp/appsettings-example.json b/src/WebApp/appsettings-example.json index b40006a..a3c51d8 100644 --- a/src/WebApp/appsettings-example.json +++ b/src/WebApp/appsettings-example.json @@ -58,6 +58,12 @@ "CheckDotnetVersionOptions": { "Enabled": true }, + "OIDC": { + "CallbackPath": "/signin-oidc-duo", + "Authority ": "[Enter the Duo application endpoint: https://{duo-subdomain}.sso.duosecurity.com/oidc/{ClientId}]", + "ClientId": "[Enter the Duo Client ID]", + "ClientSecret": "[Enter the Duo Client Secret]" + }, "AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "qualified.domain.name", diff --git a/src/WebApp/appsettings.json b/src/WebApp/appsettings.json index 076de84..6874e56 100644 --- a/src/WebApp/appsettings.json +++ b/src/WebApp/appsettings.json @@ -19,6 +19,12 @@ "CheckDotnetVersionOptions": { "Enabled": true }, + "OIDC": { + "CallbackPath": "/signin-oidc-duo", + "Authority ": "", + "ClientId": "", + "ClientSecret": "" + }, "AzureAd": { "Instance": "", "Domain": "", diff --git a/src/WebApp/wwwroot/js/site.js b/src/WebApp/wwwroot/js/site.js index 896e5c5..df50129 100644 --- a/src/WebApp/wwwroot/js/site.js +++ b/src/WebApp/wwwroot/js/site.js @@ -68,31 +68,34 @@ function getContextClass(context) { function prepCheck(checkSectionId, endPoint) { const checkEl = document.querySelector(`#${checkSectionId}`); const buttonEl = checkEl.querySelector('button'); - const resultsEl = checkEl.querySelector('.check-results'); - - buttonEl.addEventListener('click', function () { - resultsEl.innerHTML = ''; - const messageListEl = document.createElement('ul'); - messageListEl.className = 'list-group'; - resultsEl.appendChild(messageListEl); - - setAsLoading(buttonEl); - fetch(endPoint) - .then(response => response.text()) - .then(result => { - resetAsLoading(buttonEl); - resultsEl.innerHTML = result; - }) - .catch(error => { - resetAsLoading(buttonEl); - handleCheckResult(checkSectionId, 'An error occurred', error, 'Error'); - if (error instanceof Error) { - rg4js('send', {error: error, tags: ['handled_promise_rejection']}); - } else { - console.error(error); - } - }); - }); + + if (buttonEl) { + const resultsEl = checkEl.querySelector('.check-results'); + + buttonEl.addEventListener('click', function () { + resultsEl.innerHTML = ''; + const messageListEl = document.createElement('ul'); + messageListEl.className = 'list-group'; + resultsEl.appendChild(messageListEl); + + setAsLoading(buttonEl); + fetch(endPoint) + .then(response => response.text()) + .then(result => { + resetAsLoading(buttonEl); + resultsEl.innerHTML = result; + }) + .catch(error => { + resetAsLoading(buttonEl); + handleCheckResult(checkSectionId, 'An error occurred', error, 'Error'); + if (error instanceof Error) { + rg4js('send', {error: error, tags: ['handled_promise_rejection']}); + } else { + console.error(error); + } + }); + }); + } } function setAsLoading(btnEl) {