我在Azure B2C自定义策略中使用OAuth2技术配置文件,以允许我的用户向第三方OAuth提供商进行身份验证。
ClaimsProvider看起来像这样:
<ClaimsProvider>
<Domain>app.example.com</Domain>
<DisplayName>Example OAuth2</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Example-OAuth2">
<DisplayName>Example OAuth2</DisplayName>
<Protocol Name="OAuth2" />
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="AccessTokenEndpoint">https://example.com/token</Item>
<Item Key="authorization_endpoint">https://example.com/oauth2</Item>
<Item Key="ClaimsEndpoint">https://example.com/userinfo</Item>
<Item Key="BearerTokenTransmissionMethod">AuthorizationHeader</Item>
<Item Key="response_types">code</Item>
<Item Key="response_mode">query</Item>
<Item Key="scope">domainspecific openid email profile</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
<Item Key="client_id">client-id</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
<Item Key="ResolveJsonPathsInJsonTokens">true</Item>
<Item Key="AccessTokenResponseFormat">json</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="ClientSecretFile" />
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="familyName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="domain.com" PartnerClaimType="iss" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="identityProviderTenantId" PartnerClaimType="domainTenantId" />
<OutputClaim ClaimTypeReferenceId="identityProviderAccessToken" PartnerClaimType="{oauth2:access_token}" />
<OutputClaim ClaimTypeReferenceId="identityProviderRefreshToken" PartnerClaimType="{oauth2:refresh_token}"/>
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
这只适用于一个例外,即从未设置identityProviderTenantId。其来源domainTenantId在两个位置可用:
当第三方提供商回调B2C OAuth2重定向URI:时,它作为查询字符串的一部分返回
https://ourdomain.b2clogin.com/ourdomain.onmicrosoft.com/oauth2/authresp?code=546fghfgh-fghjdfgdhjg&state=someState&*domainTenantId=46475,345674**
它在idToken中可用,该idToken作为访问令牌端点的响应的一部分返回:
Connection: keep-alive
Keep-Alive: timeout=5
Strict-Transport-Security: max-age=10002000
Cache-Control: no-cache, no-store
Content-Type: application/json;charset=utf-8
{
"refreshToken": "RefreshTokenContent",
"accessToken": "AccessTokenContent",
"expires_in": 3600,
"x_refresh_token_expires_in": 8726400,
"idToken": "IDTokenContent"
}
我无法将域TenantId作为自定义声明返回,尽管进行了多次尝试,包括:
只是将其作为标准OutputClaim:返回
<OutputClaim ClaimTypeReferenceId="identityProviderTenantId" PartnerClaimType="domainTenantId" />
尝试使用ClaimResolver:返回
<OutputClaim ClaimTypeReferenceId="identityProviderTenantId" DefaultValue="{OAUTH-KV:domainTenantId}" AlwaysUseDefaultValue="true" />
或
<OutputClaim ClaimTypeReferenceId="identityProviderTenantId" PartnerClaimType="{OAUTH-KV:domainTenantId}" />
尝试从访问令牌响应中提取idToken
<OutputClaim ClaimTypeReferenceId="identityProviderIdentityToken" PartnerClaimType="idToken"/>
(在这种情况下,永远不会设置identityProviderIdentityToken(
我也尝试过使用OpenIdConnect协议。这确实会将domainTenantId作为简单的OutputClaim返回,但不会返回刷新令牌,我们的应用程序也需要刷新令牌才能正常工作。
我想做的事情可能吗?
我能够通过连续调用两个OAuth2技术概要文件来实现这一点。
上的第一个具有/me作为声明端点,而第二个具有/organization作为声明端点。第二个调用的响应中包含用户的tenantId。
第一个技术简介:
<TechnicalProfile Id="AADCommon-OAuth2-First">
<DisplayName>some display name</DisplayName>
<Description>some description</Description>
<Protocol Name="OAuth2" />
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="AccessTokenEndpoint">https://login.microsoftonline.com/organizations/oauth2/v2.0/token</Item>
<Item Key="authorization_endpoint">https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize</Item>
<Item Key="ClaimsEndpoint">https://graph.microsoft.com/v1.0/me</Item>
<Item Key="ClaimsEndpointAccessTokenName">access_token</Item>
<Item Key="BearerTokenTransmissionMethod">AuthorizationHeader</Item>
<Item Key="client_id">xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="scope">openid profile offline_access https://graph.microsoft.com/User.Read</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_AADSecret" />
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="id" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="surname" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="isMicrosoftSignInInvoked_FLAG_LOCAL" DefaultValue="true" AlwaysUseDefaultValue="true" /> <!-- This flag invokes the second OAuth2 call -->
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Social" />
</TechnicalProfile>
第二个技术简介:
<TechnicalProfile Id="AADCommon-OAuth2-Second">
<DisplayName>some display name second</DisplayName>
<Description></Description>
<Protocol Name="OAuth2" />
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="AccessTokenEndpoint">https://login.microsoftonline.com/organizations/oauth2/v2.0/token</Item>
<Item Key="authorization_endpoint">https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize</Item>
<Item Key="ClaimsEndpoint">https://graph.microsoft.com/v1.0/organization</Item>
<Item Key="ClaimsEndpointAccessTokenName">access_token</Item>
<Item Key="BearerTokenTransmissionMethod">AuthorizationHeader</Item>
<Item Key="client_id">xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="scope">openid profile offline_access https://graph.microsoft.com/User.Read</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_AADSecret" />
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="tenantId" DefaultValue="TENANT ID UNSET" Required="true" />
<OutputClaim ClaimTypeReferenceId="oauth2Access_token" PartnerClaimType="{oauth2:access_token}" />
<OutputClaim ClaimTypeReferenceId="oauth2Refresh_token_rawJSON" PartnerClaimType="{oauth2:refresh_token}" /> <!-- this is the refresh token returned as json -->
<OutputClaim ClaimTypeReferenceId="oauth2Refresh_token" /> <!-- this claim is transformed by the OutputClaimstransformations by extracting the refresh token's value from JSON -->
<OutputClaim ClaimTypeReferenceId="organizationEndpointResponseValueCollection_LOCAL" PartnerClaimType="value" /> <!-- string collection from the JSON response of /organization endpoint -->
<OutputClaim ClaimTypeReferenceId="organizationEndpointResponseValueCollectionSingleValue_LOCAL" /> <!-- first (and only) object from the string collection from the JSON response of /organization endpoint -->
</OutputClaims>
<OutputClaimsTransformations>
<!-- refresh token extraction -->
<OutputClaimsTransformation ReferenceId="GetRefreshTokenClaimFromJson" />
<!-- extract tenant id -->
<OutputClaimsTransformation ReferenceId="ExtractSingleValueObjectFromValueCollection" />
<OutputClaimsTransformation ReferenceId="GetTenantIdFromValueObjectJson" />
<OutputClaimsTransformation ReferenceId="FormatIdentityProviderFromTenantId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
使用的索赔转换:
<ClaimsTransformation Id="ExtractSingleValueObjectFromValueCollection" TransformationMethod="GetSingleItemFromStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="organizationEndpointResponseValueCollection_LOCAL" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="organizationEndpointResponseValueCollectionSingleValue_LOCAL" TransformationClaimType="extractedItem" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="GetTenantIdFromValueObjectJson" TransformationMethod="GetClaimFromJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="organizationEndpointResponseValueCollectionSingleValue_LOCAL" TransformationClaimType="inputJson" />
</InputClaims>
<InputParameters>
<InputParameter Id="claimToExtract" DataType="string" Value="id" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="tenantId" TransformationClaimType="extractedClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="GetRefreshTokenClaimFromJson" TransformationMethod="GetSingleItemFromJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="oauth2Refresh_token_rawJSON" TransformationClaimType="inputJson" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="oauth2Refresh_token_rawJSON_key" TransformationClaimType="key" />
<OutputClaim ClaimTypeReferenceId="oauth2Refresh_token" TransformationClaimType="value" />
</OutputClaims>
</ClaimsTransformation>
扩展索赔模式:
<ClaimType Id="organizationEndpointResponseValueCollection_LOCAL">
<DisplayName>Value field from response from https://graph.microsoft.com/v1.0/organization</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="organizationEndpointResponseValueCollectionSingleValue_LOCAL">
<DisplayName>Single object from the value field from response from https://graph.microsoft.com/v1.0/organization</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="isMicrosoftSignInInvoked_FLAG_LOCAL">
<DisplayName>isMicrosoftSignInInvoked</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Whether the user has selected sign in with Microsoft</AdminHelpText>
</ClaimType>
<ClaimType Id="oauth2Refresh_token_rawJSON">
<DisplayName>oauth2:refresh_token</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<ClaimType Id="oauth2Refresh_token_rawJSON_key">
<DisplayName>oauth2:refresh_token_raw_key</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>