Customiser AD B2C avec les stratégies personnalisées

Bien que AD B2C réponde à la plupart des cas d’usage en fournissant des policies et plusieurs flux d’utilisateurs possibles, nous avons parfois besoin de modifier la manière dont B2C provisionne nos utilisateurs ou qu’il nous fournisse les revendications…

Aujourd’hui nous allons entrer un peu dans le dur avec Azure AD B2C et regarder comment nous pouvons personnaliser le fonctionnement de cette plateforme en mettant en oeuvre des stratégies personnalisées ou Custom Policies.

Policies

Les policies de AD B2C définissent le comportement de votre tenant B2C. Tel que présenté en introduction, votre tenant possède des flux d’utilisateurs prédéfinis qui permettent de mettre en oeuvre les scénarii d’identités les plus communs, ex :

Flux d’utilisateurs prédéfinis
Illustration du fonctionnement d’une policy

Dans certains cas ces flux ne suffisent pas et nous souhaitons adapter leurs comportements pour ajouter des interactions avec un système externe par exemple ou demander à B2C d’ajouter des claims d’un système externe.

Créer une stratégie personalisée

Nous allons voir ensemble comment créer une stratégie personalisée de AD B2C pour inclure dans vos claims les groupes AD auxquels l’utilisateur appartient. En effet, cette fonctionnalité (bien que disponible dans Azure AD B2B) n’exite pas sur AD B2C.

Les stratégies personnalisées sont définies au travers de configurations XML que vous aurez en charge d’importer dans votre tenant B2C. Ceci permet alors de définir l’interaction avec l’utilisateur ainsi que des tiers parties en plus de la logique d’interaction.

Démarrer depuis le starter pack

Avant de commencer bille en tête, sachez qu’il existe un starter pack regroupant plusieurs stratégies prédéfinies que vous pourrez utiliser en tant que base de votre travail.

Le pack de démarrage est disponible sur le github de Azure Samples : https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack

Le starter pack plusieurs groupes de stratégies répondant à des scénarri communs comme :

  • LocalAccounts
  • SocialAccounts
  • SocialAndLocalAccounts
  • SocialAndLocalAccountsWithMfa

Chacun de ces scénarii contient des extensions de policies séparés en plusieurs fichiers :

  • TrustFrameworkBase.xml : Contient la plupart des définitions.
  • TrustFrameworkExtensions.xml : Contient les définitions personnalisées pour votre tenant.
  • TrustFrameworkLocalization.xml : Contient les chaînes de localisation.

Ensuite, vous retrouverez les extensions contenant les flux d’utilisateurs (par exemple: sign-in, password-reset, …)

Ajouter les revendications des groupes

Venons-en maintenant à notre besoin. Comme je vous l’expliquais, je souhaite ici ajouter l’appartenance aux groupes AD de l’utilisateur dans les revendications. Commençons donc par préparer nos stratégies.

Préparation des stratégies

Je vais ici démarrer à partir des stratégies prédéfinies du starter pack à partir des samples de LocalAccounts.

Une fois les extensions téléchargées sur le poste de développement, nous devons commencer par modifier toutes ces policies en configurant le nom du locataire B2C dans les fichiers. Remplacez alors yourtenant.onmicrosoft.com par le nom publique de votre tenant.

Ajout des applications AD B2C

Pour que les stratégies d’identités personnalisées puissent fonctionner correctement, il est nécessaire de créer deux applications dans votre tenant AD B2C. Je ne vais pas détailler ces étapes ici, mais vous laisse le faire depuis la documentation de Microsoft : Tutorial – Create user flows and custom policies – Azure Active Directory B2C | Microsoft Docs

Une fois créés, notez bien les identifiants de ces deux applications, nous en aurons besoin pour les référencer dans notre fichier d’extensions :

TrustFrameworkExtensions.xml :

<ClaimsProvider>
      <DisplayName>Local Account SignIn</DisplayName>
      <TechnicalProfiles>
         <TechnicalProfile Id="login-NonInteractive">
          <Metadata>
            <Item Key="client_id">ProxyIdentityExperienceFrameworkAppId</Item>
            <Item Key="IdTokenAudience">IdentityExperienceFrameworkAppId</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="ProxyIdentityExperienceFrameworkAppId" />
            <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="IdentityExperienceFrameworkAppId" />
          </InputClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

Remplacez ici IdentityExperienceFrameworkAppId et ProxyIdentityExperienceFrameworkAppId par les identifiants de vos applications (Application (client) ID) précédemment créées.

User Group Membership Provider

Dans cette démonstration, je vais vous proposer de déployer une Azure Function App chargée de collecter les groupes auxquels appartient l’utilisateur et de retourner ces groupes pour que Azure AD BC les inclue dans les revendications de l’utilisateur (dans l’access token de l’utilisateur).

Je ne vais pas ici décrire le code de cette fonction, vous retrouverez tous les détails sur mon repository github : kbeaugrand-org/ADB2CGroupsMembershipApp: The Azure AD B2C Group Membership Claims Provider is a function that is called by the Azure AD B2C token validation pipeline. (github.com)

Création de l’application dans AD B2C

Nous devons également créer une application d’entreprise dans AD B2C pour que la fonction azure puisse lire les informations de l’utilisateur et les groupes auxquels l’utilisateur appartient.

  1. Sélectionnez Inscriptions d’applications, puis Nouvelle inscription.
  2. Pour Nom, entrez ADB2CGroupsMembershipApp.
  3. Sous Types de comptes pris en charge, sélectionnez Comptes dans cet annuaire organisationnel uniquement.
  4. Sous URI de redirection, ne sélectionnez rien.
  5. Sous Autorisations, désactivez la case à cocher Accorder le consentement administrateur aux autorisations openid et offline_access.
  6. Sélectionnez Inscription.
  7. Enregistrez l’ID d’application (client) pour l’utiliser dans une étape ultérieure.

Ensuite, accordez l’autorisation d’accès aux API de Azure Graph pour l’application ADB2CGroupsMembershipApp :

  1. Dans le menu de gauche, sous Gérer, sélectionnez Autorisations d’API.
  2. Sous Autorisations configurées, sélectionnez Ajouter une autorisation.
  3. Sélectionnez l’API Microsoft Graph.
  4. Sélectionnez le type de Permissions d’application
  5. Sous Autorisations, sélectionnez l’étendue Group.Read.All
  6. Sélectionnez Accorder le consentement administrateur pour <nom de votre locataire>.
  7. Sélectionnez Oui.
  8. Sélectionnez Actualiser, puis vérifiez que « Accordé pour … » apparaît sous État pour l’étendue.

Enfin créez un secret pour votre application. Ce secret permettra à votre application de s’authentifier sur le tenant B2C au travers des API Microsoft Graph

  1. Dans le menu de gauche, sous Gérer, sélectionnez Certificatss & Secrets.
  2. Sélectionnez Nouveau secret client.
  3. Pour le nom, entrez ADB2CGroupsMembershipAppSecret.
  4. Pour la date d’expiration, sélectionnez 24 mois.
  5. Sélectionnez Ajouter.
  6. Enregistrez la valeur du secret pour l’utiliser dans une étape ultérieur.

Déploiement de la fonction

az group create --name ADB2CGroupsMembershipApp --location westeurope
az storage account create --name adb2cgroupsmembershipapp --resource-group ADB2CGroupsMembershipApp --location westeurope --sku Standard_LRS
az appservice plan create --name ADB2CGroupsMembershipAppPlan --resource-group ADB2CGroupsMembershipApp --sku B1 --is-linux

az functionapp create --name ADB2CGroupsMembershipApp --resource-group ADB2CGroupsMembershipApp --plan ADB2CGroupsMembershipAppPlan --storage-account adb2cgroupsmembershipapp --os-type Linux --runtime dotnet --deployment-container-image-name ghcr.io/kbeaugrand-org/adb2cgroupsmembership:latest 

az functionapp config appsettings set --resource-group ADB2CGroupsMembershipApp --name ADB2CGroupsMembershipApp --settings CLIENT_ID=${ADB2CGroupsMembershipAppClientId}
az functionapp config appsettings set --resource-group ADB2CGroupsMembershipApp --name ADB2CGroupsMembershipApp --settings CLIENT_SECRET=${ADB2CGroupsMembershipAppClientSecret}
az functionapp config appsettings set --resource-group ADB2CGroupsMembershipApp --name ADB2CGroupsMembershipApp --settings TENANT_ID=${ADB2CGroupsMembershipAppTenantId}

Toutes les étapes du déploiement de cette Fonction App sont présentées dans le repository. Veillez à remplacer les identifiants et secret par ceux de votre instance (ADB2CGroupsMembershipAppTenantId, ADB2CGroupsMembershipAppClientId et ADB2CGroupsMembershipAppClientSecret).

Réccupérez maintenant la clé de votre application de fonction, nous en aurons besoin pour les étapes de configuration de nos policies :

az functionapp keys list --resource-group ADB2CGroupsMembershipApp --name ADB2CGroupsMembershipApp

Enregistrez la clef dans les clefs de Identity Experience Framework :

  1. Sélectionnez Clés de Stratégie puis séslectionnez Ajouter
  2. Pour les Options, choisissez Manuel.
  3. Dans le Nom, enter GetUserGroupsApiKey. Le prefix B2C_1A_ sera ajouté automatiquement.
  4. Pour la Valeur, entrez la clé default de votre Function App.
  5. Pour l’Utilisation de la clé, sélectionnez Signature.
  6. Sélectionnez Créer.

Nous allons maintenant pouvoir modifier nos policies pour utiliser cette fonction app dans notre flux de login.

TrustFrameworkBase.xml

Dans notre fichier de base de stratégies, ajoutons déjà notre claims :

...
  <BuildingBlocks>
...
    <ClaimsSchema>
...
      <ClaimType Id="groups">
        <DisplayName>Comma delimited list of group names</DisplayName>
        <DataType>stringCollection</DataType>
        <UserInputType>Readonly</UserInputType>
      </ClaimType>
...
    </ClaimsSchema>
...
  </BuildingBlocks>
...

Puis ajoutons le provider de ce claims :

...     
  <ClaimsProvider>
      <DisplayName>Local Account</DisplayName>
      <TechnicalProfiles>
...
        <TechnicalProfile Id="GetUserGroups">
          <DisplayName>Retrieves security groups assigned to the user</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">https://adb2cgroupsmembershipapp.azurewebsites.net/api/GetGroups</Item>
            <Item Key="AuthenticationType">ApiKeyHeader</Item>
            <Item Key="SendClaimsIn">QueryString</Item>
            <Item Key="AllowInsecureAuthInProduction">false</Item>
          </Metadata>
          <CryptographicKeys>
            <Key Id="x-functions-key" StorageReferenceId="B2C_1A_GetUserGroupsApiKey" />
          </CryptographicKeys>
          <InputClaims>
            <InputClaim Required="true" ClaimTypeReferenceId="objectId" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="groups" />
          </OutputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>
...
    </ClaimsProvider>
  </ClaimsProviders>
...

Remplacez ici l’url de la Function App que vous avez déployé.

Maintenant, rajoutons l’appel à cet API dans le flux d’authentification de l’utilisateur. Dans les UserJourneys, recherchez la journey nommée SignUpOrSignIn, puis juste avant l’OrchestrationStep SendClaims, ajoutez la step suivante :

...        
        <OrchestrationStep Order="4" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="GetUserGroups" TechnicalProfileReferenceId="GetUserGroups" />
          </ClaimsExchanges>
        </OrchestrationStep>
...

Modifiez aussi la dernière step pour respecter l’ordre d’exécution des steps pour que le SendClaims soit bien exécuté en dernier.

SignUpOrSignin.xml

Enfin, il faut modifier le comportement de notre Relaying Party afin qu’il retourne dans le token les revendications de groupes de l’utilisateur. Pour ce faire, ajoutons simplement le claims dans les OutputClaims de notre TechnicalProfile :

<RelyingParty>
    <DefaultUserJourney ReferenceId="SignUpOrSignIn" />
    <TechnicalProfile Id="PolicyProfile">
      <DisplayName>PolicyProfile</DisplayName>
      <Protocol Name="OpenIdConnect" />
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="displayName" />
        <OutputClaim ClaimTypeReferenceId="givenName" />
        <OutputClaim ClaimTypeReferenceId="surname" />
        <OutputClaim ClaimTypeReferenceId="email" />
        <OutputClaim ClaimTypeReferenceId="groups" />
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
        <OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />

      </OutputClaims>
      <SubjectNamingInfo ClaimType="sub" />
    </TechnicalProfile>
  </RelyingParty>

Testons maintenant notre flux !!

Import des stratégies

L’import des stratégies peut se faire par l’interface graphique :

  1. Dans le menu de gauche, sous Stratégies, sélectionnez Infrastructure d’expérience d’identité
  2. Sélectionnez Charger une stratégie personalisée
  3. Importer dans l’ordre les fichiers suivants:
    1. TrustFrameworkBase.xml
    2. TrustFrameworkLocalization.xml
    3. TrustFrameworkExtensions.xml
    4. SignUpOrSignin.xml

Test

Voila nous avons terminé d’importer nos stratégies personalisées. Nous pouvons maintenant tester notre flux et vérifier que nous observons bien nos groupes utilisateurs.

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "04WumiSDK__5VSCn50doujI8Sdcs6ClnKKcb0ie0VMw"
}.{
  "exp": 1653251329,
  "nbf": 1653247729,
  "ver": "1.0",
  "iss": "https://demob2cgroumemberships.b2clogin.com/72250a90-fec1-4957-832f-44cb187c6799/v2.0/",
  "sub": "0a80c583-3b4f-4589-af6b-946b0520d35b",
  "aud": "c7c47d03-b198-443e-a5bb-be4aacf9a8de",
  "acr": "b2c_1a_signup_signin",
  "nonce": "defaultNonce",
  "iat": 1653247729,
  "auth_time": 1653247729,
  "email": "<email>",
  "name": "Demo",
  "given_name": "User",
  "groups": [
    "Administrators"
  ],
  "tid": "72250a90-fec1-4957-832f-44cb187c6799"
}.[Signature]

Conclusions

Nous avons pu voir ensemble comment personnaliser le comportement de Azure AD B2C pour intégrer un nouvel élément de revendication à ajouter à notre token.

Bien qu’il supporte nativement les scénarii comuns d’authentification et une intégration avec un grand nombre de providers d’identités au travers des protocoles SAML OAuth, WSFED par exemple, nous avons la possibilité de modifier facilement les stratégies pour étendre les fonctionnalités à nos besoins.

Ici j’ai vraiment survolé les fonctionnalités et pour aller plus loin je ne peux que vous conseiller d’aller faire un tour sur la documentation :

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s