Managing external identities to enable secure access for partners, customers, and other non-employees
Azure Function works with Entra ID custom authentication extension when created manually in portal, but not after deploying via Visual Studio
I have already asked this on Stackoverflow you can see imanges as well on stackoverflow
https://stackoverflow.com/questions/79683577/azure-function-works-with-entra-id-custom-authentication-extension-when-created
Case 1
Here is public endpoint which is working fine with entra
https://azuretestfunction20250627020054.azurewebsites.net/api/OnTokenIssuanceStart_CustomClaimsExtension?code=nfe8tDdGaSvhfPMn7G8TAyVXcMLLuoBYn7Th-1sF9dZQAzFu76q5PQ==
I'm using a custom authentication extension with Microsoft Entra ID for my Azure Function app. When I manually create the function using the Azure Portal and configure the Entra ID authentication extension, everything works as expected — the function URL returns the JSON response and the extension returns the expected claims (like user info) inside the request.
Here is the code which I added when created function from Azure Functions app portal, which I get from here
`#r "Newtonsoft.Json"`
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
// Read the correlation ID from the Microsoft Entra request
string correlationId = data?.data.authenticationContext.correlationId;
// Claims to return to Microsoft Entra
ResponseContent r = new ResponseContent();
r.data.actions[0].claims.CorrelationId = correlationId;
r.data.actions[0].claims.ApiVersion = "1.0.0";
r.data.actions[0].claims.DateOfBirth = "01/01/2000";
r.data.actions[0].claims.CustomRoles.Add("Writer");
r.data.actions[0].claims.CustomRoles.Add("Editor");
return new OkObjectResult(r);
}
public class ResponseContent
{
[JsonProperty("data")]
public Data data { get; set; }
public ResponseContent()
{
data = new Data();
}
}
public class Data
{
[JsonProperty("@odata.type")]
public string odatatype { get; set; }
public List<Action> actions { get; set; }
public Data()
{
odatatype = "microsoft.graph.onTokenIssuanceStartResponseData";
actions = new List<Action>();
actions.Add(new Action());
}
}
public class Action
{
[JsonProperty("@odata.type")]
public string odatatype { get; set; }
public Claims claims { get; set; }
public Action()
{
odatatype = "microsoft.graph.tokenIssuanceStart.provideClaimsForToken";
claims = new Claims();
}
}
public class Claims
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string CorrelationId { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string DateOfBirth { get; set; }
public string ApiVersion { get; set; }
public List<string> CustomRoles { get; set; }
public Claims()
{
CustomRoles = new List<string>();
}
}
Case 2
Here is public endpoint which is not working with entra even azure funciton retuning same json
https://customextensionfunctionapp.azurewebsites.net/api/OnTokenIssuranceStartFun?code=6qL_PiZglc12fMkUWlCwXt5BcW-cN7NyvC_Bntp4jJU5AzFuyhAIZw==
However, when I deploy the same function from Visual Studio (using the publish profile), the function still runs fine — the function URL returns same JSON in the browser — but the custom authentication extension doesn't return any claims and getting error. The only difference I could see here when deploy using Visual Studio function get deploy in the form of package and in other case we direct upload code.
Here is my Visual Studio code of the function:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Data.SqlClient;
using System.Collections.Generic;
namespace AzureTestFunction
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
// Read the correlation ID from the Microsoft Entra request
string correlationId = data?.data.authenticationContext.correlationId;
// Claims to return to Microsoft Entra
ResponseContent r = new ResponseContent();
r.data.actions[0].claims.CorrelationId = correlationId;
r.data.actions[0].claims.ApiVersion = "1.0.0";
r.data.actions[0].claims.DateOfBirth = "01/01/2000";
r.data.actions[0].claims.CustomRoles.Add("Writer");
r.data.actions[0].claims.CustomRoles.Add("Editor");
// Get database values
string connectionString = Environment.GetEnvironmentVariable("SqlServerConnection");
var resultList = new List<string>();
using (SqlConnection conn = new SqlConnection(connectionString))
{
await conn.OpenAsync();
var query = "SELECT Name from auth.Permissions"; // Adjust your table/query
using (SqlCommand cmd = new SqlCommand(query, conn))
using (SqlDataReader reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
resultList.Add(reader.GetString(0)); // assuming Name is the first column
}
}
}
// append claims which are coming from database
foreach (var result in resultList)
{
r.data.actions[0].claims.Permissions.Add(result);
}
return new OkObjectResult(r);
}
}
public class ResponseContent
{
[JsonProperty("data")]
public Data data { get; set; }
public ResponseContent()
{
data = new Data();
}
}
public class Data
{
[JsonProperty("@odata.type")]
public string odatatype { get; set; }
public List<Action> actions { get; set; }
public Data()
{
odatatype = "microsoft.graph.onTokenIssuanceStartResponseData";
actions = new List<Action>();
actions.Add(new Action());
}
}
public class Action
{
[JsonProperty("@odata.type")]
public string odatatype { get; set; }
public Claims claims { get; set; }
public Action()
{
odatatype = "microsoft.graph.tokenIssuanceStart.provideClaimsForToken";
claims = new Claims();
}
}
public class Claims
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string CorrelationId { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string DateOfBirth { get; set; }
public string ApiVersion { get; set; }
public List<string> CustomRoles { get; set; }
public List<string> Permissions { get; set; }
public Claims()
{
CustomRoles = new List<string>();
Permissions = new List<string>();
}
}
}
Here is the error which i am getting.
AADSTS1100001: Non-retryable error has occurred. Underlying error code: 1003002. Trace ID: 3ef48eac-d5a2-4e08-ab94-b1e998e20100 Correlation ID: db0c6f94-445d-4222-92bb-6d3215f6ea9f
If anyone could help me - how can I debug this? It would be great.
Thank you