Hi the custom policy gets called with the client id of the B2C app
https://login.microsoftonline.com/TENANT/oauth2/v2.0/authorize?p=B2C_1A_POLICY&client_id=THE-CLIENT-ID-I-WANT
How can I access this in the policy, i thought this would be hard coded to the client_id claim but I dont think it is
Its only returned as default as the aud claim but again I dont see that in the custom policy
Thanks
Ok its a bit of a work around but I tried with a standard UserJourneyContextProvider technical profile and this didnt work
so to get the client id as a claim I did the following
Create an orchestration step
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange
Id="ClientIdFromOIDC-JC"
TechnicalProfileReferenceId="Get-ClientID-FromOIDC"/>
</ClaimsExchanges>
</OrchestrationStep>
Then create a RESTFUL technical profile which will call a Function App passing the OIDC with the {OIDC:ClientID}
<TechnicalProfile Id="Get-ClientID-FromOIDC">
<DisplayName>Get-ClientID-FromOIDC</DisplayName>
<Protocol Name="Proprietary"
Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="AuthenticationType">None</Item>
<Item Key="ServiceUrl">--FUNCTION APP URL--</Item>
<Item Key="SendClaimsIn">QueryString</Item>
</Metadata>
<InputClaims>
<InputClaim
ClaimTypeReferenceId="client_id"
PartnerClaimType="client_id"
DefaultValue="{OIDC:ClientId}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="client_id" />
</OutputClaims>
</TechnicalProfile>
And then finally create a function app which accepts the client id from the querystring and returns it with the correct format for B2C to identify
using System.Net;
using System.Net.Http.Formatting;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req,
TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// parse query parameter
string client_id = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "client_id", true) == 0)
.Value;
return req.CreateResponse<ResponseContent>(
HttpStatusCode.OK, new ResponseContent
{
version = "1.0.0",
status = (int) HttpStatusCode.OK,
client_id = client_id
},
new JsonMediaTypeFormatter(), "application/json");
}
class ResponseContent {
public string version;
public int status;
public string client_id;
}
You will now get the B2C application client_id as a claim in the claim bag so you can do what you want with it now
You need to add the following into the metadata tag of technical profile:
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
For more on this. See here: OAUTH-KV Claims Resolver in AAD B2C does not work
Then use:
<OutputClaim ClaimTypeReferenceId="ClientId" DefaultValue="{OIDC:ClientId}" AlwaysUseDefaultValue="true"/>
See here: https://learn.microsoft.com/en-us/azure/active-directory-b2c/claim-resolver-overview
Related
I have azure HTTP trigger function with route defined
public async Task<IActionResult> FetchUser(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "v1/users/{profileid}")]
HttpRequest req,
string profileid)
APIM URL : https://sampleapidemo-apim-poc.azure-api.net/v1/users/PROFILEIDTOBEENTER
I had imported azure function app in APIM and trying to create a inbound policy to check whether profile id in url path parameter is provided or not.
I tried below 2 ways.
<validate-parameters specified-parameter-action="prevent" unspecified-parameter-action="prevent" errors-variable-name="requestUrlValidation">
<path specified-parameter-action="prevent">
<parameter name="profileid" action="prevent" />
</path>
</validate-parameters>
<set-variable name="profileParameter" value="#(context.Request.Url.Path.Contains("profileid"))" />
<choose>
<when condition="#((bool)context.Variables["profileParameter"] == false)">
<return-response>
<set-status code="400" reason="Bad Request" />
<set-body>
<value>Profile Id is missing in url path.</value></set-body>
</return-response>
</when>
<otherwise />
</choose>
Actual Output:
When hitting URL https://sampleapidemo-apim-poc.azure-api.net/v1/users/ or https://sampleapidemo-apim-poc.azure-api.net/v1/users
{
"statusCode": 404,
"message": "Resource not found"
}
Expected Output:
When hitting URL https://sampleapidemo-apim-poc.azure-api.net/v1/users/ or https://sampleapidemo-apim-poc.azure-api.net/v1/users
{
"statusCode": 400,
"message": "Bad Request. Profile ID is missing in URL path"
}
Since you have defined {profileid} as a template parameter in your APIM URL it cannot be omitted. APIM does not recognize the URL if the {profileid} is blank. Hence, the error 404. If you use query parameters instead it will probably work:
Instead of doing:
https://sampleapidemo-apim-poc.azure-api.net/v1/users/{profileid}
You can do this: https://sampleapidemo-apim-poc.azure-api.net/v1/users
Query parameter:
profileid
Result:
https://sampleapidemo-apim-poc.azure-api.net/v1/users?profileid={profileid}
You can fetch the parameter value in your Azure function like this:
string profileid= req.Query["profileid"];
I'm trying to use direct sign-in in the SelfAsserted-EmailCollect TechnicalProfile. I've set the default value on the InputClaim to {OIDC:LoginHint} and passed a login_hint query parameter, but in the login UI I see {OIDC:LoginHint} rather than the email address I passed in the query parameter.
Here is my claims provider:
<ClaimsProvider>
<DisplayName>SelfAsserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SelfAsserted-EmailCollect">
<DisplayName>Email</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.profileupdate</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" DefaultValue="{OIDC:LoginHint}"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
I'm using this url to login:
https://mytenant.b2clogin.com/cubiksconnectv2.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1A_Signup_Signin_Dev&client_id=myclientId&nonce=defaultNonce&redirect_uri=https%3A%2F%2Flocalhost%3A44381%2Fsignin-oidc&scope=openid&response_type=id_token&prompt=login&login_hint=test#address.com
This is what the Login UI looks like.
One possible solution is to add JavaScript to the login page to fill the e-mail field from the login_hint query parameter.
var _findSearchParameter = function (name) {
var matches = new RegExp('[\?&]' + name + '=([^&#]*)')
.exec(window.location.search);
return (matches !== null) ? matches[1] || 0 : false;
};
var loginHintValue = _findSearchParameter('login_hint');
if (loginHintValue) {
$('#email').val(loginHintValue);
}
The above solution worked well for me after enabling Javascript as per documentation.
I added the following script to the bottom of the page:
<script data-preload="false">
$(function () {
var loginHint = new URLSearchParams(window.location.search).get("login_hint");
$("#email").val(loginHint);
});
</script>
Thanks!
To use a claim resolver in a technical profile, you should set the IncludeClaimResolvingInClaimsHandling metadata key to true.
See https://learn.microsoft.com/en-us/azure/active-directory-b2c/claim-resolver-overview#using-claim-resolvers
I have a boolean variable isHere which needs to define some additional styles for a component. If I use className then the prop is passed:
<Item className={isHere ? 'hi' : null}>
// Stuff
</Item>
However when I try and pass a styled component's prop I get an error:
<Item {isHere ? {joined: true} : null}>
// Stuff
</Item>
Syntax error: Unexpected token, expected ... (30:15)
<Item joined={isHere ? true : false}>
// stuff
<LeaveLocation />
I have created a CronJob that works perfectly.
But I want to generate the sending of an email within this Cronjob. I followed a tutorial on the internet.
I start with the creation itemType of ProductsApprovedEmailProcess.
then I created productsApprovedEmailProcess to define the steps be executed by the Process Engine as follow
Then I have added an EmailContext to holds the data to be passed to the email template as follow
public class ProductsApprovedEmailContext extends CustomerEmailContext
{
private String message;
#Override
public void init(final StoreFrontCustomerProcessModel processModel, final EmailPageModel emailPageModel)
{
super.init(processModel, emailPageModel);
if (processModel instanceof ProductsApprovedEmailProcessModel)
{
setMessage(((ProductsApprovedEmailProcessModel) processModel).getMessage());
}
}
public String getMessage()
{
return message;
}
public void setMessage(final String message)
{
this.message = message;
}
}
And I had register ProductsApprovedEmailContext as a bean in Spring as follow
<bean id="productsApprovedEmailContext" class="com.hybris.training.facades.process.email.context.ProductsApprovedEmailContext"
parent="abstractEmailContext"
scope="prototype" >
</bean>
Then I created 2 Velocity templates, one for the email Subject and the other for the Body email-productsapproved-subject.vm and email-productsapproved-body.vm
And the following impex allows you to create RendererTemplates for the Subject and the Body, and attach them to an EmailPageTemplate as follow
$contentCatalog=electronicsContentCatalog
$contentCV=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$contentCatalog]),CatalogVersion.version[default=Online])[default=$contentCatalog:Online]
UPDATE GenericItem[processor=de.hybris.platform.commerceservices.impex.impl.ConfigPropertyImportProcessor];pk[unique=true]
$emailResource=$config-emailResourceValue
$emailPackageName=$config-emailContextPackageName
$lang=en
INSERT_UPDATE RendererTemplate ;code[unique=true] ;contextClass ;templateScript[lang=en,translator=de.hybris.platform.commerceservices.impex.impl.FileLoaderValueTranslator];rendererType(code)[default='velocity']
;email-productsapproved-body ;$emailPackageName.ProductsApprovedEmailContext ;$emailResource/email-productsapproved-body.vm
;email-productsapproved-subject ;$emailPackageName.ProductsApprovedEmailContext ;$emailResource/email-productsapproved-subject.vm
INSERT_UPDATE EmailPage ;$contentCV[unique=true];uid[unique=true] ;masterTemplate(uid,$contentCV) ;approvalStatus(code)[default='approved']
; ;ProductApprovedEmail ;ProductApprovedEmailTemplate ;
And in the Cronjob I added this code !
final ProductsApprovedEmailProcessModel productsApprovedEmailProcessModel = (ProductsApprovedEmailProcessModel) businessProcessService
.createProcess("productsApprovedEmailProcess" + "-" + System.currentTimeMillis(), "productsApprovedEmailProcess");
productsApprovedEmailProcessModel.setMessage("Products approved in csv file");
productsApprovedEmailProcessModel.setSite(baseSiteService.getBaseSiteForUID("electronics"));
productsApprovedEmailProcessModel.setLanguage(CommerceCommonI18NService.getCurrentLanguage());
modelService.save(productsApprovedEmailProcessModel);
businessProcessService.startProcess(productsApprovedEmailProcessModel);
But a acheive this error when I'm strating CronJob using HMC Interface :
Error executing ActionNode with ID [generateProductsApprovedEmail]: HtmlTemplate associated with MasterTemplate of EmailPageModel cannot be null
UPDATE :
Here is my business process :
<process xmlns="http://www.hybris.de/xsd/processdefinition"
start="generateProductsApprovedEmail"
name="productsApprovedEmailProcess"
processClass="com.hybris.training.core.model.process.ProductsApprovedEmailProcessModel"
onError="error">
<action id="generateProductsApprovedEmail" bean="generateProductsApprovedEmail">
<transition name="OK" to="sendEmail"/>
<transition name="NOK" to="error"/>
</action>
<action id="sendEmail" bean="sendEmail">
<transition name="OK" to="removeSentEmail"/>
<transition name="NOK" to="failed"/>
</action>
<action id="removeSentEmail" bean="removeSentEmail">
<transition name="OK" to="success"/>
<transition name="NOK" to="error"/>
</action>
<end id="error" state="ERROR">Something went wrong.</end>
<end id="failed" state="FAILED">Could not send products approved in csv File email.</end>
<end id="success" state="SUCCEEDED">Sent file in email.</end>
After declaring ProductApprovedEmailTemplate (EmailPageTemplate) i got this warn and the mail is not generated :
WARN [TaskExecutor-master-264-ProcessTask [8796715713462]] [GenerateEmailAction] Could not retrieve email page model for ProductApprovedEmail and Electronics Content Catalog:Online, cannot generate email content
Look like, the blog you have followed, it has mentioned each step correctly, but you might be missed something.
Make sure you have followed the below steps correctly.
e.g.
frontendTemplateName should be matched with EmailPageTemplate one
<bean id="generateProductApprovedEmail" parent="abstractGenerateEmailAction">
<property name="frontendTemplateName" value="ProductApprovedEmail"/>
</bean>
Create Email page Template
INSERT_UPDATE EmailPageTemplate ;$contentCV[unique=true];uid[unique=true] ;active ;frontendTemplateName ;subject(code) ;htmlTemplate(code) ;restrictedPageTypes(code)
; ;ProductApprovedEmailTemplate ;true ;ProductApprovedEmail ;email-productsapproved-subject ;email-productsapproved-body ;EmailPage
Create Email Page
INSERT_UPDATE EmailPage ;$contentCV[unique=true];uid[unique=true] ;masterTemplate(uid,$contentCV);approvalStatus(code)[default='approved']
; ;ProductApprovedEmail ;ProductApprovedEmailTemplate ;
I am using Spring Security and authenticating from an LDAP database. The authentication works fine, but I am not able to utilize roles!
In spring-security.xml there is this tag:
<security:intercept-url pattern="/app/main/admin" access="hasRole('ROLE_USER')"/>
My question is, where is "ROLE_USER" defined? How is a user made to belong to a particular role? Does this happen in the LDAP database? If yes, then how do I do that? I know little about LDAP. Is it another configuration file where I define roles?
Thank you for your help.
I know of two ways we can assign a role to a user after LDAP authentication.
We can divide users in different groups according to their roles in the LDAP database and map those groups to different roles.
See here : How to Map AD Groups to User Role Spring Security LDAP
We can authenticate the user using LDAP authentication and authorize using our local database.
Configuration:
<beans:bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg name="authenticator">
<beans:bean
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userSearch">
<beans:bean
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<beans:constructor-arg name="searchBase"
value="ou=example,dc=springframework,dc=org" />
<beans:constructor-arg name="searchFilter"
value="(uid={0})" />
<beans:constructor-arg name="contextSource"
ref="contextSource" />
</beans:bean>
</beans:property>
</beans:bean>
</beans:constructor-arg>
<beans:constructor-arg name="authoritiesPopulator"
ref="myLDAPAuthPopulator" />
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="ldapAuthProvider" />
</authentication-manager>
Implementation of myLDAPAuthPopulator:
#Component("myLDAPAuthPopulator")
public class MyLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
#Autowired
private UserDao userDao;
#Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(
DirContextOperations userData, String username) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
List<String> roleList = userDao.getRoles(username);
if (!roleList.isEmpty()) {
for (String role : roleList) {
System.out.println(role);
authorities.add(new SimpleGrantedAuthority(role));
}
}
else
{
//We know that user is authenticated. So you can add a role here and save it in the database.
}
return authorities;
}