针对我自己的WebAPI的Azure AD租户进行身份验证

时间:2017-10-23 14:17:27

标签: javascript azure authentication asp.net-web-api outlook

我有一个问题需要了解我在interwebs中找到的各种身份验证示例是否适用于我真正简单的用例,以及如何让OAuth隐式身份验证流程可靠地跨浏览器工作。

要求

我有一个(非Angular)单页内联网应用程序(SPA)用于单个Azure AD租户(租户可在安装期间配置)。该应用程序以经过身份验证的方式针对.NET WebAPI后端运行。 我不需要代表登录用户访问其他服务器,WebAPI后端只需要知道自己位于客户端SPA之前的人。(这应该是最简单的场景。)

autah进程必须在Windows上的所有浏览器中可靠地运行,并且嵌入在Outlook(JS加载项)中,即使Azure AD的auth流强制通过本地ADFS和/或MFA和/或其他的箍,auth进程必须解决弹出窗口阻止程序等浏览器功能。此外,应用程序可能不需要每小时重新进行身份验证,就像现在一样。

我希望使用标准身份验证过程可以实现这些硬性要求,而不是通过完全自编写的身份验证。 但是,在尝试使用各种OAuth客户端库之后,自编的auth看起来就是目前唯一可行的方法。

我做了什么

首先,我将Azure AD Bearer身份验证添加到WebAPI:

WindowsAzureActiveDirectoryBearerAuthenticationOptions windowsAzureActiveDirectoryBearerAuthenticationOptions = new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
    Audience = clientId,
    Tenant = tenantId
};
app.UseWindowsAzureActiveDirectoryBearerAuthentication(windowsAzureActiveDirectoryBearerAuthenticationOptions);

然后,我将ADAL.js包含在我的SPA中,尝试通过弹出窗口获取登录信息,并通过隐藏的iframe获取访问令牌:

settings: {
    tenant: '09265c06-fa36-4a7a-90cd-e3c2b0dd1c39',
    clientId: '16f66ef5-e30a-4e22-b191-3f230ef8d93c',
    popUp:true
},
acquireLogin: function() {
    var me = this,
        pollForLoginCallback = function() {
            var pollTimer = window.setInterval(function() {
                var token = me.AuthContext.getCachedToken(me.settings.clientId);
                if(token) { // We wait until a token has been returned
                    window.clearInterval(pollTimer);
                    me.acquireToken();
                }
            },50);
        };
    pollForLoginCallback();
    me.AuthContext.login();
},
acquireToken: function() {
    var me = this;
    // After login, we try to acquire the required tokens. This is necessary to get a token that can be refreshed in the background.
    me.AuthContext.getCachedUser(); // we don't need the user here, but acquireToken fails if getCachedUser is not executed before.
    me.tokenRefreshInProgress = true;
    me.AuthContext.acquireToken(me.settings.clientId, function (error, token) {
        if(!token) me.acquireLogin();
        else {
            me.tokenRefreshInProgress = false;
            me.successCallback(token);
        }
    });
},
successCallback:function(token) {
    // Apply token to all future Ajax requests.
    Ext.Ajax.setDefaultHeaders({
        'Authorization': 'Bearer ' + token,
        'Accept': 'application/json'
    });
    // Load Settings from Server.
    me.loadSettings();
}

我获得了初始身份验证,弹出窗口工作在最简单的情况下(Chrome和Firefox禁用弹出窗口阻止程序),但Microsoft自己的浏览器总是需要特殊的安全设置才能工作(否则会陷入无限的身份验证循环),并且Outlook加载项根本不允许window.open

此外,隐藏iframe中的刷新会定期失败,可能是因为未正确接收/存储刷新令牌,然后,“初始登录”必须再次弹出。

因此,对于Outlook加载项,我必须在ADAL.js中设置popUp: false,这意味着当重新登录到期时,将重新加载整个页面并再次循环执行身份验证过程,用户可能会丢失他刚输入的数据。

我还尝试了Office-js-helpers库,它比ADAL.js更容易实现,并且在Chrome中也能很好地运行:

me.Authenticator = new OfficeHelpers.Authenticator();
me.Authenticator.endpoints.registerAzureADAuth(settings.clientId, settings.tenant, {
    responseType: 'id_token',
    redirectUrl: window.location.origin + window.location.pathname
});
me.Authenticator
.authenticate(OfficeHelpers.DefaultEndpoints.AzureAD)
.then(function (token) {
    me.successCallback(token.id_token);
})
.catch(OfficeHelpers.Utilities.log);

但是在最初编写它的Outlook中,它甚至无法打开对话框窗口(I already submitted a bug report for that)。

此外,“纯”Office.js库/ API可以获得两个不同的令牌(getUserIdentityTokenAsyncgetCallbackTokenAsync),但不可能获得我的应用程序接受的令牌。我检查了令牌,发现这些令牌上的“受众”是一个常量GUID,分别代表原始Office 365和Microsoft Graph。所以,出于好奇,我告诉我的后端也接受了Office365受众:

WindowsAzureActiveDirectoryBearerAuthenticationOptions windowsAzureActiveDirectoryBearerAuthenticationOptions2 = new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
    Audience = "00000002-0000-0ff1-ce00-000000000000", // CONST Outlook/Exchange clientId
    Tenant = tenantId
};
app.UseWindowsAzureActiveDirectoryBearerAuthentication(windowsAzureActiveDirectoryBearerAuthenticationOptions2);

但是当我向该后端提交带有该受众的Outlook令牌时,它仍然返回错误401。

所以现在我完全和完全坚持使用三个微软库,它们在一半的浏览器中似乎只有一半的广告功能;实际上,我需要的只是对后端的可靠身份验证。没什么特别的。

解决方法:实施我自己的身份验证

现在通过丛林的唯一途径是直接在SPA中使用用户名/密码表单实现我自己的旧式认证,将用户名/密码发送到对AD Graph进行密码检查的WebAPI控制器或EWS,如果成功,则向客户发送cookie(有效期至少一天)。

然而,这不是所有OAuth流程的最终结果,或者是它?

所以,我的问题是:如何以跨浏览器的方式找到通过OAuth身份验证流丛林的方式,并使用Outlook 2016加载项兼容性?

0 个答案:

没有答案