|
| 1 | +--- |
| 2 | +title: No Account or Login Hint Was Passed to the AcquireTokenSilent |
| 3 | +description: Provides solutions to an error that occurs when web applications using Microsoft Authentication Library (MSAL) or Microsoft Identity Web acquire a token silently. |
| 4 | +ms.service: entra-id |
| 5 | +ms.date: 06/18/2025 |
| 6 | +ms.reviewer: willfid, v-weizhu |
| 7 | +ms.custom: sap:Developing or Registering apps with Microsoft identity platform |
| 8 | +--- |
| 9 | +# Error "No account or login hint was passed to the AcquireTokenSilent" in web applications without persistent token cache |
| 10 | + |
| 11 | +This article offers solutions for the "No account or login hint was passed to the AcquireTokenSilent" error that occurs in a web application using Microsoft Authentication Library (MSAL) or Microsoft Identity Web. |
| 12 | + |
| 13 | +## Symptoms |
| 14 | + |
| 15 | +Assume that your web applications authenticate users using MSAL or Microsoft Identity Web and don't use persistent token caches. When MSAL tries to pull a user account and acquire a token silently from its token cache, the following error message is displayed: |
| 16 | + |
| 17 | +> No account or login hint was passed to the AcquireTokenSilent |
| 18 | +
|
| 19 | +## Cause |
| 20 | + |
| 21 | +This issue occurs because MSAL looks for an account that no longer exists in the token cache based on an existing authentication cookie. The authentication cookie is created only after an interactive sign-in and contains information about the user. This issue occurs in the following scenarios: |
| 22 | + |
| 23 | +- The web application has restarted. |
| 24 | +- The memory is cleared due to high memory usage or a set period of inactivity. |
| 25 | +- MSAL has a default cache size limit that automatically removes older entries. |
| 26 | + |
| 27 | +## Solution 1 (recommended): Implement a persistent token cache |
| 28 | + |
| 29 | +You can implement a persistent token cache in a durable location such as SQL Server or file-based storage. A persistent token cache ensures that tokens are retained even when the application restarts or the memory is cleared. For more information about how to implement a custom persistent token cache, see [Token cache serialization](/entra/msal/dotnet/how-to/token-cache-serialization). |
| 30 | + |
| 31 | +## Solution 2: Reject the authentication cookie |
| 32 | + |
| 33 | +You can implement a cookie authentication event to validate whether the currently signed-in user is present in the MSAL token cache. If the user isn't present, reject the authentication cookie and force the current user to sign in again. |
| 34 | + |
| 35 | +### For ASP.NET web applications using MSAL |
| 36 | + |
| 37 | +Create a cookie authentication event: |
| 38 | + |
| 39 | +```csharp |
| 40 | +app.UseCookieAuthentication(new CookieAuthenticationOptions |
| 41 | +{ |
| 42 | + Provider = new CookieAuthenticationProvider() |
| 43 | + { |
| 44 | + OnValidateIdentity = async context => |
| 45 | + { |
| 46 | + IConfidentialClientApplication clientApp = MsalAppBuilder.BuildConfidentialClientApplication(); |
| 47 | + |
| 48 | + var signedInUserIdentity = new ClaimsPrincipal(context.Identity); |
| 49 | + |
| 50 | + if (await clientApp.GetAccountAsync(signedInUserIdentity.GetAccountId()) == null) |
| 51 | + |
| 52 | + { |
| 53 | + |
| 54 | + context.RejectIdentity(); |
| 55 | + |
| 56 | + } |
| 57 | + |
| 58 | + } |
| 59 | + |
| 60 | + } |
| 61 | + |
| 62 | +}); |
| 63 | +``` |
| 64 | + |
| 65 | +> [!NOTE] |
| 66 | +> To implement a cookie authentication event, the web application must install the following helper classes and the `Microsoft.Identity.Web.TokenCache` NuGet package: |
| 67 | +> |
| 68 | +> - `MsalAppBuilder.cs` |
| 69 | +> - `AuthenticationConfig.cs` |
| 70 | +
|
| 71 | +### For ASP.NET Core web applications using MSAL |
| 72 | + |
| 73 | +1. Create a custom cookie authentication event: |
| 74 | + |
| 75 | + ```csharp |
| 76 | + using Microsoft.AspNetCore.Authentication.Cookies; |
| 77 | + using Microsoft.Extensions.DependencyInjection; |
| 78 | + using Microsoft.Identity.Client; |
| 79 | + using System.Threading.Tasks; |
| 80 | + using System.Security.Claims; |
| 81 | + |
| 82 | + namespace SampleApp.Services |
| 83 | + { |
| 84 | + internal class RejectSessionCookieWhenAccountNotInCacheEvents : CookieAuthenticationEvents |
| 85 | + { |
| 86 | + public async override Task ValidatePrincipal(CookieValidatePrincipalContext context) |
| 87 | + { |
| 88 | + var msalInstance = context.HttpContext.RequestServices.GetRequiredService(); |
| 89 | + IConfidentialClientApplication msalClient = msalInstance.GetClient(); |
| 90 | + |
| 91 | + var accounts = await msalClient.GetAccountsAsync(); |
| 92 | + |
| 93 | + var account = await msalClient.GetAccountAsync(accounts.FirstOrDefault()); |
| 94 | + |
| 95 | + if (account == null) |
| 96 | + |
| 97 | + { |
| 98 | + |
| 99 | + context.RejectPrincipal(); |
| 100 | + |
| 101 | + } |
| 102 | + |
| 103 | + await base.OnValidatePrincipal(context); |
| 104 | + |
| 105 | + } |
| 106 | + |
| 107 | + } |
| 108 | + |
| 109 | + } |
| 110 | + ``` |
| 111 | + |
| 112 | +2. Register the custom cookie authentication event: |
| 113 | + |
| 114 | + ```csharp |
| 115 | + Services.Configure<CookieAuthenticationOptions>(cookieScheme, options=>options.Events=new RejectSessionCookieWhenAccountNotInCacheEvents()); |
| 116 | + ``` |
| 117 | + |
| 118 | +### For web applications using Microsoft Identity Web |
| 119 | + |
| 120 | +Microsoft Identity Web provides built-in mechanisms to manage token caches. For more information, see [Managing incremental consent and conditional access](https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access). If this document doesn't help you resolve the issue, you can manually clear the authentication cookie using a custom cookie authentication event: |
| 121 | +
|
| 122 | +1. Create a custom cookie authentication event: |
| 123 | + |
| 124 | + ```csharp |
| 125 | + using Microsoft.AspNetCore.Authentication.Cookies; |
| 126 | + using Microsoft.Extensions.DependencyInjection; |
| 127 | + using Microsoft.Identity.Client; |
| 128 | + using Microsoft.Identity.Web; |
| 129 | + using System; |
| 130 | + using System.Collections.Generic; |
| 131 | + using System.Linq; |
| 132 | + using System.Threading.Tasks; |
| 133 | + |
| 134 | + namespace SampleApp.Services |
| 135 | + { |
| 136 | + internal class RejectSessionCookieWhenAccountNotInCacheEvents : CookieAuthenticationEvents |
| 137 | + { |
| 138 | + public async override Task ValidatePrincipal(CookieValidatePrincipalContext context) |
| 139 | + { |
| 140 | + try |
| 141 | + { |
| 142 | + var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService(); |
| 143 | + string token = await tokenAcquisition.GetAccessTokenForUserAsync( |
| 144 | + scopes: new[] { "profile" }, |
| 145 | + user: context.Principal); |
| 146 | + } |
| 147 | + catch (MicrosoftIdentityWebChallengeUserException ex) |
| 148 | + when (AccountDoesNotExistInTokenCache(ex)) |
| 149 | + { |
| 150 | + context.RejectPrincipal(); |
| 151 | + } |
| 152 | + } |
| 153 | + /// <summary> |
| 154 | + /// Is the exception due to no account in the token cache? |
| 155 | + /// </summary> |
| 156 | + /// <param name="ex">Exception thrown by <see cref="ITokenAcquisition"/>.GetTokenForXX methods.</param> |
| 157 | + /// <returns>A boolean indicating if the exception relates to the absence of an account in the cache.</returns> |
| 158 | + private static bool AccountDoesNotExistInTokenCache(MicrosoftIdentityWebChallengeUserException ex) |
| 159 | + { |
| 160 | + return ex.InnerException is MsalUiRequiredException |
| 161 | + |
| 162 | + && (ex.InnerException as MsalUiRequiredException).ErrorCode == "user_null"; |
| 163 | + |
| 164 | + } |
| 165 | + |
| 166 | + } |
| 167 | + |
| 168 | + } |
| 169 | + ``` |
| 170 | + |
| 171 | +2. Register the custom cookie authentication event: |
| 172 | + |
| 173 | + ```csharp |
| 174 | + // Add Microsoft Identity Web |
| 175 | + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) |
| 176 | + .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) |
| 177 | + .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) |
| 178 | + .AddMicrosoftGraph(Configuration.GetSection("GraphBeta")) |
| 179 | + .AddInMemoryTokenCaches(); |
| 180 | + |
| 181 | + // Register the Custom Cookie Authentication event |
| 182 | + Services.Configure<CookieAuthenticationOptions>(cookieScheme, options=>options.Events=new RejectSessionCookieWhenAccountNotInCacheEvents()); |
| 183 | + ``` |
| 184 | + |
| 185 | +[!INCLUDE [Azure Help Support](../../../includes/azure-help-support.md)] |
0 commit comments