samples
samples copied to clipboard
The terms of use are unexpectedly displayed
I'm trying this sample for terms of use agreement and expect the following results like described in the doc.
- A new user is signing up. The terms of use are displayed, and the acceptance result is stored.
- A user is signing in who has previously accepted the latest or active terms of use. The terms of use are not displayed.
But even though a user already accepted it, the TOU are displayed again when the user is signing in with the same session where the user accepted the TOU. For example,
- An existing user signs in to an app, inputs his credentials and accepts the TOU.
- He signs in to another app with the same session. Then he doesn't need to input his credentials but is required to accept the TOU again.
- If once he signs out, he can sign in without looking the TOU.
The TOU agreement in the step 2 must be unnecessary because it's already done in step 1.
Workaround: This works for me.
<TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" PolicySchemaVersion="0.3.0.0" TenantId="yourtenant.onmicrosoft.com" PolicyId="B2C_1A_TOU_SUSI" PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_TOU_SUSI">
<BasePolicy>
<TenantId>yourtenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="extension_termsOfUseConsentChoice">
<DisplayName></DisplayName>
<DataType>string</DataType>
<UserInputType>CheckboxMultiSelect</UserInputType>
<Restriction>
<Enumeration Text=" I agree to the Terms Of Service" Value="AgreeToTermsOfUseConsentYes" SelectByDefault="false" />
</Restriction>
</ClaimType>
<ClaimType Id="currentTime">
<DisplayName>Current Time</DisplayName>
<DataType>dateTime</DataType>
<AdminHelpText>Current date time in UTC.</AdminHelpText>
<UserHelpText>Current date time in UTC.</UserHelpText>
</ClaimType>
<ClaimType Id="extension_termsOfUseConsentDateTime">
<DisplayName>Terms of Use Consent Date Time</DisplayName>
<DataType>dateTime</DataType>
<AdminHelpText>Terms of Use Consent date time in UTC.</AdminHelpText>
<UserHelpText>Terms of Use Consent date time in UTC.</UserHelpText>
</ClaimType>
<ClaimType Id="termsOfUseConsentRequired">
<DisplayName>Terms of Use Consent Required</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Boolean that specifies if Terms of Use Consent is required or not.</AdminHelpText>
<UserHelpText>Boolean that specifies if Terms of Use Consent is required or not</UserHelpText>
</ClaimType>
<ClaimType Id="extension_termsOfUseConsentVersion">
<DisplayName>Terms of Use Consent Version</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Terms of Use Consent Version.</AdminHelpText>
<UserHelpText>Terms of Use Consent Version.</UserHelpText>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="GetCurrentDateTime" TransformationMethod="GetCurrentDateTime">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="currentTime" TransformationClaimType="currentDateTime" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="IsTermsOfUseConsentRequiredForDateTime" TransformationMethod="IsTermsOfUseConsentRequired">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_termsOfUseConsentDateTime" TransformationClaimType="termsOfUseConsentDateTime" />
</InputClaims>
<InputParameters>
<InputParameter Id="termsOfUseTextUpdateDateTime" DataType="dateTime" Value="2020-01-30T23:03:45" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="termsOfUseConsentRequired" TransformationClaimType="result" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="GetNewUserAgreeToTermsOfUseConsentVersion" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="V1"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_termsOfUseConsentVersion" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- Not used here, but this OR IsTermsOfUseConsentRequiredForDateTime check can be used -->
<ClaimsTransformation Id="IsTermsOfUseConsentRequiredForVersion" TransformationMethod="CompareClaimToValue">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_termsOfUseConsentVersion" TransformationClaimType="inputClaim1" />
</InputClaims>
<InputParameters>
<InputParameter Id="compareTo" DataType="string" Value="V2" />
<InputParameter Id="operator" DataType="string" Value="not equal" />
<InputParameter Id="ignoreCase" DataType="string" Value="true" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="termsOfUseConsentRequired" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- Change termsOfUseConsentRequired value after TOU evaluation during sign-in -->
<ClaimsTransformation Id="ChangeTOUSessionStatus" TransformationMethod="NotClaims">
<InputClaims>
<InputClaim ClaimTypeReferenceId="termsOfUseConsentRequired" TransformationClaimType="inputClaim" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="termsOfUseConsentRequired" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<!-- Page that prompts for a new TOU on sign in if the users TOU is out of date-->
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SelfAsserted-Input-ToU-SignIn">
<DisplayName>Self Asserted ToU</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
<Item Key="AllowGenerationOfClaimsWithNullValues">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" />
<InputClaim ClaimTypeReferenceId="extension_termsOfUseConsentChoice" DefaultValue="AgreeToTermsOfUseConsentNo" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_termsOfUseConsentChoice" Required="true" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="ChangeTOUSessionStatus" />
</OutputClaimsTransformations>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="Update-TOU-Status" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<DisplayName>Local Account Signin</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="SignUpTarget">SignUpWithLogonEmailExchange</Item>
<Item Key="setting.operatingMode">Email</Item>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
<InputClaim ClaimTypeReferenceId="currentTime" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="password" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="termsOfUseConsentRequired"/>
<OutputClaim ClaimTypeReferenceId="extension_termsOfUseConsentDateTime" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetCurrentDateTime" />
</OutputClaimsTransformations>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
<ValidationTechnicalProfile ReferenceId="Check-TOU-Status" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
<!-- read the last time the user consented -->
<TechnicalProfile Id="Check-TOU-Status">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>true</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_termsOfUseConsentDateTime" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="IsTermsOfUseConsentRequiredForDateTime" />
</OutputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
<!-- write the current time to the consent attribute if the user reconsents to TOU on sign in -->
<TechnicalProfile Id="Update-TOU-Status">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="currentTime" PartnerClaimType="extension_termsOfUseConsentDateTime" />
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<!-- sign up page with TOU checkbox-->
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmailCustom">
<DisplayName>Email signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="language.button_continue">Create</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GetCurrentDateTime" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_termsOfUseConsentChoice" DefaultValue="AgreeToTermsOfUseConsentNo" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<!-- Optional claims, to be collected from the user -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
<OutputClaim ClaimTypeReferenceId="extension_termsOfUseConsentChoice" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- During sign up, write the current time of consent, and version -->
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="currentTime" PartnerClaimType="extension_termsOfUseConsentDateTime"/>
<PersistedClaim ClaimTypeReferenceId="extension_termsOfUseConsentChoice" />
<PersistedClaim ClaimTypeReferenceId="extension_termsOfUseConsentVersion" DefaultValue="V1" />
</PersistedClaims>
</TechnicalProfile>
<TechnicalProfile Id="SM-AAD">
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="termsOfUseConsentRequired" />
</PersistedClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="B2CSignUpOrSignInWithPasswordToU" NonInteractive="false">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSignUp" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmailCustom" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent
in the token. -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<!-- Add condition to not execute this step for sign up scenario based on newUser claim -->
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>newUser</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Display Terms of Use consent page for any SignIn scenario based on termsOfUseConsentRequired claim -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<!-- Add condition to not execute this step for sign up scenario based on newUser claim -->
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>newUser</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>termsOfUseConsentRequired</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="ShowToUConsentPageForNewUser" TechnicalProfileReferenceId="SelfAsserted-Input-ToU-SignIn" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="B2CSignUpOrSignInWithPasswordToU" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="extension_termsOfUseConsentDateTime" />
<OutputClaim ClaimTypeReferenceId="currentTime" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="termsOfUseConsentRequired" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
The solution would be to add SM-AAD as session management to the offending technical profile which re prompts for TOU on SSO scenarios.
I shall update the sample.
@JasSuri I keep getting this error too, have you updated the sample?
I have the same issue and it does not seem like the sample was updated to fix this. @JasSuri Can you please adapt the sample accordingly? Also @tomatsue's solution did not work for me, because then the terms of use step is skipped when coming from a different user flow like password reset.
I did apply a fix: https://github.com/azure-ad-b2c/samples/commit/f2824296696c42e207840a6c11733fa0dc20a033
Can you provide more details on the behavior?
I have the following behavior:
- The first time a user logs in, the terms of use (tou) form appears
- The user accepts the tou and has successfully completed the login flow
- When the user opens a new tab in his browser, and tries to get a new token there, the tou appears again, even though the consent date is already set
- When the user logs out or closes his browser (we store his accounts in the session storage), he will not see the tou form again
This becomes worse when the session storage cannot be used, like in incognito mode. Then, there might an endless loop between the application/MSAL and the tou form.
I guess the problem is the "termsOfUseConsentRequired" claim remaining true as long as the ID token is valid. So every access token request still requires a consent. Is there a way to re-valuate this claim?
I added session management to the SelfAsserted-Input-ToU-SignIn
technical profile. That should solve this. Please let me know.
It did solve the issue for this example. But after integrating the changes into my flows, I faced the issue that a user does not need to approve the terms of use when doing another flow before, like password reset. I kept only this change:
<TechnicalProfile Id="SelfAsserted-Input-ToU-SignIn">
...
- <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
+ <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
This was enough to get the desired behavior. Thank you!
Ok thats good feedback. I will see if there is a better solution.
I was wrong. I also had to revert the mentioned change, i.e. I set the session management back to SM-Noop. Otherwise users did not need to approve the terms of use after the password reset flow. So back to the start - now users have to approve multiple times per session again (in incognito mode or when opening another tab).
Ok, I will take another look at this one.
Made updates on this sample, test it out and let me know.
Thanks @JasSuri. I applied only the fix for the login flow, and it still works as expected now.