首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使用Microsoft.Identity.Client加载outlook /IMAP所需的用户令牌

如何使用Microsoft.Identity.Client加载outlook /IMAP所需的用户令牌
EN

Stack Overflow用户
提问于 2022-07-29 07:02:38
回答 1查看 262关注 0票数 0

我确实有一个窗口服务,它读取电子邮件(IMAP),没有任何用户交互。微软是弃用 POP/IMAP在线交换的基本身份验证。

基于微软的文档,应该使用MSAL客户端库来获取用户令牌。

我试图让用户令牌使用

代码语言:javascript
运行
复制
result = await app.AcquireTokenByUsernamePassword(scopes, USERNAME, securePassword).ExecuteAsync();

但这只返回AADSTS7000218:请求主体必须包含以下参数:“client_assertion”或“client_secret”

提问

我不太清楚我哪里错了。

  • 我是否需要提供一个client_secret,还是可以在Azure中对用户进行配置?
  • ClientID是我需要在Azure上创建的应用程序,还是我在线交换的ClientID?
    • 如果是网上交换的ID,我在哪里可以得到这个ID?

  • 是否有为exchange联机用户加载令牌的良好示例?
  • 不需要编程就可以得到令牌吗?
    • 例如通过网上交换网站

完整的工作示例:

代码语言:javascript
运行
复制
using Microsoft.Identity.Client;
using System;
using System.Linq;
using System.Security;
using System.Threading.Tasks;

namespace MSALTest
{
    /// <summary>
    /// Sample application for loading a token for an outlook account
    /// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Username-Password-Authentication
    /// </summary>
    internal class Program
    {
        //     URL of the security token service (STS) from which MSAL.NET will acquire the
        //     tokens. Usual authorities endpoints for the Azure public Cloud are:
        //     • https://login.microsoftonline.com/tenant/ where tenant is the tenant ID of
        //     the Azure AD tenant or a domain associated with this Azure AD tenant, in order
        //     to sign-in users of a specific organization only
        //     • https://login.microsoftonline.com/common/ to sign-in users with any work and
        //     school accounts or Microsoft personal account
        //     • https://login.microsoftonline.com/organizations/ to sign-in users with any
        //     work and school accounts
        //     • https://login.microsoftonline.com/consumers/ to sign-in users with only personal
        //     Microsoft accounts (live)
        //     Note that this setting needs to be consistent with what is declared in the application
        //     registration portal
        const string AUTHORITY = "https://login.microsoftonline.com/<my tenantn>/";

        //     Client ID (also known as App ID) of the application as registered in the application
        //     registration portal (https://aka.ms/msal-net-register-app)/.
        const string CLIENTID = "<my clientid>";

        //     Identifier of the user application requests token on behalf. Generally in UserPrincipalName
        //     (UPN) format, e.g. john.doe@contoso.com
        const string USERNAME = "<my user>";


        static async Task GetTokenTest()
        {
            string[] scopes = new string[] { "user.read" };
            IPublicClientApplication app;
            app = PublicClientApplicationBuilder.Create(CLIENTID)
                                              .WithAuthority(AUTHORITY)
                                              .Build();
            var accounts = await app.GetAccountsAsync();

            AuthenticationResult result = null;
            if (accounts.Any())
            {
                result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
            }
            else
            {
                var securePassword = GetPassword();
                result = await app.AcquireTokenByUsernamePassword(scopes, USERNAME, securePassword).ExecuteAsync();
            }
            Console.WriteLine(result.Account.Username);
        }


        static async Task Main(string[] args)
        {
            try
            {
                await GetTokenTest();
            }
            catch (MsalUiRequiredException ex) when (ex.Message.Contains("AADSTS65001"))
            {
                // Here are the kind of error messages you could have, and possible mitigations

                // ------------------------------------------------------------------------
                // MsalUiRequiredException: AADSTS65001: The user or administrator has not consented to use the application
                // with ID '{appId}' named '{appName}'. Send an interactive authorization request for this user and resource.

                // Mitigation: you need to get user consent first. This can be done either statically (through the portal), 
                /// or dynamically (but this requires an interaction with Azure AD, which is not possible with 
                // the username/password flow)
                // Statically: in the portal by doing the following in the "API permissions" tab of the application registration:
                // 1. Click "Add a permission" and add all the delegated permissions corresponding to the scopes you want (for instance
                // User.Read and User.ReadBasic.All)
                // 2. Click "Grant/revoke admin consent for <tenant>") and click "yes".
                // Dynamically, if you are not using .NET Core (which does not have any Web UI) by 
                // calling (once only) AcquireTokenInteractive.
                // remember that Username/password is for public client applications that is desktop/mobile applications.
                // If you are using .NET core or don't want to call AcquireTokenInteractive, you might want to:
                // - use device code flow (See https://aka.ms/msal-net-device-code-flow)
                // - or suggest the user to navigate to a URL to consent: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read
                // ------------------------------------------------------------------------

                // ------------------------------------------------------------------------
                // ErrorCode: invalid_grant
                // SubError: basic_action
                // MsalUiRequiredException: AADSTS50079: The user is required to use multi-factor authentication.
                // The tenant admin for your organization has chosen to oblige users to perform multi-factor authentication.
                // Mitigation: none for this flow
                // Your application cannot use the Username/Password grant.
                // Like in the previous case, you might want to use an interactive flow (AcquireTokenInteractive()), 
                // or Device Code Flow instead.
                // Note this is one of the reason why using username/password is not recommended;
                // ------------------------------------------------------------------------

                // ------------------------------------------------------------------------
                // ex.ErrorCode: invalid_grant
                // subError: null
                // Message = "AADSTS70002: Error validating credentials.
                // AADSTS50126: Invalid username or password
                // In the case of a managed user (user from an Azure AD tenant opposed to a
                // federated user, which would be owned
                // in another IdP through ADFS), the user has entered the wrong password
                // Mitigation: ask the user to re-enter the password
                // ------------------------------------------------------------------------

                // ------------------------------------------------------------------------
                // ex.ErrorCode: invalid_grant
                // subError: null
                // MsalServiceException: ADSTS50034: To sign into this application the account must be added to 
                // the {domainName} directory.
                // or The user account does not exist in the {domainName} directory. To sign into this application, 
                // the account must be added to the directory.
                // The user was not found in the directory
                // Explanation: wrong username
                // Mitigation: ask the user to re-enter the username.
                // ------------------------------------------------------------------------$
                Console.WriteLine($"Exception: invalid_grant. {ex.Message}");
            }
            catch (MsalServiceException ex) when (ex.ErrorCode == "invalid_request")
            {
                // ------------------------------------------------------------------------
                // AADSTS90010: The grant type is not supported over the /common or /consumers endpoints. 
                // Please use the /organizations or tenant-specific endpoint.
                // you used common.
                // Mitigation: as explained in the message from Azure AD, the authority you use in the application needs 
                // to be tenanted or otherwise "organizations". change the
                // "Tenant": property in the appsettings.json to be a GUID (tenant Id), or domain name (contoso.com) 
                // if such a domain is registered with your tenant
                // or "organizations", if you want this application to sign-in users in any Work and School accounts.
                // ------------------------------------------------------------------------
                Console.WriteLine($"Exception: invalid_request. {ex.Message}");
            }
            catch (MsalServiceException ex) when (ex.ErrorCode == "unauthorized_client")
            {
                // ------------------------------------------------------------------------
                // AADSTS700016: Application with identifier '{clientId}' was not found in the directory '{domain}'.
                // This can happen if the application has not been installed by the administrator of the tenant or consented 
                // to by any user in the tenant.
                // You may have sent your authentication request to the wrong tenant
                // Cause: The clientId in the appsettings.json might be wrong
                // Mitigation: check the clientId and the app registration
                // ------------------------------------------------------------------------
                Console.WriteLine($"Exception: unauthorized_client. {ex.Message}");
            }
            catch (MsalServiceException ex) when (ex.ErrorCode == "invalid_client")
            {
                // ------------------------------------------------------------------------
                // AADSTS70002: The request body must contain the following parameter: 'client_secret or client_assertion'.
                // Explanation: this can happen if your application was not registered as a public client application in Azure AD
                // Mitigation: in the Azure portal, edit the manifest for your application and set the `allowPublicClient` to `true`
                // ------------------------------------------------------------------------
                Console.WriteLine($"Exception: invalid_client. {ex.Message}");
            }
            catch (MsalServiceException ex)
            {
                Console.WriteLine($"Exception: MsalServiceException. {ex.Message}");
            }
            catch (MsalClientException ex) when (ex.ErrorCode == "unknown_user_type")
            {
                // Message = "Unsupported User Type 'Unknown'. Please see https://aka.ms/msal-net-up"
                // The user is not recognized as a managed user, or a federated user. Azure AD was not
                // able to identify the IdP that needs to process the user
                Console.WriteLine($"U/P: Wrong username. {ex.Message}");
            }
            catch (MsalClientException ex) when (ex.ErrorCode == "user_realm_discovery_failed")
            {
                // The user is not recognized as a managed user, or a federated user. Azure AD was not
                // able to identify the IdP that needs to process the user. That's for instance the case
                // if you use a phone number
                Console.WriteLine($"user_realm_discovery_failed. {ex.Message}");
            }
            catch (MsalClientException ex) when (ex.ErrorCode == "unknown_user")
            {
                // the username was probably empty
                // ex.Message = "Could not identify the user logged into the OS. See http://aka.ms/msal-net-iwa for details."
                Console.WriteLine($"unknown_user. {ex.Message}");
            }
            catch (MsalClientException ex) when (ex.ErrorCode == "parsing_wstrust_response_failed")
            {
                // ------------------------------------------------------------------------
                // In the case of a Federated user (that is owned by a federated IdP, as opposed to a managed user owned in an Azure AD tenant)
                // ID3242: The security token could not be authenticated or authorized.
                // The user does not exist or has entered the wrong password
                // ------------------------------------------------------------------------
                Console.WriteLine($"parsing_wstrust_response_failed. {ex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception: {ex.Message}");
                if (ex.InnerException != null)
                {
                    Console.WriteLine($"InnerException: {ex.InnerException.Message}");
                }



            }
            Console.ReadLine();
        }

        /// <summary>
        /// Asks the user for the password
        /// The password is never show in the console and the password is only saved as SecureString
        /// </summary>
        /// <returns>The entered password as a secure string</returns>
        public static SecureString GetPassword()
        {
            Console.WriteLine("Enter your password. Confirm by pressing enter. (The password is neither displayed nor saved)");
            var pwd = new SecureString();
            while (true)
            {
                ConsoleKeyInfo i = Console.ReadKey(true);
                if (i.Key == ConsoleKey.Enter)
                {
                    break;
                }
                else if (i.Key == ConsoleKey.Backspace)
                {
                    if (pwd.Length > 0)
                    {
                        pwd.RemoveAt(pwd.Length - 1);
                        Console.Write("\b \b");
                    }
                }
                else if (i.KeyChar != '\u0000') // KeyChar == '\u0000' if the key pressed does not correspond to a printable character, e.g. F1, Pause-Break, etc
                {
                    pwd.AppendChar(i.KeyChar);
                    Console.Write("*");
                }
            }
            Console.WriteLine();
            return pwd;
        }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-30 13:19:28

AcquireTokenByUsernamePassword()方法使用ROPC流对用户进行身份验证并获取accessToken。这个流程有一些缺点(没有MFA,可能无法与联邦用户一起工作,等等),所以仔细检查前面的文档。

要使这个流在没有client_secret或client_assertion的情况下工作,您需要在应用程序注册上“允许公共客户端流”。详情如下:

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73162777

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档