web-dev-qa-db-fra.com

Authentification pour les fonctions Azure

J'ai passé les dernières 24 heures à lire comment créer des fonctions Azure et j'ai réussi à convertir un MVC WebApi en une nouvelle application de fonction avec plusieurs fonctions. Mon problème est que je n'ai pas trouvé de documentation claire ou de tutoriels sur la façon de faire l'authentification la plus élémentaire avec eux.

Mon scénario est assez simple. Mettre à disposition des utilisateurs dans mon AAD, puis leur accorder l'accès à des fonctions spécifiques. Les utilisateurs d'un site Web cliquent sur les éléments de l'interface utilisateur qui déclenchent à leur tour Javascript qui appelle mes fonctions Azure. Dans la fonction, je dois pouvoir vérifier leur identité d'une manière ou d'une autre, car je vais transmettre cela à d'autres fonctions qui interagissent avec une instance SQL.

Quelqu'un peut-il me montrer des documents, des articles, un exemple, quelque chose qui montre comment je peux y parvenir?

Pour mémoire, j'ai trouvé dans le portail la configuration "Authentification" pour mon application de fonction et j'ai choisi AAD comme fournisseur d'authentification. J'y ai ajouté mon application fonctionnelle et j'ai fourni quelques utilisateurs. J'ai ensuite écrit la fonction de test suivante:

[FunctionName("GetThings")]
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.User, "GET", Route = null)]HttpRequestMessage req, TraceWriter log)
{
    log.Info("Getting all the things");
    var identity = ClaimsPrincipal.Current.Identity;

    return identity.IsAuthenticated ?
        req.CreateResponse(HttpStatusCode.Unauthorized, "Not authenticated!") :
        req.CreateResponse(HttpStatusCode.OK, $"Hi {identity.Name}!");
}

Actuellement, lorsque j'essaie d'atteindre le point de terminaison directement, je suis redirigé vers une page de connexion ... donc je suppose que cette partie fonctionne. Cependant, la façon dont je génère/récupère les jetons utilisateur, les envoie sur demande aux fonctions ou les traite sur le serveur n'est pas claire pour moi.

Aidez-moi?

19
ThatCreole

Une fois que l'utilisateur s'authentifie auprès d'Azure AD, un cookie AppServiceAuthSessoin vous sera présenté. C'est un cookie opaque mais vous pouvez l'échanger contre des réclamations en appelant

https://yourFunctionApp.azurewebsites.net/.auth/me

et en passant le cookie opaque comme en-tête Cookie. De plus, le id_token vous récupérez peut être utilisé comme jeton Bearer.

En fait, c'est juste ça me semble bien, je ne l'ai pas vraiment testé en tant que porteur, donc un peu de prudence.

Get claims

Le mécanisme est appelé Easy Auth , il est plus facile pour Google pour ce nom.

Plus d'informations sur le magasin de jetons ici -
https://cgillum.tech/2016/03/07/app-service-token-store/

... qui dit que vous pouvez récupérer les revendications simplement en lisant les en-têtes HTTP provenant du navigateur de l'utilisateur:

Accès aux jetons

Depuis votre code backend, accéder à ces jetons est aussi simple que de lire un en-tête de requête HTTP. Les en-têtes sont nommés comme X-MS-TOKEN-{provider}-{type}. Les noms d'en-tête de jeton possibles sont répertoriés ci-dessous:

En-têtes de demande de jeton Azure Active Directory:

X-MS-TOKEN-AAD-ID-TOKEN
X-MS-TOKEN-AAD-ACCESS-TOKEN
X-MS-TOKEN-AAD-EXPIRES-ON
X-MS-TOKEN-AAD-REFRESH-TOKEN

En fait, je viens de découvrir cela maintenant, alors merci pour la question!

MISE À JOUR:

Mon intuition était correcte, le id_token est également bon en tant que porteur:

$ curl -isk https://{funcApp}.azurewebsites.net/api/{someFunc} \
       -H "Authorization: Bearer eyJ0eXAiOi....oEU-Q"

HTTP/1.1 200 OK
Cache-Control: no-cache
Server: Microsoft-IIS/8.0
...

La principale différence entre les deux façons de lire les revendications (lire les en-têtes vs appeler /.auth/me du backend avec le cookie de l'utilisateur) est la quantité de détails que vous obtenez. Il y a beaucoup plus dans ce dernier.

Voici l'ensemble des en-têtes que vous obtenez d'Easy Auth pour un utilisateur authentifié Twitter:

{
   "cookie": "AppServiceAuthSession=Lx43...xHDTA==",
   ...
   "x-ms-client-principal-name": "evilSnobu",
   "x-ms-client-principal-id": "35....",
   "x-ms-client-principal-idp": "Twitter",
   "x-ms-token-Twitter-access-token": "35...Dj",
   "x-ms-token-Twitter-access-token-secret": "OK3...Jx",
}

et les réclamations que vous obtenez en appelant /.auth/me:

{
   "access_token": "35...FDj",
   "access_token_secret": "OK3...sJx",
   "provider_name": "Twitter",
   "user_claims": [
      {
         "typ": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
         "val": "352660979"
      },
      {
         "typ": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn",
         "val": "evilSnobu"
      },
      {
         "typ": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
         "val": "Safarihat Hacker"
      },
      {
         "typ": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/webpage",
         "val": "..."
      },
      {
         "typ": "urn:Twitter:description",
         "val": "GENIUS. HAVE BRAIN. WILL TRAVEL."
      },
      {
         "typ": "urn:Twitter:location",
         "val": ""
      },
      {
         "typ": "urn:Twitter:time_zone",
         "val": "London"
      },
      {
         "typ": "urn:Twitter:lang",
         "val": "en"
      },
      {
         "typ": "urn:Twitter:verified",
         "val": "False"
      },
      {
         "typ": "urn:Twitter:profile_image_url_https",
         "val": "https://pbs.twimg.com/profile_images/867473646876545024/1elebfK1_normal.jpg"
      }
   ],
   "user_id": "evilSnobu"
}
13
evilSnobu

J'ai créé une petite extension à Azure Functions v2, qui pourrait vous aider lorsqu'elle est utilisée avec des jetons au porteur.

Par exemple, pour travailler avec Azure B2C, lorsque vous souhaitez autoriser les demandes anonymes vers l'application.

Vous pouvez donc obtenir votre droit ClaimsPrincipal dans la fonction Azure sans utiliser de passe-partout.

[FunctionName("Example")]
public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    [FunctionToken] FunctionTokenResult token,
    ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");
    return (ActionResult) new OkObjectResult($"Hello, {token}");
}

Le code est publié sur Github

0
user9035756