OAuth2ProtectedResourceFilter in org.springframework.security.oauth2.provider.filter:
Collection<String> resourceIds = auth.getClientAuthentication().getResourceIds();
if (resourceIds!=null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
throw new InvalidTokenException("Invalid token does not contain resource id ("+resourceId+"): " + token);
}
I think it is not useful. What does this code check for?
Based on what I've gathered, it is the id of the resource service.
It becomes more clear when you consider separating your oauth token provider servlet and your resource servers for the purpose of api versioning. For example, say Client A (cA) has access to api1 and Client B (cB) has access to api2, you enforce this access by dictating in your resource server xml for api1 that its resource-id=api1 and then configure your client details for cA that they have resourceIds="api1", and likewise for [cB,api2].
This lets us protect api access and keep its protection declaration separate from, say, our client roles declaration.
It looks like it checks whether a client is authorized to view a particular resource. Not sure how the token variable is involved, it looks like there is some more relevant code that you have not shown.
Related
I have a .NET 4.7 Web API project (not .NET CORE).
I am trying to setup authentication with an Azure AD directory, I setup an application in my AD, and I got the client id (application id)
I would like to use the Client Credentials grant type. So I went ahead and retrieved a token via the access token URL https://login.microsoftonline.com/HIDDEN/oauth2/v2.0/token I am passing in the client id, and secret, for this I am using Postman
Now in my project I've implemented the following logic in my web api project:
var clientId = "AZURE APPLICATION ID";
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AllowedAudiences = new List<string> { clientId },
TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidAudience = clientId
}
});
On my controller, I applied the [Authorize] attribute
When calling the API controller endpoint (making sure I am passing in the Authorization header with the value "Bearer MYTOKEN") I get the error returned in Postman:
"Message": "Authorization has been denied for this request."
Is there a way I can dive deeper to figure out what might be wrong?
I don't see anything in my output window in visual studio, are there some events I can hook into why it is failing?
EDIT: Adding more information per Carl:
The token seems to be valid, here are the results from jwt.ms, i even setup an "admin" role via the manifest:
Here is my code, I am not specifying the public signature (not sure how to do that yet), but I've even turned off IssueSignature validation.
This is what my controller looks like:
My fiddler request and response (does having an http endpoint instead of https for local development make a difference?) I don't believe it does:
Inspect your access token and ensure the aud claim value equals the clientId. Usually the aud claim will be something like api://clientId which is not what you have setup in your code. If that's the case set it as "api://" + clientId
You should get a 401 error, which means that the aud of your token is not your api. The cause of the error is usually that you set the wrong scope when requesting the token. I used the client credential flow Make a demo for you:
You need to create two applications in Azure ad, one representing the client application and the other representing the api application, and then use the client application to call the Web api application.
First, you need to expose the api of the application representing the web api, you can configure it according to the following process:
Azure portal>App registrations>Expose an API>Add a scope>Add a client application
Next, you need to define the manifest of api applications and grant application permissions to your client applications (this is the role permissions you define yourself, you can find it in My APIs when you add permissions)
This is the process of defining the manifest.
This is to grant permissions for the client application (You can find your expose api permissions in My APIs.):
Request access token:
Parse the token:
I'd like to know if and how it is possible to set and update Keycloak (OpenID-Connect) AccessToken or IdToken attributes (so called Claims) by a client web application, after successful authentication.
The use case is to add specific user-attributes (e.g. number of pets, hair color, favorite car, etc.) to the Access- or Id-Token, while the user is logged in to our web application based on a Vue.js Frontend and a SpringBoot Backend, mainly exposing REST Services to the Frontend.
A second web-application, also using the Keycloak Token for user authentication/authorization (Single-Sign-On feature) should be able to read the user-attributes added by the first web-application to the Token.
Even I'm afraid that adding and changing of Token payload is not allowed by architectural design of OpenId-Connect, I nevertheless hope it will be possible anyhow.
Token-attributes are implemented as 'Claims' in OpenId-Connect. And Keycloak supports 'Claim' mappings during the authentication process (set by static mappings on Keycloak server as well as by code that runs on the Keycloak server).
The appropriate methods to set and get Claim key-value pairs are mentioned by the following articles:
How to create a Script Mapper in Keycloak?:
token.getOtherClaims().put("myClaimName", "claim value");
Include user locale to the Keycloak ID token:
Map<String, Object> otherClaims = token.getOtherClaims();
if (otherClaims.containsKey("myClaimName")) {
String claimValue = String.valueOf(otherClaims.get("myClaimName"));
}
For the case changing of Token payload by Keycloak clients is not allowed by architectural design, I appreciate any suggestion on best practices to hand over dynamically added user-attributes from one webapp to another webapp, having the same Keycloak Access- and/or Id-Token in common.
Yes, changing of token payload by user application is not allowed/possible by architectural design. App doesn't own private key, which is required to create proper token signature, when you change payload.
I have a multi-tenant application (springboot keycloak adapter + spring security) secured by Keycloak. Given the multi-tenant nature of the project, I wrote a multi-client connector which works fine.
On the official Keycloak doc, it is recommended (for multi-tenant applications) to model each tenant as a new realm, but for me it works better to have multiple clients within the same same realm. This is due to following advantages:
Client scopes, groups and other configs can be shared
Users don't need to be duplicated on N different realms
SSO login works perfectly within same realm clients (by using bearer
services +CORS)
So, everything works fine except for 1 thing, my initial SSO access_token (which is then shared across all bearer-only services by means of CORS) is kind of big (it shows all the resources - tenants - and its roles within each resource/tenant).
I'd like to limit the size of the access_token, by means of using "scopes" to restrict the roles in the token to only those meaningful to the tenant where I'm logged in at that time. For this, I'm manually firing a Request to the auth server (outside of the standard functionality provided by springboot/spring security) with the goal of manually overwriting whatever access-token exists within my app, with the new one generated by my extra request.
My "new" token request looks similar to this:
SimpleKeycloakAccount currentUserAccount = (SimpleKeycloakAccount) auth.getDetails();
String authServerUrl = currentUserAccount.getKeycloakSecurityContext().getDeployment().getAuthServerBaseUrl();
String realm = currentUserAccount.getKeycloakSecurityContext().getDeployment().getRealm();
String resource = currentUserAccount.getKeycloakSecurityContext().getDeployment().getResourceName();
String refreshToken = currentUserAccount.getKeycloakSecurityContext().getRefreshToken();
String token = currentUserAccount.getKeycloakSecurityContext().getTokenString();
Http http = new Http( new Configuration(authServerUrl, realm, resource,
currentUserAccount.getKeycloakSecurityContext().getDeployment().getResourceCredentials()
, null),
(params, headers) -> {});
String url = authServerUrl + "/realms/" + realm + "/protocol/openid-connect/token";
AccessTokenResponse response = http.<AccessTokenResponse>post(url)
.authentication()
.client()
.form()
.param("grant_type", "refresh_token")
.param("refresh_token", refreshToken)
.param("client_id", resource)
.param("client_secret", "SOME_SECRET")
.param("scope", "SOME_SCOPE_TO_RESTRICT_ROLES")
.response()
.json(AccessTokenResponse.class)
.execute();
// :) - response.getToken() and response.getRefreshToken(), contain new successfully generated tokens
My question is, how can I force my-app to change/reset the standard access-token & refresh_token obtained by the usual means, with these "custom created" tokens? or is that possible at all?
Thx for any feedback!
Further Information
To clarify more, lets analyze the behavior of a typical springboot/spring security project integrated with Keycloak:
You protect your endpoints with "roles" via configurations (either on the application.properties, or on the SecurityContext)
You know that this Spring application talks in the back channel with the Keycloak authorization server, that's how you become the access_token (But all this is a black box for the developer, you only know a Principal was created, a Security Context, Credentials; etc - everything happens behind the curtains)
Considering those 2 points above, imagine that you use an Http library to basically request a new token towards the auth server token endpoint like in the code above (yes filtered by scopes and everything). So the situation now is that though you have created a valid access_token (and refresh_token); since they were created "manually" by firing a request towards the token endpoint, this new token hasn't been "incorporated" to the application because No new Principal has been created, no new security context has been generated, etc. In other words, to the springboot application this new token is non-existent.
What I'm trying to accomplish is to tell sprinboot/spring security: "Hey pal, I know you didn't generate this token yourself, but please accept it and behave as if you'd have created it".
I hope this clarifies the intent of my question.
You can revoke a token using org.springframework.security.oauth2.provider.token.ConsumerTokenServices#revokeToken method.
On the Autorization Server:
#Resource(name="tokenServices")
ConsumerTokenServices tokenServices;
#RequestMapping(method = RequestMethod.POST, value = "/tokens/revoke/{tokenId:.*}")
#ResponseBody
public String revokeToken(#PathVariable String tokenId) {
tokenServices.revokeToken(tokenId);
return tokenId;
}
Of course, you'll have to secure this method since is a very sensitive one.
In the case that each tenant is a separate client you can just use keycloak's "Scope" mapping at each client. Just turn off Full Scope Allowed and your tokens will only contain the user's roles for that specific client (tenant).
"Scope Mappings" is a a non intuitive way of saying "Define what roles should go into the access token" :-)
When turned off the UI changes and you even can configure what other roles of other clients should additionally go into the access token.
Just to give some closure to this question:
No, there doesn't seem to be any elegant or intended way to force a manual token renewal by means of using springboot/spring security keycloak connector.
The Javascript connector can do this trivially like this:
// for creating your keycloak connector
var keycloak = Keycloak({
url: 'http://localhost:8080/auth',
realm: '[YOUR_REALM]',
clientId: '[YOUR_CLIENT]'
});
// for login in (change scopes list to change access capabilities)
var options = {
scope: [EMPTY_STRING_SEPARATED_LIST_OF_SCOPES] // <-- here specify valid scopes
};
keycloak.login(options); // <-- receive a new token with correctly processed scopes
Given how easy it is to do this with the Keycloak client JS adapter, and how obscure it is to do this with the springboot/spring security adapter, it follows following:
Security design seems intended to have 2 (Keycloak security) layers; the first is a front-facing public client (usually password protected), and the 2nd layer is composed of several bearer-only services which would ussually only accept acces-tokens. If for those bearer-only services you want to implement finner grained control via scopes, you achieve that trivially by using a javascript based Keycloak client (other connectors as explained won't deal nicely with the header modification necessary to deal with OAuth2 scopes).
Suppose I make an API request to the following URL:
https://myapi.com/data
This is an API I built and have full control over.
I would like to limit access to this API to only the apps I authorize.
I see many services will provide you with an API key which you can append to your URL to give you access.
Suppose I have:
https://myapi.com/data?key=a6reallly7long2string9of0numbers2and4letters
Then on the backend I have something like:
class REST {
public $ACCESS_TOKEN = 'a6reallly7long2string9of0numbers2and4letters',
public function Auth($token){
if($token===$this->ACCESS_TOKEN) return true;
return false;
}
}
If the values match, I allow access.
But all someone would have to do is look at the request the app is making on the client side and they have the token.
Even if I encrypt the token or use one-way hashing, they'll still have the value that decrypts to the correct result.
How does one approach good authentication via URL token for an API?
I would like to limit access to this API to only the apps I authorize.
What are you looking for is "access authorization". Indeed, an access token seems to be a good way, but there are some aspects missing
** Authorization header **
By default the token should be sent as an HTTP header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) not in the url
Commonly used Authorization headers types are Basic (for basic authentication) and Bearer (for OAuth authentication and authorization)
The token should not be a hardcoded constant. It should be created/generated based on the application (and optionally user) authentication. And still better if the token is temporary
Now you can ask - how can an application keep its credentials secret? Each application can have their own server services (end user should.not access application credentials) or pure web application should be comtrolled by the CORS headers
Just search for OAuth 2.0 protocol and JWT token (jwt should be self-contained and signed).
IMHO the URL token may be an option when there is no other alternative, as URL is often cached, resent, logged,...
** API Manager **
If you have resources (server) to do so, you can deploy an API manager (there are open source, commercial or cloud options, just search for some). API manager will handle the application enrollment, authorization and enforcement.
I am in the process of implementing a solution that has an MVC client (lets call this CLIENT at localhost:4077/) with a WebAPI service (called API at localhost:4078/)
I have implemented OWIN OAuth in the API but wanted to know whether the OWIN could be implemented in a separate solution (lets call it AUTH at localhost:4079/token) to generate the token for the CLIENT, then the CLIENT passes this to the API (as the Bearer authorisation token)
The reason i am querying this is that there is likely to be additional WebAPI services that will be accessed by the CLIENT and i'd like to use OWIN between the client and all API services.
The issue is i am not sure if the token generated by the AUTH service could be used to authorise all requests on the CLIENT and all API services.
Has anyone implemented anything like this and if so could you provide an example, i am pretty new to OWIN and OAUTH so any help would be greatly appreciated
Separating the authorization server from the resource server is extremely easy: it will even work without any extra code if you use IIS and if you have configured identical machine keys on both applications/servers.
Supporting multiple resource servers is a bit harder to implement with the OWIN OAuth2 server if you need to select which endpoints an access token can gain access to. If you don't care about that, just configure all your resource servers with the same machine keys, and you'll be able to access all your APIs with the same tokens.
To have more control over the endpoints that can be used with an access token, you should take a look at AspNet.Security.OpenIdConnect.Server - a fork of the OAuth2 server that comes with OWIN/Katana - that natively supports this scenario: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server.
It's relatively easy to set up:
Add a new middleware issuing tokens in your authorization server application (in Startup.cs):
app.UseOpenIdConnectServer(new OpenIdConnectServerOptions
{
Provider = new AuthorizationProvider()
});
Add new middleware validating access tokens in your different API servers (in Startup.cs):
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
// AllowedAudiences MUST contain the absolute URL of your API.
AllowedAudiences = new[] { "http://localhost:11111/" },
// X509CertificateSecurityTokenProvider MUST be initialized with an issuer corresponding to the absolute URL of the authorization server.
IssuerSecurityTokenProviders = new[] { new X509CertificateSecurityTokenProvider("http://localhost:50000/", certificate) }
});
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
// AllowedAudiences MUST contain the absolute URL of your API.
AllowedAudiences = new[] { "http://localhost:22222/" },
// X509CertificateSecurityTokenProvider MUST be initialized with an issuer corresponding to the absolute URL of the authorization server.
IssuerSecurityTokenProviders = new[] { new X509CertificateSecurityTokenProvider("http://localhost:50000/", certificate) }
});
Finally, add a new OpenID Connect client middleware in your client app (in Startup.cs):
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
// Some essential parameters have been omitted for brevity.
// See https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/dev/samples/Mvc/Mvc.Client/Startup.cs for more information
// Authority MUST correspond to the absolute URL of the authorization server.
Authority = "http://localhost:50000/",
// Resource represents the different endpoints the
// access token should be issued for (values must be space-delimited).
// In this case, the access token will be requested for both APIs.
Resource = "http://localhost:11111/ http://localhost:22222/",
});
You can have a look at this sample for more information: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/dev/samples/Mvc/
It doesn't use multiple resource servers, but it shouldn't be hard to adapt it using the different steps I mentioned. Feel free to ping me if you need help.