diff --git a/CHANGELOG b/CHANGELOG
index 07d1dedbd2ba7450295491d6ce4d5cfaf1ccfb01..d010c076e34ef123fa2800eb7e541b9c8c3bd65b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,6 +7,10 @@ Changelog
 
 - Front: Now reset player colors when track has no cover (#46)
 - Front: play button now disabled for unplayable tracks
+- API: You can now enable or disable registration on the fly, via a preference (#58)
+- Front: can now signup via the web interface (#35)
+- Front: Fixed broken redirection on login
+- Front: Fixed broken error handling on settings and login form
 
 Transcoding:
 
diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index 5fe55e53a33fdc58384d858370b4e2a5ba646cb9..491babdd15f8d4c017230f097af4b274c880a000 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -264,7 +264,7 @@ AUTHENTICATION_BACKENDS = (
 )
 
 # Some really nice defaults
-ACCOUNT_AUTHENTICATION_METHOD = 'username'
+ACCOUNT_AUTHENTICATION_METHOD = 'username_email'
 ACCOUNT_EMAIL_REQUIRED = True
 ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
 
@@ -317,7 +317,6 @@ CORS_ORIGIN_ALLOW_ALL = True
 # )
 CORS_ALLOW_CREDENTIALS = True
 API_AUTHENTICATION_REQUIRED = env.bool("API_AUTHENTICATION_REQUIRED", True)
-REGISTRATION_MODE = env('REGISTRATION_MODE', default='disabled')
 REST_FRAMEWORK = {
     'DEFAULT_PERMISSION_CLASSES': (
         'rest_framework.permissions.IsAuthenticated',
diff --git a/api/config/urls.py b/api/config/urls.py
index de67ebb571de4b5f4e15cedd969d1adadeb42aee..8f7e37bc26ae56ba9967682f4ec3f19f04cc71f4 100644
--- a/api/config/urls.py
+++ b/api/config/urls.py
@@ -13,8 +13,8 @@ urlpatterns = [
     url(settings.ADMIN_URL, admin.site.urls),
 
     url(r'^api/', include(("config.api_urls", 'api'), namespace="api")),
-    url(r'^api/auth/', include('rest_auth.urls')),
-    url(r'^api/auth/registration/', include('funkwhale_api.users.rest_auth_urls')),
+    url(r'^api/v1/auth/', include('rest_auth.urls')),
+    url(r'^api/v1/auth/registration/', include('funkwhale_api.users.rest_auth_urls')),
     url(r'^accounts/', include('allauth.urls')),
 
     # Your stuff: custom urls includes go here
diff --git a/api/funkwhale_api/users/adapters.py b/api/funkwhale_api/users/adapters.py
index 792b4860fc240439f72c8cd7dc2fe319ac633fa1..96d1b8b1d6b4aaa708bc3c09490beae84c10fa1a 100644
--- a/api/funkwhale_api/users/adapters.py
+++ b/api/funkwhale_api/users/adapters.py
@@ -1,15 +1,10 @@
 from allauth.account.adapter import DefaultAccountAdapter
 
-from django.conf import settings
+from dynamic_preferences.registries import global_preferences_registry
 
 
 class FunkwhaleAccountAdapter(DefaultAccountAdapter):
 
     def is_open_for_signup(self, request):
-
-        if settings.REGISTRATION_MODE == "disabled":
-            return False
-        if settings.REGISTRATION_MODE == "public":
-            return True
-
-        return False
+        manager = global_preferences_registry.manager()
+        return manager['users__registration_enabled']
diff --git a/api/funkwhale_api/users/dynamic_preferences_registry.py b/api/funkwhale_api/users/dynamic_preferences_registry.py
new file mode 100644
index 0000000000000000000000000000000000000000..16d79da143cb3139f9a92137044b6092288e1b40
--- /dev/null
+++ b/api/funkwhale_api/users/dynamic_preferences_registry.py
@@ -0,0 +1,15 @@
+from dynamic_preferences import types
+from dynamic_preferences.registries import global_preferences_registry
+
+users = types.Section('users')
+
+
+@global_preferences_registry.register
+class RegistrationEnabled(types.BooleanPreference):
+    show_in_api = True
+    section = users
+    name = 'registration_enabled'
+    default = False
+    verbose_name = (
+        'Can visitors open a new account on this instance?'
+    )
diff --git a/api/tests/users/test_views.py b/api/tests/users/test_views.py
index 1eb8ef222a79d68f41d40c8555c0c7cb9d931680..569acbd15ee5138150dd7de4112cd7ebd2d5523a 100644
--- a/api/tests/users/test_views.py
+++ b/api/tests/users/test_views.py
@@ -6,7 +6,7 @@ from django.urls import reverse
 from funkwhale_api.users.models import User
 
 
-def test_can_create_user_via_api(settings, client, db):
+def test_can_create_user_via_api(preferences, client, db):
     url = reverse('rest_register')
     data = {
         'username': 'test1',
@@ -14,7 +14,7 @@ def test_can_create_user_via_api(settings, client, db):
         'password1': 'testtest',
         'password2': 'testtest',
     }
-    settings.REGISTRATION_MODE = "public"
+    preferences['users__registration_enabled'] = True
     response = client.post(url, data)
     assert response.status_code == 201
 
@@ -22,7 +22,7 @@ def test_can_create_user_via_api(settings, client, db):
     assert u.username == 'test1'
 
 
-def test_can_disable_registration_view(settings, client, db):
+def test_can_disable_registration_view(preferences, client, db):
     url = reverse('rest_register')
     data = {
         'username': 'test1',
@@ -30,7 +30,7 @@ def test_can_disable_registration_view(settings, client, db):
         'password1': 'testtest',
         'password2': 'testtest',
     }
-    settings.REGISTRATION_MODE = "disabled"
+    preferences['users__registration_enabled'] = False
     response = client.post(url, data)
     assert response.status_code == 403
 
diff --git a/deploy/env.prod.sample b/deploy/env.prod.sample
index 5bdfeb9c626fa074eaedb5c7f499871946d3dea9..6a4b15b67cf04857b87296c30a23060e650f2d7c 100644
--- a/deploy/env.prod.sample
+++ b/deploy/env.prod.sample
@@ -74,11 +74,6 @@ DJANGO_SECRET_KEY=
 # If True, unauthenticated users won't be able to query the API
 API_AUTHENTICATION_REQUIRED=True
 
-# What is the workflow for registration on funkwhale ? Possible values:
-# public: anybody can register an account
-# disabled: nobody can register an account
-REGISTRATION_MODE=disabled
-
 # Sentry/Raven error reporting (server side)
 # Enable Raven if you want to help improve funkwhale by
 # automatically sending error reports our Sentry instance.
diff --git a/front/src/components/auth/Login.vue b/front/src/components/auth/Login.vue
index 99b439af8b3e25c821ddb1dbc0c86c6419961d2c..2cf6d5f6db1cd8bc4e4d72d96be5c70d559e9452 100644
--- a/front/src/components/auth/Login.vue
+++ b/front/src/components/auth/Login.vue
@@ -12,13 +12,13 @@
             </ul>
           </div>
           <div class="field">
-            <label>Username</label>
+            <label>Username or email</label>
             <input
             ref="username"
             required
             type="text"
             autofocus
-            placeholder="Enter your username"
+            placeholder="Enter your username or email"
             v-model="credentials.username"
             >
           </div>
@@ -32,6 +32,9 @@
             >
           </div>
           <button :class="['ui', {'loading': isLoading}, 'button']" type="submit">Login</button>
+          <router-link class="ui right floated basic button" :to="{path: '/signup'}">
+            Create an account
+          </router-link>
         </form>
       </div>
     </div>
@@ -73,9 +76,9 @@ export default {
       // to properly make use of http in the auth service
       this.$store.dispatch('auth/login', {
         credentials,
-        next: this.next,
-        onError: response => {
-          if (response.status === 400) {
+        next: '/library',
+        onError: error => {
+          if (error.response.status === 400) {
             self.error = 'invalid_credentials'
           } else {
             self.error = 'unknown_error'
diff --git a/front/src/components/auth/Settings.vue b/front/src/components/auth/Settings.vue
index f090581ef7305927e293e3f6ffce3e6033131004..4e8f33289b470526539e71ff40f6de32a2857dcb 100644
--- a/front/src/components/auth/Settings.vue
+++ b/front/src/components/auth/Settings.vue
@@ -37,7 +37,6 @@
 
 <script>
 import axios from 'axios'
-import config from '@/config'
 import logger from '@/logging'
 
 export default {
@@ -61,12 +60,16 @@ export default {
         new_password1: this.new_password,
         new_password2: this.new_password
       }
-      let url = config.BACKEND_URL + 'api/auth/registration/change-password/'
+      let url = 'auth/registration/change-password/'
       return axios.post(url, credentials).then(response => {
         logger.default.info('Password successfully changed')
-        self.$router.push('/profile/me')
-      }, response => {
-        if (response.status === 400) {
+        self.$router.push({
+          name: 'profile',
+          params: {
+            username: self.$store.state.auth.username
+          }})
+      }, error => {
+        if (error.response.status === 400) {
           self.error = 'invalid_credentials'
         } else {
           self.error = 'unknown_error'
diff --git a/front/src/components/auth/Signup.vue b/front/src/components/auth/Signup.vue
new file mode 100644
index 0000000000000000000000000000000000000000..13b723d201437933d9f6fd46bcaccfb2d316f5ed
--- /dev/null
+++ b/front/src/components/auth/Signup.vue
@@ -0,0 +1,137 @@
+<template>
+  <div class="main pusher">
+    <div class="ui vertical stripe segment">
+      <div class="ui small text container">
+        <h2>Create a funkwhale account</h2>
+        <form
+          v-if="$store.state.instance.settings.users.registration_enabled.value"
+          :class="['ui', {'loading': isLoadingInstanceSetting}, 'form']"
+          @submit.prevent="submit()">
+          <div v-if="errors.length > 0" class="ui negative message">
+            <div class="header">We cannot create your account</div>
+            <ul class="list">
+              <li v-for="error in errors">{{ error }}</li>
+            </ul>
+          </div>
+          <div class="field">
+            <label>Username</label>
+            <input
+            ref="username"
+            required
+            type="text"
+            autofocus
+            placeholder="Enter your username"
+            v-model="username">
+          </div>
+          <div class="field">
+            <label>Email</label>
+            <input
+            ref="email"
+            required
+            type="email"
+            placeholder="Enter your email"
+            v-model="email">
+          </div>
+          <div class="field">
+            <label>Password</label>
+            <div class="ui action input">
+              <input
+              required
+              :type="passwordInputType"
+              placeholder="Enter your password"
+              v-model="password">
+              <span @click="showPassword = !showPassword" title="Show/hide password" class="ui icon button">
+                <i class="eye icon"></i>
+              </span>
+            </div>
+          </div>
+          <button :class="['ui', 'green', {'loading': isLoading}, 'button']" type="submit">Create my account</button>
+        </form>
+        <p v-else>Registration is currently disabled on this instance, please try again later.</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+import logger from '@/logging'
+
+export default {
+  name: 'login',
+  props: {
+    next: {type: String, default: '/'}
+  },
+  data () {
+    return {
+      username: '',
+      email: '',
+      password: '',
+      isLoadingInstanceSetting: true,
+      errors: [],
+      isLoading: false,
+      showPassword: false
+    }
+  },
+  created () {
+    let self = this
+    this.$store.dispatch('instance/fetchSettings', {
+      callback: function () {
+        self.isLoadingInstanceSetting = false
+      }
+    })
+  },
+  methods: {
+    submit () {
+      var self = this
+      self.isLoading = true
+      this.errors = []
+      var payload = {
+        username: this.username,
+        password1: this.password,
+        password2: this.password,
+        email: this.email
+      }
+      return axios.post('auth/registration/', payload).then(response => {
+        logger.default.info('Successfully created account')
+        self.$router.push({
+          name: 'profile',
+          params: {
+            username: this.username
+          }})
+      }, error => {
+        self.errors = this.getErrors(error.response)
+        self.isLoading = false
+      })
+    },
+    getErrors (response) {
+      let errors = []
+      if (response.status !== 400) {
+        errors.push('An unknown error occured, ensure your are connected to the internet and your funkwhale instance is up and running')
+        return errors
+      }
+      for (var field in response.data) {
+        if (response.data.hasOwnProperty(field)) {
+          response.data[field].forEach(e => {
+            errors.push(e)
+          })
+        }
+      }
+      return errors
+    }
+  },
+  computed: {
+    passwordInputType () {
+      if (this.showPassword) {
+        return 'text'
+      }
+      return 'password'
+    }
+  }
+
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+</style>
diff --git a/front/src/router/index.js b/front/src/router/index.js
index ea8854bbe48e4f06ec4369a2853b3e98271df733..c1d03e059442ff98731bb903159d532b62ef4286 100644
--- a/front/src/router/index.js
+++ b/front/src/router/index.js
@@ -3,6 +3,7 @@ import Router from 'vue-router'
 import PageNotFound from '@/components/PageNotFound'
 import Home from '@/components/Home'
 import Login from '@/components/auth/Login'
+import Signup from '@/components/auth/Signup'
 import Profile from '@/components/auth/Profile'
 import Settings from '@/components/auth/Settings'
 import Logout from '@/components/auth/Logout'
@@ -38,6 +39,11 @@ export default new Router({
       component: Login,
       props: (route) => ({ next: route.query.next || '/library' })
     },
+    {
+      path: '/signup',
+      name: 'signup',
+      component: Signup
+    },
     {
       path: '/logout',
       name: 'logout',
diff --git a/front/src/store/instance.js b/front/src/store/instance.js
index a0071f0961d6536f2133331787d90245a6dc1df4..80003db0dce3e1d27ff3de814a8149826dafd6db 100644
--- a/front/src/store/instance.js
+++ b/front/src/store/instance.js
@@ -6,6 +6,11 @@ export default {
   namespaced: true,
   state: {
     settings: {
+      users: {
+        registration_enabled: {
+          value: true
+        }
+      },
       raven: {
         front_enabled: {
           value: false
@@ -23,7 +28,7 @@ export default {
   },
   actions: {
     // Send a request to the login URL and save the returned JWT
-    fetchSettings ({commit}) {
+    fetchSettings ({commit}, payload) {
       return axios.get('instance/settings/').then(response => {
         logger.default.info('Successfully fetched instance settings')
         let sections = {}
@@ -34,6 +39,9 @@ export default {
           sections[e.section][e.name] = e
         })
         commit('settings', sections)
+        if (payload && payload.callback) {
+          callback()
+        }
       }, response => {
         logger.default.error('Error while fetching settings', response.data)
       })