Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 87 additions & 2 deletions App_Start/ServicesConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@
using System.Net;
using System.Net.Http;
using MultiFactor.SelfService.Windows.Portal.Services.Ldap;
using MultiFactor.SelfService.Windows.Portal.Integrations.MultiFactorApi;
using MultiFactor.SelfService.Windows.Portal.Integrations.MultiFactorIdpApi;
using MultiFactor.SelfService.Windows.Portal.Options;
using MultiFactor.SelfService.Windows.Portal.Stories.Authenticate;
using MultiFactor.SelfService.Windows.Portal.Stories.ChangeExpiredPassword;
using MultiFactor.SelfService.Windows.Portal.Stories.ChangeValidPassword;
using MultiFactor.SelfService.Windows.Portal.Stories.CheckExpiredPasswordSession;
using MultiFactor.SelfService.Windows.Portal.Stories.LoadProfile;
using MultiFactor.SelfService.Windows.Portal.Stories.LoadProfileStory;
using MultiFactor.SelfService.Windows.Portal.Stories.RecoverPassword;
using MultiFactor.SelfService.Windows.Portal.Stories.SearchExchangeActiveSyncDevices;
using MultiFactor.SelfService.Windows.Portal.Stories.SignIn;
using MultiFactor.SelfService.Windows.Portal.Stories.SignOut;
using MultiFactor.SelfService.Windows.Portal.Authentication;
using MultiFactor.SelfService.Windows.Portal.Core.Authentication.AuthenticationClaims;
using MultiFactor.SelfService.Windows.Portal.Integrations.Ldap.PasswordChanging;
using MultiFactor.SelfService.Windows.Portal.Core.Caching;
using MultiFactor.SelfService.Windows.Portal.Integrations.Ldap.CredentialVerification;
using MultiFactor.SelfService.Windows.Portal.Stories.SignIn.ClaimsSources;
using MultiFactor.SelfService.Windows.Portal.Core.Authentication.AdditionalClaims.Description;
using MultiFactor.SelfService.Windows.Portal.Core.Metadata;
using MultiFactor.SelfService.Windows.Portal.Core.Authentication.AdditionalClaims.Description.Conditions;
using MultiFactor.SelfService.Windows.Portal.Core.Authentication.AdditionalClaims;

namespace MultiFactor.SelfService.Windows.Portal.App_Start
{
Expand All @@ -36,9 +59,12 @@ internal static void RegisterServices(ServiceCollection services)
{
services.AddSingleton<IJsonDataSerializer, NewtonsoftJsonDataSerializer>();
ConfigureHttpClients(services);
ConfigureApplicationSerivces(services);
ConfigureGoogleApi(services);
ConfigureYandexCaptchaApi(services);
ConfigureCaptchaVerifier(services);
ConfigureCloudConfiguration(services);
ConfigureStories(services);

services.AddScoped<JwtTokenProvider>();
services.AddSingleton<ApiClient>();
Expand All @@ -52,6 +78,11 @@ internal static void RegisterServices(ServiceCollection services)
services.AddSingleton<AuthService>();
services.AddApplicationCache();

services.AddTransient<IMultiFactorApi, MultiFactorApi>()
.AddTransient<MultifactorHttpClientAdapterFactory>();
services.AddTransient<IMultifactorIdpApi, MultifactorIdpApi>()
.AddTransient<MultifactorIdpHttpClientAdapterFactory>();

services.AddSingleton<ContentCache>();

services.AddSingleton<PasswordPolicyService>();
Expand Down Expand Up @@ -107,7 +138,10 @@ private static void ConfigureHttpClients(ServiceCollection services)
.ConfigurePrimaryHttpMessageHandler(() => CreateHttpClientHandler(proxy));

services
.AddHttpClient(Constants.HttpClients.MultifactorIdpApi)
.AddHttpClient(Constants.HttpClients.MultifactorIdpApi, client =>
{
client.BaseAddress = new Uri(Configuration.Current.MultiFactorIdpApiUrl);
})
.ConfigurePrimaryHttpMessageHandler(() => CreateHttpClientHandler(proxy));
}

Expand All @@ -131,7 +165,58 @@ private static HttpClientHandler CreateHttpClientHandler(WebProxy webProxy = nul
handler.Proxy = webProxy;
return handler;
}

private static void ConfigureApplicationSerivces(ServiceCollection services)
{
services
.AddSingleton<SafeHttpContextAccessor>()
.AddSingleton<HttpClientTokenProvider>()
.AddSingleton<TokenVerifier>()
.AddSingleton<TokenClaimsAccessor>()
.AddSingleton<DataProtection>()
.AddSingleton<ICredentialVerifier, CredentialVerifierAdapter>()
.AddSingleton<UserPasswordChanger>()
.AddSingleton<ForgottenPasswordChanger>();

services.AddSingleton<IApplicationCache, Core.Caching.ApplicationCache>();

services
.AddSingleton<ClaimsProvider>()
.AddSingleton<IClaimsSource, MultiFactorClaimsSource>()
.AddSingleton<IClaimsSource, SsoClaimsSource>()
.AddSingleton<IClaimsSource, AdditionalClaimsSource>();

services.AddSingleton<AdditionalClaimDescriptorsProvider>();
services.AddSingleton<AdditionalClaimsMetadata>();

services.AddSingleton<IApplicationValuesContext, ClaimValuesContext>();
services.AddSingleton<ApplicationGlobalValuesProvider>();
services.AddSingleton<ClaimConditionEvaluator>();
}
private static void ConfigureStories(ServiceCollection services)
{
services
.AddTransient<SignInStory>()
.AddTransient<IdentityStory>()
.AddTransient<RedirectToCredValidationAfter2FaStory>()
.AddTransient<AuthnStory>()
.AddTransient<SignOutStory>()
.AddTransient<LoadProfileStory>()
.AddTransient<LoadIdpProfileStory>()
.AddTransient<FilterShowcaseLinksStory>()
.AddTransient<RecoverPasswordStory>()
.AddTransient<AuthenticateSessionStory>()
.AddTransient<CheckExpiredPasswordSessionStory>()
.AddTransient<ChangeExpiredPasswordStory>()
.AddTransient<ChangeValidPasswordStory>()
.AddTransient<SearchExchangeActiveSyncDevicesStory>();
}

private static void ConfigureCloudConfiguration(ServiceCollection services)
{
services.AddSingleton<IShowcaseSettingsOptions, ShowcaseSettingsOptions>();
services.AddSingleton<ShowcaseSettingsUpdater>();
}

private static WebProxy BuildProxy(string proxyUri)
{
var uri = new Uri(proxyUri);
Expand Down
41 changes: 41 additions & 0 deletions Authentication/TokenClaims.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;

namespace MultiFactor.SelfService.Windows.Portal.Authentication
{
public class TokenClaims
{
public string Id { get; set; }
public string Identity { get; set; }
public string RawUserName { get; set; }
public bool MustChangePassword { get; set; }
public DateTime ValidTo { get; set; }
public bool MustResetPassword { get; set; }
public string SamlClaim { get; set; }
public string OidcClaim { get; set; }
public bool MustUnlockUser { get; set; }

public TokenClaims() { }

public TokenClaims(
string id,
string identity,
string rawUserName,
bool mustChangePassword,
DateTime validTo,
bool mustResetPassword,
string samlClaim,
string oidcClaim,
bool mustUnlockUser = false)
{
Id = id;
Identity = identity;
RawUserName = rawUserName;
MustChangePassword = mustChangePassword;
ValidTo = validTo;
MustResetPassword = mustResetPassword;
SamlClaim = samlClaim;
OidcClaim = oidcClaim;
MustUnlockUser = mustUnlockUser;
}
}
}
34 changes: 34 additions & 0 deletions Authentication/TokenClaimsAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using MultiFactor.SelfService.Windows.Portal.Core.Exceptions;
using MultiFactor.SelfService.Windows.Portal.Core.Http;

namespace MultiFactor.SelfService.Windows.Portal.Authentication
{
public class TokenClaimsAccessor
{
private readonly TokenVerifier _tokenVerifier;
private readonly SafeHttpContextAccessor _contextAccessor;

public TokenClaimsAccessor(TokenVerifier tokenVerifier, SafeHttpContextAccessor contextAccessor)
{
_tokenVerifier = tokenVerifier ?? throw new ArgumentNullException(nameof(tokenVerifier));
_contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}

public TokenClaims GetTokenClaims()
{
var token = ExtractBearerToken(_contextAccessor.HttpContext.Request.Headers["Authorization"]);
return _tokenVerifier.Verify(token);
}

private static string ExtractBearerToken(string headerValue)
{
if (headerValue is null) throw new UnauthorizedException("Empty token");

const string bearer = "Bearer";
if (!headerValue.StartsWith(bearer)) throw new UnauthorizedException("Invalid token");

return headerValue.Replace(bearer, "").Trim();
}
}
}
20 changes: 20 additions & 0 deletions Authentication/TokenVerifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using MultiFactor.SelfService.Windows.Portal.Services;

namespace MultiFactor.SelfService.Windows.Portal.Authentication
{
public class TokenVerifier
{
private readonly TokenValidationService _tokenValidationService;

public TokenVerifier(TokenValidationService tokenValidationService)
{
_tokenValidationService = tokenValidationService ?? throw new ArgumentNullException(nameof(tokenValidationService));
}

public TokenClaims Verify(string accessToken)
{
return _tokenValidationService.Verify(accessToken);
}
}
}
17 changes: 16 additions & 1 deletion Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ public bool IsPermittedDomain(string domain)
/// </summary>
public string MultiFactorApiSecret { get; private set; }

/// <summary>
/// Multifactor IDP API URL
/// </summary>
public string MultiFactorIdpApiUrl { get; private set; }

public bool PreAuthnMode { get; private set; }

/// <summary>
Expand Down Expand Up @@ -163,6 +168,9 @@ public bool IsPermittedDomain(string domain)

public PasswordRequirements PasswordRequirements { get; set; }

public string TokenValidation { get; private set; }
public string Environment { get; private set; }

public static void Load()
{
var appSettings = PortalSettings;
Expand All @@ -181,6 +189,7 @@ public static void Load()
var apiKeySetting = GetRequiredValue(appSettings, ConfigurationConstants.General.MULTIFACTOR_API_KEY);
var apiProxySetting = GetValue(appSettings, ConfigurationConstants.General.MULTIFACTOR_API_PROXY);
var apiSecretSetting = GetRequiredValue(appSettings, ConfigurationConstants.General.MULTIFACTOR_API_SECRET);
var idpApiUrlSetting = GetRequiredValue(appSettings, ConfigurationConstants.General.MULTIFACTOR_IDP_API_URL);
var logLevelSetting = GetRequiredValue(appSettings, ConfigurationConstants.General.LOGGING_LEVEL);
var preAuthnMode = ParseBoolean(appSettings, ConfigurationConstants.General.PRE_AUTHN_MODE);

Expand All @@ -198,6 +207,9 @@ public static void Load()
var activeDirectoryGroupSetting = GetValue(appSettings, ConfigurationConstants.General.ACTIVE_DIRECTORY_GROUP);
var nestedGroupsBaseDn = GetValue(appSettings, ConfigurationConstants.General.NESTED_GROUPS_BASE_DN);

var tokenValidation = GetValue(appSettings, ConfigurationConstants.General.TOKEN_VALIDATION);
var environment = GetValue(appSettings, ConfigurationConstants.General.ENVIRONMENT_KEY);

var useAttributeAsIdentitySetting = GetValue(appSettings, ConfigurationConstants.General.USE_ATTRIBUTE_AS_IDENTITY);
if (useUpnAsIdentitySetting && !string.IsNullOrWhiteSpace(useAttributeAsIdentitySetting))
{
Expand All @@ -214,6 +226,7 @@ public static void Load()
MultiFactorApiKey = apiKeySetting,
MultiFactorApiSecret = apiSecretSetting,
MultiFactorApiProxy = apiProxySetting,
MultiFactorIdpApiUrl = idpApiUrlSetting,
LogLevel = logLevelSetting,
EnableExchangeActiveSyncDevicesManagement = enableExchangeActiveSyncServicesManagementSetting,
EnablePasswordManagement = enablePasswordManagementSetting,
Expand All @@ -226,7 +239,9 @@ public static void Load()
PreAuthnMode = preAuthnMode,
LoadActiveDirectoryNestedGroups = loadActiveDirectoryNestedGroups,
PrivacyModeDescriptor = PrivacyModeDescriptor.Create(privacyMode),
PasswordRequirements = PasswordRequirementsSection.GetRequirements()
PasswordRequirements = PasswordRequirementsSection.GetRequirements(),
TokenValidation = tokenValidation,
Environment = environment
};

if (!string.IsNullOrEmpty(activeDirectory2FaGroupSetting))
Expand Down
36 changes: 36 additions & 0 deletions Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ public class Constants
public const string SESSION_EXPIRED_PASSWORD_USER_KEY = "multifactor:expired-password:user";
public const string SESSION_EXPIRED_PASSWORD_CIPHER_KEY = "multifactor:expired-password:cipher";
public const string PREAUTHENTICATION_AUTHN_SUCCEED_KEY = "multifactor:preauthentication-authn-succesd:user";
public const string TOKEN_VALIDATION = "TokenValidation:JsonWebKeySet";
public const string ENVIRONMENT_KEY = "Environment";
public const string PRODUCTION_ENV = "production";
public const string CAPTCHA_TOKEN = "responseToken";

public const string PWD_RECOVERY_COOKIE = "PSession";
public const string PWD_RENEWAL_PURPOSE = "PwdRenewal";

public const string CredentialVerificationResult = "CredentialVerificationResult";
public const string SsoClaims = "SsoClaims";
public const string LoadedLdapAttributes = "LoadedLdapAttributes";

public static readonly string WORKING_DIRECTORY = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);

Expand All @@ -28,6 +37,7 @@ public static class General
public const string MULTIFACTOR_API_KEY = "multifactor-api-key";
public const string MULTIFACTOR_API_PROXY = "multifactor-api-proxy";
public const string MULTIFACTOR_API_SECRET = "multifactor-api-secret";
public const string MULTIFACTOR_IDP_API_URL = "multifactor-idp-api-url";
public const string LOGGING_LEVEL = "logging-level";
public const string USE_ACTIVE_DIRECTORY_USER_PHONE = "use-active-directory-user-phone";
public const string USE_ACTIVE_DIRECTORY_MOBILE_USER_PHONE = "use-active-directory-mobile-user-phone";
Expand All @@ -43,11 +53,37 @@ public static class General
public const string ACTIVE_DIRECTORY_GROUP = "active-directory-group";
public const string NESTED_GROUPS_BASE_DN = "nested-groups-base-dn";
public const string USE_ATTRIBUTE_AS_IDENTITY = "use-attribute-as-identity";
public const string TOKEN_VALIDATION = "token-validation";
public const string ENVIRONMENT_KEY = "environment";
#if DEBUG
public const string ACT_AS = "act-as";
# endif
}

public static class MultiFactorClaims
{
public const string SamlSessionId = "samlSessionId";
public const string OidcSessionId = "oidcSessionId";
public const string AdditionSsoStep = "additionSsoStep";
public const string ChangePassword = "changePassword";
public const string PasswordExpirationDate = "passwordExpirationDate";
public const string ResetPassword = "resetPassword";
public const string RawUserName = "rawUserName";
public const string UnlockUser = "unlockUser";
public const string Name = "name";
}
public static class AuthenticationClaims
{
public const string AUTHENTICATION_METHODS_REFERENCES = "amr";
public const string PASSWORD_METHOD = "pwd";
public const string KERBEROS_METHOD = "kerberos";
}
public static class SsoMasterSessionTypes
{
public const string SamlSessionType = "saml";
public const string OidcSessionType = "oidc";
}

public static class ObsoleteCaptcha
{
public const string ENABLE_GOOGLE_RECAPTCHA = "enable-google-re-captcha";
Expand Down
7 changes: 7 additions & 0 deletions Content/images/ssoResource.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions Content/style/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -654,9 +654,17 @@ p {
scrollbar-color: #579ad7 #fff;
}

.showcase-link {
display: flex;
flex-direction: column;
align-items: center;
}

.showcase-link-image {
max-width: 4em;
max-height: 4em;
min-width: 4em;
min-height: 4em;
margin-bottom: 0.5em;
}

Expand Down
Loading