use msal to connect to Azure B2C - state parameter - azure-ad-b2c

I am using sample from: https://github.com/Azure-Samples/active-directory-b2c-javascript-msal-singlepageapp as a base to implement B2C signup.
How do I pass the state parameter in the example? I saw there was an issue about the state, so i guess it is possible to use state in the example. But, I can't figure out how to use it and how to retrieve it after token is returned.

I use state in my loginRedirect() method.. so I'll post my code here which should help you enough to make this work. I'm using MSAL in angular but the methods that I call should be the same.
In this example user clicks on a login button which calls a login method:
{
const args: AuthenticationParameters = {
state: "some string" //set state parameter (type: string)
};
this.msalService.loginRedirect(args);
}
This code will then redirect user to login.. and then back to your website (your redirectURI).. On this page you should implement handleRedirectCallback method which will be triggered after user is redirected. In this callback you will also get a response (or error) from login process which will include your state string.
this.msalService.handleRedirectCallback((authError, response) => {
if (response) {
const state = this.msalService.getAccountState(response.accountState);
// you don't need to use "this.msalService.getAccountState()" I think
...
do something with state. I use it to redirect back to initial page url.
...
}
});

In reviewing the source code for MSAL.js, I don't see how you can control the value of state. AuthenticationRequestParameters is not exposed and the value of state is set to a new guid when AuthenticationRequestParameters is constructed.
Example:
In the following code of MSAL.js, we have no control over the authenticationRequest variable.
loginRedirect(scopes? : Array<string> , extraQueryParameters? : string): void {
...
this.authorityInstance.ResolveEndpointsAsync()
.then(() => {
const authenticationRequest = new AuthenticationRequestParameters(this.authorityInstance, this.clientId, scopes, ResponseTypes.id_token, this._redirectUri);
...
});
...
}

You can send the state parameter on the loginRequest:
const loginRequest = {
...
scopes: "your scopes",
state: "my custom state value"
}
Then you capture it in the response on accountState, like this:
clientApplication.loginPopup(loginRequest).then(function (loginResponse) {
if (loginResponse.account) {
// will print: my custom state value
console.log(loginResponse.accountState);
....
}
});
You can find the documentation here: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-js-pass-custom-state-authentication-request

Related

NestJS: Authorization based on instances property best practice

I need authorization in NestJS based on instances property.
Ex. user can update only his own articles.
Is there another way despite defining the logic in each services? ( I know it is possible using CASL )
Not having a global guard will facility errors, and everything is authorized by default unless add logic on the service.
What about creating a function that takes the request, the model and the name of the proprety and use it wherever you want ?
const verifAuthorization = (
req: Request,
propName: string,
model: any
): void => {
const sender: User = req.user;
if (!sender) {
throw new BadRequestException("there is no user in the token");
}
if (!sender._id.equals(model[propName])) {
throw new UnauthorizedException();
}
};
Yes ! you will call it in every service you want to check the authorization in, but it will save you a lot of time and code

auth0 callback doesn't set profile date after login angular2

I have an angular 2 app which uses auth0 for authentication. The issue I'm having is that when a successful login occurs, it seems like the lock callback is not being called. Only after I do a manually refresh of the page will the profile data be sent to local storage.
When a user logs in, I need to grab that profile object and use it within the component class. This code works only after I manually refresh the page following a successful login. Here is my code (only including the important parts).
auth.service
lock = new Auth0Lock('.....9cNzyzJ3DZc2VpDyXSYF5', '.....12.auth0.com', {});
user: any;
constructor() {
// Add callback for lock `authenticated` event
this.user = JSON.parse(localStorage.getItem('profile'));
this.lock.on("authenticated", (authResult:any) => {
this.lock.getProfile(authResult.idToken, function(error: any, profile: any){
if(error){
throw new Error(error);
}
localStorage.setItem('id_token', authResult.idToken);
localStorage.setItem('profile', JSON.stringify(profile));
this.user = profile;
});
});
}
public login() {
// Call the show method to display the widget.
this.lock.show();
console.log('login func');
};
nav.component
constructor(private auth: Auth, private groupsService: GroupsService){
}
ngOnInit(){
// need to access the profile object here. Ocject is correctly logged only after refreshing.
console.log(JSON.parse(localStorage.getItem('profile')));
this.groupsService.getGroups(this.userId).subscribe(groups => {
this.groups = groups;
// sort the groups
this.groups.sort((a, b) => new Date(b.date_created).getTime() - new Date(a.date_created).getTime());
});
}
According to the documentation for ngOnInit:
ngOnInit is called right after the directive's data-bound properties have been checked for the first time, and before any of its children have been checked. It is invoked only once when the directive is instantiated.
(emphasis is mine)
Which indicates that when its run there isn't a user profile available yet because the authentication was still not processed. If you cause a page refresh after authentication, the user profile is already available in Web Storage and the manual refresh causes the ngOnInit to be executed leading to the behavior you described.

"User not found" Error when creating a child entity using ControllerB action, soon after an aspUser is created in controllerA

After the successful creation of an application user and the following line of code (in Register action in AccountController) :
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
I am trying to add a child object
var controller=DependencyResolver.Current.GetService<AnotherController>();
controller.ControllerContext = new ControllerContext(Request.RequestContext, controller);
var res = controller.Create(
new ChildEntity
{
ApplicationUserId = user.Id,
IsAcative = true
});
my create Method looks like this
public async Task<ActionResult> Create(ChildEntity entity)
{
if (ModelState.IsValid)
{
db.ChildEntity.Add(entity);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(entity);
}
My object is not created. the return valueres contains the error "user not found" propertyName : "ApplicationUserId"
Can anybody help me to understand what is going on?
ps : i have noticed that the User.Identity.GetUserId() return null !!! (may be fo some other reason, may be my problem is linked to this..)
First and foremost, the user principal is not populated until after the next page load. The sign-in process merely sets the auth cookie. That cookie needs to be sent back and the auth machinery needs to run (as part of the request pipeline), before you can get anything from User.
Second, what you're doing here is just absolutely wrong. If you want to reuse the user creation code, factor it out into another class that all your controllers can utilize. It's absolutely the wrong approach to try to new up a controller inside another action to call an action on that.

Azure Active Directory: Where can I insert a function to run after a user is signed in?

I've been following this example https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-web-dotnet/ and I'm pretty certain that in the SignIn method I can put my function there to run, but I'm not sure how you would go
In that top portion of the AccountController.cs example:
namespace WebApp_OpenIDConnect_DotNet_B2C.Controllers
{
public class AccountController : Controller
{
public void SignIn()
{
if (!Request.IsAuthenticated)
{
// To execute a policy, you simply need to trigger an OWIN challenge.
// You can indicate which policy to use by adding it to the AuthenticationProperties using the PolicyKey provided.
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties (
new Dictionary<string, string>
{
{Startup.PolicyKey, Startup.SignInPolicyId}
})
{
RedirectUri = "/",
}, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
Where do I insert toDoAfterSignedIn(); method ? or is this the wrong way of doing it, is there some listener or event emitter that will notify me when a user has successfully signed in?
https://github.com/AzureADQuickStarts/B2C-WebApp-OpenIdConnect-DotNet
The correct place to plug-in yourself is the Startup.Auth.cs file. And register yourself for one of the OpenIdConnectNotifications. The notification you need is SecurityTokenValidated. And you will need to add it similarly to the other two notifications which are already in the sample. Or just add the following line within the the OpenIdConnectnotifications initializer:
SecurityTokenValidated = (notification) => {
return Task.Delay(0);
}
The notification parameter you get contains everything you need to know about the end user and his/her successfull authentication.

Retrieve role in jhipster immediately after Auth.login

I want to send a user to a particular view after login based on a role that person has. For example, I want to send a user with ROLE_STUDENT to a student page, and a person with ROLE_TEACHER to a teacher page. Unfortunately, if I call isInRole in the controller immediately after Auth.login, that fails. Specifically, in the login function (which I moved to main.controller.js so that the login dialog appears on the main page), I have code like this:
$scope.login = function () {
Auth.login({
username: $scope.username,
password: $scope.password
}).then(function (account, $state) {
Principal.identity(true);
$scope.authenticationError = false;
$scope.account = account;
$scope.isAuthenticated = Principal.isAuthenticated;
$scope.isInRole = Principal.isInRole;
if ($scope.isInRole('ROLE_STUDENT')) {
$scope.state.go('student_dashboard');
}
}).catch(function () {
$scope.authenticationError = true;
});
};
However, the isInRole method always returns false. If I debug it, I can see in principal.service.js shows that at this point, _authenticated is false and _identity is undefined.
Now, if I comment out the isInRole conditional in the controller, so that the user always goes to the student_dashboard page, I can put isInRole code on the student_dashboard page itself, and it works great. So, it appears that something is happening between the time of the redirect, and the time the target page loads, and I want to know what that is so that I can cause it to happen and thus determine if a user has a particular role and then redirect appropriately.
I believe your problem is with
Principal.identity(true);
This actually returns a promise which does an ajax call to update the principal user, so to use the principal functions you will need to do something like this
Principal.identity(true).then(function(profile) {
$scope.authenticationError = false;
$scope.account = account;
$scope.isAuthenticated = Principal.isAuthenticated;
$scope.isInRole = Principal.isInRole;
if ($scope.isInRole('ROLE_STUDENT')) {
$scope.state.go('student_dashboard');
}
});
Otherwise the current identity is undefined.

Resources