how to handle/manage a lot of permissions in access-token? what's the best approach? - permissions

I'm getting a "Request header is too long" error when i was trying to access my API and send token in header. How we can manage a permissions in access-token because i have a lot of permissions in access-token it's approximately 15kb in size.
I know it's not recommended to store permissions in access-token then what's the best approach to authenticate and authorize the users on API side?
We are getting all the permissions in access token but now permissions are getting large as we have lot of modules. What is the other way to access user permissions in asp.net core API instead of keeping it in access token?
{
"roles": [
"Admin"
],
"iss": "Issuer",
"sub": "sub",
"aud": [
"https://example.com/api",
"https://example.com/userinfo"
],
"iat": 1666198659,
"exp": 1666205859,
"azp": "azp",
"scope": "openid profile email offline_access",
"org_id": "company1",
"permissions": [
"permission.1",
"permission.2",
........
"permission.150",
]
}

This could be a solution: https://fga.dev/. There's also the open source version: https://openfga.dev/
Basically, since every user has a lot of permissions, you don't store them in the token anymore; you can call this service which stores them for you.
Disclaimer: I am part of the team building this solution :)

You have two options.
You can try to shorten the permissions down using something like the approach here. There are lots of other similar questions with similar approaches. However, know that you limit your number of permissions based on what you inherit from so this may or may not work. You can stick a string into the JWT with each char working as a flag for a permission. This comes with more downsides, namely keeping the data up-to-date.
You setup a remote system for authorization. Something like Policy Server from Duende. This means no authorization data in the JWT. For instance you can make a simple http call to your identity server from your api/client and have the identity server evaluate if the user can do what they want to.
The latter seems right for your scenario because of your large amounts of permissions. It comes with overhead but there isn't really an alternative. https://github.com/Perustaja/PermissionServerDemo is an example that uses the built in ASP.NET Core authorization evaluation along with gRPC for the network calls. You can make something leaner and simpler that basically does the same thing if you don't want a lot of infrastructure.

Related

Buildfire: How can I get subscriber status from user information via the API in an efficient way?

I am using the BuildFire user database.
Using the Public API from an external service, I need to validate that the user is logged in and that they are a subscriber. I have the currentUser's access token.
This works to validate the token:
https://public.buildfire.com/1.0/auth/verify_credentials?access_token={TOKEN}
If the token is valid, the request returns a partial user object:
{
"user_id": "62d59708df0fd803aa64251b",
"created_on": "2022-07-18T17:23:20.793Z",
"is_active": true,
"failed_attempt_count": 0,
"last_access": "2022-07-19T17:08:48.270Z",
"display_name": "",
"last_name": "",
"username": "bill#microsoft.com",
"first_name": "",
"email": "bill#microsoft.com",
"last_updated": "2022-07-19T17:10:27.143Z"
}
Validate user is logged in: DONE
QUESTION:
What is the recommended way to determine if this user is a subscriber?
I think this is a little bit different than the traditional use. Generally, the app will use SSO to use your backend system User Authorization/Authentication. Your system returns back an Access Token to BuildFire. Than when you webview some content in from your server, BuildFire passes you along the AccessToken for you to re-authenticate. If this is a viable option than this is your best option.
However, if the option above isnt viable then you can use BuildFire Gateway to call from your server over to BuildFire's auth servers. You'll need an API Key that may or may not be part of your plan. Here is the documentation you need https://sdk.buildfire.com/AWSGatewayPublicAPI/

How to achieve the Dropbox equivalent of long-lived token now that they're gone (dropbox-sdk-js, Meteor, React)

For a while now I've been using dropbopx-sdk-js in a Meteor application without any trouble.
My Meteor app simply uses Dropbox to fetch images to be used in product cards. These files are synced now and then and that's it. By synced what I mean is they are scanned, shared links created or obtained, and some info is then saved in Mongo (name, extension, path, public link)
End users do not remove nor add files, nor are the files related to an end user specific account.
To achieve this, I created (in the far past) an App in the Dropbox App Console, generated a permanent token, and used that token in my Meteor app to handle all the syncing.
Now I've tried to replicate that very same thing in a new similar project, but found that the permanent tokens have been recently deprecated and are no longer an option.
Now, checking Dropbox's Authentication Types it seems to me like "App Authentication"
"This type only uses the app's own app key and secret, and doesn't
identify a specific user or team".
is what I'm after. I can safely provide app key and secret in the server exclusively, as the client will never need those. The question is how do I achieve such kind of authentication? Or for that matter, how do I achieve an equivalent of the long-lived token for my app, ultimately meaning that end users don't actually need to know Dropbox is behind the scenes in any way (and they surely don't need dropbox accounts to use this app nor should be prompted with any Dropbox authentication page)
In the js-sdk examples repo, I only found this example using app key and secret. Yet afterwards it goes through the oauth process in the browser anyways. If I don't do the oauth part, I get an error
"error": {
"name": "DropboxResponseError",
"status": 409,
"headers": {},
"error": {
"error_summary": "path/unsupported_content_type/...",
"error": {
".tag": "path",
"path": {
".tag": "unsupported_content_type"
}
}
}
}
as a result of calling
dbx.filesListFolders({ path: '', recursive: true }):
If I replace the initialization of the dbx object with a generated token everything works out. However eventually the token expires and I'm back in square one.
Any ideas what may I be missing?
The short answer is:
You need to obtain a refresh-token. You can then use this token for as long as you want. But in order to get it is necessary to go through at least one oauth flow in the browser. Then capturing the generated refresh-token in the backend. Then store it and use it to initialize the API. So it's kind of "hacky" (IMO).
For example, you can use the mentioned example code, and log/store the obtained refresh token in this line (as per Greg's accepted answer in the forum). Then use that value as a constant to immediately call the setRefreshToken method (as done in that very same line) upon initialization.
The long answer is:
ClientId + Client secret are not enough to programmatically generate a refresh token.
Going through the oauth flow at least once is mandatory to obtain a refresh token
If you want to hide such flow from your clients, you'll need to do what the short answer says.
The intended flow of usage according to Dropbox is: each user access his own files. Having several users accessing a single folder is not officially supported.
The longer answer is:
Check out the conversation we had in the dropbox forum
I suggested to replace the "Generate Access Token" button in the console for a "Generate Refresh Token" button instead. At least it made sense to me according to what we discussed. Maybe if it gets some likes... ;).

Cloudant User Logins/Management

I am fairly new to Cloudant/PouchDB. I have managed to get the PouchDB/cloudant instances to sync properly, so I am able to create databases, PUT data, and read data from both the local and Remote databases.
My issue is with authentication.
I'm not sure how to handle the authentication in my scenario. It seems like i have to create the API key/Password for basic authentication and pass that with each request. But how does my 'User' have access to that information?
In my tests the value is simply hardcoded in my javascript page - which is obviously not ideal. What id like to do is simply have a database of users with hashed credentials, and have the user submit their login username/pass against that database to allow login.
I've done some reading regarding enabling the '_users' database...but i cant seem to find anything that really lays out how that would work fully...
[EDIT]
I've manage to setup the _users database with cloudant, and add a user, and give them a membership to one of my databases.
However, this user still doesnt seem to be able to access the db.
Here is the user:
{
"_id": "org.couchdb.user:jan",
"_rev": "1-ec0b2b9c25815ab71a04df745151a85b",
"name": "jan",
"roles": [],
"type": "user",
"password_scheme": "simple",
"salt": "a4f47458b436e705378224896a3260f2",
"password_sha": "6ba718935b8ee170879973baae1b7ce953fd4258"
}
and here is the response from cloudant regardign the _securty of the db i want 'jan' to access:
My understanding is that i should be able to get a response via
jan:apple#cloudanturl/dbname
Instead i get:
{"error":"forbidden","reason":"_reader access is required for this request"}

How to handle PUT of a group resource in SCIM? What happens with members?

We have a SCIM api running on production and we are running into problems with a Group PUT.
I have scanned the official SCIM spec, but I am not able to find an answer for this-what should happen when a Group PUT is called with a payload that has members: [...]?
To be absolutely exact, if we receive a PUT with body like this:
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:Group"
],
"externalId": "8a06f2c2-aaaa-401b-b2b6-f54e5dac6f34",
"id": "7447",
"displayName": "Whatever",
"members": [
{
"value": "322257"
}
],
"meta": {
"resourceType": "Group"
}
Since this is a PUT we remove all members from that group and then add user 322257 to the group in a single DB transaction. Is this correct? PUT should replace all the SCIM attributes, so I would hope it is, but from what Azure cloud SCIM calls to our side it might seem like maybe we should not remove members here? Maybe we should keep the current members and just add the user 322257?
Your interpretation is correct - the PUT request you provided should remove all group members and add only 322257 as a member. While the SCIM RFC mandates that you need to support PUT, in practice only POST + PATCH should be used for create/update, respectively.
You mentioned:
but from what Azure cloud SCIM calls to our side it might seem like maybe we should not remove members here?
The only way that Azure AD's SCIM service will issue PUT calls is if an application in the gallery with provisioning enabled has been configured to use PUT rather than PATCH. At this point in time any new provisioning integrations built for Azure AD require PATCH support for groups, and will never see PUT used. If you have an application that is still configured to use PUT on groups, that application should be updated from Microsoft's side to update that behavior, assuming the SCIM implementation on the other side supports PATCH on groups.

Is setting Roles in JWT a best practice?

I am considering to use JWT. In the jwt.io example I am seeing the following information in the payload data:
"admin": true
Admin can be considered as a Role, hence my question. Is setting the role in the token payload a habitual/good practice? Given that roles can be dynamically modified, I'm quite interrogative.
The official JWT site explicitly mentions "authorization" (in contrast to "authentication") as a usecase for JWTs:
When should you use JSON Web Tokens?
Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.
That being said, from a security-perspective you should think twice whether you really want to include roles or permissions in the token.
(The text below can be understood as a more "in-depth" follow up to the rather short-kept accepted answer)
Once you created and signed the token you grant the permission until the token expires. But what if you granted admin permissions by accident? Until the token expires, somebody is now operating on your site with permissions that were assigned by mistake.
Some people might argue that the token is short-lived, but this is not a strong argument given the amount of harm a person can do in short time. Some other people advocate to maintain a separate blacklist database table for tokens, which solves the problem of invalidating tokens, but adds some kind of session-state tracking to the backend, because you now need to keep track of all current sessions that are out there – so you would then have to make a db-call to the blacklist every time a request arrives to make sure it is not blacklisted yet. One may argue that this defeats the purpose of "putting the roles into the JWT to avoid an extra db-call" in the first place, since you just traded the extra "roles db-call" for an extra "blacklist db-call".
So instead of adding authorization claims to the token, you could keep information about user roles and permissions in your auth-server's db over which you have full control at any time (e.g. to revoke a certain permission for a user). If a request arrives, you fetch the current roles from the auth-server (or wherever you store your permissions).
By the way, if you have a look at the list of public claims registered by the IANA, you will see that these claims evolve around authentication and are not dealing with what the user is allowed to do (authorization).
So in summary you can...
add roles to your JWT if (a) convenience is important to you and (b) you want to avoid extra database calls to fetch permissions and (c) do not care about small time windows in which a person has rights assigned he shouldn't have and (d) you do not care about the (slight) increase in the JWT's payload size resulting from adding the permissions.
add roles to your JWT and use a blacklist if (a) you want to prevent any time windows in which a person has rights assigned he shouldn't have and (b) accept that this comes at the cost of making a request to a blacklist for every incoming request and (c) you do not care about the (slight) increase in the JWT's payload size resulting from adding the permissions.
not add roles to your JWT and fetch them on demand if (a) you want to prevent any time windows in which a person has rights assigned he shouldn't have or (b) avoid the overhead of a blacklist or (c) avoid increasing the size of your JWT payload to increase slightly and (d) if you accept that this comes at the cost of sometimes/always querying the roles on incoming requests.
Nothing stops you from creating claims to store extra information in your token if they can be useful for your client.
However I would rely on JWT only for authentication (who the caller is). If you need to perform authorization (what the caller can do), look up the caller roles/permissions from your persistent storage to get the most updated value.
For short-lived tokens (for example, when propagating authentication and authorization in a microservices cluster), I find it useful to have the roles in the token.
As mentioned here, ASP.NET Core will automatically detect any roles mentioned in the JWT:
{
"iss": "http://www.jerriepelser.com",
"aud": "blog-readers",
"sub": "123456",
"exp": 1499863217,
"roles": ["Admin", "SuperUser"]
}
and 'map' them to ASP.NET Roles which are commonly used to secure certain parts of your application.
[Authorize(Roles = "Admin")]
public class SettingsController : Controller
The server which is giving out (and signing) the JWT is commonly called an authorization server and not just an authentication server, so it makes sense to include role information (or scope) in the JWT, even though they're not registered claims.