Uwierzytelnianie w Azure Functions z wykorzystaniem Service Principal

Pisane i publikowane na szybko. Za gramatykę i język nie odpowiadam, do czasu korekty ;-)

Ponad dwa lata temu popełniłem wpis o tym jak przeprowadzić autoryzacje w Azure Functions za pomocą klucza. Dziś pora na uwierzytelnianie za pomocą Azure AD, a dokładniej uwierzytelnianie za pomocą Service Principal, czyli tożsamością aplikacji, a nie użytkownika.

Ważne: uwierzytelnianie != autoryzacja

Na potrzeby testów bardzo prosta funkcja w C#, która loguje i w odpowiedzi zwraca wszystkie nagłówki.

public static class test
{
    [FunctionName("test")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
        ILogger log)
    {
        foreach (var header in req.Headers)
        {
            log.LogInformation($"{header.Key}: {header.Value}");
        }
        return (ActionResult)new OkObjectResult(req.Headers);
    }
}

Wymuszenie uwierzytelniania w Functions włączone za pomocą Authentication / Authorization z Azure AD.

Azure Functions i Authentication / Authorization z Azure AD

Na potrzeby testów sam SP stworzyłem z az cli: az ad sp create-for-rbac -n funcaaddemo-client --skip-assignment. Tak naprawdę, można go stworzyć dowolnym sposobem ;-)

Mając to wszystko trzeba uzyskać właściwy token, którym można się przedstawić w aplikacji. W tym wypadku będzie to scenariusz Client Credentials Grant. W tym scenariuszu należy przekazać cztery parametry przy uzyskiwaniu tokenu - grant_type, client_id, client_secret, scope. Cała zabawa polega na prawidłowym zdefiniowaniu scope. Przy tworzonej domyślnie aplikacji będzie to domyślny scope. W tym wypadku będzie on następujący: <Application ID użytej w funkcji (Authentication / Authorization)>/.default.

Przetestować można to za pomocą najprostszego basha z cURL i jq na pokładzie. Poniżej prosty skrypt z wykorzystaniem tych narzędzi.

tenantID=<id lub nazwa tenanta>
scope="<Application ID użytej w funkcji (Authentication / Authorization)>/.default"
ClientID="<Application ID Service Principal>"
ClientKey="<Secret dla Service Principal>"
url="<Url do funkcji>"

access_token=$(curl -d "scope=$scope&grant_type=client_credentials&client_id=$ClientID&client_secret=$ClientKey" "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" | jq '.access_token' -r)

curl -H "Authorization: Bearer ${access_token}" $url | jq