Vidyano Documentation
HomepageDemo
  • Vidyano Documentation
  • Vidyano v6 (.NET 8.0 based)
    • Getting started
    • Documentation
      • Courier Feature
      • Managing User Secrets
      • Vidyano.Core
      • Icons
      • Reports
      • Grids
        • Grouping
      • Instant Search
      • Verbose Logs
        • Storage Provider
      • SourceGenerators
    • Migrating from v5.x
      • Migrating an existing Vidyano v5 project
      • Migration scripts for v5 Repository database
  • Release Notes
    • Client
      • 3.0
      • 2.0.0
    • Service
      • 6.0
      • Previous
        • 5.45.0+26864bd
        • 5.44.0+6e65421
        • 5.40.2+2a48896
        • 5.39.1+f04e696
        • 5.38.3+a697611
        • 5.37.1+3fd7ebea
        • 5.36.0+5338103
        • 5.35.5+2316022
        • 5.34.3+d278982
        • 5.33.1+12ad63a
        • 5.32.1+0c41761
        • 5.31.2+c8aabb2
        • 5.30.0+530afaf
        • 5.29.3+30608c3
        • 5.28.2+bc49431
        • 5.27.0+6b9495e
        • 5.26.2+bccf416
        • 5.25.3+8224b3b
        • 5.24.0+a20f7c
        • 5.23.0+9b8b99
        • 5.22.1+557c11
        • 5.21.1+923828
        • 5.20.0+95f4d1
        • 5.19.0+0964f9
        • 5.18.0+de3495
        • 5.17.0+aaa255
        • 5.16.0+aae2a8
        • 5.15.2+5ed89a
        • 5.14.1+ec0dbd
        • 5.13.1+c8fdb1
        • 5.12.0+66cbb5
        • 5.11.1+d7647c
        • 5.10.2+a3acd1
        • 5.9.0+68a51e
        • 5.8.1+67bcab
        • 5.8.0+aab7d8
        • 5.7.1+554316
        • 5.6.4+151e2e
        • 5.1.60401.4035
  • Legacy v5.x
    • Installation (Legacy)
    • Tutorial 1: Your first application (Legacy)
    • Computed attributes
    • Actions
      • Labels
      • Actions classes
    • Security
      • Architecture
      • Allow user registration
      • Forgot password
      • Best Practices
      • Azure AD SAML based Sign-on
      • SCIM 2.0 Service Provider
    • Overriding Vidyano Settings
Powered by GitBook
On this page

Was this helpful?

Export as PDF
  1. Legacy v5.x
  2. Security

Forgot password

This information is still applicable to v6 but the code snippets on [Schema]Web need to be updated to return an IResult instead of HttpResponseMessage and Results.XX methods should be used instead of args.Request.CreateXX, i.e. Results.Problem and Results.Redirect

When working with email addresses as user names we can use this information to provide a forgot password functionality. By implementing the HandleForgotPassword method on the Advanced class you’ll get the “Forgot password?” button on the Sign in screen.

Best practice recommends for generating a random reset password token (ObjectEx.GetSecureRandomPassword can be used), storing the token in the user profile, emailing the user a link that triggers an api method that will verify the token and user combination. At that moment you can use the IUser.ResetPasswordNextLogin() method to trigger a password change on next login and redirecting the user to an existing logged in session.

// In [Schema]Advanced.cs
public override void HandleForgotPassword(ForgotPasswordArgs args)
{
    // NOTE: Always inform for success so that we don't leak information about users
    args.Notification = "An email has been sent with all the information to reset your password.";

    var user = Manager.Current.GetUser(args.UserName);
    if (user == null)
        return;

    var userHostAddress = Manager.Current.RequestMessage.GetClientIpAddress();
    var token = user.Profile["ResetPasswordToken"];
    if (string.IsNullOrEmpty(token))
    {
        token = ObjectEx.GetSecureRandomString(16).Replace("-", null).Replace("_", null);
        user.Profile.SetValue("ResetPasswordToken", token);
    }

    var location = Manager.Current.WebsiteRoot + $"api/ResetPassword?UserName={user.Name}&Token={token}";
    // TODO: Send email to user with information (password change requested, from ip, location to click, ...)
}
// In [Schema]Web.cs
public HttpResponseMessage ResetPassword(ApiArgs args)
{
    var userName = args.Query["UserName"];
    var token = args.Query["Token"];
    if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(token))
        return args.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid request");

    var user = Manager.Current.GetUser(userName);
    if (user != null && token == user.Profile["ResetPasswordToken"])
    {
        user.Profile.SetValue("ResetPasswordToken", null); // NOTE: Resetting the token can be a problem if the user doesn't complete the change password prompt
        user.ResetPasswordNextLogin();

        var encodedUser = Convert.ToBase64String(Encoding.UTF8.GetBytes(user.Name)).Replace('/', '_');
        var authToken = WebController.GenerateAuthToken(user.Name, DateTimeOffset.Now.AddDays(28), Manager.Current.RequestMessage.GetClientIpAddress()).Replace('/', '_');
        var response = args.Request.CreateResponse(HttpStatusCode.Redirect);
        response.Headers.Location = new Uri(Manager.Current.WebsiteRoot + "#!/SignInWithToken/" + encodedUser + "/" + authToken);
        return response;
    }

    return args.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Invalid request");
}

PreviousAllow user registrationNextBest Practices

Last updated 2 months ago

Was this helpful?