I created single page app that requires username
and password
for users authentication. Each account in the system is created by administrators. There is no option for user to click on the link and create account. After researching and looking for the best solution first thing that I changed was the way how user can recover their password. Here is the short schema on how that logic works. User first has to click on the link Forgot Password
enter their email and submit the form. They will see the message:
An email has been sent to example@gmail.com with further instructions.
The function that will execute this process looks like this:
cfstoredproc( procedure="CheckEmail", datasource=dsn ) {
cfprocparam( maxlength=80, null=!len(trim(arguments.email)), cfsqltype="cf_sql_varchar", dbvarname="@Email", value=trim(arguments.email) );
cfprocresult( name="EmailResult" );
}
if( EmailResult.recordCount EQ 1) {
// Generate new token
local.newToken = createUUID();
// Build URL with token parameter and add hashed value
local.theUrl = "https://example.com/Reset.cfm?token=" & local.newToken;
// Set expiration time (30 minutes)
local.Expires = DateAdd("n", 30, now());
cfstoredproc( procedure="SaveToken", datasource=dsn ) {
cfprocparam( maxlength=80, null=!len(trim(arguments.email)), cfsqltype="cf_sql_varchar", dbvarname="@Email", value=trim(arguments.email) );
cfprocparam( maxlength=35, null=!len(trim(local.newToken)), cfsqltype="cf_sql_varchar", dbvarname="@Token", value=trim(local.newToken) );
cfprocparam( null=!len(trim(local.Expires)), cfsqltype="cf_sql_timestamp", dbvarname="@Expires", value=trim(local.Expires) );
cfprocresult( name="TokenResult" );
}
if ( len(TokenResult.RecID) ) {
savecontent variable="mailBody"{
writeOutput('<br>Here is your password reset link: <a href="' & theUrl & '">Click here</a> as soon as possible and change your password.<br>' );
}
local.mail = new mail();
// Set it's properties
local.mail.setSubject("Example Application");
local.mail.setTo(arguments.email);
local.mail.setFrom("noreply@example.com");
local.mail.setType("html");
// Send the email
local.mail.send(body = mailBody);
local.fnResults = {status : "200", message : "An email has been sent to <b>" & arguments.email & "</b> with further instructions."};
} else {
local.fnResults = {status : "400", message : "Error! Something went wrong."};
}
}else{
savecontent variable="mailBody"{
writeOutput('<br>We recieved a password reset request. The email you have provided does not exist in our system.<br>');
}
local.mail = new mail();
// Set it's properties
local.mail.setSubject("Example Application");
local.mail.setTo(arguments.email);
local.mail.setFrom("noreply@example.com");
local.mail.setType("html");
// Send the email
local.mail.send(body = mailBody);
local.fnResults = {status : "200", message : "An email has been sent to <b>" & arguments.email & "</b> with further instructions."};
}
Then next step is if email exists and user clicks on the link I will either show the form where they can enter new password or they will see message This link has expired or does not exist anymore.
. Here is example of Reset.cfm
page:
if (structKeyExists(url,"token") && isValid("uuid", url.token) && len(trim(url.token)) == 35){
cfstoredproc( procedure="CheckToken", datasource=dsn ) {
cfprocparam( maxlength=35, null=!len(trim(url.token)), cfsqltype="cf_sql_varchar", dbvarname="@Token", value=trim(url.token) );
cfprocresult( name="TokenResult" );
}
if( TokenResult.recordCount == 1 ){ //If token is valid (not expired) show the form.
<form name="frmRecovery" id="frmRecovery" autocomplete="off">
<input type="hidden" name="token" value="<cfoutput>#url.token#</cfoutput>">
<div class="form-group">
<div class="alert alert-info"><strong>Info!</strong> After saving your changes, you will be taken back to the login screen. Log into the system with the account credentials you have just saved.</div>
</div>
<div class="form-group">
<label class="control-label" for="password"><span class="label label-primary">Password</span></label>
<input type="password" class="form-control" name="frmRecovery_password" id="frmRecovery_password" placeholder="Enter Password" maxlength="64" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<div class="form-group">
<div class="alert message-submit"></div>
</div>
</form>
}else{
<div class="alert alert-warning">
<strong>Warrning!</strong> This link has expired or does not exist anymore! </div>
}
}
If user gets directed to the form to enter the password, I will save new password and delete the token. The next step is directing them to login page and they can enter credentials and login. So my question is can I use similar approach when administrator creates new account? the admin needs to enter first
, last name
, username
, etc. Then click the button Send Email
that will forward username
and temporary link where new user can enter they password and login in the application. The logic that is used before is generating temporary password, user logs in and then have to reset the password. I'm wondering if solution that I proposed wold have any security risks or is equally good as one with temp password?