diff --git a/README.md b/README.md
index e075141d8decffe015559e3060718d428c15cf80..234f11786f0b5404a5c661ef6e557f240076d7fe 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,7 @@ BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_LOCATION_APP = "test_app_1"
 BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_NAME = "MyUserPreferences"
 BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL = False
 BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED = False
+BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION = False
 
 ################################################################################
 ```
diff --git a/README.rst b/README.rst
index 8418549e1ad8c952c9343b5a0d6c2d2b058bcabb..3b4ab3d3d21a2cc979f031ac03cafcf306d9516a 100644
--- a/README.rst
+++ b/README.rst
@@ -52,6 +52,7 @@ Quick start
     BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_NAME = "MyUserPreferences"
     BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL = False
     BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED = False
+    BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION = False
 
     ################################################################################
 
diff --git a/basetheme_bootstrap/default_settings.py b/basetheme_bootstrap/default_settings.py
new file mode 100644
index 0000000000000000000000000000000000000000..a6583f05903239995025f6b7d644997ff592ed03
--- /dev/null
+++ b/basetheme_bootstrap/default_settings.py
@@ -0,0 +1,22 @@
+from django.conf import settings
+
+
+def is_username_is_email():
+    try:
+        return settings.BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL
+    except AttributeError:
+        return False
+
+
+def is_first_last_name_required():
+    try:
+        return settings.BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED
+    except AttributeError:
+        return False
+
+
+def is_validating_email():
+    try:
+        return settings.BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION
+    except AttributeError:
+        return False
diff --git a/basetheme_bootstrap/forms.py b/basetheme_bootstrap/forms.py
index 19e1964ae258f4f89582634b985fd76880d6d8e4..5754f91539b03cb7b3577e6f40714f3c70cd6786 100644
--- a/basetheme_bootstrap/forms.py
+++ b/basetheme_bootstrap/forms.py
@@ -1,4 +1,4 @@
-from django.conf import settings
+
 from django.contrib.auth import get_user_model, forms as auth_forms
 from django.db.models import Q
 from django.forms import ModelForm
@@ -6,19 +6,7 @@ from django.urls import reverse
 from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext_lazy as _
 
-
-def is_username_is_email():
-    try:
-        return settings.BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL
-    except AttributeError:
-        return False
-
-
-def is_first_last_name_required():
-    try:
-        return settings.BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED
-    except AttributeError:
-        return False
+from basetheme_bootstrap.default_settings import is_username_is_email, is_first_last_name_required
 
 
 class CleanUsernameAndSuggestReset:
diff --git a/basetheme_bootstrap/tests.py b/basetheme_bootstrap/tests.py
index 78a60049a695b8cd107fd649f96accf9a99b1b86..f1752c0b7c184ef5bca024c001f3fe94b46ec386 100644
--- a/basetheme_bootstrap/tests.py
+++ b/basetheme_bootstrap/tests.py
@@ -1,14 +1,16 @@
 import os
+import re
 
 from django.conf import settings
 from django.contrib.auth import get_user_model
-from django.contrib.auth.models import AnonymousUser
+from django.contrib.auth.models import AnonymousUser, Group
+from django.core import mail
 from django.core.cache import cache
 from django.core.exceptions import ValidationError
 from django.test import TestCase, RequestFactory, override_settings
 from django.urls import reverse
 
-from basetheme_bootstrap import user_preferences
+from basetheme_bootstrap import user_preferences, tokens
 from basetheme_bootstrap.user_preferences import get_user_preferences_for_user
 
 
@@ -138,6 +140,99 @@ class SignUpWithFirstLastNameRequiredTests(TestCase):
         self.assertEqual(response.status_code, 200)
 
 
+@override_settings(
+    BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION=True,
+    PASSWORD_RESET_TIMEOUT_DAYS=1,
+)
+class SignUpWithValidationTests(TestCase):
+
+    def setUp(self):
+        cache.clear()
+        get_user_model().objects.create(
+            username="root",
+        )
+        self.data = {
+            'username': "userAAA",
+            'email': "userAAA@mp.com",
+            'password1': "user@mp.comuser@mp.comuser@mp.comuser@mp.com",
+            'password2': "user@mp.comuser@mp.comuser@mp.comuser@mp.com",
+            'first_name': "user"
+        }
+
+    def test_sign_up_form_view(self):
+        response = self.client.post(reverse('basetheme_bootstrap:signup'), self.data)
+        self.assertEqual(response.status_code, 200)
+
+        user = get_user_model().objects.last()
+        self.assertFalse(user.is_active)
+        self.assertIn(Group.objects.get(name="PendingAccountUser"), user.groups.all())
+
+        self.assertEqual(len(mail.outbox), 1)
+        activate_link_example = reverse('basetheme_bootstrap:activate', args=["AAA", "AAA-AAA"])
+        activate_link_example = activate_link_example.replace("AAA", "([a-zA-Z0-9]+)")
+        m = re.findall(activate_link_example, mail.outbox[0].body)
+        self.assertEqual(len(m), 1)
+        self.client.post(reverse('basetheme_bootstrap:activate', args=[m[0][0], m[0][1] + "-" + m[0][2]]))
+        user = get_user_model().objects.last()
+        self.assertTrue(user.is_active)
+        self.assertNotIn(Group.objects.get(name="PendingAccountUser"), user.groups.all())
+
+        ## test an account cannot be re-activated with the link:
+        user.is_active=False
+        user.save()
+        self.client.post(reverse('basetheme_bootstrap:activate', args=[m[0][0], m[0][1] + "-" + m[0][2]]))
+        self.assertFalse(get_user_model().objects.last().is_active)
+
+    def test_sign_up_with_user_pending_resend_email(self):
+        user_count = get_user_model().objects.count()
+        response = self.client.post(reverse('basetheme_bootstrap:signup'), self.data)
+        self.assertEqual(get_user_model().objects.count(), user_count + 1)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(mail.outbox), 1)
+
+        response = self.client.post(reverse('basetheme_bootstrap:signup'), self.data)
+        self.assertEqual(get_user_model().objects.count(), user_count + 1)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(mail.outbox), 2)
+        activate_link_example = reverse('basetheme_bootstrap:activate', args=["AAA", "AAA-AAA"])
+        activate_link_example = activate_link_example.replace("AAA", "([a-zA-Z0-9]+)")
+        m = re.findall(activate_link_example, mail.outbox[-1].body)
+        self.assertEqual(len(m), 1)
+
+    def test_activate_too_late_with_user_pending_resend_email(self):
+        actual_account_activation_token = tokens.account_activation_token
+
+        class MockedTokenGenerator(tokens.TokenGenerator):
+            def _today(self):
+                from datetime import date, timedelta
+                # Used for mocking in tests
+                return date.today() - timedelta(days=2)
+
+        tokens.account_activation_token = MockedTokenGenerator()
+
+        user_count = get_user_model().objects.count()
+        response = self.client.post(reverse('basetheme_bootstrap:signup'), self.data)
+        self.assertEqual(get_user_model().objects.count(), user_count + 1)
+        self.assertEqual(response.status_code, 200)
+        self.assertFalse(get_user_model().objects.last().is_active)
+
+        tokens.account_activation_token = actual_account_activation_token
+
+        self.assertEqual(len(mail.outbox), 1)
+        activate_link_example = reverse('basetheme_bootstrap:activate', args=["AAA", "AAA-AAA"])
+        activate_link_example = activate_link_example.replace("AAA", "([a-zA-Z0-9]+)")
+        m = re.findall(activate_link_example, mail.outbox[0].body)
+        self.assertEqual(len(m), 1)
+        self.client.post(reverse('basetheme_bootstrap:activate', args=[m[0][0], m[0][1] + "-" + m[0][2]]))
+
+        self.assertFalse(get_user_model().objects.last().is_active)
+        self.assertEqual(len(mail.outbox), 2)
+        activate_link_example = reverse('basetheme_bootstrap:activate', args=["AAA", "AAA-AAA"])
+        activate_link_example = activate_link_example.replace("AAA", "([a-zA-Z0-9]+)")
+        m = re.findall(activate_link_example, mail.outbox[1].body)
+        self.assertEqual(len(m), 1)
+
+
 class TestWithTemplatesInPlace(SignUpTests):
 
     def setUp(self):
diff --git a/basetheme_bootstrap/tokens.py b/basetheme_bootstrap/tokens.py
new file mode 100644
index 0000000000000000000000000000000000000000..d1501a6857ad17623de9e2b269aafb9e87029982
--- /dev/null
+++ b/basetheme_bootstrap/tokens.py
@@ -0,0 +1,13 @@
+from django.contrib.auth.tokens import PasswordResetTokenGenerator
+from django.utils import six
+
+
+class TokenGenerator(PasswordResetTokenGenerator):
+    def _make_hash_value(self, user, timestamp):
+        return (
+                six.text_type(user.pk) + six.text_type(timestamp) +
+                six.text_type(user.is_active)
+        )
+
+
+account_activation_token = TokenGenerator()
diff --git a/basetheme_bootstrap/urls.py b/basetheme_bootstrap/urls.py
index af4d5eb3e92282913918950e8b0c4a9ba95861d8..a5658e27f297fe43cbae7023886e8e3c45af2564 100644
--- a/basetheme_bootstrap/urls.py
+++ b/basetheme_bootstrap/urls.py
@@ -19,6 +19,8 @@ urlpatterns = [
     url(r'^accounts/signup/$', views.signup, name='signup'),
     url(r'^accounts/edit/$', views.user_update, name='user-update'),
     url(r'^accounts/remove/$', views.user_delete, name='user-delete'),
+    url(r'^accounts/activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
+        views.activate, name='activate'),
     ################################################################################
     # Lost password
     ################################################################################
diff --git a/basetheme_bootstrap/views.py b/basetheme_bootstrap/views.py
index a0fae35ca8d266cff7b64833604985fe238909e7..0ca369fbeab5c94cfc25328d84f6517dd504d40c 100644
--- a/basetheme_bootstrap/views.py
+++ b/basetheme_bootstrap/views.py
@@ -6,15 +6,21 @@ from django.contrib import messages
 from django.contrib.auth import update_session_auth_hash, authenticate, login, get_user_model
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.forms import PasswordChangeForm
+from django.contrib.auth.models import Group
 from django.core.mail import send_mail
 from django.db.models import ProtectedError
 from django.forms import widgets
 from django.http import HttpResponseForbidden
 from django.shortcuts import render, redirect
 from django.template import TemplateDoesNotExist
+from django.urls import reverse
+from django.utils.encoding import force_bytes, force_text
+from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
 from django.utils.translation import ugettext
 
+from basetheme_bootstrap import tokens
 from basetheme_bootstrap import user_preferences
+from basetheme_bootstrap.default_settings import is_validating_email
 from basetheme_bootstrap.forms import UserCreationFormWithMore, \
     MyUserChangeForm, UserDeleteForm
 
@@ -53,35 +59,38 @@ def signup(request):
     if request.method == 'POST':
         form = UserCreationFormWithMore(request.POST)
         if form.is_valid():
+            auto_active = not is_validating_email()
             user = form.save()
             if get_user_model().objects.filter(pk__gt=1).count() == 0:
                 user.is_superuser = True
                 user.is_staff = True
-                user.save()
+                user.is_active = True
+                Group.objects.get_or_create(name="PendingAccountUser")
+            else:
+                if not auto_active:
+                    g, created = Group.objects.get_or_create(name="PendingAccountUser")
+                    user.groups.add(g)
+                user.is_active = auto_active
+            user.save()
+
+            send_account_created(request, user, auto_active=auto_active)
+
+            if not auto_active:
+                return account_is_pending_view(request, email=request.POST['email'])
+
             username = user.username
             raw_password = form.cleaned_data.get('password1')
             user = authenticate(username=username, password=raw_password)
             request.user = user
 
-            try:
-                send_mail(
-                    subject=ugettext('Account successfully created'),
-                    message=ugettext(
-                        'Dear %(first_name)s %(last_name)s\n\n'
-                        'Your account have successfully been created on %(joined)s.\n\nBest regards') % dict(
-                        first_name=request.user.first_name,
-                        last_name=request.user.last_name,
-                        joined=str(request.user.date_joined),
-                    ),
-                    from_email=settings.DEFAULT_FROM_EMAIL,
-                    recipient_list=[request.user.email],
-                    fail_silently=False,
-                )
-            except Exception as e:
-                logging.exception("Sending email to user %i failed" % user.pk)
-
             login(request, user)
             return redirect('home')
+        else:
+            user = get_user_model().objects.filter(groups__name="PendingAccountUser",
+                                                   email=request.POST['email']).first()
+            if user is not None:
+                send_account_created(request, user, auto_active=not is_validating_email())
+                return account_is_pending_view(request, email=request.POST['email'])
     else:
         if not request.user.is_anonymous:
             return HttpResponseForbidden()
@@ -89,6 +98,57 @@ def signup(request):
     return render(request, 'registration/signup.html', {'form': form})
 
 
+def account_is_pending_view(request, email):
+    return render(request, 'basetheme_bootstrap/simple_message_page.html', {
+        'page_title': ugettext('Account activation pending'),
+        'message': ugettext('An email was sent with a link to validate your account, '
+                            'please click on the link to enable your account.'),
+        'sub_message': ugettext('The email has been addressed  to %s.') % email,
+    })
+
+
+def send_account_created(request, user, auto_active=False):
+    try:
+        activation_link = request.scheme + "://" + request.get_host()
+        activation_link += reverse('basetheme_bootstrap:activate', kwargs={
+            'uidb64': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
+            'token': tokens.account_activation_token.make_token(user)
+        })
+        if auto_active:
+            message = ugettext(
+                'Dear %(first_name)s %(last_name)s\n\n'
+                'Your account have successfully been created on %(joined)s.'
+                '\n\n'
+                'Best regards') % dict(
+                first_name=user.first_name,
+                last_name=user.last_name,
+                joined=str(user.date_joined),
+            )
+        else:
+            message = ugettext(
+                'Dear %(first_name)s %(last_name)s\n\n'
+                'Your account have successfully been created on %(joined)s.'
+                '\n\n'
+                'Please click on the link to confirm your registration\n'
+                '%(activation_link)s'
+                '\n\n'
+                'Best regards') % dict(
+                first_name=user.first_name,
+                last_name=user.last_name,
+                joined=str(user.date_joined),
+                activation_link=activation_link,
+            )
+        send_mail(
+            subject=ugettext('Account successfully created'),
+            message=message,
+            from_email=settings.DEFAULT_FROM_EMAIL,
+            recipient_list=[user.email],
+            fail_silently=False,
+        )
+    except Exception as e:
+        logging.exception("Sending email to user %i failed" % user.pk)
+
+
 def user_update(request):
     if request.method == 'POST':
         form = MyUserChangeForm(instance=request.user, data=request.POST)
@@ -165,3 +225,31 @@ def account_detail(request):
         'form_prefs': form_prefs,
         'btn_classes': 'pull-right float-right'
     })
+
+
+def activate(request, uidb64, token):
+    try:
+        uid = force_text(urlsafe_base64_decode(uidb64))
+        user = get_user_model().objects.get(pk=uid)
+    except(TypeError, ValueError, OverflowError, get_user_model().DoesNotExist):
+        user = None
+    if user is not None and user.groups.filter(name="PendingAccountUser").exists():
+        if tokens.account_activation_token.check_token(user, token):
+            user.is_active = True
+            user.groups.remove(user.groups.get(name="PendingAccountUser"))
+            user.save()
+            login(request, user)
+            # return redirect('home')
+            # return HttpResponse('Thank you for your email confirmation. Now you can login your account.')
+            return render(request, 'basetheme_bootstrap/simple_message_page.html', {
+                'page_title': ugettext('Account activated'),
+                'message': ugettext('Thank you for your email confirmation, you account have been activated '
+                                    'and you are now logged in.'),
+            })
+        else:
+            send_account_created(request, user)
+            return account_is_pending_view(request, email=user.email)
+    else:
+        return render(request, 'basetheme_bootstrap/simple_message_page.html', {
+            'page_title': ugettext('Activation link is invalid!'),
+        })
diff --git a/setup.py b/setup.py
index cbad581badf5325fc03a0139662bdcc04cbff788..c933e1fc9247344922958562fdd399f1dc78390f 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ readme = open('README.rst').read()
 
 setup(
     name='django-basetheme-bootstrap',
-    version='0.2.29',
+    version='0.2.30',
     description='Django Basetheme Bootstrap',
     long_description=readme,
     author='Bryan Brancotte',
diff --git a/tests/settings.py b/tests/settings.py
index 38b8df31420626ea8ccd9fe996fa788503c6d5d9..55ceb8ef2c6905f2e01e197aee9a66a202c2b734 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -138,6 +138,7 @@ BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_LOCATION_APP = "test_app_1"
 BASETHEME_BOOTSTRAP_USER_PREFERENCE_MODEL_NAME = "MyUserPreferences"
 # BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL = True # default is False
 # BASETHEME_BOOTSTRAP_FIRST_LAST_NAME_REQUIRED = True # default is False
+# BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION = True # default is False
 
 ################################################################################
 # Various debug stuff