Repository: FreeRDP/FreeRDP
Pull request #11608: Sso mib deferred callback
Author: login: akallabeth
Created: 2025-05-21T11:39:52Z | Merged: 2025-05-21T15:54:14Z



Diff:
diff --git a/client/common/client.c b/client/common/client.c
index e79ff3ade204..4fdb60c7f36e 100644
--- a/client/common/client.c
+++ b/client/common/client.c
@@ -196,20 +196,9 @@ int freerdp_client_start(rdpContext* context)
 
 #ifdef WITH_SSO_MIB
 	rdpClientContext* client_context = (rdpClientContext*)context;
-	client_context->mibClientWrapper = (MIBClientWrapper*)calloc(1, sizeof(MIBClientWrapper));
+	client_context->mibClientWrapper = sso_mib_new(context);
 	if (!client_context->mibClientWrapper)
-		return ERROR_NOT_ENOUGH_MEMORY;
-	const char* client_id =
-	    freerdp_settings_get_string(context->settings, FreeRDP_GatewayAvdClientID);
-	client_context->mibClientWrapper->app =
-	    mib_public_client_app_new(client_id, MIB_AUTHORITY_COMMON, NULL, NULL);
-	if (!client_context->mibClientWrapper->app)
-	{
-		free(client_context->mibClientWrapper);
-		client_context->mibClientWrapper = NULL;
 		return ERROR_INTERNAL_ERROR;
-	}
-	client_context->mibClientWrapper->state = SSO_MIB_STATE_INIT;
 #endif
 
 	pEntryPoints = context->instance->pClientEntryPoints;
@@ -223,15 +212,15 @@ int freerdp_client_stop(rdpContext* context)
 	if (!context || !context->instance || !context->instance->pClientEntryPoints)
 		return ERROR_BAD_ARGUMENTS;
 
+	pEntryPoints = context->instance->pClientEntryPoints;
+	const int rc = IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
+
 #ifdef WITH_SSO_MIB
 	rdpClientContext* client_context = (rdpClientContext*)context;
-	if (client_context->mibClientWrapper->app)
-		g_object_unref(client_context->mibClientWrapper->app);
-	free(client_context->mibClientWrapper);
-#endif
-
-	pEntryPoints = context->instance->pClientEntryPoints;
-	return IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
+	sso_mib_free(client_context->mibClientWrapper);
+	client_context->mibClientWrapper = NULL;
+#endif // WITH_SSO_MIB
+	return rc;
 }
 
 freerdp* freerdp_client_get_instance(rdpContext* context)
@@ -1126,27 +1115,6 @@ static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
 
 	*token = NULL;
 
-#ifdef WITH_SSO_MIB
-	rdpClientContext* client_context = (rdpClientContext*)instance->context;
-	if (client_context->mibClientWrapper->state == SSO_MIB_STATE_INIT ||
-	    client_context->mibClientWrapper->state == SSO_MIB_STATE_SUCCESS)
-	{
-		rc = sso_mib_get_avd_access_token(instance, token);
-		if (rc)
-		{
-			client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
-			return rc;
-		}
-		else
-		{
-			WLog_WARN(TAG, "Getting AVD token from identity broker failed, falling back to "
-			               "browser-based authentication.");
-			client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
-			// Fall through to regular avd access token retrieval
-		}
-	}
-#endif
-
 	const char* client_id =
 	    freerdp_settings_get_string(instance->context->settings, FreeRDP_GatewayAvdClientID);
 	const char* base = freerdp_settings_get_string(instance->context->settings,
@@ -1227,42 +1195,7 @@ BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, c
 			va_start(ap, count);
 			const char* scope = va_arg(ap, const char*);
 			const char* req_cnf = va_arg(ap, const char*);
-			BOOL rc = FALSE;
-
-#ifdef WITH_SSO_MIB
-			rdpClientContext* client_context = (rdpClientContext*)instance->context;
-			if (client_context->mibClientWrapper->state == SSO_MIB_STATE_INIT ||
-			    client_context->mibClientWrapper->state == SSO_MIB_STATE_SUCCESS)
-			{
-				// Setup scope without URL encoding for sso-mib
-				char* scope_copy = winpr_str_url_decode(scope, strlen(scope));
-				if (!scope_copy)
-				{
-					WLog_ERR(TAG, "Failed to decode scope");
-					va_end(ap);
-					return FALSE;
-				}
-
-				rc = sso_mib_get_rdsaad_access_token(instance, scope_copy, req_cnf, token);
-				free(scope_copy);
-				if (rc)
-				{
-					client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
-					va_end(ap);
-					return rc;
-				}
-				else
-				{
-					WLog_WARN(TAG, "Getting RDS token from identity broker failed, falling back to "
-					               "browser-based authentication.");
-					client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
-					// Fall through to regular rdsaad access token retrieval
-				}
-			}
-#endif // WITH_SSO_MIB
-
-			rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
-
+			BOOL rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
 			va_end(ap);
 			return rc;
 		}
diff --git a/client/common/sso_mib_tokens.c b/client/common/sso_mib_tokens.c
index 5e9ad9fc596c..e17283b64f91 100644
--- a/client/common/sso_mib_tokens.c
+++ b/client/common/sso_mib_tokens.c
@@ -7,13 +7,29 @@
 #include <freerdp/crypto/crypto.h>
 #include <winpr/json.h>
 
+#include <sso-mib/sso-mib.h>
 #include "sso_mib_tokens.h"
 
-BOOL sso_mib_get_avd_access_token(freerdp* instance, char** token)
+#include <freerdp/log.h>
+#define TAG CLIENT_TAG("common.sso")
+
+enum sso_mib_state
+{
+	SSO_MIB_STATE_INIT = 0,
+	SSO_MIB_STATE_FAILED = 1,
+	SSO_MIB_STATE_SUCCESS = 2,
+};
+
+struct MIBClientWrapper
+{
+	MIBClientApp* app;
+	enum sso_mib_state state;
+	pGetCommonAccessToken GetCommonAccessToken;
+};
+
+static BOOL sso_mib_get_avd_access_token(rdpClientContext* client_context, char** token)
 {
-	WINPR_ASSERT(instance);
-	WINPR_ASSERT(instance->context);
-	rdpClientContext* client_context = (rdpClientContext*)instance->context;
+	WINPR_ASSERT(client_context);
 	WINPR_ASSERT(client_context->mibClientWrapper);
 	WINPR_ASSERT(client_context->mibClientWrapper->app);
 	WINPR_ASSERT(token);
@@ -52,12 +68,10 @@ BOOL sso_mib_get_avd_access_token(freerdp* instance, char** token)
 	return rc;
 }
 
-BOOL sso_mib_get_rdsaad_access_token(freerdp* instance, const char* scope, const char* req_cnf,
-                                     char** token)
+static BOOL sso_mib_get_rdsaad_access_token(rdpClientContext* client_context, const char* scope,
+                                            const char* req_cnf, char** token)
 {
-	WINPR_ASSERT(instance);
-	WINPR_ASSERT(instance->context);
-	rdpClientContext* client_context = (rdpClientContext*)instance->context;
+	WINPR_ASSERT(client_context);
 	WINPR_ASSERT(client_context->mibClientWrapper);
 	WINPR_ASSERT(client_context->mibClientWrapper->app);
 	WINPR_ASSERT(scope);
@@ -117,3 +131,115 @@ BOOL sso_mib_get_rdsaad_access_token(freerdp* instance, const char* scope, const
 	g_slist_free_full(scopes, g_free);
 	return rc;
 }
+
+static BOOL sso_mib_get_access_token(rdpContext* context, AccessTokenType tokenType, char** token,
+                                     size_t count, ...)
+{
+	BOOL rc = FALSE;
+	rdpClientContext* client_context = (rdpClientContext*)context;
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+
+	if (!client_context->mibClientWrapper->app)
+	{
+		const char* client_id =
+		    freerdp_settings_get_string(context->settings, FreeRDP_GatewayAvdClientID);
+		client_context->mibClientWrapper->app =
+		    mib_public_client_app_new(client_id, MIB_AUTHORITY_COMMON, NULL, NULL);
+	}
+
+	if (!client_context->mibClientWrapper->app)
+		return ERROR_INTERNAL_ERROR;
+
+	const char* scope = NULL;
+	const char* req_cnf = NULL;
+
+	va_list ap;
+	va_start(ap, count);
+
+	if (tokenType == ACCESS_TOKEN_TYPE_AAD)
+	{
+		scope = va_arg(ap, const char*);
+		req_cnf = va_arg(ap, const char*);
+	}
+
+	if ((client_context->mibClientWrapper->state == SSO_MIB_STATE_INIT) ||
+	    (client_context->mibClientWrapper->state == SSO_MIB_STATE_SUCCESS))
+	{
+		switch (tokenType)
+		{
+			case ACCESS_TOKEN_TYPE_AVD:
+			{
+				rc = sso_mib_get_avd_access_token(client_context, token);
+				if (rc)
+					client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
+				else
+				{
+					WLog_WARN(TAG, "Getting AVD token from identity broker failed, falling back to "
+					               "browser-based authentication.");
+					client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
+				}
+			}
+			break;
+			case ACCESS_TOKEN_TYPE_AAD:
+			{
+				// Setup scope without URL encoding for sso-mib
+				char* scope_copy = winpr_str_url_decode(scope, strlen(scope));
+				if (!scope_copy)
+					WLog_ERR(TAG, "Failed to decode scope");
+				else
+				{
+					rc =
+					    sso_mib_get_rdsaad_access_token(client_context, scope_copy, req_cnf, token);
+					free(scope_copy);
+					if (rc)
+						client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
+					else
+					{
+						WLog_WARN(TAG,
+						          "Getting RDS token from identity broker failed, falling back to "
+						          "browser-based authentication.");
+						client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
+					}
+				}
+			}
+			break;
+			default:
+				break;
+		}
+	}
+	if (!rc && client_context->mibClientWrapper->GetCommonAccessToken)
+		rc = client_context->mibClientWrapper->GetCommonAccessToken(context, tokenType, token,
+		                                                            count, scope, req_cnf);
+	va_end(ap);
+
+	return rc;
+}
+
+MIBClientWrapper* sso_mib_new(rdpContext* context)
+{
+
+	MIBClientWrapper* mibClientWrapper = (MIBClientWrapper*)calloc(1, sizeof(MIBClientWrapper));
+	if (!mibClientWrapper)
+		return NULL;
+
+	mibClientWrapper->GetCommonAccessToken = freerdp_get_common_access_token(context);
+	if (!freerdp_set_common_access_token(context, sso_mib_get_access_token))
+	{
+		sso_mib_free(mibClientWrapper);
+		return NULL;
+	}
+	mibClientWrapper->state = SSO_MIB_STATE_INIT;
+	return mibClientWrapper;
+}
+
+void sso_mib_free(MIBClientWrapper* sso)
+{
+	if (!sso)
+		return;
+
+	if (sso->app)
+		g_object_unref(sso->app);
+
+	free(sso);
+}
diff --git a/client/common/sso_mib_tokens.h b/client/common/sso_mib_tokens.h
index 69fab38150c2..196dfd38d4f7 100644
--- a/client/common/sso_mib_tokens.h
+++ b/client/common/sso_mib_tokens.h
@@ -7,24 +7,10 @@
 #define FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H
 
 #include <freerdp/freerdp.h>
-#include <sso-mib/sso-mib.h>
 
-enum sso_mib_state
-{
-	SSO_MIB_STATE_INIT = 0,
-	SSO_MIB_STATE_FAILED = 1,
-	SSO_MIB_STATE_SUCCESS = 2,
-};
+void sso_mib_free(MIBClientWrapper* sso);
 
-struct MIBClientWrapper
-{
-	MIBClientApp* app;
-	enum sso_mib_state state;
-};
-
-BOOL sso_mib_get_avd_access_token(freerdp* instance, char** token);
-
-BOOL sso_mib_get_rdsaad_access_token(freerdp* instance, const char* scope, const char* req_cnf,
-                                     char** token);
+WINPR_ATTR_MALLOC(sso_mib_free, 1)
+MIBClientWrapper* sso_mib_new(rdpContext* context);
 
 #endif /* FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H */


Reviews:
user: login: github-actions[bot]
body: clang-tidy made some suggestions
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T11:42:48Z
user: login: rupran
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T11:48:21Z
user: login: rupran
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T11:51:17Z
user: login: akallabeth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T11:57:57Z
user: login: rupran
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T11:58:21Z
user: login: akallabeth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T11:59:12Z

Review comments:
diff_hunk: @@ -201,7 +219,16 @@ int freerdp_client_stop(rdpContext* context)
 		return ERROR_BAD_ARGUMENTS;
 
 	pEntryPoints = context->instance->pClientEntryPoints;
-	return IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
+	const UINT rc = IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
+
+#ifdef WITH_SSO_MIB
+	rdpClientContext* client_context = (rdpClientContext*)context;
+	if (client_context->mibClientWrapper->app)
+		g_object_unref(client_context->mibClientWrapper->app);
+	free(client_context->mibClientWrapper);
+	client_context->mibClientWrapper = NULL;
+#endif // WITH_SSO_MIB
+	return rc;
path: client/common/client.c
user: login: github-actions[bot]
body: warning: narrowing conversion from 'UINT' (aka 'unsigned int') to signed type 'int' is implementation-defined [[bugprone-narrowing-conversions](https://clang.llvm.org/extra/clang-tidy/checks/bugprone/narrowing-conversions.html)]
```cpp
	return rc;
        ^
```

created_at: 2025-05-21T11:42:47Z
author_association: NONE
diff_hunk: @@ -0,0 +1,199 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright 2025 Siemens
+ */
+
+#include <sso-mib/sso-mib.h>
path: client/common/sso_mib_tokens.c
user: login: github-actions[bot]
body: warning: 'sso-mib/sso-mib.h' file not found [clang-diagnostic-error]
```cpp
#include <sso-mib/sso-mib.h>
         ^
```

created_at: 2025-05-21T11:42:47Z
author_association: NONE
diff_hunk: @@ -0,0 +1,199 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright 2025 Siemens
+ */
+
+#include <sso-mib/sso-mib.h>
+#include <freerdp/crypto/crypto.h>
+#include <winpr/json.h>
+
+#include "sso_mib_tokens.h"
+
+#include <freerdp/log.h>
+#define TAG CLIENT_TAG("common.sso")
+
+static BOOL sso_mib_get_avd_access_token(rdpClientContext* client_context, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(token);
+
+	MIBAccount* account = NULL;
+	GSList* scopes = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+
+	account = mib_client_app_get_account_by_upn(client_context->mibClientWrapper->app, NULL);
+	if (!account)
+	{
+		goto cleanup;
+	}
+
+	scopes = g_slist_append(scopes, g_strdup("https://www.wvd.microsoft.com/.default"));
+
+	MIBPrt* prt = mib_client_app_acquire_token_silent(client_context->mibClientWrapper->app,
+	                                                  account, scopes, NULL, NULL, NULL);
+	if (prt)
+	{
+		const char* access_token = mib_prt_get_access_token(prt);
path: client/common/sso_mib_tokens.c
user: login: github-actions[bot]
body: warning: variable 'access_token' is not initialized [[cppcoreguidelines-init-variables](https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/init-variables.html)]

```suggestion
		const char* access_token = NULL = mib_prt_get_access_token(prt);
```

created_at: 2025-05-21T11:42:47Z
author_association: NONE
diff_hunk: @@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright 2025 Siemens
+ */
+
+#ifndef FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H
+#define FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H
+
+#include <freerdp/freerdp.h>
+#include <sso-mib/sso-mib.h>
path: client/common/sso_mib_tokens.h
user: login: github-actions[bot]
body: warning: 'sso-mib/sso-mib.h' file not found [clang-diagnostic-error]
```cpp
#include <sso-mib/sso-mib.h>
         ^
```

created_at: 2025-05-21T11:42:47Z
author_association: NONE
diff_hunk: @@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright 2025 Siemens
+ */
+
+#ifndef FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H
+#define FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H
+
+#include <freerdp/freerdp.h>
+#include <sso-mib/sso-mib.h>
+
+enum sso_mib_state
+{
+	SSO_MIB_STATE_INIT = 0,
+	SSO_MIB_STATE_FAILED = 1,
+	SSO_MIB_STATE_SUCCESS = 2,
+};
+
+struct MIBClientWrapper
path: client/common/sso_mib_tokens.h
user: login: github-actions[bot]
body: warning: constructor does not initialize these fields: app, state, GetCommonAccessToken [[cppcoreguidelines-pro-type-member-init](https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-type-member-init.html)]

client/common/sso_mib_tokens.h:20:
```diff
- 	MIBClientApp* app;
+ 	MIBClientApp* app{};
```

client/common/sso_mib_tokens.h:22:
```diff
- 	pGetCommonAccessToken GetCommonAccessToken;
+ 	pGetCommonAccessToken GetCommonAccessToken{};
```

created_at: 2025-05-21T11:42:48Z
author_association: NONE
diff_hunk: @@ -0,0 +1,199 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright 2025 Siemens
+ */
+
+#include <sso-mib/sso-mib.h>
+#include <freerdp/crypto/crypto.h>
+#include <winpr/json.h>
+
+#include "sso_mib_tokens.h"
+
+#include <freerdp/log.h>
+#define TAG CLIENT_TAG("common.sso")
+
+static BOOL sso_mib_get_avd_access_token(rdpClientContext* client_context, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(token);
+
+	MIBAccount* account = NULL;
+	GSList* scopes = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+
+	account = mib_client_app_get_account_by_upn(client_context->mibClientWrapper->app, NULL);
+	if (!account)
+	{
+		goto cleanup;
+	}
+
+	scopes = g_slist_append(scopes, g_strdup("https://www.wvd.microsoft.com/.default"));
+
+	MIBPrt* prt = mib_client_app_acquire_token_silent(client_context->mibClientWrapper->app,
+	                                                  account, scopes, NULL, NULL, NULL);
+	if (prt)
+	{
+		const char* access_token = mib_prt_get_access_token(prt);
+		if (access_token)
+		{
+			*token = strdup(access_token);
+		}
+		g_object_unref(prt);
+	}
+
+	rc = TRUE && *token != NULL;
+cleanup:
+	if (account)
+		g_object_unref(account);
+	g_slist_free_full(scopes, g_free);
+	return rc;
+}
+
+static BOOL sso_mib_get_rdsaad_access_token(rdpClientContext* client_context, const char* scope,
+                                            const char* req_cnf, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(scope);
+	WINPR_ASSERT(token);
+	WINPR_ASSERT(req_cnf);
+
+	GSList* scopes = NULL;
+	WINPR_JSON* json = NULL;
+	MIBPopParams* params = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+	BYTE* req_cnf_dec = NULL;
+	size_t req_cnf_dec_len = 0;
+
+	scopes = g_slist_append(scopes, g_strdup(scope));
+
+	// Parse the "kid" element from req_cnf
+	crypto_base64_decode(req_cnf, strlen(req_cnf) + 1, &req_cnf_dec, &req_cnf_dec_len);
+	if (!req_cnf_dec)
+	{
+		goto cleanup;
+	}
+
+	json = WINPR_JSON_Parse((const char*)req_cnf_dec);
+	if (!json)
+	{
+		goto cleanup;
+	}
+	WINPR_JSON* prop = WINPR_JSON_GetObjectItem(json, "kid");
+	if (!prop)
+	{
+		goto cleanup;
+	}
+	const char* kid = WINPR_JSON_GetStringValue(prop);
+	if (!kid)
+	{
+		goto cleanup;
+	}
+
+	params = mib_pop_params_new(MIB_AUTH_SCHEME_POP, MIB_REQUEST_METHOD_GET, "");
+	mib_pop_params_set_kid(params, kid);
+	MIBPrt* prt = mib_client_app_acquire_token_interactive(
+	    client_context->mibClientWrapper->app, scopes, MIB_PROMPT_NONE, NULL, NULL, NULL, params);
+	if (prt)
+	{
+		*token = strdup(mib_prt_get_access_token(prt));
+		rc = TRUE;
+		g_object_unref(prt);
+	}
+
+cleanup:
+	if (params)
+		g_object_unref(params);
+	WINPR_JSON_Delete(json);
+	free(req_cnf_dec);
+	g_slist_free_full(scopes, g_free);
+	return rc;
+}
+
+BOOL sso_mib_get_access_token(rdpContext* context, AccessTokenType tokenType, char** token,
+                              size_t count, ...)
+{
+	BOOL rc = FALSE;
+	rdpClientContext* client_context = (rdpClientContext*)context;
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+
+	if (!client_context->mibClientWrapper->app)
+	{
+		const char* client_id =
+		    freerdp_settings_get_string(context->settings, FreeRDP_GatewayAvdClientID);
+		client_context->mibClientWrapper->app =
+		    mib_public_client_app_new(client_id, MIB_AUTHORITY_COMMON, NULL, NULL);
+	}
+
+	if (!client_context->mibClientWrapper->app)
+		;
path: client/common/sso_mib_tokens.c
user: login: rupran
body: Stray `;`
created_at: 2025-05-21T11:48:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,199 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright 2025 Siemens
+ */
+
+#include <sso-mib/sso-mib.h>
+#include <freerdp/crypto/crypto.h>
+#include <winpr/json.h>
+
+#include "sso_mib_tokens.h"
+
+#include <freerdp/log.h>
+#define TAG CLIENT_TAG("common.sso")
+
+static BOOL sso_mib_get_avd_access_token(rdpClientContext* client_context, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(token);
+
+	MIBAccount* account = NULL;
+	GSList* scopes = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+
+	account = mib_client_app_get_account_by_upn(client_context->mibClientWrapper->app, NULL);
+	if (!account)
+	{
+		goto cleanup;
+	}
+
+	scopes = g_slist_append(scopes, g_strdup("https://www.wvd.microsoft.com/.default"));
+
+	MIBPrt* prt = mib_client_app_acquire_token_silent(client_context->mibClientWrapper->app,
+	                                                  account, scopes, NULL, NULL, NULL);
+	if (prt)
+	{
+		const char* access_token = mib_prt_get_access_token(prt);
+		if (access_token)
+		{
+			*token = strdup(access_token);
+		}
+		g_object_unref(prt);
+	}
+
+	rc = TRUE && *token != NULL;
+cleanup:
+	if (account)
+		g_object_unref(account);
+	g_slist_free_full(scopes, g_free);
+	return rc;
+}
+
+static BOOL sso_mib_get_rdsaad_access_token(rdpClientContext* client_context, const char* scope,
+                                            const char* req_cnf, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(scope);
+	WINPR_ASSERT(token);
+	WINPR_ASSERT(req_cnf);
+
+	GSList* scopes = NULL;
+	WINPR_JSON* json = NULL;
+	MIBPopParams* params = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+	BYTE* req_cnf_dec = NULL;
+	size_t req_cnf_dec_len = 0;
+
+	scopes = g_slist_append(scopes, g_strdup(scope));
+
+	// Parse the "kid" element from req_cnf
+	crypto_base64_decode(req_cnf, strlen(req_cnf) + 1, &req_cnf_dec, &req_cnf_dec_len);
+	if (!req_cnf_dec)
+	{
+		goto cleanup;
+	}
+
+	json = WINPR_JSON_Parse((const char*)req_cnf_dec);
+	if (!json)
+	{
+		goto cleanup;
+	}
+	WINPR_JSON* prop = WINPR_JSON_GetObjectItem(json, "kid");
+	if (!prop)
+	{
+		goto cleanup;
+	}
+	const char* kid = WINPR_JSON_GetStringValue(prop);
+	if (!kid)
+	{
+		goto cleanup;
+	}
+
+	params = mib_pop_params_new(MIB_AUTH_SCHEME_POP, MIB_REQUEST_METHOD_GET, "");
+	mib_pop_params_set_kid(params, kid);
+	MIBPrt* prt = mib_client_app_acquire_token_interactive(
+	    client_context->mibClientWrapper->app, scopes, MIB_PROMPT_NONE, NULL, NULL, NULL, params);
+	if (prt)
+	{
+		*token = strdup(mib_prt_get_access_token(prt));
+		rc = TRUE;
+		g_object_unref(prt);
+	}
+
+cleanup:
+	if (params)
+		g_object_unref(params);
+	WINPR_JSON_Delete(json);
+	free(req_cnf_dec);
+	g_slist_free_full(scopes, g_free);
+	return rc;
+}
+
+BOOL sso_mib_get_access_token(rdpContext* context, AccessTokenType tokenType, char** token,
+                              size_t count, ...)
+{
+	BOOL rc = FALSE;
+	rdpClientContext* client_context = (rdpClientContext*)context;
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+
+	if (!client_context->mibClientWrapper->app)
+	{
+		const char* client_id =
+		    freerdp_settings_get_string(context->settings, FreeRDP_GatewayAvdClientID);
+		client_context->mibClientWrapper->app =
+		    mib_public_client_app_new(client_id, MIB_AUTHORITY_COMMON, NULL, NULL);
+	}
+
+	if (!client_context->mibClientWrapper->app)
+		;
+	return ERROR_INTERNAL_ERROR;
+
+	const char* scope = NULL;
+	const char* req_cnf = NULL;
+
+	va_list ap;
+	va_start(ap, count);
+
+	if ((client_context->mibClientWrapper->state == SSO_MIB_STATE_INIT) ||
+	    (client_context->mibClientWrapper->state == SSO_MIB_STATE_SUCCESS))
+	{
+		switch (tokenType)
+		{
+			case ACCESS_TOKEN_TYPE_AVD:
+			{
+				rc = sso_mib_get_avd_access_token(client_context, token);
+				if (rc)
+					client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
+				else
+				{
+					WLog_WARN(TAG, "Getting AVD token from identity broker failed, falling back to "
+					               "browser-based authentication.");
+					client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
+				}
+			}
+			break;
+			case ACCESS_TOKEN_TYPE_AAD:
+			{
+				scope = va_arg(ap, const char*);
+				req_cnf = va_arg(ap, const char*);
+				// Setup scope without URL encoding for sso-mib
+				char* scope_copy = winpr_str_url_decode(scope, strlen(scope));
+				if (!scope_copy)
+					WLog_ERR(TAG, "Failed to decode scope");
+				else
+				{
+					rc =
+					    sso_mib_get_rdsaad_access_token(client_context, scope_copy, req_cnf, token);
+					free(scope_copy);
+					if (rc)
+						client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
+					else
+					{
+						WLog_WARN(TAG,
+						          "Getting RDS token from identity broker failed, falling back to "
+						          "browser-based authentication.");
+						client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
+					}
+				}
+			}
+			break;
+			default:
+				break;
+		}
+	}
+	if (!rc && client_context->mibClientWrapper->GetCommonAccessToken)
+		rc = client_context->mibClientWrapper->GetCommonAccessToken(context, tokenType, token,
path: client/common/sso_mib_tokens.c
user: login: rupran
body: Does this needs to call `context->instance->GetAccessToken(context->instance, [...])`?

Additionally, we need to move error checking around here as otherwise `scope` and `req_cnf` would be NULL in case the first request fails (and sets the `state` to `SSO_MIB_STATE_FAILED`).
created_at: 2025-05-21T11:51:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,199 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright 2025 Siemens
+ */
+
+#include <sso-mib/sso-mib.h>
+#include <freerdp/crypto/crypto.h>
+#include <winpr/json.h>
+
+#include "sso_mib_tokens.h"
+
+#include <freerdp/log.h>
+#define TAG CLIENT_TAG("common.sso")
+
+static BOOL sso_mib_get_avd_access_token(rdpClientContext* client_context, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(token);
+
+	MIBAccount* account = NULL;
+	GSList* scopes = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+
+	account = mib_client_app_get_account_by_upn(client_context->mibClientWrapper->app, NULL);
+	if (!account)
+	{
+		goto cleanup;
+	}
+
+	scopes = g_slist_append(scopes, g_strdup("https://www.wvd.microsoft.com/.default"));
+
+	MIBPrt* prt = mib_client_app_acquire_token_silent(client_context->mibClientWrapper->app,
+	                                                  account, scopes, NULL, NULL, NULL);
+	if (prt)
+	{
+		const char* access_token = mib_prt_get_access_token(prt);
+		if (access_token)
+		{
+			*token = strdup(access_token);
+		}
+		g_object_unref(prt);
+	}
+
+	rc = TRUE && *token != NULL;
+cleanup:
+	if (account)
+		g_object_unref(account);
+	g_slist_free_full(scopes, g_free);
+	return rc;
+}
+
+static BOOL sso_mib_get_rdsaad_access_token(rdpClientContext* client_context, const char* scope,
+                                            const char* req_cnf, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(scope);
+	WINPR_ASSERT(token);
+	WINPR_ASSERT(req_cnf);
+
+	GSList* scopes = NULL;
+	WINPR_JSON* json = NULL;
+	MIBPopParams* params = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+	BYTE* req_cnf_dec = NULL;
+	size_t req_cnf_dec_len = 0;
+
+	scopes = g_slist_append(scopes, g_strdup(scope));
+
+	// Parse the "kid" element from req_cnf
+	crypto_base64_decode(req_cnf, strlen(req_cnf) + 1, &req_cnf_dec, &req_cnf_dec_len);
+	if (!req_cnf_dec)
+	{
+		goto cleanup;
+	}
+
+	json = WINPR_JSON_Parse((const char*)req_cnf_dec);
+	if (!json)
+	{
+		goto cleanup;
+	}
+	WINPR_JSON* prop = WINPR_JSON_GetObjectItem(json, "kid");
+	if (!prop)
+	{
+		goto cleanup;
+	}
+	const char* kid = WINPR_JSON_GetStringValue(prop);
+	if (!kid)
+	{
+		goto cleanup;
+	}
+
+	params = mib_pop_params_new(MIB_AUTH_SCHEME_POP, MIB_REQUEST_METHOD_GET, "");
+	mib_pop_params_set_kid(params, kid);
+	MIBPrt* prt = mib_client_app_acquire_token_interactive(
+	    client_context->mibClientWrapper->app, scopes, MIB_PROMPT_NONE, NULL, NULL, NULL, params);
+	if (prt)
+	{
+		*token = strdup(mib_prt_get_access_token(prt));
+		rc = TRUE;
+		g_object_unref(prt);
+	}
+
+cleanup:
+	if (params)
+		g_object_unref(params);
+	WINPR_JSON_Delete(json);
+	free(req_cnf_dec);
+	g_slist_free_full(scopes, g_free);
+	return rc;
+}
+
+BOOL sso_mib_get_access_token(rdpContext* context, AccessTokenType tokenType, char** token,
+                              size_t count, ...)
+{
+	BOOL rc = FALSE;
+	rdpClientContext* client_context = (rdpClientContext*)context;
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+
+	if (!client_context->mibClientWrapper->app)
+	{
+		const char* client_id =
+		    freerdp_settings_get_string(context->settings, FreeRDP_GatewayAvdClientID);
+		client_context->mibClientWrapper->app =
+		    mib_public_client_app_new(client_id, MIB_AUTHORITY_COMMON, NULL, NULL);
+	}
+
+	if (!client_context->mibClientWrapper->app)
+		;
+	return ERROR_INTERNAL_ERROR;
+
+	const char* scope = NULL;
+	const char* req_cnf = NULL;
+
+	va_list ap;
+	va_start(ap, count);
+
+	if ((client_context->mibClientWrapper->state == SSO_MIB_STATE_INIT) ||
+	    (client_context->mibClientWrapper->state == SSO_MIB_STATE_SUCCESS))
+	{
+		switch (tokenType)
+		{
+			case ACCESS_TOKEN_TYPE_AVD:
+			{
+				rc = sso_mib_get_avd_access_token(client_context, token);
+				if (rc)
+					client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
+				else
+				{
+					WLog_WARN(TAG, "Getting AVD token from identity broker failed, falling back to "
+					               "browser-based authentication.");
+					client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
+				}
+			}
+			break;
+			case ACCESS_TOKEN_TYPE_AAD:
+			{
+				scope = va_arg(ap, const char*);
+				req_cnf = va_arg(ap, const char*);
+				// Setup scope without URL encoding for sso-mib
+				char* scope_copy = winpr_str_url_decode(scope, strlen(scope));
+				if (!scope_copy)
+					WLog_ERR(TAG, "Failed to decode scope");
+				else
+				{
+					rc =
+					    sso_mib_get_rdsaad_access_token(client_context, scope_copy, req_cnf, token);
+					free(scope_copy);
+					if (rc)
+						client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
+					else
+					{
+						WLog_WARN(TAG,
+						          "Getting RDS token from identity broker failed, falling back to "
+						          "browser-based authentication.");
+						client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
+					}
+				}
+			}
+			break;
+			default:
+				break;
+		}
+	}
+	if (!rc && client_context->mibClientWrapper->GetCommonAccessToken)
+		rc = client_context->mibClientWrapper->GetCommonAccessToken(context, tokenType, token,
path: client/common/sso_mib_tokens.c
user: login: akallabeth
body: it does call this.
* The `GetCommonAccessToken` saved in the context is the one set before, so `freerdp_common_context`
* `scope` and `req_cnf` are not `NULL`, they are extracted from the `va_list` if `` is `ACCESS_TOKEN_TYPE_AAD`
created_at: 2025-05-21T11:57:57Z
author_association: MEMBER
diff_hunk: @@ -231,7 +226,16 @@ int freerdp_client_stop(rdpContext* context)
 #endif
 
 	pEntryPoints = context->instance->pClientEntryPoints;
-	return IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
+	const UINT rc = IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
+
+#ifdef WITH_SSO_MIB
+	rdpClientContext* client_context = (rdpClientContext*)context;
+	if (client_context->mibClientWrapper->app)
+		g_object_unref(client_context->mibClientWrapper->app);
+	free(client_context->mibClientWrapper);
+	client_context->mibClientWrapper = NULL;
+#endif // WITH_SSO_MIB
path: client/common/client.c
user: login: rupran
body: Needs deduplication with the `#ifdef SSO_MIB` above
created_at: 2025-05-21T11:58:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,199 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright 2025 Siemens
+ */
+
+#include <sso-mib/sso-mib.h>
+#include <freerdp/crypto/crypto.h>
+#include <winpr/json.h>
+
+#include "sso_mib_tokens.h"
+
+#include <freerdp/log.h>
+#define TAG CLIENT_TAG("common.sso")
+
+static BOOL sso_mib_get_avd_access_token(rdpClientContext* client_context, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(token);
+
+	MIBAccount* account = NULL;
+	GSList* scopes = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+
+	account = mib_client_app_get_account_by_upn(client_context->mibClientWrapper->app, NULL);
+	if (!account)
+	{
+		goto cleanup;
+	}
+
+	scopes = g_slist_append(scopes, g_strdup("https://www.wvd.microsoft.com/.default"));
+
+	MIBPrt* prt = mib_client_app_acquire_token_silent(client_context->mibClientWrapper->app,
+	                                                  account, scopes, NULL, NULL, NULL);
+	if (prt)
+	{
+		const char* access_token = mib_prt_get_access_token(prt);
+		if (access_token)
+		{
+			*token = strdup(access_token);
+		}
+		g_object_unref(prt);
+	}
+
+	rc = TRUE && *token != NULL;
+cleanup:
+	if (account)
+		g_object_unref(account);
+	g_slist_free_full(scopes, g_free);
+	return rc;
+}
+
+static BOOL sso_mib_get_rdsaad_access_token(rdpClientContext* client_context, const char* scope,
+                                            const char* req_cnf, char** token)
+{
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+	WINPR_ASSERT(client_context->mibClientWrapper->app);
+	WINPR_ASSERT(scope);
+	WINPR_ASSERT(token);
+	WINPR_ASSERT(req_cnf);
+
+	GSList* scopes = NULL;
+	WINPR_JSON* json = NULL;
+	MIBPopParams* params = NULL;
+
+	BOOL rc = FALSE;
+	*token = NULL;
+	BYTE* req_cnf_dec = NULL;
+	size_t req_cnf_dec_len = 0;
+
+	scopes = g_slist_append(scopes, g_strdup(scope));
+
+	// Parse the "kid" element from req_cnf
+	crypto_base64_decode(req_cnf, strlen(req_cnf) + 1, &req_cnf_dec, &req_cnf_dec_len);
+	if (!req_cnf_dec)
+	{
+		goto cleanup;
+	}
+
+	json = WINPR_JSON_Parse((const char*)req_cnf_dec);
+	if (!json)
+	{
+		goto cleanup;
+	}
+	WINPR_JSON* prop = WINPR_JSON_GetObjectItem(json, "kid");
+	if (!prop)
+	{
+		goto cleanup;
+	}
+	const char* kid = WINPR_JSON_GetStringValue(prop);
+	if (!kid)
+	{
+		goto cleanup;
+	}
+
+	params = mib_pop_params_new(MIB_AUTH_SCHEME_POP, MIB_REQUEST_METHOD_GET, "");
+	mib_pop_params_set_kid(params, kid);
+	MIBPrt* prt = mib_client_app_acquire_token_interactive(
+	    client_context->mibClientWrapper->app, scopes, MIB_PROMPT_NONE, NULL, NULL, NULL, params);
+	if (prt)
+	{
+		*token = strdup(mib_prt_get_access_token(prt));
+		rc = TRUE;
+		g_object_unref(prt);
+	}
+
+cleanup:
+	if (params)
+		g_object_unref(params);
+	WINPR_JSON_Delete(json);
+	free(req_cnf_dec);
+	g_slist_free_full(scopes, g_free);
+	return rc;
+}
+
+BOOL sso_mib_get_access_token(rdpContext* context, AccessTokenType tokenType, char** token,
+                              size_t count, ...)
+{
+	BOOL rc = FALSE;
+	rdpClientContext* client_context = (rdpClientContext*)context;
+	WINPR_ASSERT(client_context);
+	WINPR_ASSERT(client_context->mibClientWrapper);
+
+	if (!client_context->mibClientWrapper->app)
+	{
+		const char* client_id =
+		    freerdp_settings_get_string(context->settings, FreeRDP_GatewayAvdClientID);
+		client_context->mibClientWrapper->app =
+		    mib_public_client_app_new(client_id, MIB_AUTHORITY_COMMON, NULL, NULL);
+	}
+
+	if (!client_context->mibClientWrapper->app)
+		;
+	return ERROR_INTERNAL_ERROR;
+
+	const char* scope = NULL;
+	const char* req_cnf = NULL;
+
+	va_list ap;
+	va_start(ap, count);
+
+	if ((client_context->mibClientWrapper->state == SSO_MIB_STATE_INIT) ||
+	    (client_context->mibClientWrapper->state == SSO_MIB_STATE_SUCCESS))
+	{
+		switch (tokenType)
+		{
+			case ACCESS_TOKEN_TYPE_AVD:
+			{
+				rc = sso_mib_get_avd_access_token(client_context, token);
+				if (rc)
+					client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
+				else
+				{
+					WLog_WARN(TAG, "Getting AVD token from identity broker failed, falling back to "
+					               "browser-based authentication.");
+					client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
+				}
+			}
+			break;
+			case ACCESS_TOKEN_TYPE_AAD:
+			{
+				scope = va_arg(ap, const char*);
+				req_cnf = va_arg(ap, const char*);
+				// Setup scope without URL encoding for sso-mib
+				char* scope_copy = winpr_str_url_decode(scope, strlen(scope));
+				if (!scope_copy)
+					WLog_ERR(TAG, "Failed to decode scope");
+				else
+				{
+					rc =
+					    sso_mib_get_rdsaad_access_token(client_context, scope_copy, req_cnf, token);
+					free(scope_copy);
+					if (rc)
+						client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
+					else
+					{
+						WLog_WARN(TAG,
+						          "Getting RDS token from identity broker failed, falling back to "
+						          "browser-based authentication.");
+						client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
+					}
+				}
+			}
+			break;
+			default:
+				break;
+		}
+	}
+	if (!rc && client_context->mibClientWrapper->GetCommonAccessToken)
+		rc = client_context->mibClientWrapper->GetCommonAccessToken(context, tokenType, token,
path: client/common/sso_mib_tokens.c
user: login: akallabeth
body: ah, ok, now I see, did miss that case :grin: 
created_at: 2025-05-21T11:59:12Z
author_association: MEMBER

PR comments:
user: login: freerdp-bot
created_at: 2025-05-21T11:54:09Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14908/

author_association: NONE
user: login: freerdp-bot
created_at: 2025-05-21T12:02:39Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14909/

author_association: NONE
user: login: freerdp-bot
created_at: 2025-05-21T12:14:22Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14910/

author_association: NONE
user: login: akallabeth
created_at: 2025-05-21T12:27:16Z
body: @rupran ok, did finish this, now should work.
can you confirm?
author_association: MEMBER
user: login: freerdp-bot
created_at: 2025-05-21T12:43:42Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14911/

author_association: NONE
user: login: rupran
created_at: 2025-05-21T12:47:40Z
body: > @rupran ok, did finish this, now should work. can you confirm?

Will do a deeper test later but looked good on first happy path test 👍 
author_association: CONTRIBUTOR
user: login: akallabeth
created_at: 2025-05-21T12:53:12Z
body: @rupran great. I did push another commit to make the whole thing fully opaque.
author_association: MEMBER
user: login: freerdp-bot
created_at: 2025-05-21T13:04:46Z
body: 
Refer to this link for build results (access rights to CI server needed): 
https://ci.freerdp.com//job/PullRequestTester/14912/

author_association: NONE
user: login: rupran
created_at: 2025-05-21T15:20:40Z
body: Yep, this works as expected (in xfreerdp):

- If everything is working, the broker either asks for an interactive login for the RDSAAD token or silently retrieves it and opens the RDP connection
- If the first broker call fails, it falls back to the URL method for both requests as the `state` is kept as `FAILED` - I simulated this by editing the scope to some random string
- If the first broker request succeeds, but the second request fails, we need to get the RDSAAD token from the browser (with the `ms-appx-web://` redirect) - I simulated this by passing `""` into the call to `mib_pop_params_set_kid`  instead of the actual `kid` value from `req_cnf`.

Compiling with `-DWITH_SSO_MIB=OFF` is the same as before :)
author_association: CONTRIBUTOR