Cordova应用程序中使用JWT OAuth 2.0令牌的Azure MFA

Cordova应用程序中使用JWT OAuth 2.0令牌的Azure MFA,cordova,azure,oauth,multi-factor-authentication,Cordova,Azure,Oauth,Multi Factor Authentication,我正在开发一个Cordova应用程序,该应用程序(到目前为止)使用密码授权从Azure中的Microsoft标准OAuth提供商检索JWTs: https://login.microsoftonline.com/[tenant]/oauth2/token 它很好用。然而,我们正在向外部贸易商开放我们的应用程序,业主希望增加MFA 因此,我在Azure中创建了一个MFA提供程序,并为MFA启用了一个测试帐户 我目前正在使用InAppBrowser插件打开重定向请求-这似乎很有效-它打开登录页面,

我正在开发一个Cordova应用程序,该应用程序(到目前为止)使用密码授权从Azure中的Microsoft标准OAuth提供商检索JWTs:

https://login.microsoftonline.com/[tenant]/oauth2/token
它很好用。然而,我们正在向外部贸易商开放我们的应用程序,业主希望增加MFA

因此,我在Azure中创建了一个MFA提供程序,并为MFA启用了一个测试帐户

我目前正在使用InAppBrowser插件打开重定向请求-这似乎很有效-它打开登录页面,发送文本,我输入代码,然后它完成登录到“应用程序”初始屏幕(用户的默认Azure登录)

我的问题是确定登录成功,并检索JWTs。由于MFA,登录服务器现在在初始登录时返回以下“MFA”错误(实际上不是错误):

然而,一旦MFA完成,我不知道去哪里获取我的令牌/刷新令牌。如果我重新提交登录,它只会发回一条“interaction_required”消息,即使在MFA过程中选择了“不再要求[X]天”

我希望问题是清楚的。如果没有,请告诉我,必要时我会修改


我目前没有使用ADAL或任何cordova插件进行身份验证。我正在自己到达终点。答案可能是我必须使用ADAL。

好了,伙计们,问题来了。因为我使用的是密码授权,所以我没有点击/oauth2/authorize端点-密码授权不需要它-直接转到/oauth2/token

对于MFA,/oauth2/authorize是必需的。如果启用了MFA,它将重定向并为您处理所有事情(非常简单)。您只需等待重定向url,身份验证代码是一个查询参数,因此很容易推断

在浏览器重定向后,您获取授权代码,然后将其提交到/oauth2/token服务器,而不使用用户名/密码(也不需要授权头,这很好,因为您不必两次请求它—一次请求MFA,一次请求传递到/token-good call Microsoft)

流动

}

然后,在“loadStartCallBack”中:

else if (event.url.split('/')[2] == '[returnURLWithoutHttps://]') {
        var fullstring = event.url.split('/')[3].split('?code=')[1] 
        var code = fullstring.split('&')[0];
        var sess_state = fullstring.split('session_state=')[1];
        localStorage.tokenCode = code;
        sessionStorage.sess_state = sess_state;
        inAppBrowserRef.close();
        getToken();
    }
然后将授权代码传递到/oauth2/token服务器,并接收回您的令牌(我将在password grant stuff中留下注释,以供将来从密码授予开始的读者使用):


就这样。希望有一天它能帮助到别人。

我相信这个问题源于InAppBrowser不与我的应用共享本地存储。一旦InAppBrowser关闭,它的cookie就会超出范围并被销毁。至少这是目前研究的方向。我必须弄清楚如何通过XMLHttpRequest(或ADAL或其他)直接与MFA提供者对话。
testMFA = function () {
var url = "https://login.microsoftonline.com/[tenantID]/oauth2/authorize?client_id=[clientID]&response_type=code&response_mode=query";;
var target = "_blank";
var options = "location=yes";
inAppBrowserRef = cordova.InAppBrowser.open(url, target, options);
with (inAppBrowserRef) {
    try {
        addEventListener('loadstart', loadStartCallBack);
        addEventListener('loadstop', loadStartCallBack);
        addEventListener('loaderror', loadStartCallBack);
        addEventListener('exit', loadStartCallBack);
    }
    catch (ex) {
        alert(ex);
    }
}
else if (event.url.split('/')[2] == '[returnURLWithoutHttps://]') {
        var fullstring = event.url.split('/')[3].split('?code=')[1] 
        var code = fullstring.split('&')[0];
        var sess_state = fullstring.split('session_state=')[1];
        localStorage.tokenCode = code;
        sessionStorage.sess_state = sess_state;
        inAppBrowserRef.close();
        getToken();
    }
var data =
'resource=[resourceURL]' +
//'&username=' + window.sessionStorage.loginUser + 
//'&password=' + password +
'&client_id=' + clientId +
'&code=' + authCode +
'&grant_type=authorization_code' + 
//'&grant_type=password';
'&response_type=token';
var dataFinal = encodeURI(data);