Index: src/wp-admin/customize.php
===================================================================
--- src/wp-admin/customize.php	(revision 32014)
+++ src/wp-admin/customize.php	(working copy)
@@ -216,7 +216,8 @@
 
 	$login_url = add_query_arg( array(
 		'interim-login' => 1,
-		'customize-login' => 1
+		'customize-login' => 1,
+		'theme' => $wp_customize->get_stylesheet(),
 	), wp_login_url() );
 
 	// Prepare Customizer settings to pass to JavaScript.
Index: src/wp-admin/js/customize-controls.js
===================================================================
--- src/wp-admin/js/customize-controls.js	(revision 32014)
+++ src/wp-admin/js/customize-controls.js	(working copy)
@@ -2358,7 +2358,9 @@
 
 			messenger.targetWindow( iframe[0].contentWindow );
 
-			messenger.bind( 'login', function() {
+			messenger.bind( 'login', function ( params ) {
+				api.trigger( 'nonce-refresh', params.nonce );
+
 				iframe.remove();
 				messenger.destroy();
 				delete previewer._login;
@@ -2538,6 +2540,12 @@
 			$.extend( this.nonce, nonce );
 		});
 
+		// Refresh the nonces if login sends updated nonces over.
+		api.bind( 'nonce-refresh', function( nonce ) {
+			$.extend( api.settings.nonce, nonce );
+			$.extend( api.previewer.nonce, nonce );
+		});
+
 		// Create Settings
 		$.each( api.settings.settings, function( id, data ) {
 			api.create( id, id, data.value, {
Index: src/wp-admin/js/customize-widgets.js
===================================================================
--- src/wp-admin/js/customize-widgets.js	(revision 32014)
+++ src/wp-admin/js/customize-widgets.js	(working copy)
@@ -1898,6 +1898,11 @@
 		sidebar_widgets: api.Widgets.SidebarControl
 	});
 
+	// Refresh the nonces if login sends updated nonces over.
+	api.bind( 'nonce-refresh', function( nonce ) {
+		api.Widgets.data.nonce = nonce['update-widget'];
+	});
+
 	/**
 	 * Init Customizer for widgets.
 	 */
Index: src/wp-login.php
===================================================================
--- src/wp-login.php	(revision 32014)
+++ src/wp-login.php	(working copy)
@@ -767,8 +767,13 @@
 default:
 	$secure_cookie = '';
 	$customize_login = isset( $_REQUEST['customize-login'] );
-	if ( $customize_login )
+	$customize_theme = null;
+	if ( $customize_login ) {
 		wp_enqueue_script( 'customize-base' );
+		if ( ! empty( $_REQUEST['theme'] ) ) {
+			$customize_theme = wp_unslash( $_REQUEST['theme'] );
+		}
+	}
 
 	// If the user wants ssl but the session is not ssl, force a secure cookie.
 	if ( !empty($_POST['log']) && !force_ssl_admin() ) {
@@ -792,7 +797,23 @@
 
 	$reauth = empty($_REQUEST['reauth']) ? false : true;
 
+	$GLOBALS['_wp_login_logged_in_cookie'] = null;
+
+	/**
+	 * Workaround to obtain the LOGGED_IN_COOKIE when wp_signon() is called.
+	 *
+	 * @ignore
+	 * @since 4.2.0
+	 *
+	 * @param string $cookie The logged-in cookie.
+	 */
+	function _capture_wp_login_logged_in_cookie( $cookie ) {
+		$GLOBALS['_wp_login_logged_in_cookie'] = $cookie;
+	}
+
+	add_action( 'set_logged_in_cookie', '_capture_wp_login_logged_in_cookie' );
 	$user = wp_signon( '', $secure_cookie );
+	remove_action( 'set_logged_in_cookie', '_capture_wp_login_logged_in_cookie' );
 
 	if ( empty( $_COOKIE[ LOGGED_IN_COOKIE ] ) ) {
 		if ( headers_sent() ) {
@@ -827,7 +848,38 @@
 			/** This action is documented in wp-login.php */
 			do_action( 'login_footer' ); ?>
 			<?php if ( $customize_login ) : ?>
-				<script type="text/javascript">setTimeout( function(){ new wp.customize.Messenger({ url: '<?php echo wp_customize_url(); ?>', channel: 'login' }).send('login') }, 1000 );</script>
+				<?php
+				$theme = wp_get_theme( $customize_theme );
+				$messenger_login_params = array(
+					'url' => wp_customize_url(),
+					'channel' => 'login',
+				);
+				$login_message = array();
+
+				// Update Customizer nonces
+				if ( ! $theme->errors() ) {
+					/*
+					 * Set the current user and auth cookie so that wp_create_nonce() will succeed;
+					 * wp_signon() calls wp_set_auth_cookie() which does not set $_COOKIE, and
+					 * wp_create_nonce() calls wp_get_session_token() which calls wp_parse_auth_cookie()
+					 * which expects the $_COOKIE to be set.
+					 */
+					wp_set_current_user( $user->ID );
+					$_COOKIE[ LOGGED_IN_COOKIE ] = $GLOBALS['_wp_login_logged_in_cookie'];
+					$login_message['nonce'] = array(
+						'save' => wp_create_nonce( 'save-customize_' . $theme->get_stylesheet() ),
+						'preview' => wp_create_nonce( 'preview-customize_' . $theme->get_stylesheet() ),
+						'update-widget' => wp_create_nonce( 'update-widget' ),
+					);
+				}
+
+				?>
+				<script type="text/javascript">
+					setTimeout( function () {
+						var messenger = new wp.customize.Messenger( <?php echo wp_json_encode( $messenger_login_params ); ?> );
+						messenger.send( 'login', <?php echo wp_json_encode( $login_message ); ?> );
+					}, 1000 );
+				</script>
 			<?php endif; ?>
 			</body></html>
 <?php		exit;
@@ -924,6 +976,7 @@
 <?php 	} ?>
 <?php   if ( $customize_login ) : ?>
 		<input type="hidden" name="customize-login" value="1" />
+		<input type="hidden" name="theme" value="<?php echo esc_attr( $customize_theme ) ?>" />
 <?php   endif; ?>
 		<input type="hidden" name="testcookie" value="1" />
 	</p>
