我确实有一个窗口服务,它读取电子邮件(IMAP),没有任何用户交互。微软是弃用 POP/IMAP在线交换的基本身份验证。
基于微软的文档,应该使用MSAL客户端库来获取用户令牌。
我试图让用户令牌使用
result = await app.AcquireTokenByUsernamePassword(scopes, USERNAME, securePassword).ExecuteAsync();
但这只返回AADSTS7000218:请求主体必须包含以下参数:“client_assertion”或“client_secret”
提问
我不太清楚我哪里错了。
完整的工作示例:
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;
}
}
}
发布于 2022-07-30 13:19:28
AcquireTokenByUsernamePassword()方法使用ROPC流对用户进行身份验证并获取accessToken。这个流程有一些缺点(没有MFA,可能无法与联邦用户一起工作,等等),所以仔细检查前面的文档。
要使这个流在没有client_secret或client_assertion的情况下工作,您需要在应用程序注册上“允许公共客户端流”。详情如下:
https://stackoverflow.com/questions/73162777
复制相似问题