針對企業應用程式在 JSON Web 權杖 (JWT) 中核發的自訂宣告
Microsoft 身分識別平台已針對 Microsoft Entra 應用程式庫中大部分預先整合的應用程式及自訂應用程式支援單一登入 (SSO)。 當使用者使用 OIDC 通訊協定向 Microsoft 身分識別平台驗證應用程式時,會將權杖傳送到應用程式。 應用程式會驗證並使用權杖將使用者登入,而不會提示輸入使用者名稱和密碼。
OIDC 和 OAuth 應用程式所使用的這些 JSON Web 權杖 (JWT) 包含稱為宣告的使用者相關資訊片段。 宣告是識別提供者在發給使用者的權杖內用以描述使用者的資訊。 在 OIDC 回應中,宣告資料通常會包含在識別提供者以 JWT 形式發出的識別碼權杖中。
檢視或編輯宣告
提示
根據您開始使用的入口網站,本文中的步驟可能略有不同。
選用 JWT 宣告可以在原始應用程式註冊中設定,但也可以在企業應用程式中設定。 若要檢視或編輯在 JWT 中對應用程式發出的宣告:
- 以至少 雲端應用程式系統管理員 的身分登入 Microsoft Entra 系統管理中心。
- 瀏覽至 [身分識別] > [應用程式] > [企業應用程式] > [所有應用程式]。
- 選取應用程式,選取左側功能表中的 [單一登入],然後在 [屬性與宣告] 區段中選取 [編輯]。
由於各種原因,應用程式可能需要自訂宣告。 例如,如有應用程式需要一組不同的宣告 URI 或宣告值的情況。 使用 [屬性與宣告] 區段,您可以新增或移除應用程式的宣告。 您也可以根據使用案例,建立應用程式特有的自訂宣告。
下列步驟說明如何指派常數值:
- 選取您要修改的宣告。
- 依據您的組織,在 [來源屬性] 中輸入不含引號的常數值,然後選取 [儲存]。
[屬性] 概觀會顯示常數值。
特殊宣告 - 轉換
您可以使用下列特殊宣告轉換函式。
函式 | 描述 |
---|---|
ExtractMailPrefix() | 從電子郵件地址或使用者主體名稱中移除網域尾碼。 此函式只會擷取使用者名稱的第一個部分。 例如,joe_smith 而不是 joe_smith@contoso.com 。 |
ToLower() | 將所選取屬性中的字元轉換成小寫字元。 |
ToUpper() | 將所選取屬性中的字元轉換成大寫字元。 |
新增應用程式特定宣告
若要新增應用程式特定宣告:
- 在 [使用者屬性與宣告] 中,選取 [新增宣告] 以開啟 [管理使用者宣告] 窗格。
- 輸入宣告的 [名稱]。 該值並不一定需要遵循 URI 模式。 如果您需要 URI 模式,請將其置於 [命名空間] 欄位。
- 選取宣告要擷取其值的來源。 您可以使用來源屬性下拉式選單中的屬性,或對使用者屬性套用轉換再發出為宣告。
宣告轉換
若要將轉換套用到使用者屬性:
- 在 [管理宣告] 中,選取 [轉換] 作為宣告來源以開啟 [管理轉換] 頁面。
- 從轉換下拉式清單選取函式。 視所選取的函式而定,提供參數與常數值以在轉換中進行評估。
- 將來源視為多重值指出轉換是套用至所有值,還是只套用到第一個值。 根據預設,多重值宣告中的第一個元素會套用轉換。 核取此方塊後,可確保其套用至全部。 這個核取方塊只會針對多重值屬性啟用。 例如:
user.proxyaddresses
。 - 若要套用多個轉換,請選取 [新增轉換]。 您最多可以將兩個轉換套用至宣告。 例如,您可以先擷取
user.mail
的電子郵件前置詞。 接著將字串設定為大寫。
您可以使用下列函式來轉換宣告。
函式 | 描述 |
---|---|
ExtractMailPrefix() | 從電子郵件地址或使用者主體名稱中移除網域尾碼。 此函式只會擷取使用者名稱的第一個部分。 例如,joe_smith 而不是 joe_smith@contoso.com 。 |
Join() | 透過聯結兩個屬性來建立新值。 您也可以選擇性地在兩個屬性之間使用分隔符號。 針對 NameID 宣告轉換,在轉換輸入具有網域部分時,Join () 函式會有特定行為。 該函式會先從輸入中移除網域部分,再將其與分隔符號和選取的參數聯結。 例如,如果轉換的輸入是 joe_smith@contoso.com ,分隔符號是 @ ,而參數是 fabrikam.com ,則此輸入組合會產生 joe_smith@fabrikam.com 。 |
ToLowercase() | 將所選取屬性中的字元轉換成小寫字元。 |
ToUppercase() | 將所選取屬性中的字元轉換成大寫字元。 |
Contains() | 如果輸入符合指定的值,則輸出屬性或常數。 否則,您可以在沒有相符結果時指定另一個輸出。 例如,假設您想要發出宣告,其中如果值包含網域 @contoso.com ,便代表其為使用者的電子郵件地址,否則您便想要輸出使用者主體名稱。 若要執行此函式,請設定下列值:參數 1 (輸入):user.email 值:"@contoso.com" 參數 2 (輸出):user.email 參數 3 (在沒有相符項目下的輸出):user.userprincipalname |
EndWith() | 如果輸入的結尾是指定的值,則輸出屬性或常數。 否則,您可以在沒有相符結果時指定另一個輸出。 例如,假設您想要發出宣告,其中如果員工識別碼是以 000 作為結尾,便代表值為使用者的員工識別碼,否則您便想要輸出擴充屬性。 若要執行此函式,請設定下列值:參數 1 (輸入):user.employeeid 值:"000" 參數 2 (輸出):user.employeeid 參數 3 (在沒有相符項目下的輸出):user.extensionattribute1 |
StartWith() | 如果輸入的開頭是指定的值,則輸出屬性或常數。 否則,您可以在沒有相符結果時指定另一個輸出。 例如,假設您想要發出宣告,其中如果國家/地區是以 US 作為開頭,便代表值為使用者的員工識別碼,否則您便想要輸出擴充屬性。 若要執行此函式,請設定下列值:參數 1 (輸入):user.country 值:"US" 參數 2 (輸出):user.employeeid 參數 3 (在沒有相符項目下的輸出):user.extensionattribute1 |
Extract() - 比對之後 | 在子字串符合指定值之後加以傳回。 例如,假設輸入的值是 Finance_BSimon ,相符的值是 Finance_ ,則宣告的輸出便是 BSimon 。 |
Extract() - 比對之前 | 傳回子字串,直到其符合指定值為止。 例如,假設輸入的值是 BSimon_US ,相符的值是 _US ,則宣告的輸出便是 BSimon 。 |
Extract() - 比對之間 | 傳回子字串,直到其符合指定值為止。 例如,假設輸入的值是 Finance_BSimon_US ,第一個相符的值是 Finance_ ,第二個相符的值是 _US ,則宣告的輸出便是 BSimon 。 |
ExtractAlpha() - 前置詞 | 傳回字串的前置詞字母部分。 例如,假設輸入的值是 BSimon_123 ,其便會傳回 BSimon 。 |
ExtractAlpha() - 後置詞 | 傳回字串的後置詞字母部分。 例如,假設輸入的值是 123_Simon ,其便會傳回 Simon 。 |
ExtractNumeric() - 前置詞 | 傳回字串的前置詞數值部分。 例如,假設輸入的值是 123_BSimon ,其便會傳回 123 。 |
ExtractNumeric() - 後置詞 | 傳回字串的後置詞數值部分。 例如,假設輸入的值是 BSimon_123 ,其便會傳回 123 。 |
IfEmpty() | 如果輸入為 Null 或空白,則輸出屬性或常數。 例如,假設您想要在指定使用者的員工識別碼為空白時,輸出儲存在擴充屬性中的屬性。 若要執行此函式,請設定下列值: 參數 1 (輸入):user.employeeid 參數 2 (輸出):user.extensionattribute1 參數 3 (在沒有相符項目下的輸出):user.employeeid |
IfNotEmpty() | 如果輸入為 Null 或空白,則輸出屬性或常數。 例如,假設您想要在指定使用者的員工識別碼不是空白時,輸出儲存在擴充屬性中的屬性。 若要執行此函式,請設定下列值: 參數 1 (輸入):user.employeeid 參數 2 (輸出):user.extensionattribute1 |
Substring():固定長度 | 從指定位置的字元開始擷取部分的字串宣告類型,並傳回指定數目的字元。 SourceClaim:應執行轉換的宣告來源。 StartIndex - 這個執行個體中的子字串其以零為基礎的起始字元位置。 長度 - 子字串的長度 (以字元為單位)。 例如: sourceClaim:PleaseExtractThisNow StartIndex:6 長度 – 11 輸出:ExtractThis |
Substring():EndOfString | 從指定位置的字元開始擷取部分的字串宣告類型,並從指定的起始索引傳回其餘的宣告部分。 SourceClaim:轉換的宣告來源。 StartIndex - 這個執行個體中的子字串其以零為基礎的起始字元位置。 例如: sourceClaim:PleaseExtractThisNow StartIndex:6 輸出:ExtractThisNow |
RegexReplace() | RegexReplace() 轉換接受作為輸入參數: - 參數 1:作為 RegEx 輸入的使用者屬性 - 信任來源作為多重值的選項 - Regex 模式 - 取代模式。 取代模式可能包含靜態文字格式,以及指向 RegEx 輸出群組和更多輸入參數的參考。 |
如需其他轉換,請在 Microsoft Entra ID 的意見反應論壇中,在 SaaS 應用程式類別下提交您的想法。
RegEx 型宣告轉換
下圖顯示第一層轉換的範例:
下表提供有關第一層轉換的資訊。 下表中所列的動作會對應至上圖中的標籤。 選取 [編輯] 以開啟宣告轉換刀鋒視窗。
動作 | 欄位 | 描述 |
---|---|---|
1 | Transformation |
從 [轉換] 選項中選取 [RegexReplace()] 選項,以使用 RegEx 型宣告轉換方法進行宣告轉換。 |
2 | Parameter 1 |
規則運算式轉換的輸入。 例如,具有使用者電子郵件地址 (例如,admin@fabrikam.com ) 的 user.mail。 |
3 | Treat source as multivalued |
某些輸入使用者屬性可以是多重值使用者屬性。 如果選取的使用者屬性支援多個值,而且使用者想要使用多個值進行轉換,則必須選取 [將來源視為多重值]。 如果已選取,則會使用所有值進行 RegEx 比對,否則只會使用第一個值。 |
4 | Regex pattern |
根據選取為 [參數 1] 的使用者屬性值進行評估的規則運算式。 例如,從使用者的電子郵件地址擷取使用者別名的規則運算式會以 (?'domain'^.*?)(?i)(\@fabrikam\.com)$ 表示。 |
5 | Add additional parameter |
可使用多個使用者屬性進行轉換。 屬性的值則會與 RegEx 轉換輸出合併。 最多支援五個參數。 |
6 | Replacement pattern |
取代模式是文字範本,其中包含 RegEx 結果的預留位置。 所有群組名稱都必須以大括號括住,例如 {group-name}。 假設系統管理員想要一併使用使用者別名及某些其他網域名稱 (例如 xyz.com ),並與國家/地區名稱加以合併。 在本案例中,取代模式會是 {country}.{domain}@xyz.com ,其中 {country} 是輸入參數的值,而 {domain} 是規則運算式評估中的群組輸出。 在這種情況下,預期的結果是 US.swmal@xyz.com 。 |
下圖顯示第二層轉換的範例:
下表提供有關第二層轉換的資訊。 下表中所列的動作會對應至上圖中的標籤。
動作 | 欄位 | 描述 |
---|---|---|
1 | Transformation |
RegEx 型宣告轉換不限制於第一層轉換,也可以用來作為第二層轉換。 任何其他轉換方法都可以作為第一層轉換。 |
2 | Parameter 1 |
如果選取 RegexReplace() 作為第二層轉換,則第一層轉換的輸出會作為第二層轉換的輸入。 若要套用轉換,第二層 RegEx 運算式應該符合第一層轉換的輸出。 |
3 | Regex pattern |
[RegEx 模式] 是第二層轉換的規則運算式。 |
4 | Parameter input |
第二層轉換的使用者屬性輸入。 |
5 | Parameter input |
如果系統管理員不再需要選取的輸入參數,則可以將其刪除。 |
6 | Replacement pattern |
取代模式是文字範本,其中包含 RegEx 結果群組名稱的預留位置、輸入參數群組名稱的預留位置和靜態文字值的預留位置。 所有群組名稱都必須以大括弧括住,例如 {group-name} 。 假設系統管理員想要一併使用使用者別名及某些其他網域名稱 (例如 xyz.com ),並與國家/地區名稱加以合併。 在本案例中,取代模式會是 {country}.{domain}@xyz.com ,其中 {country} 是輸入參數的值,而 {domain} 是規則運算式評估中的群組輸出。 在這種情況下,預期的結果是 US.swmal@xyz.com 。 |
7 | Test transformation |
只有在 [參數 1] 的選取使用者屬性值符合 [RegEx 模式] 文字方塊中提供的規則運算式時,才會評估 RegexReplace() 轉換。 如果不符合,預設宣告值會新增至權杖。 若要根據輸入參數值驗證規則運算式,則可在 [轉換] 刀鋒視窗中使用測試體驗。 此測試體驗僅適用於虛擬值。 使用更多輸入參數時,參數的名稱會新增至測試結果,而不是實際值。 若要存取測試區段,請選擇 [測試轉換]。 |
下圖顯示測試轉換的範例:
下表提供有關測試轉換的資訊。 下表中所列的動作會對應至上圖中的標籤。
動作 | 欄位 | 描述 |
---|---|---|
1 | Test transformation |
選取 [關閉] 或 (X) 按鈕以隱藏測試區段,再次於刀鋒視窗上重新顯示 [測試轉換] 按鈕。 |
2 | Test regex input |
接受用於規則運算式測試評估的輸入。 如果 RegEx 型宣告轉換設定為第二層轉換,請提供第一層轉換的預期輸出值。 |
3 | Run test |
提供測試 RegEx 輸入並設定 [RegEx 模式]、[取代模式] 和 [輸入參數] 之後,即可選取 [執行測試] 來評估運算式。 |
4 | Test transformation result |
如果評估成功,則會根據 [測試轉換結果] 標籤轉譯測試轉換的輸出。 |
5 | Remove transformation |
選取 [移除轉換] 可移除第二層轉換。 |
6 | Specify output if no match |
如果已根據 [參數 1] 設定 RegEx 輸入值,但與 [規則運算式] 不符,則會略過轉換。 在這種情況下,可設定替代使用者屬性,這會藉由核取 [如果沒有相符項目,則指定輸出],將其新增至宣告的權杖。 |
7 | Parameter 3 |
當沒有相符項目且核取了 [如果沒有相符項目,則指定輸出] 時,如果必須傳回替代使用者屬性,則可以使用此下拉式清單選取替代使用者屬性。 此下拉式清單是針對 [參數 3 (如果沒有相符項目的輸出)] 所提供。 |
8 | Summary |
在刀鋒視窗底部,會顯示格式的完整摘要,簡單說明轉換的意義。 |
9 | Add |
驗證轉換的設定之後,即可選取 [新增],將其儲存至宣告原則。 在 [管理宣告] 刀鋒視窗上選取 [儲存],以儲存變更。 |
RegexReplace() 轉換也適用於群組宣告轉換。
轉換驗證
選取 [新增] 或 [執行測試] 之後發生下列情況時,訊息會提供詳細資訊:
- 使用的輸入參數有重複的使用者屬性。
- 找到未使用的輸入參數。 已定義的輸入參數應該具有取代模式文字的個別用法。
- 提供的測試 RegEx 輸入不符合提供的規則運算式。
- 找不到取代模式中群組的來源。
根據條件發出宣告
您可以根據使用者類型和使用者所屬的群組來指定宣告的來源。
使用者類型可以是:
- 任何:所有使用者都可以存取應用程式。
- 成員:租用戶的原生成員
- 所有來賓:從外部組織移轉的使用者 (無論是否具有 Microsoft Entra ID)。
- Microsoft Entra 來賓:來賓使用者屬於使用 Microsoft Entra ID 的另一個組織。
- 外部來賓:來賓使用者屬於沒有 Microsoft Entra ID 的外部組織。
例如,當宣告的來源對於存取應用程式的來賓和員工而言不同時,使用者類型會很有幫助。 您可以指定如果使用者是員工,則從 user.email 取得 NameID。 如果使用者是來賓,則 NameID 的來源為 user.extensionattribute1。
若要新增宣告條件:
- 在 [管理宣告] 中,展開 [宣告條件]。
- 選取使用者類型。
- 選取該使用者應隸屬的群組。 針對指定應用程式,您在所有宣告上最多可以選取 50 個唯一群組。
- 選取宣告要擷取其值的來源。 您可以使用來源屬性下拉式選單中的屬性,或對使用者屬性套用轉換再發出為宣告。
您新增條件的順序很重要。 Microsoft Entra 會先以來源 Attribute
評估所有條件,然後以來源 Transformation
評估所有條件,以決定要在宣告中發出哪個值。 Microsoft Entra ID 會從上到下評估具有相同來源的條件。 宣告會發出符合宣告中運算式的最後一個值。 轉換 (例如 IsNotEmpty
和 Contains
) 的行為類似於限制。
例如,Britta Simon 是 Contoso 租用戶中的來賓使用者。 Britta 隸屬同樣也使用 Microsoft Entra ID 的另一個組織。 在針對 Fabrikam 應用程式套用下列設定的情況下,當 Britta 嘗試登入 Fabrikam 時,Microsoft 身分識別平台會評估條件。
首先,Microsoft 身分識別平台會驗證 Britta 的使用者類型是否為 [所有來賓]。 因為類型為 [所有來賓],Microsoft 身分識別平台會將宣告的來源指派給 user.extensionattribute1
。 其次,Microsoft 身分識別平台會驗證 Britta 的使用者類型是否為 [Microsoft Entra 來賓]。 因為類型為 [所有來賓],Microsoft 身分識別平台會將宣告的來源指派給 user.mail
。 最後,系統會針對 Britta 使用 user.mail
值發出宣告。
另舉一個範例,考慮 Britta Simon 使用下列設定嘗試登入的情況。 Microsoft Entra 會先以來源 Attribute
評估所有條件。 當 Britta 的使用者類型是 Microsoft Entra 來賓時,宣告的來源是 user.mail
。 接下來,Microsoft Entra ID 會評估轉換。 因為 Britta 是來賓,所以 user.extensionattribute1
是宣告的新來源。 因為 Britta 是 [Microsoft Entra 來賓],所以 user.othermail
是此宣告的新來源。 最後,系統會針對 Britta 使用 user.othermail
值發出宣告。
最後一個範例是,考慮假設 Britta 尚未設定 user.othermail
或其為空白時,會發生什麼事。 在這兩種情況下會忽略條件項目,且宣告會回復為 user.extensionattribute1
。
安全性考量
接收權杖的應用程式依賴無法竄改的宣告值。 透過自訂宣告修改權杖內容時,這些假設可能不再成立。 應用程式必須明確認知到權杖已修改,以免受到惡意執行者所建立自訂所導致的威脅。 以下列其中一種方式防止不適當的自訂:
如果沒有,Microsoft Entra ID 會傳回 AADSTS50146 錯誤碼。
設定自訂簽署金鑰
若為多租用戶應用程式,則應使用自訂簽署金鑰。 請勿在應用程式資訊清單中設定 acceptMappedClaims
。 如果在 Azure 入口網站中設定應用程式,則會在您的租用戶中得到應用程式註冊物件和服務主體。 該應用程式會使用 Azure 全域登入金鑰,該金鑰無法用於權杖中的自訂宣告。 若要取得權杖中的自訂宣告,請從憑證建立自訂登入金鑰,並將其新增至服務主體。 如果要進行測試,您可以使用自我簽署憑證。 設定自訂簽署金鑰之後,您的應用程式程式碼需要驗證權杖簽署金鑰。
將下列資訊新增至服務主體:
從您的憑證匯出的 PFX 檔案中,擷取私密和公開金鑰 base-64 編碼。 請確定用於「簽署」之 keyCredential
的 keyId
符合 keyId
的 passwordCredential
。 您可以藉由取得憑證指紋的雜湊來產生 customkeyIdentifier
。
Request
注意
請先從 Microsoft Entra 系統管理中心的 [應用程式註冊] 刀鋒視窗,停用新建立的應用程式上的任何服務主體鎖定設定,再嘗試在服務主體上執行 PATCH,導致 400 不正確的要求。
下列範例顯示 HTTP PATCH 要求的格式,以將自訂簽署金鑰新增至服務主體。 為了方便閱讀,keyCredentials
屬性中的「金鑰」值已縮短。 此值為 base-64 編碼。 針對私密金鑰,屬性使用方式為 Sign
。 針對公開金鑰,屬性使用方式為 Verify
。
PATCH https://graph.microsoft.com/v1.0/servicePrincipals/aaaaaaaa-bbbb-cccc-1111-222222222222
Content-type: servicePrincipals/json
Authorization: Bearer {token}
{
"keyCredentials":[
{
"customKeyIdentifier": "aB1cD2eF3gH4iJ5kL6-mN7oP8qR=",
"endDateTime": "2021-04-22T22:10:13Z",
"keyId": "aaaaaaaa-0b0b-1c1c-2d2d-333333333333",
"startDateTime": "2020-04-22T21:50:13Z",
"type": "X509CertAndPassword",
"usage": "Sign",
"key":"cD2eF3gH4iJ5kL6mN7-oP8qR9sT==",
"displayName": "CN=contoso"
},
{
"customKeyIdentifier": "aB1cD2eF3gH4iJ5kL6-mN7oP8qR=",
"endDateTime": "2021-04-22T22:10:13Z",
"keyId": "bbbbbbbb-1c1c-2d2d-3e3e-444444444444",
"startDateTime": "2020-04-22T21:50:13Z",
"type": "AsymmetricX509Cert",
"usage": "Verify",
"key": "cD2eF3gH4iJ5kL6mN7-oP8qR9sT==",
"displayName": "CN=contoso"
}
],
"passwordCredentials": [
{
"customKeyIdentifier": "aB1cD2eF3gH4iJ5kL6-mN7oP8qR=",
"keyId": "cccccccc-2d2d-3e3e-4f4f-555555555555",
"endDateTime": "2022-01-27T19:40:33Z",
"startDateTime": "2020-04-20T19:40:33Z",
"secretText": "mypassword"
}
]
}
使用 PowerShell 設定自訂簽署金鑰
使用 PowerShell 來具現化 MSAL 公用用戶端應用程式,並使用授權碼授流程來取得 Microsoft Graph 的委派權限存取權杖。 使用存取權杖來呼叫 Microsoft Graph,並為服務主體設定自訂簽署金鑰。 設定自訂簽署金鑰之後,您的應用程式程式碼需要驗證權杖簽署金鑰。
若要執行此指令碼,您需要:
- 應用程式服務主體的物件識別碼,可在 Azure 入口網站 [企業應用程式] 內應用程式項目的 [總覽] 刀鋒視窗中找到。
- 用來登入使用者並取得存取權杖以呼叫 Microsoft Graph 的應用程式註冊。 在 Azure 入口網站 [應用程式註冊] 內應用程式項目的 [總覽] 刀鋒視窗中,取得此應用程式的應用程式 (用戶端) 識別碼。 應用程式註冊應具有下列設定:
- 重新導向「http://localhost"」的 URI;列示於 [行動和桌面應用程式] 平台設定中。
- 在 [API 權限] 中,Microsoft Graph 委派的權限 Application.ReadWrite.All 和 User.Read (確保您為這些權限授與管理員同意)。
- 登入以取得 Microsoft Graph 存取權杖的使用者。 使用者必須是下列其中一個 Microsoft Entra 系統管理角色 (需要才能更新服務主體):
- 雲端應用程式系統管理員
- 應用程式系統管理員
- 要設定做為應用程式自訂簽署金鑰的憑證。 您可以建立自我簽署的憑證,或從信任的憑證授權單位取得憑證。 指令碼中會使用下列憑證元件:
- 公開金鑰 (通常為 .cer 檔案)
- PKCS#12 格式的私密金鑰 (在 .pfx 檔案中)
- 私密金鑰的密碼 (.pfx 檔案)
重要
私密金鑰必須是 PKCS#12 格式,因為 Microsoft Entra ID 不支援其他格式類型。 若使用了錯誤的格式,在使用 Microsoft Graph 修補包括憑證資訊的 keyCredentials
服務主體時,可能會導致「不正確憑證:金鑰值為不正確憑證」的錯誤發生。
##########################################################
# Replace the variables below with the appropriate values
$fqdn="yourDomainHere" # This is used for the 'issued to' and 'issued by' field of the certificate
$pwd="password" # password for exporting the certificate private key
$tenantId = "aaaabbbb-0000-cccc-1111-dddd2222eeee" # Replace with your Tenant ID
$appObjId = "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" # Replace with the Object ID of the App Registration
##########################################################
# Create a self-signed cert
$cert = New-SelfSignedCertificate -certstorelocation cert:\currentuser\my -DnsName $fqdn
$pwdSecure = ConvertTo-SecureString -String $pwd -Force -AsPlainText
$path = 'cert:\currentuser\my\' + $cert.Thumbprint
$location="C:\\temp" # path to folder where both the pfx and cer file will be written to
$cerFile = $location + "\\" + $fqdn + ".cer"
$pfxFile = $location + "\\" + $fqdn + ".pfx"
# Export the public and private keys
Export-PfxCertificate -cert $path -FilePath $pfxFile -Password $pwdSecure
Export-Certificate -cert $path -FilePath $cerFile
$pfxpath = $pfxFile # path to pfx file
$cerpath = $cerFile # path to cer file
$password = $pwd # password for the pfx file
# Check PowerShell version (minimum 5.1) (.Net) or PowerShell Core (.Net Core) and read the certificate file accordingly
if ($PSVersionTable.PSVersion.Major -gt 5)
{
$core = $true
}
else
{
$core = $false
}
# this is for PowerShell Core
$Secure_String_Pwd = ConvertTo-SecureString $password -AsPlainText -Force
# reading certificate files and creating Certificate Object
if ($core)
{
$pfx_cert = get-content $pfxpath -AsByteStream -Raw
$cer_cert = get-content $cerpath -AsByteStream -Raw
$cert = Get-PfxCertificate -FilePath $pfxpath -Password $Secure_String_Pwd
}
else
{
$pfx_cert = get-content $pfxpath -Encoding Byte
$cer_cert = get-content $cerpath -Encoding Byte
# calling Get-PfxCertificate in PowerShell 5.1 prompts for password - using alternative method
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($pfxpath, $password)
}
# base 64 encode the private key and public key
$base64pfx = [System.Convert]::ToBase64String($pfx_cert)
$base64cer = [System.Convert]::ToBase64String($cer_cert)
# getting id for the keyCredential object
$guid1 = New-Guid
$guid2 = New-Guid
# get the custom key identifier from the certificate thumbprint:
$hasher = [System.Security.Cryptography.HashAlgorithm]::Create('sha256')
$hash = $hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($cert.Thumbprint))
$customKeyIdentifier = [System.Convert]::ToBase64String($hash)
# get end date and start date for our keycredentials
$endDateTime = ($cert.NotAfter).ToUniversalTime().ToString( "yyyy-MM-ddTHH:mm:ssZ" )
$startDateTime = ($cert.NotBefore).ToUniversalTime().ToString( "yyyy-MM-ddTHH:mm:ssZ" )
# building our json payload
$object = [ordered]@{
keyCredentials = @(
[ordered]@{
customKeyIdentifier = $customKeyIdentifier
endDateTime = $endDateTime
keyId = $guid1
startDateTime = $startDateTime
type = "AsymmetricX509Cert"
usage = "Sign"
key = $base64pfx
displayName = "CN=$fqdn"
},
[ordered]@{
customKeyIdentifier = $customKeyIdentifier
endDateTime = $endDateTime
keyId = $guid2
startDateTime = $startDateTime
type = "AsymmetricX509Cert"
usage = "Verify"
key = $base64cer
displayName = "CN=$fqdn"
}
)
passwordCredentials = @(
[ordered]@{
customKeyIdentifier = $customKeyIdentifier
displayName = "CN=$fqdn"
keyId = $guid1
endDateTime = $endDateTime
startDateTime = $startDateTime
secretText = $password
hint = $null
}
)
}
Connect-MgGraph -tenantId $tenantId -Scopes Application.ReadWrite.All
$graphuri = "https://graph.microsoft.com/v1.0/applications/$appObjId"
Invoke-MgGraphRequest -Method PATCH -Uri $graphuri -Body $object
$json = $object | ConvertTo-Json -Depth 99
Write-Host "JSON Payload:"
Write-Output $json
驗證權杖簽署金鑰
已啟用宣告對應的應用程式必須將 appid={client_id}
附加至其 OpenID Connect 中繼資料要求,以驗證其權杖簽署金鑰。 下列範例顯示您應該使用的 OpenID Connect 中繼資料文件格式:
https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration?appid={client-id}
更新應用程式資訊清單
對於單一租用戶應用程式,您可以在應用程式資訊清單中,將 acceptMappedClaims
屬性設定為 true
。 如 apiApplication
資源類型所述。 設定屬性可讓應用程式使用宣告對應,而不需指定自訂簽署金鑰。
警告
若為多租用戶應用程式,請不要將 acceptMappedClaims 屬性設定為 true,此舉可讓惡意執行者為您的應用程式建立宣告對應原則。
要求的權杖對象會需要使用您 Microsoft Entra 租用戶的已驗證網域名稱,這表示應該將 Application ID URI
(在應用程式資訊清單中以 identifierUris
表示) 設為例如 https://contoso.com/my-api
或 (直接使用預設租用戶名稱) https://contoso.onmicrosoft.com/my-api
。
若未使用已驗證的網域,則 Microsoft Entra ID 會傳回 AADSTS501461
錯誤碼,訊息為「_AcceptMappedClaims 僅支援符合應用程式 GUID 的權杖對象或租用戶已驗證網域內的對象。 請變更資源識別碼,或使用應用程式特定的簽署金鑰。」
進階宣告選項
設定 OIDC 應用程式的進階宣告選項,以公開與 SAML 權杖相同的宣告。 此外,對於想要針對 SAML2.0 和 OIDC 回應權杖使用相同的宣告的應用程式。
在 [管理宣告] 刀鋒視窗中核取 [進階宣告選項] 底下的方塊,即可設定進階宣告選項。