Hướng Dẫn Thêm Custom Attribute Vào Azure Active Directory Claims Token

Khanh Nam DoKhanh Nam Do
3 min read

Dự án mình đang làm có yêu cầu sử dụng Azure Active Directory (AAD) để lưu trữ thông tin người dùng & xác thực đăng nhập các kiểu. Ngoài các trường thông tin cơ bản mà AAD hỗ trợ ra thì dự án mình cần lưu thêm 1 số thông tùy chọn khác cho người dùng. AAD hỗ trợ khá nhiều cách để lưu custom attribute vào user object, dĩ nhiên là ăn sẵn thì sẽ mất thêm tiền 😂

Vậy nên mình tiếp cận theo hướng xài Microsoft Graph extensions. Tại thời điểm mình dùng thì nó hỗ trợ 4 kiểu extension:

  1. Extension attributes properties

  2. Directory (Azure AD) extensions

  3. Schema extensions

  4. Open extensions

Mình đã thử hết cả 4 kiểu, cho đến khi gặp yêu cầu phải "nhét được cái đám custom attribute ở trên vào trong token claims" (customize claims emitted in tokens) 🤣

May mắn là thằng AAD nó hỗ trợ luôn cả việc nhét thêm các custom attribute vào claim của token bằng cách xài optional claims thông qua GUI hoặc application manifest, đơn giản như sau:

"optionalClaims": {
  "idToken": [            
    {
      "name": "extension_jkhkjhh9u081233239b9b7bee985bad11b3_coreBankId",
      "source": "user",
      "essential": true,
      "additionalProperties": []
    }
  ]
}

Vấn đề bắt đầu từ đây! Mình xài 2 app, app_1 cho admin thao tác với AAD và các Azure services khác (dạng server - server communication), app_2 cho end user nên khi tạo directory extension bằng app_1 thì chỉ auth qua app_1 mới thấy cái coreBankId kia trả về trong token claims, user mà auth qua app_2 thì chả thấy gì luôn 😑

Mày mò tìm trong tài liệu của Azure một lúc thì thấy họ có ghi với trường hợp này thì phải xài claims mapping policy. Tóm lại, sẽ cần làm theo các bước như sau:

  1. Tạo 1 claimsMappingPolicy

  2. Assign claimsMappingPolicy vào một servicePrincipal resource

Để claimsMappingPolicy, mình xài MS Graph API:

const claimsMappingPolicy = {
  definition: [
    JSON.stringify({
      ClaimsMappingPolicy: {
        Version: 1,
        IncludeBasicClaimSet: "true",
        ClaimsSchema: [
          {
            Source: "User",
            ExtensionID:
              "extension_jkhkjhh9u081233239b9b7bee985bad11b3_coreBankId",
            JWTClaimType: "coreBankId",
          },
        ],
      },
    }),
  ],
  displayName: "Core Bank ID",
};

await client.api("/policies/claimsMappingPolicies").post(claimsMappingPolicy);

Tiếp đó là assign claimsMappingPolicy này vào servicePrincipal. Mình sử dụng đoạn code sau để liệt kê tất cả các servicePrincipal rồi lấy ID tương ứng để dùng cho bước assign:

// Get list of service principal
const servicePrincipals = await client.api("/servicePrincipals").get();

// Assign claimsMappingPolicy to a servicePrincipal
const serviceId = "xxx"; // Get from above step
const policyId = "yyy";
const claimsMappingPolicy = {
  "@odata.id": `https://graph.microsoft.com/v1.0/policies/claimsMappingPolicies/${policyId}`,
};

await client
  .api(`/servicePrincipals/${serviceId}/claimsMappingPolicies/$ref`)
  .post(claimsMappingPolicy);

Okie, giờ thử login lại rồi decode cái id_token ra xem thấy gì nha :)

Tuyệt vời, thuộc tính coreBankId được tạo bởi app_1 đã xuất hiện trong claims token khi người dùng auth ở app_2 😍

0
Subscribe to my newsletter

Read articles from Khanh Nam Do directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Khanh Nam Do
Khanh Nam Do

Khanh is a full stack web developer with over 15 years of experience developing for the web