Trying to get redeemed Auth token using AcquireTokenSilent does not work

Parag Kale 25 Reputation points
2025-05-05T13:16:22.9266667+00:00

Hi,I have followed the auth code flow in my functions app to get auth token using auth code. The problem I am seeing is my http triggered function gets invoked multiple times and eventually I get an exception that that auth token was already redeemed for the auth code. Below is my function definition. I am trying to use MSAL library mechanism to get token from cache ( if it was redeemed earlier ) , but it didn't work

public class MyCustomDfMonEndpoint : ServeStatics
{
    
    
    public MyCustomDfMonEndpoint(DfmSettings dfmSettings, DfmExtensionPoints extensionPoints, ILoggerFactory loggerFactory, HttpClient httpClient) :
        base(dfmSettings, extensionPoints, loggerFactory)
    {
    }

    [Function(nameof(MyCustomDfMonEndpoint))]
    public async Task<HttpResponseData> ServeDfMonStatics(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = Globals.DfMonRoutePrefix + "/{p1?}/{p2?}/{p3?}")] HttpRequestData req,
        string p1,
        string p2,
        string p3
    )
    {
        // Extract the 'code' query parameter returned after authentication
        
        string code = req.Query["code"]!;

        // If no code is present, redirect user to the Microsoft identity platform's sign-in page
        if (string.IsNullOrEmpty(code))
        {
            string tenant = "<tenantId>"; // or use your tenant ID
            string clientId = "ClientId";
            // Ensure this redirect URI matches what is in your app registration
            string redirectUri = "http://localhost:7079/api/DFM";
            // Define the scopes you need (openid, profile, offline_access, etc.)
            string scope = "openid profile email";

            string authUrl = $"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize" +
                             $"?client_id={clientId}" +
                             $"&response_type=code" +
                             $"&redirect_uri={WebUtility.UrlEncode(redirectUri)}" +
                             $"&response_mode=query" +
                             $"&scope={WebUtility.UrlEncode(scope)}" +
                             $"&state=12345"; // ideally, generate a dynamic state value

            var response = req.CreateResponse(HttpStatusCode.Redirect);
            response.Headers.Add("Location", authUrl);

            return response;
        }
        else
        {

            string tenant = "tenantId"; // or your tenant-specific ID
            string clientId = "clientId";
            string clientSecret = "Secret";
            string redirectUri = "http://localhost:7079/api/DFM";

            // Build the MSAL confidential client application
            IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithClientSecret(clientSecret)
                .WithRedirectUri(redirectUri)
                .WithAuthority($"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token")
                .Build();

            var account = await app.GetAccountAsync("user-identifier");
            AuthenticationResult result = null;
            if (account != null)
            {
                result = await app.AcquireTokenSilent(new string[] { "openid", "profile", "email" }, account)
    .ExecuteAsync();

            } else
            {
                // Acquire token using the authorization code
                result = await app.AcquireTokenByAuthorizationCode(
                    new string[] { "openid", "profile", "email" }, code)
                    .ExecuteAsync();
            }
            

            if (result.AccessToken != null)
            {

                return await this.DfmServeStaticsFunction(req, p1, p2, p3);
            }
            else
            {
                return req.CreateResponse(HttpStatusCode.BadRequest);
            }
        }
    }

    
}

Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,786 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Khadeer Ali 5,065 Reputation points Microsoft External Staff Moderator
    2025-05-06T17:13:29.11+00:00

    @Parag Kale ,

    Thank you for sharing the implementation details.

    We understand that your custom Durable Functions Monitor endpoint is being executed multiple times due to the auth flow, and you're encountering a “token already redeemed” error on repeated invocations. Based on our analysis, this can occur when the same authorization code is used more than once or when the token is not cached correctly.

    To address this, please try the following update to your function logic:

    Suggested Fix in Code

    Update your function to ensure the code is only processed once, and cache the token to avoid redeeming the code multiple times:

    
    // Token cache (could be improved with distributed or persistent cache for production use)
    private static AuthenticationResult _cachedAuthResult;
    
    [Function(nameof(MyCustomDfMonEndpoint))]
    public async Task<HttpResponseData> ServeDfMonStatics(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = Globals.DfMonRoutePrefix + "/{p1?}/{p2?}/{p3?}")] HttpRequestData req,
        string p1, string p2, string p3)
    {
        string code = req.Query["code"];
    
        if (_cachedAuthResult == null)
        {
            if (string.IsNullOrEmpty(code))
            {
                // Redirect to login if no code is present
                var authUrl = $"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?...";
                var response = req.CreateResponse(HttpStatusCode.Redirect);
                response.Headers.Add("Location", authUrl);
                return response;
            }
    
            // Acquire token only once
            var app = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithClientSecret(clientSecret)
                .WithRedirectUri(redirectUri)
                .WithAuthority(new Uri($"https://login.microsoftonline.com/{tenant}"))
                .Build();
    
            _cachedAuthResult = await app.AcquireTokenByAuthorizationCode(
                new[] { "openid", "profile", "email" }, code).ExecuteAsync();
        }
    
        // Serve monitor UI if token is cached
        if (_cachedAuthResult != null && !string.IsNullOrEmpty(_cachedAuthResult.AccessToken))
        {
            return await this.DfmServeStaticsFunction(req, p1, p2, p3);
        }
    
        return req.CreateResponse(HttpStatusCode.BadRequest);
    }
    

    Note: This sample uses a simple static cache. In a production setting, you may want to store the token securely per user/session or integrate with a more robust caching mechanism.

    We recommend testing this approach in your environment. If it resolves the repeated invocation issue, we can further help harden the solution for your needs.

    Please let us know how it goes, and feel free to reach out with any follow-up questions.

    Hope this helps. Do let us know if you have any further queries.


    If this answers your query, do click Accept Answer and Yes for "Was this answer helpful." And if you have any further questions, let us know.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.