Setting new security token on logout in Symfony2 - security

I'm using Symfony 2.4 with FOSUserBundle.
Admin user has ability to switch his security token to one for any other user, so he can log on any account in the system. I've did it with following code:
$newToken = new MyOwnToken($adminId, $user, null, 'main', $user->getRoles());
$this->get('security.context')->setToken($newToken);
MyOwnToken extends regular UsernamePasswordToken, holding additional adminId field (for reference to original admins account).
When admin logout of regular users account, security token is unset and he is redirected to the login page. I would like to change it a bit: instead of logging out, I'd like admin to get log back in his administrative account.
My question is: what do I need to prevent logging out if some of conditions are met (like current token is instance of MyOwnToken), and switch token to another instead ?

You should use different approach all together. It is called 'impersonating' users and described in documentation here - http://symfony.com/doc/current/cookbook/security/impersonating_user.html

Related

Keycloak: Role based client log-in access restriction for users

I am trying to achieve fairly simple usecase of role based client application (VueJS multi-page applications) control using the keycloak.
As shown in image, I have three different roles and three different clients in single realm.
The arrow in the image represents which role can access which client.
So my main objectives are,
User with role Viewer should only be able to log-in to the Viewer Application. If the same user tries to access the Operator Application or Admin application then keycloak should simply deny this user from doing so.
The same rules should follow for users with Admin and Operator role. Users of Admin role should be able to log-in to any of these application by keycloak.
To achieve this usecase I tried following ways,
First by appropriate role mapping to users and role creation in the clients. In this case, I create realm level roles and then client level roles, then assigned appropriate roles to the users created in the user section.
Enabling the Authorization. In the policies, I removed default policy that grant all users access to the client. And create a User policy and Client policy to restrict the access to client application
Also tried with Group based authorization policy. In this case, I created a group with client role and then assigned user to these groups. And enabled them from the Authorization group policy.
But, unfortunately none of this works. Meaning my user with Viewer role can log-in to my admin application. Which is just strange.
You can do this without extensions.
Copy the desired flow (e.g. the browser flow)
Create a new sub flow (e.g. for the browser forms) and call it Access By Role and select generic as type.
For the new sub flow ensure that CONDITIONAL is selected in the flow overview.
For the new sub flow add execution Condition - User Role, make it REQUIRED and configure it:
alias: admin-role-missing
role: admin (or whatever your role is)
negate: true
Add another execution: Deny Access and make it REQUIRED as well.
The final result should look similar to this:
This will deny access if the condition "admin-role-missing" is true.
You an also learn more from the docs: explicitly-deny-allow-access-in-conditional-flows
Also, don't forget to go to your client and select the flow in the authentication overrides.
The solution proposed by #Stuck is a great start, but it has a significant flaw: When the user has already authenticated, e.g. via the standard flow of another client that did not require the role, the password form flow will never be triggered. Consequently, the user will be logged in via the cookie flow without ever checking for the role.
In other words: If there are other clients (such as the account console) that do not require the role, anyone can bypass the role check.
To fix this there needs to be an additional flow layer that includes all authentication executions, that is followed by the authorization step (no matter what authentication flow was used). The final result will look like this:
I managed almost the same problem using KeyCloak extension SPI. After the deployment you will have additional configurable "execution" in authentication flows available, named "Validate User Role".
The auth flow then look's like :
This execution must be placed after the "Username Password Form" (or other form which authenticates user) or the authentication will fail.
The source code is here :
https://github.com/ValentinChirikov/kc_user_role_validate_extension
Finally handled this at the application level as it wasn't working from keycloak end.
After the login, check for the keycloak object, inspecting on the same we can find some of the useful properties set during the configuration mentioned in the question above. The overall code looks like below,
let appName = 'your_app';
keycloak.init({ onLoad: 'login-required' }).success(function () {
// Confirm the role & authentication of the user
if (keycloak.authenticated && keycloak.tokenParsed.resource_access &&
keycloak.tokenParsed.resource_access.hasOwnProperty(appName)) {
// Continue with the app execution...
} else {
// Logout user
keycloak.logout();
}
}).error(function () {
keycloak.logout();
});
This way I managed to route unauthorized user out of the application.
The solution isn't what's required in the question asked, but it works. Although I think this should be handled at the keycloak level itself.
For anyone looking to do this in Keycloak version 20, see the screenshot. This is based on answer by #heilerich but for version 20.
NOTE: Create a new flow instead of duplicating an existing flow as it will not work.

Microsoft Graph and accepted by admin required

I need to have the following workflow:
Third-party company registers new admin user for Office365 (I can't control this process).
Then pass credentials of this user to us and we configuring it (add domains, additional users etc).
I need to automate this process and this should be done by background task. So, after registration we catch this event and add message to queue and then our Azure Functions add domains, register new users etc.
But for calling Microsoft Graph admin have to accept permissions manually (go to web page and accept). Without this action token is not valid. And it breaks our automate process :(
Any way to accept it without going to web page and logging by admin for accepting?
From how you describe this process it seems like what you really need is to create an application that uses Application Only permissions that your customer grants consent too. There should be no need for them to manually create an admin user and give you credentials for this user.
And to specifically answer your question, I do not believe there is anyway to automate the user consent process via an API call. That would defeat the purpose of user consent.

Getting users from MS Graph API with a $filter causes 403 for some users

We have a single-page Javascript app that makes calls to Microsoft Graph API using delegated permissions.
One of the things it does is get a list of users via the /users endpoint.
Now when the request URL was https://graph.microsoft.com/v1.0/users?$select=id,displayName,givenName,surname,mail,userPrincipalName,
everything worked fine.
But then we changed it to include a filter.
Specifically we only want Guest users.
So we changed the request URL to https://graph.microsoft.com/v1.0/users?$filter=userType eq 'Guest'&$select=id,displayName,givenName,surname,mail,userPrincipalName,userType.
Now some of the users get a 403 Forbidden when we try to make the query.
What is puzzling is that they can get the full list of users, but are unable to get a subset of the users.
This user is themselves a Guest user, and has the Guest Inviter directory role.
This gives them the ability to read all users.
I have a Global Admin account which is able to use the second request as well (it would be pretty stunning if it could not).
The app itself has the necessary scopes since it is able to read the users, it just depends on the user and their permissions in AAD.
My theory is that the user does not have permission to access the userType property, and this causes the 403.
It is probably part of the "full profile".
Philippe confirmed this by stating you cannot access this property through the User.ReadBasic.All scope.
If we look at the Guest Inviter role's permissions: https://learn.microsoft.com/en-us/azure/active-directory/users-groups-roles/directory-assign-admin-roles#guest-inviter.
We can see that they have microsoft.aad.directory/users/basic/read, a Global admin on the other hand has microsoft.aad.directory/users/allProperties/allTasks.
My question is, what do I need to do to enable this query for the user?
I would like to avoid giving them Global Admin in this case.
The application's token has the following scopes:
Directory.AccessAsUser.All
User.Read
We used a less privileged scope before,
but we needed to add features that required higher privileges.
The scope we have is the "most privileged" scope for listing users: https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_list.
The application is also registered as a Native app, if that makes a difference.
Signing out and signing back in (to refresh the token) also does not help.
This problem occurs with a fresh sign-in with the same scopes in the token.
The only difference is the role of the user in AAD.
Request id: 6079bcb2-6f90-44cc-8a57-83a8e1676333, timestamp Thu, 15 Nov 2018 06:49:59 GMT.
Unfortunately your theory is actually correct about guest users not being able to filter on userType. I have just spoken to the engineering team behind this logic on Microsoft Graph. They are looking into a fix here so that it adheres to our Roles based access control (RBAC) for this property and not the pre RBAC logic that it is doing right now. There is no time frame currently on this, they are planning it into their sprint. I'll see if I can get an update in the next few days.
You are most likely experiencing this if your application only has delegated permissions for https://graph.microsoft.com/User.ReadBasic.All. With only User.ReadBasic.All, your app doesn't have permission to read the userType property, which also means it doesn't have permission to filter on that property.
You'll probably find any user in the tenant will have the same experience, including a member or a global admin, not just your guest user. If your app requests for User.Read.All instead, the filter should work as expected for your guest user in the Guest Inviter role.
What do you have set under Azure Active Directory -> User Settings -> External collaboration settings -> Guest user permissions are limited?
If the guest permissions are limited, guest users are unable to enumerate the directory users & groups.

Allowing users to change password-based sso password

Using AzureAD, users can log in through https://portal.office.com/myapps to their assigned apps. Some of them use the password-based sso with the option "User manages credentials".
This works fine, the user gets a question for his password and this password is used for SSO. Exept when this password changes or is mistyped the first time, then the user can't change his own saved credentials unless two factor authentication is activated for this user.
What is the best way to let the initial password prompt reappear for an user, or give another way to reset the password without activating 2FA?
To answer my own question, there are two portals, the Office 365 version at https://portal.office.com/myapps and the Azure version at https://myapps.microsoft.com. At the second portal, you can click on the three dots and select "update credentials". This can only be done by the users themselves.
Another way, is via the Azure admin portal. There you can assign permissions to an app. We do this normally based on groups. If you assign the permission individually, you can set or change the password, but also empty the fields. This way the user will be re-prompted for their password. After this, you can delete the individual permission, so it's again only group assigned. This can only be done by an admin.

What's a good way to have ordinary-user segregation but admin-user access to the same pages?

I'm using ASP.NET MVC 5 and ASP.NET Identity. I have a site with multiple users, who should only see their own data. The ordinary users access the site via URLs such as "/orders", "/orders/edit/1" etc.
I also have some "admin" users, who should be able to access all the same stuff that ordinary users see, except that they can view the data for all users. What I want to do is allow them to "impersonate" a user, and see what that user sees. So, they might access the site via URLs such as "/user-foo/orders", "/user-foo/orders/edit/1", etc.
Currently, my controllers have two variants of each action: one with a user id parameter (for admin users) and one without (for ordinary users). In the latter case, the id of the logged-in user is used. Both of these then call some shared code to render the view.
However, when rendering the view, I need to ensure that any embedded links (e.g. to an order detail page) use the correct routing form (with/without user id). That means I need to constantly check whether the user is an admin, etc. Is there a better way to do this?
I would use the claims that are available in ASP.NET Identity. Just add a claim for the impersonated user ID that will only be used if the user is an administrator. The roles are probably already in the claims. You do not need the action that passes the ID, instead add some logic that looks at the claims to see if the person is an administrator. If they are an administrator and there is a claim containing the impersonated ID then use it instead of the logged in users ID.
Here is an article that shows how to use claims with ASP.NET Identity. This shows how to set the claims during the log-in process. If you need to add a claim after the log-in process just use the SignIn method again, like this.
var AuthenticationManager = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var prinicpal = (ClaimsPrincipal)Thread.CurrentPrincipal;
prinicipal.AddClaim(new Claim(MyClaimTypes.ImpersonatedUserId, impersonatedUserId));
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = persistCookie }, principal);
I normally provide the same structure for both, but with an Authorize attribute with a role specified if i am blocking access by a certain role type. Alternatively you can check the controllers User property to find out the logged on user and perform your own logic to determine what data that user has access to view.

Resources