Copied from my issue here, which was intended to find how to debug locally with an Azure Active Directory authenticated user.
https://stackoverflow.com/a/49402152/3602057
This will work with any provider that is configured on Azure with your Azure Function, and will work while for both local debugging and deployed scenarios.
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-EEE/Stash/tree/master/AzureV2Authentication/AzureV2Authentication
Here are the steps that outline the overall process:
- Sign into your function at https://
function-name
.azurewebsites.net
- CTRL-SHIFT-C in Chrome -> Application -> Cookies -> -> AppServiceAuthSession -> Copy Value
- Open
local.settings.json
and paste value from previous step in AuthenticationToken
setting.
- While you're there, paste in the URL from first step in
AuthenticationBaseAddress
- Launch application.
- Cross fingers.
- Enjoy magic (Hopefully.)
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:
- An
HttpClient
is created for each call. This is against best practices.
- Sample code is based on EasyAuth sample by @stuartleeks
- This solution makes use of the excellent Refit project to get its data.
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. :(");
}
}