diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee
index 9110b732adc825d36a6377571cdf5b23859b74a8..59d44c30bee04297c9736bb89786996364880dc2 100644
--- a/app/assets/javascripts/profile.js.coffee
+++ b/app/assets/javascripts/profile.js.coffee
@@ -4,12 +4,13 @@ class @Profile
     $('.js-preferences-form').on 'change.preference', 'input[type=radio]', ->
       $(this).parents('form').submit()
 
-    $('.update-username form').on 'ajax:before', ->
-      $('.loading-gif').show()
+    $('.update-username').on 'ajax:before', ->
+      $('.loading-username').show()
       $(this).find('.update-success').hide()
       $(this).find('.update-failed').hide()
 
-    $('.update-username form').on 'ajax:complete', ->
+    $('.update-username').on 'ajax:complete', ->
+      $('.loading-username').hide()
       $(this).find('.btn-save').enable()
       $(this).find('.loading-gif').hide()
 
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index c98e43ad09f586ffa00355b420e829eb7346dd6e..ff551f151f1eb7eb1c3e507ef35da3afe3d921a3 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -12,11 +12,13 @@
 .prepend-top-default { margin-top: $gl-padding !important; }
 .prepend-top-20 { margin-top:20px }
 .prepend-left-10 { margin-left:10px }
-.prepend-left-default { margin-left:$gl-padding }
+.prepend-left-default { margin-left: $gl-padding; }
 .prepend-left-20 { margin-left:20px }
 .append-right-5 { margin-right: 5px }
 .append-right-10 { margin-right:10px }
+.append-right-default { margin-right: $gl-padding; }
 .append-right-20 { margin-right:20px }
+.append-bottom-0 { margin-bottom:0 }
 .append-bottom-10 { margin-bottom:10px }
 .append-bottom-15 { margin-bottom:15px }
 .append-bottom-20 { margin-bottom:20px }
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 6561b3de7c1d8299e3767bf86554df04e2c8b324..0fd2f74cf89e3ac7aff9437c53cc517e8856e41a 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -70,7 +70,7 @@ $orange-light: rgba(252, 109, 38, 0.80);
 $orange-normal: #E75E40;
 $orange-dark: #CE5237;
 
-$red-light: #F43263;
+$red-light: #F06559;
 $red-normal: #E52C5A;
 $red-dark: #D22852;
 
@@ -94,7 +94,7 @@ $border-orange-light: #fc6d26;
 $border-orange-normal: #CE5237;
 $border-orange-dark: #C14E35;
 
-$border-red-light: #E52C5A;
+$border-red-light: #F24F41;
 $border-red-normal: #D22852;
 $border-red-dark: #CA264F;
 
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 4826b994e372243a68da6ada51f88adad24f2ce9..f8aeab6857f360f544d4ff9a45f06f13855a3da8 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -1,7 +1,6 @@
-.account-page {
-  fieldset {
-    margin-bottom: 15px;
-    padding-bottom: 15px;
+.profile-avatar-form-option {
+  hr {
+    margin: 10px 0;
   }
 }
 
@@ -175,3 +174,44 @@
     color: $profile-settings-link-color;
   }
 }
+
+.change-username-title {
+  color: #FC6D26;
+}
+
+.remove-account-title {
+  color: #F00;
+}
+
+.provider-btn-group {
+  display: inline-block;
+  margin-right: 10px;
+  border: 1px solid #E5E5E5;
+  border-radius: 3px;
+
+  &:last-child {
+    margin-right: 0;
+  }
+}
+
+.provider-btn-image {
+  display: inline-block;
+  padding: 5px 10px;
+  border-right: 1px solid #E5E5E5;
+
+  > img {
+    width: 20px;
+  }
+}
+
+.provider-btn {
+  display: inline-block;
+  padding: 5px 10px;
+  margin-left: -3px;
+  line-height: 22px;
+  background-color: $gray-light;
+
+  &.not-active {
+    color: #4688F1;
+  }
+}
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index 175afbf84259fa09434ce6da423a14d90d7a9b3f..669fe05e5c7f75bd96be06b5b66028bae41d7aa1 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -1,6 +1,18 @@
 class Profiles::AccountsController < Profiles::ApplicationController
   def show
+    unless current_user.otp_secret
+      current_user.otp_secret = User.generate_otp_secret(32)
+    end
+
+    unless current_user.otp_grace_period_started_at && two_factor_grace_period
+      current_user.otp_grace_period_started_at = Time.current
+    end
+
+    current_user.save! if current_user.changed?
+
     @user = current_user
+
+    @qr_code = build_qr_code
   end
 
   def unlink
@@ -8,4 +20,16 @@ class Profiles::AccountsController < Profiles::ApplicationController
     current_user.identities.find_by(provider: provider).destroy
     redirect_to profile_account_path
   end
+
+  private
+
+  def build_qr_code
+    issuer = "#{issuer_host} | #{current_user.email}"
+    uri = current_user.otp_provisioning_uri(current_user.email, issuer: issuer)
+    RQRCode::render_qrcode(uri, :svg, level: :m, unit: 3)
+  end
+
+  def issuer_host
+    Gitlab.config.gitlab.host
+  end
 end
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 9fa96084f9423c98dba50ac879f5d643ac8ce584..3e8f660635820d5a51ee38af778d0a306881381e 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -5,114 +5,131 @@
   .alert.alert-info
     Some options are unavailable for LDAP accounts
 
-.account-page.prepend-top-default
-  .panel.panel-default.update-token
-    .panel-heading
-      Reset Private token
-    .panel-body
-      = form_for @user, url: reset_private_token_profile_path, method: :put do |f|
-        .data
-          %p
-            Your private token is used to access application resources without authentication.
-            %br
-            It can be used for atom feeds or the API.
-            %span.cred
-              Keep it secret!
-
-          %p.cgray
-            - if current_user.private_token
-              = text_field_tag "token", current_user.private_token, class: "form-control"
-            - else
-              %span You don`t have one yet. Click generate to fix it.
-
-        .form-actions
-          - if current_user.private_token
-            = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default"
+.row.prepend-top-default
+  .col-lg-3.profile-settings-sidebar
+    %h4.prepend-top-0
+      Private Token
+    %p
+      Your private token is used to access application resources without authentication.
+  .col-lg-9
+    = form_for @user, url: reset_private_token_profile_path, method: :put do |f|
+      %p.cgray
+        - if current_user.private_token
+          = label_tag "token", "Private token", class: "label-light"
+          = text_field_tag "token", current_user.private_token, class: "form-control"
+        - else
+          %span You don`t have one yet. Click generate to fix it.
+      %p.help-block
+        It can be used for atom feeds or the API. Keep it secret!
+      .prepend-top-default
+        - if current_user.private_token
+          = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default"
+        - else
+          = f.submit 'Generate', class: "btn btn-default"
+%hr
+.row.prepend-top-default
+  .col-lg-3.profile-settings-sidebar
+    %h4.prepend-top-0
+      Two-factor Authentication
+    %p
+      Increase your account's security by enabling two-factor authentication (2FA).
+  .col-lg-9
+    %p
+      Status: #{current_user.two_factor_enabled? ? 'enabled' : 'disabled'}
+    - if !current_user.two_factor_enabled?
+      %p
+        Download the Google Authenticator application from App Store for iOS or Google Play for Android and scan this code.
+        More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}.
+      .row.append-bottom-10
+        .col-md-3
+          = raw @qr_code
+        .col-md-9
+          .account-well
+            %p.prepend-top-0.append-bottom-0
+              Can't scan the code?
+            %p.prepend-top-0.append-bottom-0
+              To add the entry manually, provide the following details to the application on your phone.
+            %p.prepend-top-0.append-bottom-0
+              Account:
+              = current_user.email
+            %p.prepend-top-0.append-bottom-0
+              Key:
+              = current_user.otp_secret.scan(/.{4}/).join(' ')
+            %p.two-factor-new-manual-content
+              Time based: Yes
+      = form_for @user, url: "", method: :put do |f|
+        .form-group
+          = label_tag :pin_code, nil, class: "label-light"
+          = text_field_tag :pin_code, nil, class: "form-control", required: true
+        .prepend-top-default
+          = submit_tag 'Enable two-factor authentication', class: 'btn btn-success'
+%hr
+- if button_based_providers.any?
+  .row.prepend-top-default
+    .col-lg-3.profile-settings-sidebar
+      %h4.prepend-top-0
+        Social sign-in
+      %p
+        Activate signin with one of the following services
+    .col-lg-9
+      %label.label-light
+        Connected Accounts
+      %p Click on icon to activate signin with one of the following services
+      - button_based_providers.each do |provider|
+        .provider-btn-group
+          .provider-btn-image
+            = provider_image_tag(provider)
+          - if auth_active?(provider)
+            = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
+              Disconnect
           - else
-            = f.submit 'Generate', class: "btn btn-default"
+            = link_to user_omniauth_authorize_path(provider), method: :post, class: "provider-btn #{'not-active' if !auth_active?(provider)}", "data-no-turbolink" => "true" do
+              Connect
+  %hr
+- if current_user.can_change_username?
+  .row.prepend-top-default
+    .col-lg-3.profile-settings-sidebar
+      %h4.prepend-top-0.change-username-title
+        Change username
+      %p
+        Changing your username will change path to all personal projects!
+    .col-lg-9
+      = form_for @user, url: update_username_profile_path, method: :put, remote: true, html: {class: "update-username"} do |f|
+        .form-group
+          = f.label :username, "Path", class: "label-light"
+          .input-group
+            .input-group-addon
+              = "#{root_url}u/"
+            = f.text_field :username, required: true, class: 'form-control'
+        .help-block
+          Current path:
+          = "#{root_url}u/#{current_user.username}"
+        .prepend-top-default
+          = f.button class: "btn btn-warning", type: "submit" do
+            = icon "spinner spin", class: "hidden loading-username"
+            Update username
+  %hr
 
-  .panel.panel-default
-    .panel-heading
-      Two-factor Authentication
-    .panel-body
-      - if current_user.two_factor_enabled?
-        .pull-right
-          = link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
-              data: { confirm: 'Are you sure?' }
-        %p.text-success
-          %strong
-            Two-factor Authentication is enabled
+- if signup_enabled?
+  .row.prepend-top-default
+    .col-lg-3.profile-settings-sidebar
+      %h4.prepend-top-0.remove-account-title
+        Remove account
+    .col-lg-9
+      - if @user.can_be_removed?
         %p
-          If you lose your recovery codes you can
-          %strong
-            = succeed ',' do
-              = link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
-          invalidating all previous codes.
-
+          Deleting an account has the following effects:
+        %ul
+          %li All user content like authored issues, snippets, comments will be removed
+          - rp = current_user.personal_projects.count
+          - unless rp.zero?
+            %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
+        = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
       - else
-        %p
-          Increase your account's security by enabling two-factor authentication (2FA).
-        %p
-          Each time you log in you’ll be required to provide your username and
-          password as usual, plus a randomly-generated code from your phone.
-
-        .form-actions
-          = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
-
-  - if button_based_providers.any?
-    .panel.panel-default
-      .panel-heading
-        Connected Accounts
-      .panel-body
-        .oauth-buttons.append-bottom-10
-          %p Click on icon to activate signin with one of the following services
-          - button_based_providers.each do |provider|
-            .btn-group
-              = link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: "btn btn-lg #{'active' if auth_active?(provider)}", "data-no-turbolink" => "true"
-
-              - if auth_active?(provider)
-                = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
-                  = icon('close')
-
-  - if current_user.can_change_username?
-    .panel.panel-warning.update-username
-      .panel-heading
-        Change Username
-      .panel-body
-        = form_for @user, url: update_username_profile_path,  method: :put, remote: true do |f|
+        - if @user.solo_owned_groups.present?
           %p
-            Changing your username will change path to all personal projects!
-          %div
-            .input-group
-              .input-group-addon
-                = "#{root_url}u/"
-              = f.text_field :username, required: true, class: 'form-control'
-            &nbsp;
-          .loading-gif.hide
-            %p
-              = icon('spinner spin')
-              Saving new username
-          .form-actions
-            = f.submit 'Save username', class: "btn btn-warning"
-
-  - if signup_enabled?
-    .panel.panel-danger.remove-account
-      .panel-heading
-        Remove account
-      .panel-body
-        - if @user.can_be_removed?
-          %p Deleting an account has the following effects:
-          %ul
-            %li All user content like authored issues, snippets, comments will be removed
-            - rp = current_user.personal_projects.count
-            - unless rp.zero?
-              %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
-          .form-actions
-            = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
-        - else
-          - if @user.solo_owned_groups.present?
-            %p
-              Your account is currently an owner in these groups:
-              %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
-            %p
-              You must transfer ownership or delete these groups before you can delete your account.
+            Your account is currently an owner in these groups:
+            %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
+          %p
+            You must transfer ownership or delete these groups before you can delete your account.
+.append-bottom-default