I'm trying out the Quarkus JWT quickstart. But when I use the token that I normally use in my Spring application. I get the following error.
rejected due to invalid claims. Additional details: [[17] Unexpected
exception thrown from validator org.jose4j.jwt.consumer.IssValidator:
java.lang.NullPointerException at
org.jose4j.jwt.consumer.IssValidator.expectedValue(IssValidator.java:72);
org.jose4j.jwt.consumer.IssValidator.validate(IssValidator.java:59);
...omitted..., [14] No Subject (sub) claim is present
Looks like the Microprofile JWT auth spec, specifies the Subject (sub) as one of the minimum claims required in a JWT. So, is there an alternate JWT auth mechanism that can be used, that doesn't require the Subject claim? i.e any other parameter that can be passed to the quarkus.smallrye-jwt.auth-mechanism
property, other than MP_JWT?
Related
This is mostly a thought exercise that I couldn't figure out how to solve. :)
Does anyone know how to set a custom request header for all requests that use the omniauth or omniauth-oauth2 gems?
I've tried something like the following, but I don't see the header in my developer tools in Chrome, for example.
OmniAuth.config.before_request_phase do |env|
env['MY_CUSTOM_HEADER'] = 'true'
end
What am I doing wrong here? What am I missing here?
Edit based on responses:
Why I need the custom header set doesn't really matter here -- as I mentioned at the top of the question: this is just a curiosity I had. The constraint is that I'd like to test the custom header on any OS or browser, so it's not sufficient to just play with developer tools in Chrome here as not all browsers have that capability on across all OSes.
What I'm trying to do is add a custom header to all outgoing OAuth requests coming out the gem. That's it.
In browser developer tools you can only see request headers that are sent by or response headers received by the browser. Any request headers set by load balancers, reverse proxies, middleware and so on - are only visible to next stage in request handling chain.
OmniAuth lives in middleware, so technically any request in your app uses it, unless some other middleware terminates request chain and renders some response.
Also "request" in omniauth terms is not http request, it's an auth phase, usually happens on /auth/:provider (also there're "options" and "callback" phases).
It's not clear why you need setting a request header in a middleware on a request that goes into your own application - when request is going to be handled by omniauth it will not hit your controllers, except for callback phase where request.env['omniauth.auth'] is going to be set.
Since you're mentioned developer tools - probably you want response headers on request phase, to set these you need to override rack response returned from request_phase in your strategy. But for oauth2 there's only a redirect (still possible to set headers, but even less sense).
So first you need to be exact on which headers you want to be set at which request/response, there're several of those. Simplified sequence:
An OAuth request handler cannot force a browser (or any similar user-agent) to disclose more information than specified in the HTTP protocol. Be glad of that: any other posture could lead to information leakage.
The only connection that Omniauth might make itself is that exchanging a code for an access/refresh token. That is specific to the strategy in question, but strategies have the opportunity to include arbitrary headers in their internal client. If you're writing a custom strategy that required a basic authentication header during the access token exchange, it might look like this:
require 'omniauth-oauth2'
module OmniAuth
module Strategies
class DemoStrategy < OmniAuth::Strategies::OAuth2
option :name, "demo"
option :client_options, {
site: 'https://api.example.org',
authorize_url: 'https://auth.example.org/oauth2/authorize',
token_url: 'https://auth.example.org/oauth2/token'
}
uid { raw_info['id'].to_s }
info do
{ email: raw_info['email'], image: raw_info['avatar_url'] }
end
extra do
{ raw_info: raw_info }
end
def raw_info
#raw_info ||= access_token.get('user').parsed
end
def build_access_token
options.token_params.merge!(headers: {
'Authorization' => special_auth_header
})
super
end
def basic_auth_header
"Basic " + Base64.strict_encode64("#{options[:demo_id]}:#{options[:demo_secret]}")
end
end
end
end
Here, build_access_token is overriding the superclass's standard constructor for the internal HTTP client, and injecting extra headers before handing it back up the stack. Internally that's handed off to the oauth2 gem, which in turn uses Faraday, so it's likely anything Faraday accepts is a valid option.
If you need additional information carried to the authentication server, it may be encoded in the redirect URL by the strategy. For example, the omniauth-google-oauth2 strategy is configurable to carry authentication scopes and email hints in the URL that lands on Google's authentication endpoint.
It is also common to include a XSRF state parameter, in conjunction with an encrypted session cookie, to protect against identity spoofing. Depending on the co-operation of the authentication server, some or all of this data may be reflected in the redirection back to your handler.
At it simplest, that is handled by the authorize_params method in the strategy subclass e.g.
def authorize_params
super.tap do |params|
params[:something] = 'my_extra_value'
end
end
However, the volume of code involved in setting up extended parameters may be quite substantial in practice. For a worked example of doing this with Omniauth I'd suggest taking a looking at the source code of the Google strategy, and again I'll draw your attention to the authorize_params method which is the entry point for this heavy lifting.
In the overall flow of things, those are the touch points where server-side code can actually influence matters. There's a fundamental expectation that the user's client/browser is participating by executing nothing but normal HTTPS request.
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")
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.
I am attempting to access the Office 365 API from a Ruby on Rails backend and am having problems.
Whether I use the ruby_outlook gem (github) or follow Microsoft's official Ruby on Rails sample, I am getting 401 unauthorized.
My access_token is being saved using Omniauth and is valid, I checked by pasting it in here.
Am I using the correct access_token? It is over 1400 characters long (1442 to be exact). Can anyone show me an example of how to properly call the Office 365 Mail API from Ruby?
Code Example (using Faraday):
key = #auth[:key]
conn = Faraday.new(:url => 'https://outlook.office.com') do |faraday|
# Outputs to the console
faraday.response :logger
# Uses the default Net::HTTP adapter
faraday.adapter Faraday.default_adapter
end
response = conn.get do |request|
request.url '/api/v2.0/me/contacts'
request.headers['Authorization'] = "Bearer #{key}"
request.headers['Accept'] = 'application/json'
end
Code Example (using ruby_outlook gem):
client = RubyOutlook::Client.new
key = #auth[:key]
page = 1
view_size = 30
fields = [
'DisplayName',
'EmailAddresses'
]
sort = {:sort_field => 'DisplayName', :sort_order => 'ASC'}
contacts = client.get_contacts key, view_size, page, fields, sort
The exact error that the ruby_outlook gem returns is:
{"ruby_outlook_error"=>401}
The problem is a mismatch between the scopes in your token and the API endpoint you're using. The scope has to match the endpoint.
In your case, you requested a Graph API scope, but you're calling the Outlook API endpoint.
You should only have to register in one place for your client ID and secret: https://apps.dev.microsoft.com. It sounds like you may have also registered an app in the Azure Management Portal (which requires you to specify scopes in the registration itself).
Make sure you're using a client ID from apps.dev.microsoft.com and make sure your scopes are requested as 'https://outlook.office.com' scopes, and you should be good to go.
That Omniauth strategy might require that you register in the Azure Management Portal if they are dependent on Azure's v1 auth endpoints. In that case, forget what I said about apps.dev.microsoft.com and instead change your app registration to use the appropriate permissions from Microsoft Exchange Online.
UPDATE: Based on your comments, that Omniauth strategy DOES require the v1 Azure auth/token endpoints, so you have 2 options if you want to keep using that strategy:
Change your code to use the Graph endpoints. You'll need to use the Faraday option above (ruby_outlook is designed for the Outlook endpoints), and change your URL to https://graph.microsoft.com, and the request.url to /v1.0/me/contacts.
Create a new app registration at https://dev.outlook.com/appregistration, which will create the proper scopes for your code. You'll need an Office 365 account to login to the app registration tool.