I have been spending the day learning how Azure Functions work. I have gotten my Azure Function registered with my Azure Active Directory, and have registered this application to be secured by way of my Azure Active Directory provide in my Azure Portal.
I have deployed this to Azure and everything works as expected, asking for my Azure AD User account, and once I sign in it shows me my HelloWorld Azure Function as I expect.
Additionally, I have been able to debug my Azure Function locally. However, locally it is using the AuthorizationLevel
as configured on my HttpTriggerAttribute
(AuthorizationLevel.Anonymous
).
This, of course, is ignored when deployed in Azure, but locally now I have lost my user identity as it is configured to be anonymous and not using Azure Active Directory.
Is there a way to enable Azure Active Directory authentication on my locally deployed Azure Function?
To be clear here, I would like to sign in with my Azure Function locally just as I do with my deployed Azure Function (so I will be redirected to login.microsoftonline.com
to login), but have that same identity be available to my local Azure Function development environment.
Thank you for any assistance you can provide!
Alright after a few more (OK, a LOT) hours, I have figured out a solution for now. This works in both local and deployed scenarios. I have posted a template solution here:
https://github.com/Mike-E-angelo/Stash/tree/master/AzureV2Authentication/AzureV2Authentication
Here are the steps that outline the overall process:
function-name
.azurewebsites.netlocal.settings.json
and paste value from previous step in AuthenticationToken
setting.AuthenticationBaseAddress
Here is the main event:
public static class AuthenticationExtensions
{
public static Authentication Authenticate(this HttpRequest @this)
{
var handler = new HttpClientHandler();
var client = new HttpClient(handler) // Will want to make this a singleton. Do not use in production environment.
{
BaseAddress = new Uri(Environment.GetEnvironmentVariable("AuthenticationBaseAddress") ?? new Uri(@this.GetDisplayUrl()).GetLeftPart(UriPartial.Authority))
};
handler.CookieContainer.Add(client.BaseAddress, new Cookie("AppServiceAuthSession", @this.Cookies["AppServiceAuthSession"] ?? Environment.GetEnvironmentVariable("AuthenticationToken")));
var service = RestService.For<IAuthentication>(client);
var result = service.GetCurrentAuthentication().Result.SingleOrDefault();
return result;
}
}
Note that:
HttpClient
is created for each call. This is against best practices.Here are the remaining classes of interest, for the sake of completeness:
public class Authentication // structure based on sample here: https://cgillum.tech/2016/03/07/app-service-token-store/
{
[JsonProperty("access_token", NullValueHandling = NullValueHandling.Ignore)]
public string AccessToken { get; set; }
[JsonProperty("provider_name", NullValueHandling = NullValueHandling.Ignore)]
public string ProviderName { get; set; }
[JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
public string UserId { get; set; }
[JsonProperty("user_claims", NullValueHandling = NullValueHandling.Ignore)]
public AuthenticationClaim[] UserClaims { get; set; }
[JsonProperty("access_token_secret", NullValueHandling = NullValueHandling.Ignore)]
public string AccessTokenSecret { get; set; }
[JsonProperty("authentication_token", NullValueHandling = NullValueHandling.Ignore)]
public string AuthenticationToken { get; set; }
[JsonProperty("expires_on", NullValueHandling = NullValueHandling.Ignore)]
public string ExpiresOn { get; set; }
[JsonProperty("id_token", NullValueHandling = NullValueHandling.Ignore)]
public string IdToken { get; set; }
[JsonProperty("refresh_token", NullValueHandling = NullValueHandling.Ignore)]
public string RefreshToken { get; set; }
}
public class AuthenticationClaim
{
[JsonProperty("typ")]
public string Type { get; set; }
[JsonProperty("val")]
public string Value { get; set; }
}
interface IAuthentication
{
[Get("/.auth/me")]
Task<Authentication[]> GetCurrentAuthentication();
}
public static class Function1
{
[FunctionName("Function1")]
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequest req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
var authentication = req.Authenticate();
return authentication != null
? (ActionResult)new OkObjectResult($"Hello, {authentication.UserId}")
: new BadRequestObjectResult("Authentication not found. :(");
}
}
Here is another alternative if you are developing a SPA with JWT tokens that uses Azure-AD or Azure B2C via Easy Auth.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With