使用Azure AD在API后端(.NET Core)中进行身份验证基础知识

3
也许这个问题比标题更全面。有很多关于身份验证、令牌、JWT、Azure AD等的帖子,但所有这些帖子似乎都在讲述不同的故事,这使得基本概念对我来说有点不清楚。我将尝试使用我的案例来解释我的问题。
我用React在Visual Studio Code中构建了一个前端应用程序,在Visual Studio .Net Core 2(Web API)中构建了一个后端应用程序。这些应用程序正在Azure中托管,我们想要使用Azure AD进行身份验证。
前端使用Adal-React (https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-libraries)和Axios (https://github.com/axios/axios)。
在教程的帮助下,我已经在前端和API(后端)中设置了配置,主要是使用在Azure环境中列出的TenantID和ClientID。
虽然我已经设置好并且它正在工作,但它的工作方式对我来说还不太清楚。

我会尝试解释一下我的应用程序中当前的工作流程,并列出我目前的问题:

1 - 用户导航到前端应用程序,adal检查用户是否已经通过身份验证,如果没有,则将其重定向到我们的Azure登录环境,这是在adal配置(前端)中设置的,如下所示:

  const adalConfig = {
         tenant: 'vhsprod.onmicrosoft.com',
         clientId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx',
         endpoints: {
          api: 'https://xxxxx.onmicrosoft.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx (client id)',
         },
         postLogoutRedirectUri: window.location.origin,
         redirectUri: 'http://localhost:3000/user-form',
         cacheLocation: 'sessionStorage'
        };
        export const authContext = new AuthenticationContext(adalConfig);
        export const getToken = () => {
         return authContext.getCachedToken(authContext.config.clientId);
        };

2 - 用户需要登录并检查用户是否存在于Azure AD环境中,如果存在,则用户将被重定向回前端,并获得一个令牌

3 - 用户在前端打开一个需要来自后端的数据的表单/页面,使用刚刚收到的令牌进行API调用。在前端使用Axios进行调用,Axios也已配置好:

export const axiosCallToMyAPI = axios.create({
  baseURL: 'http://localhost:52860/api/',
  timeout: 5000,
  headers: {'Authorization': 'Bearer ' + initialToken}
});

初始令牌正在从Adal接收,这是我们刚刚收到的令牌:

let initialToken = sessionStorage.getItem('adal.idtoken')

4 - 此处正在调用API,我们还设置了 Azure AD 的配置(appsettings.json):

"AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantDomain": "xxxx.onmicrosoft.com",
    "TenantId": "xxxx.onmicrosoft.com",
    "ClientId": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx"
  }

5 - 在API的启动类中,正在验证令牌:

services
                .AddAuthentication(sharedOptions =>
                {
                    sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 
                    sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 
                })
                .AddJwtBearer(options =>
                {
                    options.Audience = this.Configuration["AzureAd:ClientId"];
                    options.Authority = $"{this.Configuration["AzureAd:Instance"]}{this.Configuration["AzureAd:TenantId"]}";

                    options.Events = new JwtBearerEvents()
                    {

                        OnTokenValidated = context =>
                        {
                            return Task.CompletedTask;
                        }
                    };
                });

根据我阅读的几篇文章,仍有一些问题和假设需要澄清:

  • 使用前端接收到的Azure令牌,API发起调用,那么API如何知道这个令牌是否“有效”?我知道API的启动类使用事件OnTokenValidated,但是这背后有什么“魔法”吗?它纯粹是将appsettings中配置的租户/客户端ID与接收到的令牌进行比较吗?
  • 在几篇文章中,他们使用/提到了SecretKey / Signing Key,在我的例子中,我没有实现SecretKey,但它仍然可以工作。我需要在我的API(后端)中实现秘密/签名密钥吗?这是必要的吗?
  • 我还了解了隐式流、OpenID连接和一些其他术语。像在我的例子中使用Azure AD身份验证这样做时,我是否自动执行了implicit flow(前端-->后端)和使用openid connect进行身份验证?换句话说,当使用Azure身份验证时,您是否自动执行这些/最佳实践,还是应该自己实现?
  • 令牌具有特定的声明/范围定义,这些声明由Azure提供(您也可以设置这些声明,如电子邮件和角色),但是当使用AspNet(Identity)时,您还可以将自己的声明添加到当前令牌/会话中,例如当我从aspnetidentity向我的令牌添加角色声明时。这些声明与原始声明有何不同(仍然是承载者/jwt?),这是一个好的实践方法吗?

请问是否有人能够确认/解释这些问题?

如果我的故事有点笼统,我很抱歉,但目前我们的试错率太高了,我只是想澄清一些事情。

1个回答

2
使用 Azure 令牌,前端接收到后会调用 API,那么 API 是如何知道这个令牌是否“有效”的呢?我知道 API 的启动类使用了 OnTokenValidated 事件,但是背后的原理是什么呢?它是纯粹通过检查 appsettings 中配置的租户 / 客户端 ID 和接收到的令牌来判断的吗?
在配置身份验证时,您指定了 Authority。当应用程序启动时,身份验证处理程序从以下 URL 下载 OpenId Connect 元数据:https://login.microsoftonline.com/joonasapps.onmicrosoft.com/.well-known/openid-configuration
然后,处理程序获取公共签名密钥和有效发行者 URI。
处理程序检查令牌的数字签名是否有效(与其获得的签名密钥相匹配),发行者是否有效,受众是否有效,以及令牌是否过期。
在多篇文章中,他们使用/提到了 SecretKey / Signing Key,在我的示例中,我没有实现 SecretKey,但它仍然可以工作。我需要在我的 API(后端)中实现秘密 / 签名密钥吗?这是必要的吗?
请参考上面的内容。
我还了解了隐式流、OpenID Connect和其他一些术语。在使用Azure AD身份验证时,像我的示例中这样做,那么我是否自动执行隐式流(前端-->后端)并使用openid connect进行身份验证?换句话说,在使用Azure身份验证时,您会自动执行这些最佳实践吗?或者您是否仍然需要进行实现?
隐式流意味着客户端可以直接从授权终结点获取访问令牌,而不是从令牌终结点请求它。单页应用程序使用隐藏的iframe获取访问令牌和刷新过期的令牌。这取决于用户与Azure AD的会话保持活动状态。如果您有一个更“传统”的后端应用程序,您可能会使用授权代码流而不是隐式流。
令牌具有特定的声明/作用域,由Azure提供(您也可以设置这些声明,如电子邮件和角色),但是当使用AspNet(Identity)时,您还可以将自己的声明添加到当前令牌/会话中,例如当我从aspnetidentity添加一个roleclaim到我的令牌时。这些声明与原始声明有何不同(仍为承载人/jwt?),这是一种好的做法吗?
您的应用程序无法修改Azure AD令牌,这是绝对的。 如果您修改了它,它的签名将不再有效。 相反,ASP.NET Core Identity使用cookie +会话来存储已登录用户的声明。 因此,生成的用户会话将包含来自令牌的声明以及您在用户存储中拥有的声明。

1
关于使用哪种流程的问题,不能说Azure AD(AAD)总是默认使用特定的流程。但是@juunas在他对隐式流程的陈述中是正确的。
要验证正在使用哪个流程以及是否正在接收,请检查发送到AAD的URL请求中由ADAL设置的以下参数:
  1. scope:对于OIDC,必须包含“openid”。
  2. response_type:对于OIDC,必须包括“id_token”。如果存在“code”,则还会收到OAuth承载令牌。
    参考:https://preview.tinyurl.com/yaqatsua
- Toby Artisan
1
@juunas 很棒的帖子!这确实澄清了一些事情。感谢您花时间详细解答我的问题! - Nicolas
1
流程基本上是由发送到授权终点的 response_type 决定的。如果设置为 code,它将使用授权码流程。如果是 tokenid_token,则使用隐式流程。如果两者都组合在一起,则使用混合流程 :) 接下来还有后端流程... - juunas
更多信息请参见:https://learn.microsoft.com/zh-cn/azure/active-directory/develop/active-directory-authentication-scenarios - juunas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接