Files
AutopilotMonitor/src/Backend/AutopilotMonitor.Functions.Tests/GraphResolution/TestGraphToken.cs
T
Oliver Kieselbach 0ed3258d4f Graph access: a refused app token becomes visible, with the repair in the portal
When Microsoft Entra refuses a tenant's app token, its agents only see 503 and nobody is told. The refusal now:

- names its Entra code in the log, at most every 15 minutes per tenant and app
- becomes tenant state in two stages: a silent suspicion on an agent route, confirmed by a further refusal at least 15 minutes later, from an agent request or a recheck in the maintenance run
- shows a red dashboard banner and a repair card in Settings once confirmed, and writes GraphAccessRejected
- ends with the next freshly issued token that carries an application role. A cached token proves nothing, and on the agent path a token without a role counts as a refusal, because a sign-in consent re-creates a removed enterprise application without any permission

A rejected secret of our own raises GraphAppCredentialRejected and touches no tenant. The new consent trigger "restore" repairs access without changing a validation gate. A proven missing service principal of the previous app no longer blocks the switch to the new one when the budget cuts the retries. Agent behaviour does not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 21:34:38 +01:00

22 lines
853 B
C#

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
namespace AutopilotMonitor.Functions.Tests.GraphResolution;
/// <summary>What Entra's token endpoint answers on success, with the application roles of the tenant's service principal.</summary>
internal static class TestGraphToken
{
public const string ValidationRole = "DeviceManagementServiceConfig.Read.All";
public static string Body(params string[] roles)
{
var jwt = new JwtSecurityToken(
issuer: "https://sts.windows.net/test",
audience: "https://graph.microsoft.com",
claims: roles.Select(r => new Claim("roles", r)),
notBefore: null,
expires: DateTime.UtcNow.AddHours(1));
return $"{{\"access_token\":\"{new JwtSecurityTokenHandler().WriteToken(jwt)}\",\"expires_in\":3599}}";
}
}