diff --git a/store/account_test.go b/store/account_test.go
index 2c980cf..d623089 100644
--- a/store/account_test.go
+++ b/store/account_test.go
@@ -4,21 +4,33 @@ import (
 	"errors"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/mattn/go-sqlite3"
 
 	"lbryio/lbry-id/auth"
 )
 
-func expectAccountMatch(t *testing.T, s *Store, normEmail auth.NormalizedEmail, expectedEmail auth.Email, password auth.Password, seed auth.ClientSaltSeed) {
+func expectAccountMatch(
+	t *testing.T,
+	s *Store,
+	normEmail auth.NormalizedEmail,
+	expectedEmail auth.Email,
+	password auth.Password,
+	seed auth.ClientSaltSeed,
+	expectedVerifyTokenString auth.VerifyTokenString,
+	approxVerifyExpiration *time.Time,
+) {
 	var key auth.KDFKey
 	var salt auth.ServerSalt
 	var email auth.Email
+	var verifyExpiration *time.Time
+	var verifyTokenString auth.VerifyTokenString
 
 	err := s.db.QueryRow(
-		`SELECT key, server_salt, email from accounts WHERE normalized_email=? AND client_salt_seed=?`,
+		`SELECT key, server_salt, email, verify_token, verify_expiration from accounts WHERE normalized_email=? AND client_salt_seed=?`,
 		normEmail, seed,
-	).Scan(&key, &salt, &email)
+	).Scan(&key, &salt, &email, &verifyTokenString, &verifyExpiration)
 	if err != nil {
 		t.Fatalf("Error finding account for: %s %s - %+v", normEmail, password, err)
 	}
@@ -34,6 +46,31 @@ func expectAccountMatch(t *testing.T, s *Store, normEmail auth.NormalizedEmail,
 	if email != expectedEmail {
 		t.Fatalf("Email case not as expected. Want: %s Got: %s", email, expectedEmail)
 	}
+
+	if verifyTokenString != expectedVerifyTokenString {
+		t.Fatalf(
+			"Verify token string not as expected. Want: %s Got: %s",
+			verifyTokenString,
+			expectedVerifyTokenString,
+		)
+	}
+
+	if approxVerifyExpiration != nil {
+		if verifyExpiration == nil {
+			t.Fatalf("Expected verify expiration to not be nil")
+		}
+		expDiff := approxVerifyExpiration.Sub(*verifyExpiration)
+		if time.Second < expDiff || expDiff < -time.Second {
+			t.Fatalf(
+				"Verify expiration not as expected. Want approximately: %s Got: %s",
+				verifyExpiration,
+				approxVerifyExpiration,
+			)
+		}
+	}
+	if approxVerifyExpiration == nil && verifyExpiration != nil {
+		t.Fatalf("Expected verify expiration to be nil. Got: %+v", verifyExpiration)
+	}
 }
 
 func expectAccountNotExists(t *testing.T, s *Store, normEmail auth.NormalizedEmail) {
@@ -65,19 +102,20 @@ func TestStoreCreateAccount(t *testing.T) {
 	// Get an account, come back empty
 	expectAccountNotExists(t, &s, normEmail)
 
-	// Create an account
-	if err := s.CreateAccount(email, password, seed); err != nil {
+	// Create an account. Make it verified (i.e. no token) for the usual
+	// case. We'll test unverified (with token) separately.
+	if err := s.CreateAccount(email, password, seed, ""); err != nil {
 		t.Fatalf("Unexpected error in CreateAccount: %+v", err)
 	}
 
 	// Get and confirm the account we just put in
-	expectAccountMatch(t, &s, normEmail, email, password, seed)
+	expectAccountMatch(t, &s, normEmail, email, password, seed, "", nil)
 
 	newPassword := auth.Password("xyz")
 
 	// Try to create a new account with the same email and different password,
 	// fail because email already exists
-	if err := s.CreateAccount(email, newPassword, seed); err != ErrDuplicateAccount {
+	if err := s.CreateAccount(email, newPassword, seed, ""); err != ErrDuplicateAccount {
 		t.Fatalf(`CreateAccount err: wanted "%+v", got "%+v"`, ErrDuplicateAccount, err)
 	}
 
@@ -85,12 +123,30 @@ func TestStoreCreateAccount(t *testing.T) {
 
 	// Try to create a new account with the same email different capitalization.
 	// fail because email already exists
-	if err := s.CreateAccount(differentCaseEmail, password, seed); err != ErrDuplicateAccount {
+	if err := s.CreateAccount(differentCaseEmail, password, seed, ""); err != ErrDuplicateAccount {
 		t.Fatalf(`CreateAccount err (for case insensitivity check): wanted "%+v", got "%+v"`, ErrDuplicateAccount, err)
 	}
 
 	// Get the email and same *first* password we successfully put in
-	expectAccountMatch(t, &s, normEmail, email, password, seed)
+	expectAccountMatch(t, &s, normEmail, email, password, seed, "", nil)
+}
+
+// Try CreateAccount with a verification string, thus unverified
+func TestStoreCreateAccountUnverified(t *testing.T) {
+	s, sqliteTmpFile := StoreTestInit(t)
+	defer StoreTestCleanup(sqliteTmpFile)
+
+	email, normEmail := auth.Email("Abc@Example.Com"), auth.NormalizedEmail("abc@example.com")
+	password, seed := auth.Password("123"), auth.ClientSaltSeed("abcd1234abcd1234")
+
+	// Create an account
+	if err := s.CreateAccount(email, password, seed, "abcd1234abcd1234abcd1234abcd1234"); err != nil {
+		t.Fatalf("Unexpected error in CreateAccount: %+v", err)
+	}
+
+	// Get and confirm the account we just put in
+	approxVerifyExpiration := time.Now().Add(time.Hour * 24 * 2).UTC()
+	expectAccountMatch(t, &s, normEmail, email, password, seed, "abcd1234abcd1234abcd1234abcd1234", &approxVerifyExpiration)
 }
 
 // Test GetUserId for nonexisting email
@@ -165,7 +221,7 @@ func TestStoreAccountEmptyFields(t *testing.T) {
 
 			var sqliteErr sqlite3.Error
 
-			err := s.CreateAccount(tc.email, tc.password, tc.clientSaltSeed)
+			err := s.CreateAccount(tc.email, tc.password, tc.clientSaltSeed, "")
 			if errors.As(err, &sqliteErr) {
 				if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintCheck) {
 					return // We got the error we expected
diff --git a/store/password_test.go b/store/password_test.go
index 7df6af9..8a66abd 100644
--- a/store/password_test.go
+++ b/store/password_test.go
@@ -47,7 +47,7 @@ func TestStoreChangePasswordSuccess(t *testing.T) {
 		t.Errorf("ChangePasswordWithWallet (lower case email): unexpected error: %+v", err)
 	}
 
-	expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed)
+	expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed, "", nil)
 	expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac)
 	expectTokenNotExists(t, &s, token)
 
@@ -63,7 +63,7 @@ func TestStoreChangePasswordSuccess(t *testing.T) {
 		t.Errorf("ChangePasswordWithWallet (upper case email): unexpected error: %+v", err)
 	}
 
-	expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed)
+	expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed, "", nil)
 }
 
 func TestStoreChangePasswordErrors(t *testing.T) {
@@ -161,7 +161,7 @@ func TestStoreChangePasswordErrors(t *testing.T) {
 			// This tests the transaction rollbacks in particular, given the errors
 			// that are at a couple different stages of the txn, triggered by these
 			// tests.
-			expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed)
+			expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, "", nil)
 			if tc.hasWallet {
 				expectWalletExists(t, &s, userId, oldEncryptedWallet, oldSequence, oldHmac)
 			} else {
@@ -196,7 +196,7 @@ func TestStoreChangePasswordNoWalletSuccess(t *testing.T) {
 		t.Errorf("ChangePasswordNoWallet (lower case email): unexpected error: %+v", err)
 	}
 
-	expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed)
+	expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed, "", nil)
 	expectWalletNotExists(t, &s, userId)
 	expectTokenNotExists(t, &s, token)
 
@@ -209,7 +209,7 @@ func TestStoreChangePasswordNoWalletSuccess(t *testing.T) {
 		t.Errorf("ChangePasswordNoWallet (upper case email): unexpected error: %+v", err)
 	}
 
-	expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed)
+	expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed, "", nil)
 }
 
 func TestStoreChangePasswordNoWalletErrors(t *testing.T) {
@@ -292,7 +292,7 @@ func TestStoreChangePasswordNoWalletErrors(t *testing.T) {
 			// deleted. This tests the transaction rollbacks in particular, given the
 			// errors that are at a couple different stages of the txn, triggered by
 			// these tests.
-			expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed)
+			expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, "", nil)
 			if tc.hasWallet {
 				expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac)
 			} else {
diff --git a/store/store.go b/store/store.go
index 6778e9c..ce20599 100644
--- a/store/store.go
+++ b/store/store.go
@@ -120,6 +120,8 @@ func (s *Store) Migrate() error {
 			key TEXT NOT NULL,
 			client_salt_seed TEXT NOT NULL,
 			server_salt TEXT NOT NULL,
+			verify_token TEXT NOT NULL,
+			verify_expiration DATETIME,
 			user_id INTEGER PRIMARY KEY AUTOINCREMENT,
 			CHECK (
 			  email <> '' AND
@@ -369,10 +371,16 @@ func (s *Store) CreateAccount(email auth.Email, password auth.Password, seed aut
 		return
 	}
 
+	var verifyExpiration *time.Time
+	if len(verifyToken) > 0 {
+		verifyExpiration = new(time.Time)
+		*verifyExpiration = time.Now().UTC().Add(time.Hour * 24 * 2)
+	}
+
 	// userId auto-increments
 	_, err = s.db.Exec(
-		"INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed) VALUES(?,?,?,?,?)",
-		email.Normalize(), email, key, salt, seed,
+		"INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token, verify_expiration) VALUES(?,?,?,?,?,?,?)",
+		email.Normalize(), email, key, salt, seed, verifyToken, verifyExpiration,
 	)
 	var sqliteErr sqlite3.Error
 	if errors.As(err, &sqliteErr) {
diff --git a/store/store_test.go b/store/store_test.go
index 2b56d92..094b52c 100644
--- a/store/store_test.go
+++ b/store/store_test.go
@@ -45,8 +45,8 @@ func makeTestUser(t *testing.T, s *Store) (userId auth.UserId, email auth.Email,
 	seed = auth.ClientSaltSeed("abcd1234abcd1234")
 
 	rows, err := s.db.Query(
-		"INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed) values(?,?,?,?,?) returning user_id",
-		normEmail, email, key, salt, seed,
+		"INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token) values(?,?,?,?,?,?) returning user_id",
+		normEmail, email, key, salt, seed, "",
 	)
 	if err != nil {
 		t.Fatalf("Error setting up account: %+v", err)