diff --git a/SciPost_v1/urls.py b/SciPost_v1/urls.py
index 7b29434e2aae982aa946d1363cfefcf35d4f4d79..13711bb780de959a0325ff40ee66840435e020c2 100644
--- a/SciPost_v1/urls.py
+++ b/SciPost_v1/urls.py
@@ -1,18 +1,3 @@
-"""SciPost_v1 URL Configuration
-
-The `urlpatterns` list routes URLs to views. For more information please see:
-    https://docs.djangoproject.com/en/1.8/topics/http/urls/
-Examples:
-Function views
-    1. Add an import:  from my_app import views
-    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
-Class-based views
-    1. Add an import:  from other_app.views import Home
-    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
-Including another URLconf
-    1. Add an import:  from blog import urls as blog_urls
-    2. Add a URL to urlpatterns:  url(r'^blog/', include(blog_urls))
-"""
 from django.conf import settings
 from django.conf.urls import include, url
 from django.contrib import admin
diff --git a/partners/forms.py b/partners/forms.py
index 32e4956b75281473bfb567a54da5ed1266126017..3f7b2d9f53fb14c9805a44a9ec212e9b48734535 100644
--- a/partners/forms.py
+++ b/partners/forms.py
@@ -1,5 +1,7 @@
 from django import forms
 from django.contrib.auth.models import User
+from django.contrib.auth.password_validation import validate_password
+from django.core.exceptions import ValidationError
 from django.db.models import Q
 
 from captcha.fields import ReCaptchaField
@@ -14,6 +16,37 @@ from .models import Partner, ProspectivePartner, ProspectiveContact, Prospective
 from scipost.models import TITLE_CHOICES
 
 
+class ActivationForm(forms.ModelForm):
+    class Meta:
+        model = User
+        fields = []
+
+    password_new = forms.CharField(label='* Password', widget=forms.PasswordInput())
+    password_verif = forms.CharField(label='* Verify password', widget=forms.PasswordInput(),
+                                     help_text='Your password must contain at least 8 characters')
+
+    def clean_password(self):
+        password = self.cleaned_data.get('password_new', '')
+        try:
+            validate_password(password, self.instance)
+        except ValidationError as error_message:
+            self.add_error('password_new', error_message)
+        return password
+
+    def clean_password_verif(self):
+        if self.cleaned_data.get('password_new', '') != self.cleaned_data.get('password_verif', ''):
+            self.add_error('password_verif', 'Your password entries must match')
+        return self.cleaned_data.get('password_verif', '')
+
+    def activate_user(self):
+        if self.errors:
+            return forms.ValidationError
+        self.instance.is_active = True
+        self.instance.set_password(self.cleaned_data['password_new'])
+        self.instance.save()
+        return self.instance
+
+
 class InstitutionForm(forms.ModelForm):
     class Meta:
         model = Institution
@@ -123,6 +156,7 @@ class NewContactForm(ContactForm):
             title=self.cleaned_data['title'],
             kind=self.cleaned_data['kind']
         )
+        contact.generate_key()
         contact.save()
         contact.partners.add(self.partner)
         # TODO: Send mail to contact to let him/her activate account
diff --git a/partners/migrations/0016_auto_20170624_0905.py b/partners/migrations/0016_auto_20170624_0905.py
new file mode 100644
index 0000000000000000000000000000000000000000..e44535151dfb413e5aea7db04c29a82f36dea374
--- /dev/null
+++ b/partners/migrations/0016_auto_20170624_0905.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-06-24 07:05
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('partners', '0015_auto_20170620_1634'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='contact',
+            name='activation_key',
+            field=models.CharField(blank=True, max_length=40),
+        ),
+        migrations.AddField(
+            model_name='contact',
+            name='key_expires',
+            field=models.DateTimeField(default=django.utils.timezone.now),
+        ),
+        migrations.AlterField(
+            model_name='membershipagreement',
+            name='partner',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='agreements', to='partners.Partner'),
+        ),
+        migrations.AlterField(
+            model_name='partner',
+            name='status',
+            field=models.CharField(choices=[('Initiated', 'Initiated'), ('Contacted', 'Contacted'), ('Negotiating', 'Negotiating'), ('Uninterested', 'Uninterested'), ('Active', 'Active'), ('Inactive', 'Inactive')], default='Initiated', max_length=16),
+        ),
+        migrations.AlterField(
+            model_name='prospectivecontact',
+            name='prospartner',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prospective_contacts', to='partners.ProspectivePartner'),
+        ),
+    ]
diff --git a/partners/models.py b/partners/models.py
index 5989cd4abc133c8fffa644bd2073f5cb5aed4de9..1b6b277084f4e91f642e80a608fcbf0307d714de 100644
--- a/partners/models.py
+++ b/partners/models.py
@@ -1,5 +1,11 @@
+import datetime
+import hashlib
+import random
+import string
+
 from django.contrib.auth.models import User
 from django.db import models
+from django.utils import timezone
 
 from django_countries.fields import CountryField
 
@@ -126,10 +132,23 @@ class Contact(models.Model):
     consortia = models.ManyToManyField('partners.Consortium', blank=True,
                                        help_text=('All Consortia for which the Contact has'
                                                   ' explicit permission to view/edit its data.'))
+    activation_key = models.CharField(max_length=40, blank=True)
+    key_expires = models.DateTimeField(default=timezone.now)
 
     def __str__(self):
         return '%s %s, %s' % (self.get_title_display(), self.user.last_name, self.user.first_name)
 
+    def generate_key(self, feed=''):
+        """
+        Generate and save a new activation_key for the Contact, given a certain feed.
+        """
+        for i in range(5):
+            feed += random.choice(string.ascii_letters)
+        feed = feed.encode('utf8')
+        salt = self.user.username.encode('utf8')
+        self.activation_key = hashlib.sha1(salt+salt).hexdigest()
+        self.key_expires = datetime.datetime.now() + datetime.timedelta(days=2)
+
     def delete_or_remove_partner(self, partner, *args, **kwargs):
         """
         Custom `delete` method as the contact does not always need to be deleted,
diff --git a/partners/templates/partners/activate_account.html b/partners/templates/partners/activate_account.html
new file mode 100644
index 0000000000000000000000000000000000000000..0e3aef12f1e53ddb919c6a2fc5f06620e70f6f36
--- /dev/null
+++ b/partners/templates/partners/activate_account.html
@@ -0,0 +1,31 @@
+{% extends 'scipost/base.html' %}
+
+
+{% block pagetitle %}{{block.super}} Activate Account{% endblock pagetitle %}
+
+{% load bootstrap %}
+
+{% block content %}
+
+<div class="row">
+    <div class="col-12">
+        <h1 class="highlight">Activate Account</h1>
+    </div>
+    <div class="col-md-8 offset-md-2">
+        <h2>{{contact.get_title_display}} {{contact.user.first_name}} {{contact.user.last_name}}</h2>
+        <h3>{{contact.user.email}}</h3>
+    </div>
+</div>
+
+<div class="row">
+    <div class="col-md-8 offset-md-2 mb-5">
+      <form method="post">
+        {% csrf_token %}
+        {{ form|bootstrap }}
+
+        <input class="btn btn-primary" type="submit" value="Activate"/>
+      </form>
+    </div>
+</div>
+
+{% endblock content %}
diff --git a/partners/urls.py b/partners/urls.py
index 3c3e1d14fc31304bd9cb181604353b4e7ea66f43..6faf56a0a6eb3e180c35e5dfaab2d70170e77761 100644
--- a/partners/urls.py
+++ b/partners/urls.py
@@ -23,6 +23,9 @@ urlpatterns = [
     url(r'institutions/(?P<institution_id>[0-9]+)/edit$', views.institution_edit,
         name='institution_edit'),
 
+    # Users
+    url(r'activate/(?P<activation_key>.+)$', views.activate_account, name='activate_account'),
+
     # Partners
     url(r'(?P<partner_id>[0-9]+)/edit$', views.partner_edit, name='partner_edit'),
     url(r'(?P<partner_id>[0-9]+)/contacts/add$', views.partner_add_contact,
diff --git a/partners/views.py b/partners/views.py
index 6d0265f2042387775ccae563f2ebf3af7bedf3c4..423fceaa671d7248d1267e6577ebac4eb18d9bfd 100644
--- a/partners/views.py
+++ b/partners/views.py
@@ -15,7 +15,7 @@ from .forms import ProspectivePartnerForm, ProspectiveContactForm,\
                    EmailProspectivePartnerContactForm, PromoteToPartnerForm,\
                    ProspectivePartnerEventForm, MembershipQueryForm, PromoteToContactForm,\
                    PromoteToContactFormset, PartnerForm, ContactForm, ContactFormset,\
-                   NewContactForm, InstitutionForm
+                   NewContactForm, InstitutionForm, ActivationForm
 
 from .utils import PartnerUtils
 
@@ -29,6 +29,7 @@ def supporting_partners(request):
     return render(request, 'partners/supporting_partners.html', context)
 
 
+# @login_required
 @permission_required('scipost.can_read_personal_page', return_403=True)
 def dashboard(request):
     '''
@@ -238,3 +239,24 @@ def add_prospartner_event(request, prospartner_id):
             return render(request, 'scipost/error.html', {'errormessage': errormessage})
     errormessage = 'This view can only be posted to.'
     return render(request, 'scipost/error.html', {'errormessage': errormessage})
+
+
+#########
+# Account
+#########
+def activate_account(request, activation_key):
+    contact = get_object_or_404(Contact, user__is_active=False,
+                                activation_key=activation_key,
+                                user__email__icontains=request.GET.get('email', None))
+
+    # TODO: Key Expires fallback
+    form = ActivationForm(request.POST or None, instance=contact.user)
+    if form.is_valid():
+        form.activate_user()
+        messages.success(request, '<h3>Thank you for registration</h3>.')
+        return redirect(reverse('partners:dashboard'))
+    context = {
+        'contact': contact,
+        'form': form
+    }
+    return render(request, 'partners/activate_account.html', context)
diff --git a/scipost/views.py b/scipost/views.py
index 229123403a324cc62918110819e7349e0045dc0b..2bba5fd6a3d6d03a0b48c64499743e8fd4720a51 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -46,7 +46,11 @@ from theses.models import ThesisLink
 ##############
 
 def is_registered(user):
-    return user.groups.filter(name='Registered Contributors').exists()
+    if user.groups.filter(name='Registered Contributors').exists():
+        return True
+    if user.partner_contact:
+        return True
+    return False
 
 
 # Global search