Resolver problemas de cache de tokens na Microsoft. Identity.Web

Este artigo ajuda-o a diagnosticar e resolver problemas de cache de tokens na Microsoft. Identidade.Web. Problemas com a cache dos tokens podem causar falhas de autenticação, desempenho degradado ou comandos inesperados para iniciar sessão. Para uma visão geral de como funciona o cache de tokens na Microsoft. Identity.Web, consulte Token cache overview.

Pré-requisitos

Antes de tentar resolver o problema, confirme o seguinte:

  • Estás a usar uma versão suportada do Microsoft. Identidade.Web.
  • A sua aplicação tem cache de tokens configurado em Program.cs ou Startup.cs.
  • Tem acesso aos registos de aplicações e, se aplicável, à sua infraestrutura de cache distribuída.

Ativar o registo e diagnóstico da cache de tokens

Ative o registo detalhado como primeiro passo de diagnóstico. Microsoft. O Identity.Web utiliza a infraestrutura de registo ASP.NET Core e emite eventos através da Biblioteca de Autenticação da Microsoft (MSAL).

Ativar registo de MSAL

Defina o nível de log para Debug nas bibliotecas de identidade do seu appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Identity.Web": "Debug",
      "Microsoft.IdentityModel": "Debug"
    }
  }
}

Subscrever eventos de cache MSAL

Subscreva os eventos de notificação da cache do token MSAL para acompanhar acertos, falhas e atividade de serialização:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.OnL2CacheFailure = (ex) =>
    {
        logger.LogWarning(ex, "L2 cache failure encountered.");
        // Return true to allow the operation to continue despite the cache failure.
        // Return false to propagate the exception.
        return true;
    };
});

Monitorizar métricas de cache

Para monitorização de produção, acompanhe estas métricas-chave:

  • Taxa de acerto no cache — uma taxa de acerto baixa indica que os tokens não estão a ser recuperados do cache.
  • Latência da cache L2 — alta latência sugere uma conectividade ou problema de desempenho na cache distribuída.
  • Erros de serialização de cache — erros durante a leitura ou escrita indicam corrupção ou incompatibilidade de versões.
  • Consumo de memória — o crescimento sustentado pode indicar a falta de políticas de expulsão.

Falhas de ligação à cache distribuída (L2)

Symptom

Os registos de aplicação mostram erros de timeout de ligação ou falhas intermitentes de autenticação. Os utilizadores experienciam atrasos ao iniciar sessão, e observa-se exceções como:

Microsoft.Extensions.Caching.StackExchangeRedis.RedisCache:
  StackExchange.Redis.RedisConnectionException: 
  No connection is active/available to service this operation.

Ou, para cache distribuído do SQL Server:

Microsoft.Data.SqlClient.SqlException:
  A network-related or instance-specific error occurred while 
  establishing a connection to SQL Server.

Motivo

O armazenamento distribuído de backup de cache (Redis ou SQL Server) é inacessível. As causas comuns incluem:

  • cadeia de ligação incorreta ou credenciais de acesso expiradas.
  • Regras de firewall de rede bloqueiam a ligação ao alojamento da app.
  • O serviço de cache está fora de serviço ou em manutenção.
  • Incompatibilidade de configuração SSL/TLS entre o cliente e o servidor de cache.

Passos de diagnóstico

Siga estes passos para identificar a falha da ligação:

  1. Verifica a conectividade. A partir do host da aplicação, teste a ligação ao Redis ou SQL Server usando Test-NetConnection (PowerShell) ou redis-cli.
  2. Verifica a cadeia de ligação. Confirme que a cadeia de ligação corresponde ao nome de host, porta e credenciais do servidor de cache.
  3. Revê as regras do firewall. No Azure, verifique se o serviço de aplicação ou a rede virtual consegue aceder ao recurso de cache.
  4. Verificar o estado de funcionamento do serviço. No portal Azure, reveja a saúde e as métricas da sua instância do Cache do Azure para Redis ou SQL Database.

Solução

Passo 1: Corrigir a cadeia de ligação

Verifique a cadeia de conexão no seu appsettings.json:

{
  "ConnectionStrings": {
    "Redis": "your-redis-instance.redis.cache.windows.net:6380,password=your-access-key,ssl=True,abortConnect=False"
  }
}

Importante

Defina abortConnect=False na string de conexão do Redis. Esta configuração permite que a aplicação se reconecte automaticamente após falhas de ligação transitórias, em vez de ser lançada imediatamente.

Passo 2: Configurar repetição e resiliência

Configure o OnL2CacheFailure callback para que a sua aplicação se degrade de forma natural quando a cache distribuída estiver temporariamente indisponível:

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.OnL2CacheFailure = (ex) =>
    {
        // Log the failure for monitoring and alerting.
        logger.LogWarning(ex, "Distributed token cache is unavailable. " +
            "Falling back to in-memory cache.");
        return true; // Continue without the L2 cache.
    };

    // Set a timeout to avoid blocking the request pipeline.
    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
});

Passo 3: Abrir regras de firewall

Se a aplicação correr no Serviço de Aplicações do Azure e a cache estiver numa rede virtual, adicione os endereços IP de saída do App Service à lista de permissões do firewall da cache.

Erros de desserialização da cache

Symptom

Depois de atualizares a Microsoft. Identity.Web ou MSAL.NET, a aplicação lança exceções de desserialização ao ler do cache distribuído. Os utilizadores têm de iniciar sessão novamente, e vê exceções como:

System.Text.Json.JsonException:
  The JSON value could not be converted to the expected type.

Ou:

Microsoft.Identity.Client.MsalClientException:
  Error code: json_parse_failed

Motivo

O formato de serialização da cache de token mudou entre as versões da biblioteca. Os tokens armazenados em cache pela versão anterior não podem ser desserializados pela nova versão. Este problema ocorre mais frequentemente durante grandes atualizações de versões do MSAL.NET ou da Microsoft. Identidade.Web.

Solução

Opção A: Limpar a cache

A solução mais simples é limpar todas as entradas na cache distribuída. Os utilizadores reautenticam-se uma vez, e os tokens subsequentes são escritos no novo formato.

Esvazie a cache Rederis:

redis-cli FLUSHDB

Ou limpar a tabela de cache distribuída do SQL Server:

DELETE FROM [dbo].[TokenCache];

Observação

Limpar a cache faz com que todos os utilizadores ativos voltem a autenticar. Planeie esta operação durante uma janela de manutenção se a sua aplicação servir uma grande base de utilizadores.

Opção B: Lidar com erros de desserialização de forma elegante

Configure o adaptador de cache de modo a tratar as falhas de desserialização como falhas de cache em vez de erros fatais.

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.OnL2CacheFailure = (ex) =>
    {
        if (ex is JsonException or MsalClientException)
        {
            logger.LogWarning(ex, "Cache deserialization failed. " +
                "Treating as cache miss.");
            return true;
        }
        return false; // Propagate unexpected errors.
    };
});

Com esta abordagem, as entradas de cache afetadas são automaticamente substituídas à medida que os utilizadores voltam a autenticar, e não é necessária limpeza manual da cache.

Incompatibilidade de chaves de encriptação entre servidores

Symptom

Erros de desserialização ocorrem em implementações multi-instância, mesmo que a cache distribuída esteja a funcionar. Tokens armazenados em cache por uma instância de servidor não podem ser lidos por outra. Vê os erros json_parse_failed ou IDW10802 nos registos.

Motivo

Quando a encriptação da cache está ativada (options.Encrypt = true), Microsoft. O Identity.Web utiliza o ASP.NET Core Data Protection para encriptar entradas de cache. Por defeito, cada instância de servidor gera as suas próprias chaves de Proteção de Dados, pelo que uma instância não pode desencriptar entradas escritas por outra.

Solução

Configure o ASP.NET Core Data Protection para partilhar chaves de encriptação entre todas as instâncias do servidor.

Opção A: Armazenamento de Blobs do Azure + Azure Key Vault (recomendado para implementações Azure)

using Microsoft.AspNetCore.DataProtection;
using Azure.Identity;

builder.Services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(
        new Uri("https://yourstorageaccount.blob.core.windows.net/dataprotection/keys.xml"),
        new DefaultAzureCredential())
    .ProtectKeysWithAzureKeyVault(
        new Uri("https://yourkeyvault.vault.azure.net/keys/dataprotection-key"),
        new DefaultAzureCredential());

builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.Encrypt = true;
});

Esta configuração armazena o anel de chaves de Proteção de Dados no Armazenamento de Blobs do Azure e protege as chaves em repouso com o Azure Key Vault. Todas as instâncias de aplicação que acedem ao mesmo blob e chave podem encriptar e desencriptar as entradas de cache umas das outras.

Opção B: Sistema de ficheiros partilhado com proteção de certificados

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\keys"))
    .ProtectKeysWithCertificate(certificate);

Sugestão

Ao rodar o certificado de Proteção de Dados, use UnprotectKeysWithAnyCertificate para incluir tanto os certificados atuais como os anteriores. Isto permite a desencriptação de chaves que estavam protegidas com o certificado antigo durante a janela de rotação.

Crescimento de memória com cache em memória

Symptom

O consumo de memória das aplicações cresce de forma constante ao longo do tempo. Se a aplicação correr num contentor ou plano de Serviço de Aplicações com um limite de memória fixo, acaba por reiniciar ou lançar OutOfMemoryException. A monitorização mostra que a pilha gerida cresce sem recuperação pela coleta de lixo.

Motivo

Usar AddInMemoryTokenCaches() sem limites de tamanho causa um crescimento ilimitado da cache. Esta situação é especialmente problemática em aplicações que servem muitos utilizadores, porque a entrada de token de cada utilizador consome memória indefinidamente.

Por defeito, MemoryCache não impõe um tamanho máximo e não expulsa entradas a menos que esteja definida uma política de validade.

Solução

Opção A: Definir um limite de tamanho e uma expiração deslizante

Configure a cache em memória com políticas de expiração:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

services.Configure<MsalMemoryTokenCacheOptions>(options =>
{
    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
    options.SlidingExpiration = TimeSpan.FromHours(2);
});

Com estas definições, as entradas expiram após 12 horas independentemente do acesso, e as entradas inativas durante 2 horas são expulsas mais cedo.

Opção B: Mudar para uma cache distribuída

Para aplicações com muitos utilizadores concorrentes, uma cache em memória não escala. Mude para uma cache distribuída como a Redis:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("Redis");
});

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

Uma cache distribuída descarrega memória do processo de aplicação, persiste tokens durante reinícios e suporta implementações multi-instância.

Opção C: Usar a arquitetura híbrida L1/L2

Microsoft. O Identity.Web suporta uma abordagem híbrida que combina uma cache L1 rápida em memória com uma cache L2 distribuída persistente. Configure a cache híbrida L1/L2:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.L1CacheOptions = new MsalMemoryTokenCacheOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
        SlidingExpiration = TimeSpan.FromMinutes(2)
    };
});

Com a cache L1/L2, os tokens frequentemente acedidos são servidos a partir da memória (L1) com latência inferior a milissegundos. A cache L2 proporciona persistência e consistência entre instâncias. A cache L1 utiliza expirações curtas para limitar o crescimento da memória.

Symptom

Os utilizadores são repetidamente solicitados a autenticação multifator (MFA) ou consentimento, mesmo tendo completado estes passos recentemente. A aplicação não consegue encontrar tokens existentes na cache.

Motivo

Este problema ocorre quando a pesquisa de cache de tokens falha em corresponder uma entrada em cache à conta de utilizador atual. As causas comuns incluem:

  • A chave de cache difere daquela usada quando o token foi armazenado. Esta situação pode ocorrer se o HomeAccountId ou o contexto do inquilino mudar.
  • A aplicação executa múltiplas instâncias atrás de um balanceador de carga com cache em memória, e os pedidos são encaminhados para uma instância que não tem o token do utilizador.
  • As reivindicações ou escalas solicitadas mudaram, portanto, o token em cache não atende ao novo requisito.
  • A afinidade de sessão não está ativada, fazendo com que os utilizadores sejam encaminhados para diferentes instâncias que não têm os seus tokens em cache.

Passos de diagnóstico

Siga estes passos para identificar porque é que os tokens não são encontrados na cache:

  1. Verifica o tipo de cache. Se usares AddInMemoryTokenCaches() numa implementação multi-instância, os tokens armazenados em cache numa instância não estão disponíveis noutra. Muda para uma cache distribuída.
  2. Verifique o identificador da conta. Ative o registo ao nível de depuração e procure o HomeAccountId. Confirme que o identificador é consistente entre os pedidos.
  3. Inspeciona os telescópios. Confirme que os âmbitos solicitados GetAccessTokenForUserAsync correspondem aos âmbitos originalmente consentidos. Uma incompatibilidade de escopo faz com que a MSAL solicite um novo token.
  4. Revise as políticas de Acesso Condicional. Uma política de Acesso Condicional do Microsoft Entra ID que exige autenticação adicional para recursos específicos causa solicitações adicionais não relacionadas com cache.

Solução

Passo 1: Mudar para cache distribuída

Se a sua aplicação executar múltiplas instâncias, use uma cache distribuída para partilhar tokens entre instâncias:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("Redis");
});

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

Passo 2: Verificar objetivos consistentes

Certifique-se de que os escopos que solicita ao adquirir tokens correspondem aos escopos configurados durante a autenticação:

// In authentication setup — initial scopes.
.EnableTokenAcquisitionToCallDownstreamApi(new[] { "User.Read", "Mail.Read" })

// When acquiring a token — use the same scopes.
var token = await tokenAcquisition.GetAccessTokenForUserAsync(
    new[] { "User.Read", "Mail.Read" });

Passo 3: Ativar a afinidade da sessão (solução temporária)

Se não conseguires mudar para uma cache distribuída imediatamente, ativa a afinidade de sessão (sessões fixas) no teu balanceador de carga. A afinidade de sessão encaminha os pedidos do utilizador para a mesma instância. Esta abordagem é uma solução temporária com limitações de escalabilidade.

Problemas de desempenho da cache

Symptom

A recuperação de tokens é lenta e as chamadas de API a jusante aumentam a latência. A monitorização mostra tempos médios de resposta elevados para pedidos de aquisição de tokens. A latência não vem do fornecedor de identidade — os tokens são servidos a partir da cache.

Motivo

Problemas de desempenho na cache resultam tipicamente de:

  • Alta latência de cache L2. A cache distribuída está sob carga elevada, geograficamente distante da aplicação ou utiliza um nível de serviço subdimensionado.
  • Entradas grandes de cache de tokens. Aplicações que armazenam tokens para muitos recursos por utilizador podem produzir grandes entradas de cache serializadas que são lentas a ler e escrever.
  • Sem cache L1. Cada aquisição de token vai para a cache distribuída através da rede, mesmo para tokens que são usados frequentemente.

Solução

Passo 1: Ativar a cache L1 em memória

A cache L1 armazena tokens frequentemente acedidos na memória do processo, evitando viagens de rede para L2:

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.L1CacheOptions = new MsalMemoryTokenCacheOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
        SlidingExpiration = TimeSpan.FromMinutes(2)
    };
});

Com esta configuração, os tokens servidos a partir de L1 têm latência submilissegundo. Os tokens que não estão no L1 recuam para a cache distribuída do L2.

Passo 2: Otimizar a camada de cache distribuída

Se a latência da cache L2 for elevada, considere as seguintes ações:

  • Escalone a instância do Redis. Mude para um nível superior (por exemplo, de Básico para Padrão ou Premium no Cache do Azure para Redis) para obter maior largura de banda e menor latência.
  • Ative a georreplicação. Se a sua aplicação serve utilizadores em várias regiões, utilize a geo-replicação do Cache do Azure para Redis para que a cache fique próxima dos recursos de computação de cada região.
  • Rever a configuração da rede. Use integração com Private Link ou VNet para reduzir os saltos de rede entre a aplicação e a cache.

Passo 3: Reduzir o tamanho dos tokens serializados

Se as entradas de cache dos tokens forem grandes, reveja se a aplicação solicita tokens para mais recursos do que o necessário. Cada combinação única de recurso e escopo aumenta o tamanho da entrada do cache. Consolidar chamadas API sempre que possível para reduzir o número de tokens de acesso distintos armazenados em cache por utilizador.

Despejo do cache Redis

Symptom

Os utilizadores são intermitentemente convidados a reautenticar-se sem padrão baseado na expiração do token. A monitorização de Redis mostra que evicted_keys está a aumentar e used_memory está a aproximar-se do limite de maxmemory.

Motivo

Quando o Redis atinge o seu maxmemory limite, remove chaves com base no parâmetro maxmemory-policy. A política padrão (volatile-lru) expulsa as chaves menos usadas recentemente que têm uma expiração. Se a instância Redis for partilhada com dados de outras aplicações, as entradas no cache do token competem por espaço e podem ser expulsas prematuramente.

Solução

Passo 1: Verifique a política de despejos

Verifique a política atual de despejos:

redis-cli CONFIG GET maxmemory-policy

Para caches de tokens, volatile-lru (o padrão) é apropriado porque as entradas de cache de token têm expirações. No entanto, se outros dados sem expirações consomem memória, as entradas do token são despejadas primeiro.

Passo 2: Use uma instância Redis dedicada

Isole a cache de tokens dos outros dados da aplicação utilizando uma instância dedicada do Redis.

{
  "ConnectionStrings": {
    "RedisTokenCache": "token-cache-redis.redis.cache.windows.net:6380,password=...,ssl=True,abortConnect=False",
    "RedisAppData": "app-data-redis.redis.cache.windows.net:6380,password=...,ssl=True,abortConnect=False"
  }
}
// Register the token cache Redis instance specifically for distributed caching.
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("RedisTokenCache");
});

Passo 3: Aumentar o limite de memória do Redis

Se uma instância dedicada não for viável, aumenta a maxmemory definição. No Cache do Azure para Redis, escala para um nível superior ou aumenta o tamanho da cache.

Passo 4: Definir expirações apropriadas para entradas de cache

Defina expirações razoáveis para que entradas obsoletas sejam removidas antes que a memória se esgote:

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
    options.SlidingExpiration = TimeSpan.FromHours(2);
});

Crescimento de tabelas de cache distribuídas em SQL

Symptom

A tabela de cache distribuída SQL cresce continuamente, consumindo espaço em disco. As consultas à base de dados contra a tabela de cache abrandam com o tempo, e podes ver avisos sobre o tamanho da tabela ou limites de armazenamento.

Motivo

A cache distribuída SQL Server (Microsoft.Extensions.Caching.SqlServer) não remove automaticamente entradas expiradas. Entradas expiradas permanecem até serem explicitamente purgadas, causando crescimento ilimitado das tabelas, desempenho degradado da consulta e consumo de armazenamento.

Solução

Passo 1: Marque um trabalho de limpeza recorrente

Crie um trabalho ou tarefa agendada no SQL Server Agent para remover periodicamente entradas expiradas:

-- Delete expired entries from the SQL distributed cache table.
-- Schedule this query to run every 30 minutes.
DELETE FROM [dbo].[TokenCache]
WHERE ExpiresAtTime < GETUTCDATE();

Sugestão

No Base de Dados SQL do Azure, onde o SQL Server Agent não está disponível, utilize o Automatização do Azure, Funções do Azure com um acionador de temporizador, ou Elastic Jobs para agendar a limpeza.

Passo 2: Adicione um índice para uma limpeza eficiente

Se a tabela de cache ainda não tiver um índice na coluna de expiração, adicione um para acelerar a operação de eliminação:

CREATE NONCLUSTERED INDEX IX_TokenCache_ExpiresAtTime
ON [dbo].[TokenCache] (ExpiresAtTime);

Passo 3: Monitorizar o tamanho da mesa

Adicione monitorização para acompanhar o número de linhas e o tamanho da tabela ao longo do tempo:

SELECT
    COUNT(*) AS TotalEntries,
    COUNT(CASE WHEN ExpiresAtTime < GETUTCDATE() THEN 1 END) AS ExpiredEntries,
    COUNT(CASE WHEN ExpiresAtTime >= GETUTCDATE() THEN 1 END) AS ActiveEntries
FROM [dbo].[TokenCache];

Passo 4: Considera mudar para Redis

Se gerenciar a limpeza do cache SQL se tornar um fardo, mude para o Redis, que trata automaticamente da expiração através do seu mecanismo TTL automático.

// Replace SQL distributed cache with Redis.
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = Configuration.GetConnectionString("Redis");
});

Sugestões gerais de resolução de problemas

Use estas dicas quando o seu problema não corresponde a um cenário específico neste artigo.

Verifique se a cache está a ser usada

Adicione registo temporário para confirmar que os tokens são lidos e gravados na cache:

services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.Encrypt = false; // Disable encryption temporarily for debugging only.
    options.OnL2CacheFailure = (ex) =>
    {
        logger.LogError(ex, "L2 cache operation failed.");
        return true;
    };
});

Verifique se há múltiplos registos de cache

Se existirem múltiplas chamadas para AddInMemoryTokenCaches() ou AddDistributedTokenCaches() no código de inicialização, o último registo prevalece. Verifique se apenas um tipo de cache está registado.

Revise a vida útil do token

Os tokens de acesso têm uma vida útil finita (tipicamente 60–90 minutos). Se os utilizadores reportarem reautenticação após este período, o comportamento é esperado e não um problema de cache. Os tokens de atualização obtêm novos tokens de acesso silenciosamente e são armazenados na cache. Se o token de atualização estiver em falta ou expirado, o utilizador deve voltar a autenticar.

Teste com uma cache limpa

Ao diagnosticar problemas, limpe a cache para excluir entradas corrompidas ou obsoletas:

  • Cache em memória: Reinicie a aplicação.
  • Redis: Executa FLUSHDB na base de dados de cache.
  • SQL Server: Apagar todas as linhas da tabela de cache.

Cache de token vazio após reinício da aplicação

Symptom

Os utilizadores devem reautenticar-se após cada reinício ou reimplementação da aplicação. A cache distribuída parece vazia ou os tokens não são preservados.

Motivo

Este problema ocorre tipicamente quando se utiliza uma cache em memória (AddInMemoryTokenCaches()) ou a cache de memória distribuída não persistente (AddDistributedMemoryCache()) em produção. Nenhuma das opções mantém os tokens após os reinícios da aplicação.

AddDistributedMemoryCache() regista uma IDistributedCache implementação que armazena dados na memória. Apesar do nome "distribuído", este não armazena dados fora do sistema e destina-se apenas ao desenvolvimento e testes.

Solução

Mudar para uma cache distribuída persistente:

// Register a persistent cache (Redis example).
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "MyApp_";
});

// Use distributed token caches instead of in-memory.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration)
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

Advertência

Não confunda AddDistributedMemoryCache() com uma cache distribuída persistente. Use AddStackExchangeRedisCache() (Redis), AddDistributedSqlServerCache() (SQL Server), ou outra implementação persistente IDistributedCache para cargas de trabalho de produção.