JWT With Role Based Access Control - security

Say I want to use JWT for authentication, and in the case of users who are using the API of my application directly I would like to issue a token that does not expire (but does contain an ID so that it can be revoked). Secondly, say I have a role based access system, where I would also like to encode the user's role into the token. However, how do I solve the problem that the user's role may change but that the token would still encode it? Obviously if someone's role changed to some thing with less privileges this would be a security issue? The issue isn't just restricted to this use case either, theoretically active tokens would have the same problem of role changes not taking immediate affect.
My initial solution is too not encode role/permission levels into the primary token, and instead use a secondary token that would only be added to request to my system upon passing through the application's boundary from the greater internet, but I'm also wondering how other people solve this problem?

If you need to invalidate tokens, you'll have to keep track of the tokens you issued and make sure you can "remove" them at some point in time.
My suggestion would be to use one token, and track somewhere a relation between USER and TOKEN_VALID_IF_ISSUED_AFTER.
At that point, when a user logs out, when their permissions change, when they change password... ...you can insert a record in this table with $USER_ID and NOW().
Next time a token goes through your API you validate that it was issued after the TOKEN_VALID_IF_ISSUED_AFTER through the iat claim -- if not, the user will have to get a brand new token.

Related

Suggestion required on how to implement a better permission system

I have a user permission system in place where i have a set of permissions within the database, for example
id
Permission
1
POST:CreateBooking
2
GET:AllBookings
And i have another table (junction table) where i put dependent permissions such as
if i want to create a Booking, i need to fetch Package details and so POST:CreateBooking requires the user to also have GET:AllPackages permission.
There's a template system in place as well, where the users can group multiple permissions together and once that template is assigned to any employee, that employee will get THAT set of permissions and it's dependent permissions.
What my nodejs system does is that when user logs in, it fetches all permissions from DB and puts it in a redis set from where on each request, the permission is checked against user id.
Is there any tool from where i can do exactly this but in an intuitive and better way?
I tried keycloak but i don't know how to cover my needs mentioned above.
Thank you
if I'm understanding correctly and trying to generify your scenario, you have a classical situation where:
You have groups which can have multiple permissions assigned;
groups can be created dinamically;
each permission correspond to a specific functionality.
So, implementing the OIDC (Open Id Connect) protocol might fit you needs. As you suggested youself you might be interested in a OpenID provider (do not reinvent the wheel) keycloak is good, you can give a look also to Vault Hashicorp.
So assuming that your backend have an already existing framework to handle security and permissions (eg. Spring Security) you can produce JWT token with the OpenId provider and check throught PreAuthorize claims (permissions) inside the token.
At the end your security layer it's just an annotation you need to insert before your method controller or before you class controller.
Behind the scenes, instead, this is what will happen:
Your user connect to your app;
User insert username and password -> the Open Id provider gives you a JWT
Your Front End app everytime it make a REST req will send also the JWT
The back end controller method called it's under authorization
Given the public keys of the OpenId provider, the validity of the token it's enstablished
If the specific permission claim it's found inside the token, the request can be elaborated else a 403 Forbidden it's returned.
OIDC - as the conceptual model/backdrop to any tool of choice, is certainly a popular/good choice, but as long as you're willing to deal with an element of complexity - the understanding required to implement an OIDC arrangement (- think of it as a possible investment - effort up front with hopefully the rewards tricking-in over time); e.g. OIDC is an ideal choice for supporting SSO (Single Sign On), especially when it comes to supporting/allowing for authentication/login via providers such as Facebook, LinkedIn & Google, etc (- as well as more Corporate OPs (OIDC Providers) including AAD/Azure AD).
Try to first step-back, and consider the possible bigger/future picture, before selecting a tool based upon only your starting/current requirements.

How to use two different authenticaion methods in one document for two different recipients?

I know that we could use Auth Code Grant, implicit grant or JWT grant. But I have the following scenario:
I have my own app. All users are usually logging in to my app, so I use JWT grant. But sometimes the contract should be assigned from a future employee, who does not have any account yet. Therefore I want to use Authenticaion Code Grant (send him a link via email and he authenticate and assign on DocuSign).
Is is possible to use two different authentication methods for two different recipients in one document/evelope?
I tested each one separelty and works. But both of them in one app?
Yes it is technically possible, but I'm not certain that's the best way to go about this. If you want to generate an OAuth token for a specific user they're required to have their own DocuSign account as they need to provide documented consent for whatever scopes you're requesting.
But to answer your question, an OAuth token gives access to any account that the authenticated user has access to. Once the tokens themselves are generated there isn't any rule or blocker that would prevent you from creating a token for a secondary user and then alternating between the two, as long as the token is used to auth against an account that it has access to. It becomes a little more difficult to keep track of the multiple tokens and corresponding userId -- but you can absolutely do that.
If you want to have one of these envelopes signed by a user that doesn't have an account yet -- I would agree with Inbar that embedded signing would be the correct way to go. Embedded signers aren't required to have their own accounts -- however in order to generate a recipientViewToken you need to authenticate as the sending user. On the customer side what they would see is a one-time-use URL that has a default lifetime of 6 minutes. This URL is typically loaded into a browser tab or some sort of modal window which then takes them directly into the signing session.
If you don't want to use embedded signing and would rather use the email link, you can still do that. Unless you're using a CFR 21 part 11 account users can still sign documents without an account -- they'll just be prompted to sign up for one afterwards so there's somewhere to house the envelope. I think based on this it would be better to use the embedded model, but we can support you on either path.

Is it safe to store the oauth2 token in the UserProperties?

I am using the the oauth2 library to impersionate a service account with a user in order to access the google api in the context of that user similar to this example:
function getService() {
return OAuth2.createService('GoogleDrive:' + USER_EMAIL)
// Set the endpoint URL.
.setTokenUrl('https://oauth2.googleapis.com/token')
// Set the private key and issuer.
.setPrivateKey(PRIVATE_KEY)
.setIssuer(CLIENT_EMAIL)
// Set the name of the user to impersonate. This will only work for
// Google Apps for Work/EDU accounts whose admin has setup domain-wide
// delegation:
// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
.setSubject(USER_EMAIL)
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Set the scope. This must match one of the scopes configured during the
// setup of domain-wide delegation.
.setScope('https://www.googleapis.com/auth/drive');
}
As you can see, I am storing the bearer token in the Userproperties and I am wondering if this has some security implications.
Can the user access this token somewhere (afaik there is no UI in the Gsuite for that?)
What can the user actually do with this token (I think it will expire in 1 hour right?)
From the discussions on the comments, I would like to propose the following answer as the current answer.
Q1
Can the user access this token somewhere (afaik there is no UI in the Gsuite for that?)
A: When PropertiesService.getUserProperties() is used, the user can retrieve the saved values on only the same GAS project. And, it seems that the values cannot be retrieved with the Google APIs and UI. Ref
Q2
What can the user actually do with this token (I think it will expire in 1 hour right?)
A: At Google, the default value of expires_in of the access token is 3600 seconds. About this, you can check this using the following curl command.
curl "https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=###"
Q3
if the user somehow can retrieve this token somehow and use this for getting access to services he should not have access to.
A: For the standalone GAS project, when the users have no permission for writing the script, the users cannot directly see the access token (In this case, users cannot use the log.), while the script can use the user's access token.
Note:
Above situation is for July 10, 2020. Google Apps Script and Google APIs are growing now. So these specification might be changed in the future update. Please be careful this. When I could confirm the specification was changed, I would like to update this answer.

Is there a way to get a second user to authorize an action without the logged in user seeing their details in the request header?

A strange situation that I am unable to find other people having to deal with. We are using Microsoft AspNetCore.Identity to handle our authentication. Everything is working fine.
The problem is that for a user to perform certain actions, they MUST get another user to 'sign' that action. This act of signing of course requires that other user to use their user name and password to sign the action.
The issue with this is that the other user's details are readily visible in the request payload. So if I am sneaky, I can open the developer tools in my browser (and hide it), then ask my admin to come and sign my action, and when they have gone i can go to the network tab and see their username and login in plain text!
Of course this is all over https but still, we can't allow one user to see another's sensitive information.
How are we to manage to allow a second userB to 'sign' an action for userA while in user A's active session, while removing the capacity for userA to steal userB's credentials??? Any ideas? (Front end is angular.js)
I imagine it's a big rework, but instead of having the "admin" sign the request on the user's machine, the admin could receive a "user A requires this action to be signed, proceed? [ok] [cancel]" on their account, the action would be stored in the database (perhaps temporarily?) & then all of the sensitive information is kept within each user's session with no cross over.
Then the authentication of who is permitted to approve actions can be handled in the backend via standard identity methods.
The user's "Please wait while an admin signs this action" modal (assumption) could then poll an API to determine the status of the action and then proceed once accepted.
I second #justcompile's answer if you need an authorised and authenticated user to sign/confirm the action, more work but the only secure way.
If you just need a second pair of eyes to confirm you could message a private group or slack channel that only "authorised" people have access to with a one-time URL containing a token (that maybe expires after a period of time too).
Assuming admins only access that channel they can follow the link, the app can validate the token and confirm the action.
Saves a second (admin) user logging in on their own machine and the need to build a workflow and UI etc, but again exposes you to risk if nefarious types get access to the channel or the links sent to it.
Depends on your appetite for risk I guess.
another user performs signing action on your local system? and you are sly?
there is no way to protect their password.
use two factor authentication.
The way this would normally be handled is for the user to request an action. This (unsigned) action-request is recorded in the database. The admin user is able to see this unsigned request in their account, and make an (authenticated) request to sign it. The user would be able to see the status of their request, and whether it has been signed yet.

Azure ad group membership claims

I've set the groupMembershipClaims property in an app's manifest in Azure AD to "All", which should result in a user's security group memberships to be returned in the id token.
However, they are not being returned. Have tried to re-login multiple times. Is there something I am doing obviously wrong?
Can you be more specific in terms of what exactly you are trying to achieve and how'd you want to do it.
Apparently, if the thing mentioned in your question is what exactly you are looking for and since the groupMembershipsClaims property is set to "All", you'll get the group claims in the JWT token.
You may want to read this article https://www.simple-talk.com/cloud/security-and-compliance/azure-active-directory-part-4-group-claims/ . This should help you resolve your issues.
Let me know in case you face this issue after you follow the procedure mentioned by the author.
Sorry for wasting people's time here. I was asking this question for a friend, and turns out they were looking at the access token, not the id token.
So as future reference, make sure you are requesting an id token from AAD, and use that to figure out things like group memberships.
Maybe another note for future reference.
As JWT tokens are used in Authorization headers, you hit a limit of maximum 6 groups in the token.
If the user has more groups they will not be returned and you will have to implement the Azure AD graph API to fetch the groups of the user.

Resources