What is the canonical way to encode resource level permissions into a JWT access_token? Or in other words, how do you best encode access to other people's resources?
Is it something like this:
{
scopes: {
me: ['user', 'repo'], // My user
repo123: ['repo'], // Someone else's repo
org541: ['admin', 'repo'], // My org
org206: ['repo:read'] // Someone else's org
}
}
Or like this, with namespaced scope tags (in this case <resource>|<scope>:
{
scopes: ['me|user', 'me|repo', 'repo123|repo', 'org541|admin'... etc]
}
Or something else again?
This applies equally to "roles" or "memberships" or similar tags (and I realise I've mixed the examples above a bit) - the core question remains is how (best) do you distinguish these tags per resource in a single JWT access_token?
I don't know the exact use case you need to implement, but I would probably try to keep the scopes just for API operations. Such as "get a list of repositories". Then a client using the access token can list the repositories it can work with and the resource server verifies the access rights by the username or user groups.
If you wanted to limit the resources available to the client, you could have a scope that would grant access to just a subset (for example just the user's own repositories).
Having resources and their permissions encoded in scopes would make them hard to use (when composing an authentication request, the client would have know resource identifiers) and the permissions may change over the life of the access token.
Related
what I am trying to do:
I have an app that takes in login credentials: username and password for a user. I have a rest api that internally calls the keycloak REST API: /auth/realms/realmname/protocol/openid-connect/token
and gets the access token for this user.
Now I am building another REST API to access a resource where I want to do the following:
doSomething(accesstoken, data)
{
a) call keycloak API to validate access token and get roles.
b) if role == manager, process(data)
c) else: return error msg.
}
Now, how do I do (a): validating the access token and getting the roles associated with it.
I know we can do: auth/realms/realmname/protocol/openid-connect/userinfo
but that only gives the details about the user like name, email, etc. but does not display any roles.
Here's an example I got:
{
"name": "test user",
"sub": "e2bad34d-a1a9-4d70-ac84-bd3a3246023e",
"email_verified": false,
"preferred_username": "user",
"given_name": "test",
"family_name": "user"
}
As seen, it doesnt give the roles at all. How do I then tell what roles this access token has? Interestingly, when I search for this, many resources are suggesting the above userinfo endpoint. But this merely tells me taht the access token I provided is valid. Does not give roles for that.
In other words - it authenticates but does not authorize.
Please suggest.
Thanks,
Anand
In Keycloak admin Console, you can configure Mappers under your client. Add a builtin Mapper of type "User Realm Role", then open its configuration e.g. change Token Claim Name if you want.
Client roles can be configured similarly, but they are returned by default in the token under the name resource_access.${client_id}.roles
The the client side you can parse the token to find the roles. E.g. In an angular application and using the keycloak-angular adapter, you can have a the token as a json object by calling keycloak.getKeycloakInstance().tokenParsed.
In a spring boot application and using the Keycloak java api, you can find the roles under the field "otherClaim" in the following class
https://www.keycloak.org/docs-api/10.0/javadocs/org/keycloak/representations/AccessTokenResponse.html
In both representations you will find the roles under the "Token Claim Name" defined in the client mapper configuration
Additionally, if the full scope is not allowed then you need to add the relevant roles to the scope, so they can appear in the token.
After adding role in the roles section , need to move available roles into the Assigned Roles of the scope tab of the respective client section.
I am using App ID as an Identity Provider and Authorization Server to protect some back-end spring-boot applications.
I have managed to set up the whole OAuth 2.0 Authorization Code flow to work but cannot manage to include custom scopes into the access token.
The only scopes that appear in the access token are the App ID default ones:
"openid appid_default appid_readuserattr appid_readprofile appid_writeuserattr appid_authenticated"
I have configured an appropriate role with the desired custom scopes and associated this role to the user profile. Furthermore I have associated these custom scopes to the client application. Everything seems fine in the App ID dashboard.
However when I call the token endpoint either programmatically or via curl I always get the same default scopes in the access token.
Reading the Swagger , I should be able to specify the scopes for the password flow and bearer token but I am in an OAuth 2.0 Authorization Code flow. Furthermore, even with password credentials flow, I do not manage to get these custom scopes although I specify them in the request.
Has anyone encountered these problems?
Any help would be much appreciated.
Many Thanks,
Chris
In order to see the application configured scopes in the token, you need to authenticate with the application that you configured scopes to and with the user you assigned the role to.
Meaning you should use username : client ID and password : secret of the application in the request authorization header, and authenticate with the user you assigned the matching role (which contains the scopes wanted).
The steps to add access control to your application:
Go to Applications and define the application that you want to protect by adding scopes.
Create your roles by going to Roles and profiles > Roles > Create role.
Assign the roles to specific users by going to Roles and profiles >
User profiles. Then, choose the user that you want to assign the
role to and click the More options menu > Assign role.
For more information see AppID Access control docs: https://cloud.ibm.com/docs/services/appid?topic=appid-access-control
I have an App ID instance in us-south, and scopes are working fine for me with default Cloud Directory.
create a new application (define your scopes)
create a role and associate your application scope
assign the role to a user
call /token endpoint
It happened to me before, I found that one way to solve it would be to inject the roles into the token claim and then instruct Spring Security to extract them. I wrote about it here in detail. The documentation explains the first part, but the gist is this cURL snippet :
curl -X PUT "https://$REGION.appid.cloud.ibm.com/management/v4/$TENANT_ID/config/tokens" -H 'Content-Type: application/json' -H "Authorization: Bearer $IAM_TOKEN" -d '{
"access": {
"expires_in": 3600
},
"refresh": {
"enabled": true,
"expires_in": 2592001
},
"anonymousAccess": {
"enabled": false
},
"accessTokenClaims": [
{
"source": "roles"
}
],
"idTokenClaims": [
{
"source": "saml",
"sourceClaim": "attributes.uid"
}
]
}'
You can also do it in the Swagger UI. Note however that this is a PUT request, so it's going to overwrite any configuration you had beforehand. Ideally, run a GET request to get the current configuration, then add the claims into it to avoid issues.
Then, in the SecurityConfiguration, add this JWT converter :
protected void configure(HttpSecurity http) throws Exception {
http
//...
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter());
}
Converter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("authorities");
converter.setAuthorityPrefix(""); //so that the role has the same name as the one that comes from App ID
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtAuthenticationConverter;
}
Now that Spring Security recognizes the roles, you can protect endpoints with annotations or with an antMatcher configuration :
.antMatchers("/api/admin").hasRole("ADMIN")
I am looking at these blueprints: https://github.com/awslabs/aws-apigateway-lambda-authorizer-blueprints/blob/master/blueprints/nodejs/index.js
What is the principalId for and how is it generated?
I see the code like this:
// this could be accomplished in a number of ways:
// 1. Call out to OAuth provider
// 2. Decode a JWT token inline
// 3. Lookup in a self-managed DB
var principalId = 'user|a1b2c3d4'
The questions I have are:
Is it unique per user? I have a dynamodb where the users table has a unique user Id.
How do I use it in lambda as an environment variable in the AWS lambda console?
What is the main purpose of it in the autnorizer?
Thanks for reading and responding.
Yes, please see: https://forums.aws.amazon.com/thread.jspa?messageID=703105
Based on the answers to the other 2 questions you probably don't want this to be an environment variable, unless I'm misinterpreting what you're asking.
See above forum post
The principalId is intended to represent the long term identifier for whatever entity is being authorized to make the API call.
The information system I sometimes use has 2 access codes that are password-masked.
Is this just a security through obscurity measure (being able to punch in username and password in front of audience) or does it have any other advantage over the conventional user/pass or token/secred?
I'm considering this when building my own IS for my business partners and myself. Is this any good or just annoying and useless unmemorable stuff for the user?
If it is a good idea, how to implement that with user.authenticate()?
I would not implement such a system, because..
The username/ID (first "access code") need not be a secret; while it should not expose confidential information (defined by policy), the purpose of this key is not to "add security" and making it hard to remember would annoy people - at least, it would annoy me.
If a user has to write down a "secret" because it is too hard to remember .. then anyone with access to the recording (e.g. text file, Post-It note) has access to the might-not-be-a-secret secret.
The way to increase security with passwords (second "access code") is to encourage passphrases, which are can be easier than "P#ssw0rds!" to remember (and are much easier than random passwords to remember!), but much harder to brute-force. It is the password/pass-phrase which is the secret token.
Assuming the use of proper connection encryption and using sound bcrypt/scrypt password hashing (and not suffering from an attack vector such as Heartbleed or a local keysniffer), then the next consideration is to mitigate brute-force attacks.
I would focus on using a solid (exiting and proven) authentication implementation, and secure server management and key policy.
That being said, here are additional thoughts ..
It might be useful/relevant to make the username/ID (first "access code") field masked, like a password field. This can prevent cases where the password/pass-phrase is accidentally exposed when entered into a username/ID field, as when such authentication is done in front of a live audience. (I've seen this mistake done several times.)
However the goal is not to add security, excepting that it can mitigate accidents, as the username/ID is not a password: it is not "encrypted", hashed, or otherwise considered a secret.
Using an additional credential provider (e.g. RSA fob, or smart card/fingerprint/pub-private keys) can be used in such cases where increased security is required. Appropriate use of such is much more secure than "two passwords".
In terms of security, access codes are probably slightly more secure than user & pass, considering they're encrypted correctly. This is my opinion
For Rails, you will have to remember 3 important factors:
Who's using your system
How will they engage with the authentication area
Are you using with any other system (such as Devise)?
--
Public Keys
If you're looking to create a preview mode of sorts, I would create a series of API keys, which you'll be able to use to gain limited functionality to the app.
We do that like this:
#users table
id | public_key | other | information | created_at | updated_at
#app/models/concerns/token.rb
module Token
extend ActiveSupport::Concern
included do
before_create :generate_token
end
protected
def generate_token
self.public_key = loop do
random_token = SecureRandom.urlsafe_base64(10, false)
break random_token unless self.class.exists?(public_key: random_token)
end
end
end
#app/models/user.rb
include Token
I found this code somewhere (I forgot where unfortunately), but it basically uses the before_create callback to populate the public_key attribute of your User model.
The public key is created using the SecureRandom method
I'm working on an API wrapper for Viddler, which will eventually be made public, and I'm trying to figure out the best way to deal with authentication/API keys, specifically with usage within Rails applications in mind.
The easiest way to write the wrapper would be to just have the code create a new client each time, and the developer could store the API key in a constant for future use:
#client = Viddler::Client.new(VIDDLER_API_KEY)
The problem with this is, it's kind of clunky to have to keep creating client objects and passing in the API key. This gets even more complicated when you throw user authentication into the mix.
I'm thinking some sort of solution where I all the the API key to be set in the environment file and then the authentication would be done in a before_filter.
Viddler::Client.api_key = 'abc123'
Viddler::Client.authenticate! 'username', 'password'
Viddler::Client would then store this in a class variable, and you could call Viddler::Client.new without any parameters and make authenticated calls. One thing I'd be concerned about is that this means the developer would have to be sure to clear out the authentication before or after each request, since the class variables would persist between requests.
Any thoughts?
Storing the API key globally would for sure be pretty useful and certainly is the way to go for that kind of information. User authentication on the other hand I think shouldn't be stored globally, never ever, especially for a high level API, because telling your users to "ensure to add an after_filter :reset_viddler_auth" might lead to some unexpected security risks.
# in a config/initializer/*.rb file or something
Viddler::Client.api_key = "abc123"
# in the controller/action/model/wherever
#client = Viddler::Client.new # anonymous
#client.authenticate!("username", "password") # authenticate anon client
#client_auth = Viddler::Client.new("username", "password") # authenticated client
Guess like that you've best of both worlds :) Maybe even provide a way to create a new client with another API key like,
#client_other = Viddler::Client.new("username", "password", :api_key => "xyz890")
So... just my 2 cents.
PS: not sure how up-to-date it is, but there's already a ruby viddler wrapper, just FYI, http://viddler.rubyforge.org/rdoc/