AspNetFormsAuth is a plugin package for Rhetos development platform. It provides an implementation of ASP.NET Core Identity authentication to Rhetos applications, with recommended security best practices such as password salting and hashing.
This is a backward-compatibility plugin for Rhetos applications that are migrated from ASP.NET to ASP.NET Core on .NET 5 (Rhetos v5). It provides the same authentication web API and database storage of users. Since Rhetos v5, there is technically no need for such plugins: Authentication is managed by ASP.NET Core, so Rhetos does not need to handle user authentication.
For documentation on older versions of this plugin, see previous release branch Readme.md.
Table of contents:
- Features
- Authentication service API
- Installation and configuration
- Configuration
- Sharing the authentication across web applications
- Session timeout
- Implementing SendPasswordResetToken
- Troubleshooting
- How to contribute
- Authentication service may be used in web applications and other services to login and logout users, and for other related actions.
- Forms authentication may be utilized for sharing the authentication across multiple web applications.
- To create a new user, insert the record in the
Common.Principal
entity. - To configure the user's permissions, enter the data in
Common.PrincipalHasRole
orCommon.PrincipalPermission
. - To set the user's password, the administrator may use
SetPassword
orGeneratePasswordResetToken
web service methods (see below). The user will later useResetPassword
with the password reset token, orChangeMyPassword
when logged-in.
There are two recommended ways of implementing forgot password functionality with AspNetFormsAuth:
-
Option 1: An administrator (or a web application with administrator privileges) may call
GeneratePasswordResetToken
web method to get the user's password reset token. The administrator or the web application should then send the token to the user on its own. -
Option 2: An end user that is not logged-in (or a web application with no special privileges) may call
SendPasswordResetToken
web method. The Rhetos sever will generate the password reset token and send it to the user. In order to use this method, an implementation of sending the token (by SMS or email, e.g.) should be provided by an additional plugin (see Implementing SendPasswordResetToken).
The JSON service is available at URI <base URL>/Resources/AspNetFormsAuth/Authentication
, with the following methods.
- Interface:
(string UserName, string Password, bool PersistCookie) -> bool
- Example of the request data:
{"UserName":"myusername","Password":"mypassword","PersistCookie":false}
. - The method does not require user authentication.
- On successful log in, the server response will contain the standard authentication cookie. The client browser will automatically use the cookie for following requests.
- Response data is boolean true if the login is successful, false if the login and password does not match, or an error message (string) with HTTP error code 4* or 5* in case of any other error.
- No request data is needed, assuming standard authentication cookie is automatically provided. Response is empty.
Sets or resets the given user's password.
- Interface:
(string UserName, string Password, bool IgnorePasswordStrengthPolicy) -> void
- Requires
SetPassword
security claim. If IgnorePasswordStrengthPolicy property is set,IgnorePasswordStrengthPolicy
security claim is required. - Response data is empty if the command is successful, an error message (string) with HTTP error code 400 if the password does not match the password strength policy, or an error message with HTTP error code 4* or 5* in case of any other error.
Changes the current user's password.
- Interface:
(string OldPassword, string NewPassword) -> bool
- Response data is boolean true if the login is successful, false if the login and password does not match, an error message (string) with HTTP error code 400 if the password does not match the password strength policy, or an error message with HTTP error code 4* or 5* in case of any other error.
Reset the number of failed login attempts.
- Interface:
(string UserName) -> void
- Response is empty.
- Requires
UnlockUser
security claim.
Generates a password reset token.
- Interface:
(string UserName) -> string
- This method is typically called by an administrator or a web application with administrator privileges in order to create a user account without initial password and let a user choose it, or to implement forgot-password functionality.
- To implement forgot-password functionality without using administrator privileges in web application,
use
SendPasswordResetToken
method instead (see Forgot password). - Requires
GeneratePasswordResetToken
security claim. - To configure the token expiration time, set the DataProtectionTokenProviderOptions.TokenLifespan property inside the Startup.ConfigureServices method of your web application.
Generates a password reset token and sends it to the user.
- Interface:
(string UserName, Dictionary<string, string> AdditionalClientInfo) -> void
- When using this method there is no need to directly call
GeneratePasswordResetToken
method (see Forgot password). - The method does not require user authentication.
- NOTE: AspNetFormsAuth package does not contain any implementation of sending the token (by SMS or email, e.g.).
The implementation must be provided by an additional plugin. For example:
- Use the SimpleSPRTEmail plugin package for sending token by email,
- or follow Implementing SendPasswordResetToken to implement a different sending method.
- To configure the token expiration time, set the DataProtectionTokenProviderOptions.TokenLifespan property inside the Startup.ConfigureServices method of your web application.
Allows a user to set the initial password or reset the forgotten password, using the token he received previously.
- Interface:
(string userName, string PasswordResetToken, string NewPassword) -> bool
- See
GeneratePasswordResetToken
method for PasswordResetToken. - The method does not require user authentication.
- Response data is boolean true if the password change is successful, false if the token is invalid or expired, or an error message (string) with HTTP error code 4* or 5* in case of any other error.
-
Add "Rhetos.AspNetFormsAuth" NuGet package, available at the NuGet.org on-line gallery.
-
Modify lines in
Startup.cs
, methodConfigureServices
to:services.AddRhetos(ConfigureRhetosHostBuilder) .AddAspNetFormsAuth();
-
Add the following lines to
Startup.cs
, methodConfigure
. Check if some are already there to avoid duplicates. The ordering is important.app.UseRhetosAspNetFormsAuth(); app.UseAuthentication(); app.UseAuthorization();
-
Make sure that you don't have this lines in
Startup.cs
, methodConfigure
:services.AddAuthentication(...
Additional remarks:
-
If you want to show authentication APIs in Swagger, add this line in
Startup.cs
, methodConfigure
:app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/rhetos/swagger.json", "Rhetos REST API"); });
If you need to host a Rhetos web app with forms authentication on IIS:
- You could publish your application by use the feature Publish project in Visual Studio or you also use dotnet cli to publish it instead.
- Install latest .NET Core module for IIS.
- Create your web application on IIS.
- Start IIS Manager -> Select the web application -> Open "Authentication" feature, make sure you set this: enable Anonymous Authentication, disable Windows Authentication, Forms Authentication and every other.
HTTPS (or any other) secure transport protocol should always be enforced when using forms authentication.
This is necessary because in forms authentication the user's password must be submitted from the client securely.
At least the services inside /Resources/AspNetFormsAuth
path must use HTTPS.
To enable HTTPS, see Configuring HTTPS in ASP.NET Core and Enforce HTTPS in ASP.NET Core.
Note: When deploying the AspNetFormsAuth packages, it will automatically create the admin user account and SecurityAdministrator role, add the account to the role and give it necessary permissions (claims) for all authentication service methods.
After deployment:
- Run the Rhetos utility
AdminSetup.exe
in the app's bin folder, to initialize the admin user's password. Run it with command-line argumentAdminSetup.exe <your app startup dll>
, it will prompt to enter the new admin password.
If needed, AdminSetup can be automated in a script with command-line arguments AdminSetup.exe <your app startup dll> --password <your password> --no-pause
All claims related to the authentication service have resource=AspNetFormsAuth.AuthenticationService
.
Admin user has all the necessary permissions (claims) for all authentication service methods.
Password attempt limits and lockout time can be customized in the IdentityOptions.LockoutOptions
Use entity (database table) Common.AspNetFormsAuthPasswordStrength to configure the password strength policy with one or more regular expressions.
- A new password must pass all rules entered in the table.
- RuleDescription is used as an error message to the user if the new password breaks the policy.
- When administrator executes
SetPassword
method, the property IgnorePasswordStrengthPolicy may be used to avoid the policy.
Examples:
RegularExpression | RuleDescription |
---|---|
.{6,} |
The password length must be at least six characters. |
\d |
The password must contain at least one digit. |
(\d.*){3,} |
The password must contain at least three digits. |
[A-Z] |
The password must contain at least one uppercase letter. |
[a-z] |
The password must contain at least one lowercase letter. |
\W |
The password must contain at least one special character (not a letter or a digit). |
Sharing the authentication cookie is useful when using separate web applications for web pages and application services, or when using multiple servers for load balancing. In these scenarios, sharing the forms authentication cookie between the sites will allow a single-point login for the user on any of the sites and seamless use of that cookie on the other sites.
You could check official document of Microsoft here Share authentication cookies among ASP.NET apps.
Sharing the authentication with ASP.NET MVC (.NET Framework)
-
In case you have ASP.NET MVC "frontend" apps shared security with Rhetos app, that uses Katana, please see this chapter Share authentication cookies between ASP.NET 4.x and ASP.NET Core apps. This is the example code how to do it:
// Rhetos app (.NET Core) config services.AddRhetos(ConfigureRhetosHostBuilder) .AddAspNetFormsAuth(); ... services.PostConfigureAll<CookieAuthenticationOptions>(options => { options.Cookie.Name = ".AspNet.SharedCookie"; options.Cookie.SameSite = SameSiteMode.Lax; options.Cookie.Path = "/"; options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; options.ExpireTimeSpan = TimeSpan.FromMinutes(120); options.CookieManager = new ChunkingCookieManager(); options.TicketDataFormat = new SecureDataFormat<AuthenticationTicket>( new TicketSerializer(), DataProtectionProvider.Create( new DirectoryInfo("C:\\keyring"), (builder) => { builder.SetApplicationName("iis-app-name"); } ).CreateProtector( "Microsoft.AspNetCore.Authentication.Cookies." + "CookieAuthenticationMiddleware", "Cookies.Application", "v2" ) ); });
// ASP.NET MVC (.NET Framework) config app.UseCookieAuthentication(new CookieAuthenticationOptions() { AuthenticationType = CookieAuthenticationDefaults.AuthenticationType, CookieName = ".AspNet.SharedCookie", SlidingExpiration = true, ExpireTimeSpan = TimeSpan.FromMinutes(120), LoginPath = PathString.FromUriComponent("/Account/Login"), LogoutPath = PathString.FromUriComponent("/Account/LogOff"), CookieManager = new ChunkingCookieManager(), TicketDataFormat = new AspNetTicketDataFormat( new DataProtectorShim( DataProtectionProvider.Create( new DirectoryInfo("C:\\keyring"), (builder) => { builder.SetApplicationName("iis-app-name"); } ).CreateProtector( "Microsoft.AspNetCore.Authentication.Cookies." + "CookieAuthenticationMiddleware", "Cookies.Application", "v2" ) ) ) });
Configure the ASP.NET Identity authentication ticket timeout with CookieAuthenticationOptions.ExpireTimeSpan. The default value is 14 days.
In order to use SendPasswordResetToken
web method (see also Forgot password),
an additional plugin must be provided that sends the token to the user (by SMS or email, e.g.).
- A sample implementation is available at https://github.com/Rhetos/SimpleSPRTEmail. This plugin package may be used for sending simple emails.
In order to implement a custom method of sending the token to the user (by SMS or email, e.g.),
create a Rhetos plugin package with a class that implements the Rhetos.AspNetFormsAuth.ISendPasswordResetToken
interface
from Rhetos.AspNetFormsAuth.Interfaces.dll
.
The class must use Export
attribute to register the plugin implementation.
For example, you could check the code here:
[Export(typeof(ISendPasswordResetToken))]
public class EmailSender : ISendPasswordResetToken
{
...
}
The AdditionalClientInfo
parameter of web service method /SendPasswordResetToken
will be provided to the implementation function.
The parameter may contain answers to security questions, preferred method of communication or any similar user provided information
required by the ISendPasswordResetToken
implementation.
The implementation class may throw a Rhetos.UserException
or a Rhetos.ClientException
to provide an error message to the client,
but use it with caution, or better avoid it: The SendPasswordResetToken
web service method allows anonymous access,
so providing any error information to the client might be a security issue.
Any other exception (Rhetos.FrameworkException
, e.g.) will only be logged on the server, but no error will be sent to the client.
Issue: Deployment results with error message "DslSyntaxException: Concept with same key is described twice with different values."
Solution: Please check if you have deployed both SimpleWindowsAuth package and AspNetFormsAuth package at the same time. Only one of the packages can be added to a Rhetos app. Read the installation instructions above for more information on the issue.
Issue: Web service responds with error message "The Role Manager feature has not been enabled."
Solution: The error occurs when the necessary modifications of Web.config file are not done. Please check that you have followed the installation instructions above.
Issue: I have accidentally deleted the admin user, SecurityAdministrator role, or some of its permissions. How can I get it back?
Solution: Execute AdminSetup.exe
again. It will regenerate the default administration settings. See Admin user password.
Other: In case of a server error, additional information on the error may be found in the Rhetos app's log file (RhetosServer.log
for Rhetos v1-v4).
If needed, more verbose logging of the authentication service may be switched on by enabling Trace
level logger AspNetFormsAuth.AuthenticationService
.
For example, in Rhetos v1-v4 add <logger name="AspNetFormsAuth.AuthenticationService" minLevel="Trace" writeTo="TraceLog" />
in Rhetos application's web.config
,
then the trace log will be written to RhetosServerTrace.log
.
Contributions are very welcome. The easiest way is to fork this repo, and then make a pull request from your fork. The first time you make a pull request, you may be asked to sign a Contributor Agreement. For more info see How to Contribute on Rhetos wiki.
- Note: This package is already available at the NuGet.org online gallery. You don't need to build it from source in order to use it in your application.
- To build the package from source, run
Clean.bat
,Build.bat
andTest.bat
. - For the test script to work, you need to create an empty database and
a settings file
test\Rhetos.AspNetFormsAuth.TestApp\rhetos-app.local.settings.json
with the database connection string (configuration key "ConnectionStrings:RhetosConnectionString"). - The build output is a NuGet package in the "Install" subfolder.