diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index de1ee5e..ac99eff 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -446,6 +446,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 {
 	u16 resp = WLAN_STATUS_SUCCESS;
 	struct wpabuf *data = NULL;
+	Boolean send_auth = 0;
 
 	if (!sta->sae) {
 		if (auth_transaction != 1)
@@ -456,12 +457,21 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 		sta->sae->state = SAE_NOTHING;
 	}
 
+	if (sta->sae->state == SAE_ACCEPTED && auth_transaction == 1) {
+		wpa_printf(MSG_DEBUG, "SAE: remove the STA "
+			   "(" MACSTR ") doing reauthentication",
+			   MAC2STR(sta->addr));
+		ap_free_sta(hapd, sta);
+		return;
+	}
+
 	if (auth_transaction == 1) {
 		const u8 *token = NULL;
 		size_t token_len = 0;
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "start SAE authentication (RX commit)");
+
 		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
 					((const u8 *) mgmt) + len -
 					mgmt->u.auth.variable, &token,
@@ -481,22 +491,45 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 					   MAC2STR(sta->addr));
 				data = auth_build_token_req(hapd, sta->addr);
 				resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
-			} else {
+				goto reply;
+			}
+			if (sta->sae->state == SAE_NOTHING ||
+			    sta->sae->state == SAE_CONFIRMED) {
+				/* (re)send commit to peer */
 				data = auth_process_sae_commit(hapd, sta);
 				if (data == NULL)
 					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-				else
-					sta->sae->state = SAE_COMMITTED;
+				sta->auth_alg = WLAN_AUTH_SAE;
+				send_auth_reply(hapd, mgmt->sa, mgmt->bssid,
+					WLAN_AUTH_SAE, 1, resp,
+					data ? wpabuf_head(data) : (u8 *) "",
+					data ? wpabuf_len(data) : 0);
+				wpabuf_free(data);
+			}
+			/* SAE_NOTHING / SAE_COMMITTED / SAE_CONFIRMED */
+			if (sae_process_commit(sta->sae) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: process peer commit failed");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			} else {
+				/* send confirm and transition to CONFIRMED */
+				data = auth_build_sae_confirm(hapd, sta);
+				if (data == NULL)
+					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				else {
+					sta->sae->state = SAE_CONFIRMED;
+					auth_transaction = 2;
+				}
 			}
 		}
 	} else if (auth_transaction == 2) {
-		if (sta->sae->state != SAE_COMMITTED) {
+		if (sta->sae->state == SAE_NOTHING ||
+		    sta->sae->state == SAE_COMMITTED) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_DEBUG,
 				       "SAE confirm before commit");
-			resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
-			goto failed;
+			return;
 		}
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -508,10 +541,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 		} else {
 			resp = WLAN_STATUS_SUCCESS;
 			sta->flags |= WLAN_STA_AUTH;
-			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
 			sta->auth_alg = WLAN_AUTH_SAE;
 			mlme_authenticate_indication(hapd, sta);
 
+			if (sta->sae->state == SAE_CONFIRMED) {
+				sta->sae->state = SAE_ACCEPTED;
+				sae_clear_temp_data(sta->sae);
+				wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+				return;
+			}
+
+			send_auth = 1;
 			data = auth_build_sae_confirm(hapd, sta);
 			if (data == NULL)
 				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -527,14 +567,14 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 			       auth_transaction);
 		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
 	}
-
-failed:
+reply:
 	sta->auth_alg = WLAN_AUTH_SAE;
-
 	send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
 			auth_transaction, resp,
 			data ? wpabuf_head(data) : (u8 *) "",
 			data ? wpabuf_len(data) : 0);
+	if (send_auth)
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
 	wpabuf_free(data);
 }
 #endif /* CONFIG_SAE */
@@ -649,10 +689,20 @@ static void handle_auth(struct hostapd_data *hapd,
 		return;
 	}
 
-	sta = ap_sta_add(hapd, mgmt->sa);
-	if (!sta) {
-		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
-		goto fail;
+#ifdef CONFIG_MESH
+	if (hapd->conf->mesh & MESH_ENABLED) {
+		/* if the mesh peer is not available, we don't do auth. */
+		sta = ap_get_sta(hapd, mgmt->sa);
+		if (!sta)
+			return;
+	} else
+#endif /* CONFIG_MESH */
+	{
+		sta = ap_sta_add(hapd, mgmt->sa);
+		if (!sta) {
+			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+			goto fail;
+		}
 	}
 
 	if (vlan_id > 0) {
@@ -737,6 +787,21 @@ static void handle_auth(struct hostapd_data *hapd,
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_SAE
 	case WLAN_AUTH_SAE:
+#ifdef CONFIG_MESH
+		if (hapd->conf->mesh & MESH_ENABLED) {
+			if (sta->wpa_sm == NULL)
+				sta->wpa_sm =
+					wpa_auth_sta_init(hapd->wpa_auth,
+							  sta->addr, NULL);
+			if (sta->wpa_sm == NULL) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: Failed to initialize WPA "
+					   "state machine");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto fail;
+			}
+		}
+#endif /* CONFIG_MESH */
 		handle_auth_sae(hapd, sta, mgmt, len, auth_transaction);
 		return;
 #endif /* CONFIG_SAE */
@@ -1758,6 +1823,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
 	    !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
 	      stype == WLAN_FC_STYPE_ACTION) &&
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_MESH
+	    !(hapd->conf->mesh & MESH_ENABLED) &&
+#endif /* CONFIG_MESH */
 	    os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
 		wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
 			   MAC2STR(mgmt->bssid));
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 2bb8aab..75d4607 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -134,6 +134,15 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
 				       encrypt);
 }
 
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr)
+{
+	if (wpa_auth->cb.start_ampe == NULL)
+		return -1;
+	return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+}
+#endif /* CONFIG_MESH */
 
 int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
 			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
@@ -1519,6 +1528,13 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
 
 	switch (event) {
 	case WPA_AUTH:
+#ifdef CONFIG_MESH
+		/* PTKs are derived through AMPE */
+		if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr))
+			/* not mesh */
+			break;
+		return 0;
+#endif /* CONFIG_MESH */
 	case WPA_ASSOC:
 		break;
 	case WPA_DEAUTH:
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 929a253..d3e9d4c 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -213,6 +213,9 @@ struct wpa_auth_callbacks {
 	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
 			 size_t tspec_ielen);
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_MESH
+	int (*start_ampe)(void *ctx, const u8 *sta_addr);
+#endif /* CONFIG_MESH */
 };
 
 struct wpa_authenticator * wpa_init(const u8 *addr,
