I am working on an Azure B2C custom policy with a rest API call. When a user signs up or signs in, i'm hitting an api endpoint to get user information and add it back to the claims.
However, when a user is not found in the external system(such as during sign up), the api will throw a 404. When this happens the error is posted in the url and the user flow errors out.
What I would like to do is ignore the 404 error and continue to the next orchestration step where we can then check if we got any information back from the api, and if not, hit another api endpoint to generate the users info and continue on with the sign up/sign in flow.
I've been looking for ways to do this, and the only answer i've seen is about handling errors from the api side which is very difficult in this situation due to access constraints.
Whenever your API sends a non 200 response to AAD B2C, it will halt the execution of the journey. If there is a page displayed to the user, and the REST API call is run as a validation technical profile, then the error is displayed on screen. Otherwise the error is sent to the App URL.
In a Validation Technical profile, you can change this behavior by using the ContinueOnError property.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/validation-technical-profile
Call the REST API technical profile via a Validation Technical profile, then add the flag ContinueOnError="true" .
<ValidationTechnicalProfile ReferenceId="REST-ReadProfileFromCustomerDatabase" ContinueOnError="true" >
Related
I have an Azure policy which works well for authentication users of different roles. I now need to add a SignUp component to this but there are some requirements that I'm not sure are possible.
I need the policy to return back to a different application than what called it. Say App A has the signup link on it's login page which will invoke the
policy's SignUp userjourney. However, I need it to not return back
to that same App, instead redirect to App B along with those claims
it's gathered. Is this even possible?
If it were possible (above), how can this be set up under the relyingparty
section? I would need to keep what is already there for the
userjourney that authentications users, but now also somehow provide
what's needed for this SignUp flow.
I am sorry if this is vague. I am just looking to get unstuck. If anyone could provide any sort of nudge in the right direction, I'd be very grateful.
This idea will not work. This is because, for at least the MSAL authentication library, it will reject a token response if MSAL was not the one who initiated the request. That is built in protection to most libraries, and uses the state parameter in the authentication request to apply this protection. Only responses that come back with the same state parameter will be accepted by the app.
You need to create a link from App 1 to App 2, and have App 2 initiate the Sign Up B2C policy.
The token response URL is controlled by the redirect_uri parameter in the authentication request. Your app will always require this to be configured within it. Both apps should provide a redirect_uri to send the token back to the respective App.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/openid-connect#send-authentication-requests
So I am currently using invite policy of azure b2c by following the link https://github.com/azure-ad-b2c/samples/tree/master/policies/invite and was able to successfully generate a signup link using id_token_hint. Instead of sending a link through email we have used an API to send the link to angular app and then trigger the link on click of sign up button. Even though the flow is working fine technically, there are two enhancement which our team is thinking to implement to make the UX better.
The first one is that after successful signup the redirect uri that we have set is triggered and the browser redirects to it but since that page is doing some API calls this is protected and the signin user flow of our application is triggered. If we do signup using the userflow provided by b2c itself we got logged in as well in the same flow but in our case of signup the signin is extra step which my team is thinking to remove. Is there any way we can do that? I have read since the flow is not initiated by our website, the token returned by invite signup is being ignored. So is there a way around this?
My Signup URL:
Signup url with id_token_hint
Token is even captured by jwt.ms but angular app does not consider it.
token after signup screen captured by jwt.ms
Secondly is there any way to enable email verification just like in default signup flow of b2c where a otp is sent to the email before moving forward? According to microsoft documentation you can do this by updating this piece of code under metadata in the policy file which I have shown below. It was 'False' by default so I did change it to 'True' but the verification flow is still not enabled. If I missing any thing here?
<Item Key="EnforceEmailVerification">True</Item>
Edit:
Since you have asked how I have added authentication in my API, I have added a picture below. This is working fine for the normal signin flow. Also meanwhile I will go through the link you have provided and check if it solves my problem.
Config in my API: Config in my backend API
Startup: In startup file
Then I have used AddMicrosoftIdentityWebApi by specifying jwt options and attached config.
Also I have attached the decoded tokens in both the cases. The kid is different also the signup token has one more field x5t.
Token of normal signin flow: Signin flow token
Token of invited email signup: Signup flow token
Almost all the other fields are similar in these tokens.
I think you should consider reworking your API so that it returns an id_token (not a link). Then the angular app should call the API and initiate the B2C_1A_signup_invitation flow from angular app with passing the received id_token.
Try to change 85th line in github example from <OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" Required="true" /> to <OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" PartnerClaimType="Verified.Email" Required="true" />.
And also set EnforceEmailVerification to True.
Context
We have created a custom policy used when users are invited to our SPA application.
The policy does one time user initialization like creating records in our database by invoking the REST API capabilities. Everything here works as expected: The custom logic is executed and we get and id token back.
The problem starts when we are supposed to get an access token for a protected API when invoking the msal.js method
"acquireTokenSilent".
We now see, that the custom policy is executed again, the REST endpoint is once again executed and it's trying to create the user again.
Question
Isn't it possible to get an access token without executing all business logic defined in a custom policy?
I thought that getting an acccesstoken was completely separate from the policy, since we are already authenticated when we got the id token.
acquireTokenSilent in MSAL.js 1.x uses an iframe that runs through the login with prompt=none.
So in the case of implicit grant flow (which is used here), the answer is that it runs through the login flow every time the token is refreshed.
There are a couple choices here:
Make the API endpoint idempotent, i.e. allow calling it multiple times (but ignore the request if the user already exists)
Set a claim on the user that is stored in session state (SSO) and skip the REST API orchestration step if that claim has a value (or you can use the objectIdFromSession claim, which I think is there in the starter template)
The first option is sort of the simplest, and I assume you've already made it like that since it would be called again also when the user logs out and logs back in.
It might result in a lot of requests to that API though.
To avoid that, you could use the newUser claim or set an extension property on the user after the API call.
When calling acquireTokenSilent(), pass in a new authority for a normal Sign In policy. That Sign In policy needs to have session management (SM-AAD) and the same SM-AAD SM technical profile must be present in a technical profile within your invite policy, such that the user can get SSO via the Sign In policy after using the Invite policy.
I have the following flow for sign in / sign up, when using sign-up:
Present user with sign up screen, allow them to enter email/password/name
Validate input, then send email (rest api) and set verification attributes in b2c custom extension properties
User then receives email with verification link
User clicks link from email and gets sent to a new user journey for the return trip
New user journey gets parameters from the querystring (email + verif code)
B2C validates the verif code + expiry
IF user is verified, they're set as verified via custom extension attributes, then sent to the (ASP.NET MVC) application.
Here's where I'm stuck - B2C is sending the jwt token back to the app, but the user doesn't get 'signed-in'.
Am I missing something at step 7? I don't have the "state" variable in my querystring, am I expected to build and include it somehow so that B2C and the app can communicate? I'm lost at this point. I'd post some of the b2c policy xml but not sure what would even help...
EDIT: reply to Jas:
Is that the only way (make an app call b2c for an auth request)? We have multiple apps that a user can use to sign up through b2c, so I was hoping to avoid having to make changes to each of them. Instead I was hoping that B2C could tell the app after account verification that "this user is ok".
I did previous look at https://github.com/azure-ad-b2c/samples/tree/master/policies/sign-in-with-magic-link (written by you!), but again, was hoping I could avoid having to do that work inside of each of our apps.
Here's an example of the jwt 'id_token' I'm trying to pass to the app:
id_token
Side note: Is 'id_token' the same as 'id_token_hint'? I couldn't find whether they're the same or different when googling it.
What you need to do is send the invite link in the email as a link to the app, eg https://myapp.com?id_token_hint=value.
Then have your apps account controller make an auth request to the policy to complete the account redemption along with an extra query parameter “id_token_hint=value”. Now a proper auth can happen with your applications openId middleware initialised.
I'm building an API to support user provisioning for other application teams around my company. I have a decent understanding of custom policies and have reviewed the invitation flow in WingTipGames, however the sample app is at times hard to follow given the sheer amount of functionality it offers. I would appreciate some clarification around what features I can ignore, vs what is required to support my use case.
Use Case:
My API's CreateUser method creates the user in B2C with ADGraph, then should generate an invitation link that includes a signed JWT with the user's email address, and finally email the link to the recipient. The new user will click the link, which should redirect them directly to the Invitation policy to reset their password.
Clarification Needed:
I'm struggling with simple generation of the Invitation link. What's shown in the sample seems overly complex for my API. In general I'm confused about the OIDC setup given that my API itself (while separately requiring callers to authenticate) will not be involved at all when the recipient clicks the invite link. And as this is an API not an MVC app, I wonder if the process can be stripped down vs the invitation flow in WingTipGames.
Why is ChallengeAsync called in the Invitation\Create method? Presumably this is why we then land in the OnRedirectToIdentityProvider event. Is the challenge what is somehow intercepted and translated into the invitation link?
Are the classes in WingTipCommon relevant here? Namely the AspnetCore.Authentication.OpenIdConnect extensions, handler, and middleware. Asking because users should never arrive at the API in response to clicking the invite link so perhaps the extra plumbing is unnecessary.
Users will always be in a new browser and session when the click the redeem the invitation link, and will redirect directly to the policy (if I understand correctly). Do I still need to worry about the skipCorrelation and XSRF handling?
Any other general suggestions around what to pull out of the sample and what to ignore to support my use case are appreciated.
Thanks
Mark
The Wingtip Games application implements the following flows for invitations:
An invitation link to an application endpoint. This invitation link contains the e-mail address of the invited user, an invitation expiration, and a HMAC-based signature. When the invitation link is opened, the application endpoint validates the HMAC-based signature and the invitation expiration and, if they are valid, then it redirects the invited user to the policy endpoint. This policy redirection contains a signed JWT with the email address of the invited user.
An invitation link to the policy endpoint. This invitation links contains a signed JWT with the email address of the invited user.
I prefer and recommend the first flow for invitations because the application endpoint can implement the invitation logic before the invitation policy is run (e.g. the application endpoint can validate the invitation expiration and, if it isn't valid, then it can display an error message) as well as after the invitation policy has run (e.g. the application endpoint can display a success message).
To answer your specific questions:
Why is ChallengeAsync called in the Invitation\Create method?
This is called for the second flow for invitations. It is called so that the authentication middleware can generate the invitation link and send the invitation message. It is implemented like this so that the application logic doesn't have to be aware of the application identifier, the policy identifier, or the redirection URIs that are required for the invitation link.
Are the classes in WingTipCommon relevant here?
They are used to support the second flow for invitations. See the next answer.
Do I still need to worry about the skipCorrelation and XSRF handling?
This is implemented for the second flow for invitations. As result of the invitation policy, Azure AD B2C issues an authentication response to the client application, where this authentication response is processed by the authentication middleware. Because the authentication middleware doesn't invoke the invitation policy (i.e. it is invoked by the invitation link), then the authentication middleware must be configured to disable the built-in checks for correlation of an authentication request with an authentication response.