Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.

Commit 285efa0

Browse files
authored
Merge pull request #296 from wheels-dev/fix/register-token
Fix: User Registration Failing Due to BIGINT ID Precision Loss
2 parents acbded4 + 34c156e commit 285efa0

3 files changed

Lines changed: 104 additions & 47 deletions

File tree

app/controllers/web/AuthController.cfc

Lines changed: 93 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -619,10 +619,10 @@ component extends="app.Controllers.Controller" {
619619
return user;
620620
}
621621

622-
private function saveUser(required struct userData) {
623-
var message = "";
622+
623+
private string function saveUser(required struct userData) {
624624
try {
625-
// check required fields
625+
// Validate required fields
626626
if (!structKeyExists(userData, "firstname") || !len(trim(userData.firstname))) {
627627
return "First name is required.";
628628
}
@@ -638,41 +638,96 @@ component extends="app.Controllers.Controller" {
638638
if (!structKeyExists(userData, "passwordHash") || !len(trim(userData.passwordHash))) {
639639
return "Password is required.";
640640
}
641-
// Check if a user with the same email already exists
642-
var existingUser = model("User").findFirst( where="email = '#userData.email#'");
643-
if (!isObject(existingUser)) {
644-
// Create a new user
645-
var newUser = model("User").new();
646-
newUser.firstname = userData.firstname;
647-
newUser.lastname = userData.lastname;
648-
newUser.email = userData.email;
649-
newUser.passwordhash = bCryptHashPW(userData.passwordHash, bCryptGenSalt());
650-
newUser.roleid = GetUserRoleId(); // user role
651-
newUser.status = SetInactive(); // set inactive
652-
if(structKeyExists(userData, "newsletter")){
653-
newUser.newsletter = true;
654-
}
655-
if(newUser.save()){
656-
// Generate a unique verification token
657-
var verificationToken = Hash(createUUID());
658-
// Save token to the user_tokens table
659-
var newToken = model("UserToken").new();
660-
newToken.token = verificationToken;
661-
newToken.user_id = newUser.id;
662-
newToken.status = false; // Not verified
663-
newToken.save();
664-
// Send verification email
665-
if(sendVerificationEmail(newUser.email, verificationToken)){
666-
message = "Registration successful. Please check your email to verify your account.";
667-
}else{
668-
message = "Unable to send verification email. Please try again or contact support.";
641+
if (len(userData.passwordHash) < 8) {
642+
return "Password must be at least 8 characters long.";
643+
}
644+
645+
// Check for duplicate email - CORRECTED: Use Dynamic Finder
646+
var existingUser = model("User").findOneByEmail(userData.email);
647+
if (isObject(existingUser)) {
648+
return message = "An account with this email address already exists.";
649+
}
650+
651+
// Create a new user
652+
var newUser = model("User").new();
653+
newUser.firstname = userData.firstname;
654+
newUser.lastname = userData.lastname;
655+
newUser.email = userData.email;
656+
newUser.passwordhash = bCryptHashPW(userData.passwordHash, bCryptGenSalt());
657+
newUser.roleid = toString(GetUserRoleId()); // Convert to string
658+
newUser.status = SetInactive();
659+
660+
if (structKeyExists(userData, "newsletter")) {
661+
newUser.newsletter = true;
662+
}
663+
664+
if (!newUser.save()) {
665+
model("Log").log(
666+
category = "wheels.auth",
667+
level = "ERROR",
668+
message = "Failed to save new user",
669+
details = {
670+
"email": userData.email,
671+
"errors": newUser.allErrors()
669672
}
670-
}else{
671-
message = "Unable to create user account. Please try again or contact support.";
673+
);
674+
return "Unable to create user account. Please try again or contact support.";
675+
}
676+
677+
// After save, ID should now be populated as a string
678+
var userId = newUser.id; // Already a string due to model config
679+
680+
// Validate we got a proper ID
681+
if (!len(userId)) {
682+
// Fallback: re-fetch from database
683+
var savedUser = model("User").findOneByEmail(userData.email);
684+
if (isObject(savedUser)) {
685+
userId = savedUser.id;
686+
} else {
687+
model("Log").log(
688+
category = "wheels.auth",
689+
level = "ERROR",
690+
message = "User saved but ID is empty and cannot re-fetch",
691+
details = {"email": userData.email}
692+
);
693+
return "Registration error. Please contact support.";
672694
}
673-
} else {
674-
message = "An account with this email address already exists.";
675695
}
696+
697+
// Generate and save verification token
698+
var verificationToken = Hash(createUUID());
699+
700+
var newToken = model("UserToken").new();
701+
newToken.token = verificationToken;
702+
newToken.user_id = userId; // Now a string, safe
703+
newToken.status = false;
704+
705+
if (!newToken.save()) {
706+
model("Log").log(
707+
category = "wheels.auth",
708+
level = "ERROR",
709+
message = "Failed to save verification token",
710+
details = {
711+
"user_id": userId,
712+
"errors": newToken.allErrors()
713+
}
714+
);
715+
return "Registration error. Please contact support.";
716+
}
717+
718+
// Send verification email
719+
if (!sendVerificationEmail(userData.email, verificationToken)) {
720+
model("Log").log(
721+
category = "wheels.auth",
722+
level = "WARN",
723+
message = "User created but verification email failed",
724+
details = {"user_id": userId, "email": userData.email}
725+
);
726+
return "Registration completed but we couldn't send the verification email. Please contact support to resend the verification link.";
727+
}
728+
729+
return "Registration successful. Please check your email to verify your account.";
730+
676731
} catch (any e) {
677732
// Log internal error, but return generic message
678733
model("Log").log(
@@ -681,12 +736,12 @@ component extends="app.Controllers.Controller" {
681736
message = "Exception in saveUser",
682737
details = {
683738
"error_message": e.message,
684-
"error_detail": e.detail
739+
"error_detail": e.detail,
740+
"email": structKeyExists(userData, "email") ? userData.email : ""
685741
}
686742
);
687-
message = "An error occurred while creating your account. Please try again later.";
743+
return "An error occurred while creating your account. Please try again later.";
688744
}
689-
return message;
690745
}
691746

692747
private function sendVerificationEmail(required string email, required string token) {

app/models/User.cfc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ component extends="app.Models.Model" {
22
function config() {
33
table("users");
44

5+
// Tell Wheels to treat the ID as a string, not an integer -- CockroachDB is storing INT8 (BIGINT), which is a 64-bit integer that ColdFusion cannot safely represent as a number.
56
// ID Property
67
property(
78
name="id",
89
column="id",
9-
dataType="integer",
10+
dataType="string",
1011
automaticValidations=false
1112
);
1213

@@ -152,8 +153,8 @@ component extends="app.Models.Model" {
152153
label="WordPress ID"
153154
);
154155

155-
property(name="website", column="website", dataType="string", default="");
156-
property(name="ip", column="ip", dataType="string", default="");
156+
property(name="website", column="website", dataType="string", defaultValue = "");
157+
property(name="ip", column="ip", dataType="string", defaultValue = "");
157158

158159
// Relationships
159160
belongsTo(name="Role", foreignKey="roleId");

app/models/UserToken.cfc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ component extends="app.Models.Model" {
22
function config() {
33
table("user_tokens");
44

5-
property(name="id", column="id", type="integer", required=true, primarykey=true);
6-
property(name="token", column="token", type="string", required=false, default="");
7-
property(name="createdAt", column="createdat", type="datetime", required=false, default="");
8-
property(name="updatedAt", column="updatedat", type="datetime", required=false, default="");
9-
property(name="deletedAt", column="deletedat", type="datetime", required=false, default="");
10-
property(name="user_id", column="user_id", type="integer", required=true, foreignkey=true, references="User(id)");
5+
// Tell Wheels to treat the ID as a string, not an integer -- CockroachDB is storing INT8 (BIGINT), which is a 64-bit integer that ColdFusion cannot safely represent as a number.
6+
property(name="id", column="id", dataType ="string", primarykey=true);
7+
property(name="token", column="token", dataType ="string", defaultValue = "");
8+
property(name="createdAt", column="createdat", dataType ="datetime", defaultValue = "");
9+
property(name="updatedAt", column="updatedat", dataType ="datetime", defaultValue = "");
10+
property(name="deletedAt", column="deletedat", dataType ="datetime", defaultValue = "");
11+
property(name="user_id", column="user_id", dataType ="string");
1112

1213
belongsTo(name="User", foreignKey="user_id");
1314
}

0 commit comments

Comments
 (0)