이 문서에서는 일반적인 로그인 오류를 처리하기 위한 다양한 유형의 오류 및 권장 사항에 대한 개요를 제공합니다.
MSAL 오류 처리 기본 사항
MSAL(Microsoft 인증 라이브러리 예외)은 앱 개발자가 최종 사용자에게 표시하는 것이 아니라 문제를 해결하기 위한 것입니다. 예외 메시지는 지역화되지 않습니다.
예외 및 오류를 처리할 때 예외 유형 자체와 오류 코드를 사용하여 예외를 구분할 수 있습니다. 오류 코드 목록은 Microsoft Entra 인증 및 권한 부여 오류 코드를 참조하세요.
로그인 환경 중에 동의, 조건부 액세스(MFA, 장치 관리, 위치 기반 제한), 토큰 발급 및 상환 및 사용자 속성에 대한 오류가 발생할 수 있습니다.
다음 섹션에서는 앱에 대한 오류 처리에 대한 자세한 내용을 제공합니다.
MSAL.NET 오류 처리
예외 유형
MsalClientException 은 라이브러리 자체가 잘못된 구성과 같은 오류 상태를 검색할 때 throw됩니다.
msalServiceException은 id 공급자(Microsoft Entra ID)가 오류를 반환할 때 throw됩니다. 서버 오류의 변환입니다.
MsalUIRequiredException 은 MsalServiceException 의 형식이며 사용자 상호 작용이 필요했음을 나타냅니다. 예를 들어 MFA(다단계 인증)가 필요하거나 사용자가 암호를 변경하고 토큰을 자동으로 획득할 수 없는 경우입니다.
예외 처리
.NET 예외를 처리할 때 예외 형식 자체와 멤버를 ErrorCode 사용하여 예외를 구분할 수 있습니다.
ErrorCode 값은 MsalError 형식의 상수입니다.
MsalClientException, MsalServiceException 및 MsalUIRequiredException의 필드를 살펴볼 수도 있습니다.
MsalServiceException이 throw되면 인증 및 권한 부여 오류 코드를 시도하여 코드가 나열되는지 확인합니다.
MsalUIRequiredException이 throw되는 경우 사용자가 문제를 해결하기 위해 대화형 흐름이 발생해야 한다는 표시입니다. 데스크톱 및 모바일 앱과 같은 공용 클라이언트 앱에서는 브라우저를 표시하는 호출 AcquireTokenInteractive을 통해 해결됩니다. 기밀 클라이언트 앱에서 웹앱은 사용자를 권한 부여 페이지로 리디렉션해야 하며, 웹 API는 인증 실패(401 권한 없음 및 WWW-Authenticate 헤더)를 나타내는 HTTP 상태 코드 및 헤더를 반환해야 합니다.
일반적인 .NET 예외
다음은 발생할 수 있는 일반적인 예외와 이에 대한 몇 가지 가능한 대응 방안입니다.
| 예외 | 오류 코드 | 완화 방법 |
|---|---|---|
| MsalUiRequiredException | AADSTS65001: 사용자 또는 관리자가 '{appName}'이라는 ID가 '{appId}'인 애플리케이션을 사용하는 데 동의하지 않았습니다. 이 사용자 및 리소스에 대한 대화형 권한 부여 요청을 보냅니다. | 먼저 사용자 동의를 가져옵니다. .NET Core(웹 UI가 없는)를 사용하지 않는 경우(한 번만) AcquireTokenInteractive호출합니다. .NET Core를 사용하거나 AcquireTokenInteractive을 수행하고 싶지 않은 경우, 사용자는 동의하기 위해 URL로 이동할 수 있습니다: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read.
AcquireTokenInteractive 호출 방법: app.AcquireTokenInteractive(scopes).WithAccount(account).WithClaims(ex.Claims).ExecuteAsync(); |
| MsalUiRequiredException | AADSTS50079: 사용자는 MFA(다단계 인증)를 사용해야 합니다. | 완화는 없습니다. 테넌트에 MFA가 구성되어 있고 Microsoft Entra ID에서 이를 적용하기로 결정한 경우, AcquireTokenInteractive와 같은 대화형 흐름으로 대체하세요. |
| MsalServiceException | AADSTS90010: 권한 부여 유형은 /common 또는 /consumers 엔드포인트에서 지원되지 않습니다. /organizations 또는 테넌트별 엔드포인트를 사용합니다. /common을 사용했습니다. | Microsoft Entra ID 메시지에 설명된 대로 권한에는 테넌트 또는 기타 /organizations가 있어야 합니다. |
| MsalServiceException | AADSTS70002: 요청 본문에는 다음 매개 변수 client_secret or client_assertion가 포함되어야 합니다. |
애플리케이션이 Microsoft Entra ID 공용 클라이언트 애플리케이션으로 등록되지 않은 경우 이 예외가 throw될 수 있습니다. Microsoft Entra 관리 센터에서 애플리케이션의 매니페스트를 편집하고 allowPublicClient를 true(으)로 설정합니다. |
| MsalClientException |
unknown_user Message: 로그인한 사용자를 식별할 수 없습니다. |
라이브러리에서 현재 Windows 로그인한 사용자를 쿼리할 수 없거나 이 사용자가 Active Directory 또는 Microsoft Entra 조인되지 않았습니다(작업 장소에 조인된 사용자는 지원되지 않음). 완화 방법: 사용자 이름(예: john@contoso.com)을 가져오는 고유한 논리를 구현하고, 사용자 이름을 받는 AcquireTokenByIntegratedWindowsAuth 형식을 사용합니다. |
| MsalClientException | 관리되는 사용자에게는 통합 Windows 인증이 지원되지 않음 | 이 메서드는 AD(Active Directory)에서 노출하는 프로토콜을 사용합니다. AD 백업("관리되는" 사용자) 없이 Microsoft Entra ID 사용자가 만들어진 경우 이 메서드는 실패합니다. AD에서 생성되고 Microsoft Entra ID의 지원을 받는 사용자("페더레이션 사용자")는 이 비대화형 인증 방식을 활용할 수 있습니다. 완화: 대화형 인증을 사용합니다. |
MsalUiRequiredException
호출 AcquireTokenSilent() 할 때 MSAL.NET 반환되는 일반적인 상태 코드 중 하나입니다MsalError.InvalidGrantError. 이 상태 코드는 애플리케이션이 인증 라이브러리를 다시 호출해야 하지만 대화형 모드(공용 클라이언트 애플리케이션의 경우 AcquireTokenInteractive 또는 AcquireTokenByDeviceCodeFlow) 웹앱에 문제가 있음을 의미합니다. 인증 토큰을 발급하기 전에 추가 사용자 상호 작용이 필요하기 때문입니다.
대부분의 경우 AcquireTokenSilent 실패하면 토큰 캐시에 요청과 일치하는 토큰이 없기 때문입니다. 액세스 토큰은 1시간 후에 만료되며 AcquireTokenSilent 새로 고침 토큰을 기반으로 새 토큰을 가져오려고 시도합니다(OAuth2 용어로는 "새로 고침 토큰 흐름). 테넌트 관리자가 보다 엄격한 로그인 정책을 구성하는 경우와 같은 여러 가지 이유로 이 흐름이 실패할 수도 있습니다.
상호 작용은 사용자가 작업을 수행하게 하는 것을 목표로 합니다. 이러한 조건 중 일부는 사용자가 쉽게 해결할 수 있으며(예: 한 번의 클릭으로 사용 약관 수락), 현재 구성으로 해결할 수 없는 조건도 있습니다(예: 문제의 컴퓨터가 특정 회사 네트워크에 연결해야 하는 경우). 일부는 사용자가 다단계 인증을 설정하거나 디바이스에 Microsoft Authenticator 설치하는 데 도움이 됩니다.
MsalUiRequiredException 분류 열거형
MSAL은 Classification 더 나은 사용자 환경을 제공하기 위해 읽을 수 있는 필드를 노출합니다. 예를 들어 암호가 만료되었거나 일부 리소스를 사용하기 위해 동의를 제공해야 한다고 사용자에게 알릴 수 있습니다. 지원되는 값은 열거형의 UiRequiredExceptionClassification 일부입니다.
| Classification | Meaning | 권장 처리 |
|---|---|---|
| BasicAction | 대화형 인증 흐름 중에 사용자 상호 작용을 통해 조건을 확인할 수 있습니다. | AcquireTokenInteractively()를 호출합니다. |
| 추가 작업 | 조건은 대화형 인증 흐름 외부에서 시스템과의 추가 수정 상호 작용을 통해 해결할 수 있습니다. | AcquireTokenInteractively()를 호출하여 수정 작업을 설명하는 메시지를 표시합니다. 호출 애플리케이션은 사용자가 수정 작업을 완료할 가능성이 낮으면 additional_action 필요한 흐름을 숨기도록 선택할 수 있습니다. |
| MessageOnly | 현재는 조건을 확인할 수 없습니다. 대화형 인증 흐름을 시작하면 조건을 설명하는 메시지가 표시됩니다. | AcquireTokenInteractively()를 호출하여 조건을 설명하는 메시지를 표시합니다. AcquireTokenInteractively()는 사용자가 메시지를 읽고 창을 닫은 후 UserCanceled 오류를 반환합니다. 호출 애플리케이션은 메시지가 사용자에게 도움이 되지 않을 가능성이 높으면 message_only로 이어지는 흐름을 숨길 수 있습니다. |
| 동의 필요 | 사용자 동의가 없거나 해지되었습니다. | 사용자가 동의할 수 있도록 AcquireTokenInteractively()를 호출합니다. |
| 사용자 암호 만료 | 사용자의 암호가 만료되었습니다. | 사용자가 암호를 재설정할 수 있도록 AcquireTokenInteractively()를 호출합니다. |
| PromptNeverFailed | 대화형 인증이 매개 변수 프롬프트=never로 호출되어 MSAL이 브라우저 쿠키를 사용하고 브라우저를 표시하지 않도록 강제했습니다. 실패했습니다. | Prompt.None 없이 AcquireTokenInteractively()를 호출합니다. |
| AcquireTokenSilentFailed | MSAL SDK에는 캐시에서 토큰을 가져오는 데 충분한 정보가 없습니다. 캐시에 토큰이 없거나 계정을 찾을 수 없기 때문일 수 있습니다. 오류 메시지에는 자세한 내용이 있습니다. | AcquireTokenInteractively()를 호출합니다. |
| 없음 | 자세한 내용은 제공되지 않습니다. 대화형 인증 흐름 중에 사용자 상호 작용을 통해 조건을 확인할 수 있습니다. | AcquireTokenInteractively()를 호출합니다. |
.NET 코드 예제
AuthenticationResult res;
try
{
res = await application.AcquireTokenSilent(scopes, account)
.ExecuteAsync();
}
catch (MsalUiRequiredException ex) when (ex.ErrorCode == MsalError.InvalidGrantError)
{
switch (ex.Classification)
{
case UiRequiredExceptionClassification.None:
break;
case UiRequiredExceptionClassification.MessageOnly:
// You might want to call AcquireTokenInteractive(). Azure AD will show a message
// that explains the condition. AcquireTokenInteractively() will return UserCanceled error
// after the user reads the message and closes the window. The calling application may choose
// to hide features or data that result in message_only if the user is unlikely to benefit
// from the message
try
{
res = await application.AcquireTokenInteractive(scopes).ExecuteAsync();
}
catch (MsalClientException ex2) when (ex2.ErrorCode == MsalError.AuthenticationCanceledError)
{
// Do nothing. The user has seen the message
}
break;
case UiRequiredExceptionClassification.BasicAction:
// Call AcquireTokenInteractive() so that the user can, for instance accept terms
// and conditions
case UiRequiredExceptionClassification.AdditionalAction:
// You might want to call AcquireTokenInteractive() to show a message that explains the remedial action.
// The calling application may choose to hide flows that require additional_action if the user
// is unlikely to complete the remedial action (even if this means a degraded experience)
case UiRequiredExceptionClassification.ConsentRequired:
// Call AcquireTokenInteractive() for user to give consent.
case UiRequiredExceptionClassification.UserPasswordExpired:
// Call AcquireTokenInteractive() so that user can reset their password
case UiRequiredExceptionClassification.PromptNeverFailed:
// You used WithPrompt(Prompt.Never) and this failed
case UiRequiredExceptionClassification.AcquireTokenSilentFailed:
default:
// May be resolved by user interaction during the interactive authentication flow.
res = await application.AcquireTokenInteractive(scopes)
.ExecuteAsync(); break;
}
}
조건부 액세스 및 클레임 문제
토큰을 자동으로 가져오는 경우 액세스하려는 API에서 MFA 정책과 같은 조건부 액세스 클레임 챌린지 가 필요한 경우 애플리케이션에 오류가 발생할 수 있습니다.
이 오류를 처리하는 패턴은 MSAL을 사용하여 토큰을 대화형으로 획득하는 것입니다. 이렇게 하면 사용자에게 프롬프트가 표시되고 필요한 조건부 액세스 정책을 충족할 수 있는 기회가 표시됩니다.
조건부 액세스가 필요한 API를 호출하는 경우 API의 오류에서 클레임 챌린지를 받을 수 있습니다. 예를 들어 조건부 액세스 정책에 관리 디바이스(Intune)가 있는 경우 오류 는 AADSTS53000 같은 것입니다. 이 리소스에 액세스하려면 디바이스를 관리해야 합니다. 이 경우 사용자에게 적절한 정책을 충족하라는 메시지가 표시되도록 토큰 획득 호출에서 클레임을 전달할 수 있습니다.
MSAL.NET 조건부 액세스가 필요한 API를 호출할 때 애플리케이션은 클레임 챌린지 예외를 처리해야 합니다. 클레임 속성이 비어 있지 않은 MsalServiceException으로 표시됩니다.
클레임 챌린지를 처리하려면 WithClaims(String)를 사용하세요.
오류 및 예외 후 다시 시도
MSAL을 호출할 때 사용자 고유의 재시도 정책을 구현해야 합니다. MSAL은 Microsoft Entra 서비스에 대한 HTTP 호출을 수행하며 경우에 따라 오류가 발생할 수 있습니다. 예를 들어 네트워크가 다운되거나 서버가 오버로드될 수 있습니다.
HTTP 429
STS(서비스 토큰 서버)가 너무 많은 요청으로 오버로드되면 응답 필드에서 다시 시도할 수 있을 때까지의 기간에 대한 힌트와 함께 HTTP 오류 429를 Retry-After 반환합니다.
HTTP 오류 코드 500-600
MSAL.NET HTTP 오류 코드 500-600을 사용하여 오류에 대한 간단한 재시도 한 번 메커니즘을 구현합니다.
MsalServiceException 은 System.Net.Http.Headers.HttpResponseHeaders 속성 namedHeaders으로 표시됩니다. 오류 코드의 추가 정보를 사용하여 애플리케이션의 안정성을 향상시킬 수 있습니다. 설명된 경우에는 RetryAfter 속성(형식: RetryConditionHeaderValue)을 사용하여 언제 재시도할지 계산할 수 있습니다.
클라이언트 자격 증명 흐름을 사용하는 디먼 애플리케이션의 예는 다음과 같습니다. 토큰을 획득하기 위한 모든 메서드에 맞게 조정할 수 있습니다.
bool retry = false;
do
{
TimeSpan? delay;
try
{
result = await publicClientApplication.AcquireTokenForClient(scopes, account).ExecuteAsync();
}
catch (MsalServiceException serviceException)
{
if (serviceException.ErrorCode == "temporarily_unavailable")
{
RetryConditionHeaderValue retryAfter = serviceException.Headers.RetryAfter;
if (retryAfter.Delta.HasValue)
{
delay = retryAfter.Delta;
}
else if (retryAfter.Date.HasValue)
{
delay = (retryAfter.Date.Value – DateTimeOffset.Now).TotalMilliseconds;
}
}
}
// . . .
if (delay.HasValue)
{
Thread.Sleep((int)delay.Value.TotalMilliseconds); // sleep or other
retry = true;
}
} while (retry);
다음 단계
문제를 진단하고 디버그하는 데 도움이 되도록 MSAL.NET 로깅을 사용하도록 설정하는 것이 좋습니다.