diff --git a/apps/member/forms.py b/apps/member/forms.py
index e6e73612fead0ceb863bb7c7a255253c83a79f58..5b20fd15a1aaec1483c7d082160c061aa4156b96 100644
--- a/apps/member/forms.py
+++ b/apps/member/forms.py
@@ -2,9 +2,8 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from django import forms
-from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
+from django.contrib.auth.forms import AuthenticationForm
 from django.contrib.auth.models import User
-from django.utils.translation import ugettext_lazy as _
 from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput
 from permission.models import PermissionMask
 
@@ -19,21 +18,6 @@ class CustomAuthenticationForm(AuthenticationForm):
     )
 
 
-class SignUpForm(UserCreationForm):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.fields['username'].widget.attrs.pop("autofocus", None)
-        self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
-        self.fields['first_name'].required = True
-        self.fields['last_name'].required = True
-        self.fields['email'].required = True
-        self.fields['email'].help_text = _("This address must be valid.")
-
-    class Meta:
-        model = User
-        fields = ('first_name', 'last_name', 'username', 'email', )
-
-
 class ProfileForm(forms.ModelForm):
     """
     A form for the extras field provided by the :model:`member.Profile` model.
diff --git a/apps/member/models.py b/apps/member/models.py
index 462b23fd855eddf385665510abd5e32ed78731a7..294643afdc5e9491f401b2fc74024bc21632731c 100644
--- a/apps/member/models.py
+++ b/apps/member/models.py
@@ -12,7 +12,6 @@ from django.urls import reverse, reverse_lazy
 from django.utils.encoding import force_bytes
 from django.utils.http import urlsafe_base64_encode
 from django.utils.translation import gettext_lazy as _
-
 from member.tokens import account_activation_token
 from note.models import MembershipTransaction
 
diff --git a/apps/member/urls.py b/apps/member/urls.py
index 9b6ccbd5e24df2e41706d11b7f8855334aec78c7..37ff8d2bafc6b93bf28fbdf8588fc1821cb5ec49 100644
--- a/apps/member/urls.py
+++ b/apps/member/urls.py
@@ -7,11 +7,6 @@ from . import views
 
 app_name = 'member'
 urlpatterns = [
-    path('signup/', views.UserCreateView.as_view(), name="signup"),
-    path('accounts/activate/sent', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'),
-    path('accounts/activate/<uidb64>/<token>', views.UserActivateView.as_view(), name='account_activation'),
-
-
     path('club/', views.ClubListView.as_view(), name="club_list"),
     path('club/create/', views.ClubCreateView.as_view(), name="club_create"),
     path('club/<int:pk>/', views.ClubDetailView.as_view(), name="club_detail"),
diff --git a/apps/member/views.py b/apps/member/views.py
index 0d12839414da76cfd4500631f7d2b10a1192d661..ac5dd59ebc1daae8c32d35fbebfc938af2259639 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -12,12 +12,9 @@ from django.contrib.auth.views import LoginView
 from django.core.exceptions import ValidationError
 from django.db.models import Q
 from django.forms import HiddenInput
-from django.shortcuts import redirect, resolve_url
+from django.shortcuts import redirect
 from django.urls import reverse_lazy
-from django.utils.decorators import method_decorator
-from django.utils.http import urlsafe_base64_decode
 from django.utils.translation import gettext_lazy as _
-from django.views.decorators.csrf import csrf_protect
 from django.views.generic import CreateView, DetailView, UpdateView, TemplateView
 from django.views.generic.base import View
 from django.views.generic.edit import FormMixin
@@ -30,10 +27,9 @@ from note.tables import HistoryTable, AliasTable
 from permission.backends import PermissionBackend
 from permission.views import ProtectQuerysetMixin
 
-from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
+from .forms import ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
 from .models import Club, Membership
 from .tables import ClubTable, UserTable, MembershipTable
-from .tokens import account_activation_token
 
 
 class CustomLoginView(LoginView):
@@ -44,98 +40,6 @@ class CustomLoginView(LoginView):
         return super().form_valid(form)
 
 
-class UserCreateView(CreateView):
-    """
-    Une vue pour inscrire un utilisateur et lui créer un profil
-    """
-
-    form_class = SignUpForm
-    success_url = reverse_lazy('member:login')
-    template_name = 'member/signup.html'
-    second_form = ProfileForm
-
-    def get_context_data(self, **kwargs):
-        context = super().get_context_data(**kwargs)
-        context["profile_form"] = self.second_form()
-
-        return context
-
-    def form_valid(self, form):
-        """
-        If the form is valid, then the user is created with is_active set to False
-        so that the user cannot log in until the email has been validated.
-        """
-        profile_form = ProfileForm(self.request.POST)
-        if not profile_form.is_valid():
-            return self.form_invalid(form)
-
-        user = form.save(commit=False)
-        user.is_active = False
-        user.profile = profile_form.save(commit=False)
-        user.save()
-        user.profile.save()
-
-        user.profile.send_email_validation_link()
-
-        return super().form_valid(form)
-
-
-class UserActivateView(TemplateView):
-    title = _("Account Activation")
-    template_name = 'registration/account_activation_complete.html'
-
-    @method_decorator(csrf_protect)
-    def dispatch(self, *args, **kwargs):
-        """
-        The dispatch method looks at the request to determine whether it is a GET, POST, etc,
-        and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed
-        if not. We chose to check the token in the dispatch method to mimic PasswordReset from
-        django.contrib.auth
-        """
-        assert 'uidb64' in kwargs and 'token' in kwargs
-
-        self.validlink = False
-        user = self.get_user(kwargs['uidb64'])
-        token = kwargs['token']
-
-        if user is not None and account_activation_token.check_token(user, token):
-            self.validlink = True
-            user.is_active = True
-            user.profile.email_confirmed = True
-            user.save()
-            return super().dispatch(*args, **kwargs)
-        else:
-            # Display the "Account Activation unsuccessful" page.
-            return self.render_to_response(self.get_context_data())
-
-    def get_user(self, uidb64):
-        print(uidb64)
-        try:
-            # urlsafe_base64_decode() decodes to bytestring
-            uid = urlsafe_base64_decode(uidb64).decode()
-            user = User.objects.get(pk=uid)
-        except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
-            user = None
-        return user
-
-    def get_context_data(self, **kwargs):
-        context = super().get_context_data(**kwargs)
-        context['login_url'] = resolve_url(settings.LOGIN_URL)
-        if self.validlink:
-            context['validlink'] = True
-        else:
-            context.update({
-                'title': _('Account Activation unsuccessful'),
-                'validlink': False,
-            })
-        return context
-
-
-class UserActivationEmailSentView(TemplateView):
-    template_name = 'registration/account_activation_email_sent.html'
-    title = _('Account activation email sent')
-
-
 class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     model = User
     fields = ['first_name', 'last_name', 'username', 'email']
diff --git a/apps/registration/__init__.py b/apps/registration/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..700d9f003c8ed466ea22d3758e75eb74bf2f6fc9
--- /dev/null
+++ b/apps/registration/__init__.py
@@ -0,0 +1,4 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+default_app_config = 'registration.apps.RegistrationConfig'
diff --git a/apps/registration/api/__init__.py b/apps/registration/api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/apps/registration/apps.py b/apps/registration/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..dec89274d4cf962741a0645f0236ac37a859c8ca
--- /dev/null
+++ b/apps/registration/apps.py
@@ -0,0 +1,10 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.apps import AppConfig
+from django.utils.translation import gettext_lazy as _
+
+
+class RegistrationConfig(AppConfig):
+    name = 'registration'
+    verbose_name = _('registration')
diff --git a/apps/registration/forms.py b/apps/registration/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f4063d56246826a15bf8bf8fe7437f3ddd4f09d
--- /dev/null
+++ b/apps/registration/forms.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.contrib.auth.forms import UserCreationForm
+from django.contrib.auth.models import User
+from django.utils.translation import gettext_lazy as _
+
+
+class SignUpForm(UserCreationForm):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields['username'].widget.attrs.pop("autofocus", None)
+        self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
+        self.fields['first_name'].required = True
+        self.fields['last_name'].required = True
+        self.fields['email'].required = True
+        self.fields['email'].help_text = _("This address must be valid.")
+
+    class Meta:
+        model = User
+        fields = ('first_name', 'last_name', 'username', 'email', )
diff --git a/apps/registration/migrations/__init__.py b/apps/registration/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/apps/registration/urls.py b/apps/registration/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..e752922c935c3bde04e9f1c7b0e46e35cd6d2584
--- /dev/null
+++ b/apps/registration/urls.py
@@ -0,0 +1,13 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.urls import path
+
+from . import views
+
+app_name = 'registration'
+urlpatterns = [
+    path('signup/', views.UserCreateView.as_view(), name="signup"),
+    path('accounts/activate/sent', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'),
+    path('accounts/activate/<uidb64>/<token>', views.UserActivateView.as_view(), name='account_activation'),
+]
diff --git a/apps/registration/views.py b/apps/registration/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4a26c3b8202112637033b509c25fd742d45e98c
--- /dev/null
+++ b/apps/registration/views.py
@@ -0,0 +1,110 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.core.exceptions import ValidationError
+from django.shortcuts import resolve_url
+from django.urls import reverse_lazy
+from django.utils.decorators import method_decorator
+from django.utils.http import urlsafe_base64_decode
+from django.utils.translation import gettext_lazy as _
+from django.views.decorators.csrf import csrf_protect
+from django.views.generic import CreateView, TemplateView
+from member.forms import ProfileForm
+from member.tokens import account_activation_token
+
+from .forms import SignUpForm
+
+
+class UserCreateView(CreateView):
+    """
+    Une vue pour inscrire un utilisateur et lui créer un profil
+    """
+
+    form_class = SignUpForm
+    success_url = reverse_lazy('member:login')
+    template_name = 'member/signup.html'
+    second_form = ProfileForm
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context["profile_form"] = self.second_form()
+
+        return context
+
+    def form_valid(self, form):
+        """
+        If the form is valid, then the user is created with is_active set to False
+        so that the user cannot log in until the email has been validated.
+        """
+        profile_form = ProfileForm(self.request.POST)
+        if not profile_form.is_valid():
+            return self.form_invalid(form)
+
+        user = form.save(commit=False)
+        user.is_active = False
+        user.profile = profile_form.save(commit=False)
+        user.save()
+        user.profile.save()
+
+        user.profile.send_email_validation_link()
+
+        return super().form_valid(form)
+
+
+class UserActivateView(TemplateView):
+    title = _("Account Activation")
+    template_name = 'registration/account_activation_complete.html'
+
+    @method_decorator(csrf_protect)
+    def dispatch(self, *args, **kwargs):
+        """
+        The dispatch method looks at the request to determine whether it is a GET, POST, etc,
+        and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed
+        if not. We chose to check the token in the dispatch method to mimic PasswordReset from
+        django.contrib.auth
+        """
+        assert 'uidb64' in kwargs and 'token' in kwargs
+
+        self.validlink = False
+        user = self.get_user(kwargs['uidb64'])
+        token = kwargs['token']
+
+        if user is not None and account_activation_token.check_token(user, token):
+            self.validlink = True
+            user.is_active = True
+            user.profile.email_confirmed = True
+            user.save()
+            return super().dispatch(*args, **kwargs)
+        else:
+            # Display the "Account Activation unsuccessful" page.
+            return self.render_to_response(self.get_context_data())
+
+    def get_user(self, uidb64):
+        print(uidb64)
+        try:
+            # urlsafe_base64_decode() decodes to bytestring
+            uid = urlsafe_base64_decode(uidb64).decode()
+            user = User.objects.get(pk=uid)
+        except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
+            user = None
+        return user
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['login_url'] = resolve_url(settings.LOGIN_URL)
+        if self.validlink:
+            context['validlink'] = True
+        else:
+            context.update({
+                'title': _('Account Activation unsuccessful'),
+                'validlink': False,
+            })
+        return context
+
+
+class UserActivationEmailSentView(TemplateView):
+    template_name = 'registration/account_activation_email_sent.html'
+    title = _('Account activation email sent')
+
diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py
index 61e5ea519acf223b1155242fc077040ec79c15c8..283f8e5606dec26e89b738314b4c4d0f881005b8 100644
--- a/note_kfet/settings/base.py
+++ b/note_kfet/settings/base.py
@@ -54,13 +54,14 @@ INSTALLED_APPS = [
     'rest_framework.authtoken',
 
     # Note apps
+    'api',
     'activity',
+    'logs',
     'member',
     'note',
-    'treasury',
     'permission',
-    'api',
-    'logs',
+    'registration',
+    'treasury',
 ]
 LOGIN_REDIRECT_URL = '/note/transfer/'
 
diff --git a/note_kfet/urls.py b/note_kfet/urls.py
index a7afab29c19491762e45de07d1ecab6d50788ea4..90d44a07e5dbae32e70629291a43fedc1519ba4f 100644
--- a/note_kfet/urls.py
+++ b/note_kfet/urls.py
@@ -16,6 +16,7 @@ urlpatterns = [
     # Include project routers
     path('note/', include('note.urls')),
     path('accounts/', include('member.urls')),
+    path('registration/', include('registration.urls')),
     path('activity/', include('activity.urls')),
     path('treasury/', include('treasury.urls')),
 
@@ -37,14 +38,7 @@ if "cas_server" in settings.INSTALLED_APPS:
         # Include CAS Server routers
         path('cas/', include('cas_server.urls', namespace="cas_server")),
     ]
-if "cas" in settings.INSTALLED_APPS:
-    from cas import views as cas_views
-    urlpatterns += [
-        # Include CAS Client routers
-        path('accounts/login/cas/', cas_views.login, name='cas_login'),
-        path('accounts/logout/cas/', cas_views.logout, name='cas_logout'),
 
-    ]
 if "debug_toolbar" in settings.INSTALLED_APPS:
     import debug_toolbar
     urlpatterns = [
diff --git a/templates/base.html b/templates/base.html
index 9f1310543bc264a11b07aabf73163447d39ebd51..8e16a419662d8653f6d53cedb16f09a429b15daa 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -124,7 +124,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                     </li>
                 {% else %}
                     <li class="nav-item active">
-                        <a class="nav-link" href="{% url 'member:signup' %}">
+                        <a class="nav-link" href="{% url 'registration:signup' %}">
                             <i class="fa fa-user-plus"></i> S'inscrire
                         </a>
                     </li>
@@ -138,7 +138,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
         </div>
     </nav>
     <div class="container-fluid my-3" style="max-width: 1600px;">
-        {% if not user.profile.email_confirmed %}
+        {% if user.is_authenticated and not user.profile.email_confirmed %}
             <div class="alert alert-warning">
                 {% trans "Your e-mail address is not validated. Please check your mail inbox and click on the validation link." %}
             </div>