Skip to content

Commit

Permalink
VCST-1353: Add validators and custom calim providers for token reques…
Browse files Browse the repository at this point in the history
…ts (#2810)
  • Loading branch information
artem-dudarev committed Jul 3, 2024
1 parent a185c69 commit 73f01ae
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 147 deletions.
3 changes: 2 additions & 1 deletion VirtoCommerce.Platform.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=auditable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=hangfire/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=openIddict/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=postgre/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=prerelease/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=virto/@EntryIndexedValue">True</s:Boolean>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Security.Claims;
using OpenIddict.Abstractions;

Expand All @@ -14,5 +15,20 @@ public static bool IsSsoAuthenticationMethod(this ClaimsPrincipal claimsPrincipa
{
return claimsPrincipal?.GetClaim(ClaimTypes.AuthenticationMethod) != null;
}

public static ClaimsPrincipal SetClaimWithDestinations(this ClaimsPrincipal claimsPrincipal, string type, string value, IList<string> destinations)
{
claimsPrincipal.SetClaim(type, value);

foreach (var claim in claimsPrincipal.Claims)
{
if (claim.Type == type && claim.Value == value)
{
claim.SetDestinations(destinations);
}
}

return claimsPrincipal;
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace VirtoCommerce.Platform.Security.OpenIddict
{
public class BaseUserSignInValidator : ITokenRequestValidator
{
public int Priority { get; set; }

public Task<IList<TokenResponse>> ValidateAsync(TokenRequestContext context)
{
IList<TokenResponse> result = [];

if (context.SignInResult != null && !context.SignInResult.Succeeded)
{
TokenResponse error;

if (context.DetailedErrors && context.SignInResult.IsLockedOut)
{
var permanentLockOut = context.User.LockoutEnd == DateTime.MaxValue.ToUniversalTime();
error = permanentLockOut
? SecurityErrorDescriber.UserIsLockedOut()
: SecurityErrorDescriber.UserIsTemporaryLockedOut();
}
else
{
error = SecurityErrorDescriber.LoginFailed();
}

result.Add(error);
}

return Task.FromResult(result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Security.Claims;
using System.Threading.Tasks;

namespace VirtoCommerce.Platform.Security.OpenIddict;

public interface ITokenClaimProvider
{
Task SetClaimsAsync(ClaimsPrincipal principal, TokenRequestContext context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;

namespace VirtoCommerce.Platform.Security.OpenIddict
{
public interface ITokenRequestValidator
{
public int Priority { get; set; }

Task<IList<TokenResponse>> ValidateAsync(TokenRequestContext context);
}
}
Original file line number Diff line number Diff line change
@@ -1,68 +1,67 @@
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Platform.Security.Model;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace VirtoCommerce.Platform.Security
namespace VirtoCommerce.Platform.Security.OpenIddict
{
public static class SecurityErrorDescriber
{
public static TokenLoginResponse LoginFailed() => new()
public static TokenResponse LoginFailed() => new()
{
Error = Errors.InvalidGrant,
Code = nameof(LoginFailed).ToSnakeCase(),
ErrorDescription = "Login attempt failed. Please check your credentials."
};

public static TokenLoginResponse UserIsLockedOut() => new()
public static TokenResponse UserIsLockedOut() => new()
{
Error = Errors.InvalidGrant,
Code = nameof(UserIsLockedOut).ToSnakeCase(),
ErrorDescription = "Your account has been locked. Please contact support for assistance."
};

public static TokenLoginResponse UserIsTemporaryLockedOut() => new()
public static TokenResponse UserIsTemporaryLockedOut() => new()
{
Error = Errors.InvalidGrant,
Code = nameof(UserIsLockedOut).ToSnakeCase(),
ErrorDescription = "Your account has been temporarily locked. Please try again after some time."
};

public static TokenLoginResponse PasswordExpired() => new()
public static TokenResponse PasswordExpired() => new()
{
Error = Errors.InvalidGrant,
Code = nameof(PasswordExpired).ToSnakeCase(),
ErrorDescription = "Your password has been expired and must be changed.",
};

public static TokenLoginResponse PasswordLoginDisabled() => new()
public static TokenResponse PasswordLoginDisabled() => new()
{
Error = Errors.InvalidGrant,
Code = nameof(PasswordLoginDisabled).ToSnakeCase(),
ErrorDescription = "The username/password login is disabled."
};

public static TokenLoginResponse TokenInvalid() => new()
public static TokenResponse TokenInvalid() => new()
{
Error = Errors.InvalidGrant,
Code = nameof(TokenInvalid).ToSnakeCase(),
ErrorDescription = "The token is no longer valid."
};

public static TokenLoginResponse SignInNotAllowed() => new()
public static TokenResponse SignInNotAllowed() => new()
{
Error = Errors.InvalidGrant,
Code = nameof(SignInNotAllowed).ToSnakeCase(),
ErrorDescription = "The user is no longer allowed to sign in."
};

public static TokenLoginResponse InvalidClient() => new()
public static TokenResponse InvalidClient() => new()
{
Error = Errors.InvalidClient,
Code = nameof(InvalidClient).ToSnakeCase(),
ErrorDescription = "The client application was not found in the database."
};

public static TokenLoginResponse UnsupportedGrantType() => new()
public static TokenResponse UnsupportedGrantType() => new()
{
Error = Errors.UnsupportedGrantType,
Code = nameof(UnsupportedGrantType).ToSnakeCase(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using OpenIddict.Abstractions;
using VirtoCommerce.Platform.Core.Security;

namespace VirtoCommerce.Platform.Security.OpenIddict
{
public class TokenRequestContext
{
public string AuthenticationScheme { get; set; }

public OpenIddictRequest Request { get; set; }

public SignInResult SignInResult { get; set; }

public ApplicationUser User { get; set; }

public ClaimsPrincipal Principal { get; set; }

public AuthenticationProperties Properties { get; set; }

public bool DetailedErrors { get; set; }

public IDictionary<string, object> AdditionalParameters { get; set; } = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
using Microsoft.AspNetCore.Identity;
using OpenIddict.Abstractions;

namespace VirtoCommerce.Platform.Security.Model
namespace VirtoCommerce.Platform.Security.OpenIddict
{
public class TokenLoginResponse : OpenIddictResponse
public class TokenResponse : OpenIddictResponse
{
public string UserId { get; set; }

Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit 73f01ae

Please sign in to comment.