How to create and persist a custom user object before endpoint execution in webflux? - kotlin

I am currently developing an OAuth 2.0 resource server (REST API) with Spring Webflux.
I have Spring Security set up and I can successfully authenticate users with Spring's built-in principal class.
The problem:
The application needs to store additional information about the user as per requirement.
Users that access the API for the first time are not stored in the REST API's internal user database. Right now I have to check if the provided principal name (I have access to the respective OAuth UUID - the one stored in the authorization server) already exists in the application database.
I would like to avoid code duplication, where I would have to call a method that does exactly that on every endpoint
Here is what already works:
#GetMapping("/secure")
fun secureEndpoint(principal: Principal): ResponseEntity<Void> {
println(principal.name)
// here I would have to check if the user has already been
// created in the database
return ResponseEntity.ok().build()
}
As I said, the principals correct UUID is already being displayed.
What I am looking for is some kind of webfilter, that runs before every (authenticated) endpoint, and automatically creates the user, so I do not have to do it in the controller layer.
This is my Security configuration:
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val cookieServerCsrfTokenRepository = CookieServerCsrfTokenRepository()
cookieServerCsrfTokenRepository.setCookieHttpOnly(false)
http.authorizeExchange()
.pathMatchers("/**").permitAll()
.pathMatchers("/secure/**").authenticated()
.and()
.oauth2ResourceServer()
.jwt()
http.csrf()
.csrfTokenRepository(cookieServerCsrfTokenRepository)
return http.build()
}
I would greatly appreciate any help!

Related

Update User Role Mapping in Keycloak using User Storage SPI

I have developed a Keycloak Provider and have registered it under User Federation. My users are stored in a MySQL Database. It is working fine, and whenever getUserById() is called, keycloak calls my implemented method, and user with proper roles are returned, and the user gets cached.
The problem occurs when I update user roles externally (in some other/external portal), now I want to tell Keycloak to invalidate the cache (may be via some REST API), so that it calls my getUserById() or getUserByUsername() method again.
What is the best possible way to do it?
Thanks!
Dealing with cache sometimes is undersirable for Authentication purposes, you can force your User Storage to load user from database every time isValid method is called.
In Example:
I put this code at top of isValid method.
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input)
{
CustomUserAdapter userDb;
if (user instanceof CachedUserModel)
{
userDb = (CustomUserAdapter)this.getUserByUsername(user.getUsername(), realm);
}
else if (user instanceof CustomUserAdapter)
{
userDb = (CustomUserAdapter)user;
}
[...]
Your custom code
[...]
}
Another way is change your User Federation Settings by setting Cache Policy to NO_CACHE.
Disable Cache Policy User Storage SPI

Custom Auth request in ServiceStack for multi-tenancy

I am already using a custom authentication provider in my ServiceStack based web services application.
I'm overriding the Authenticate method, and validating my user against one of multiple backend tenant databases. I currently determine the tenant database by matching an API key to a database string.
public override object Authenticate(
IServiceBase authService,
IAuthSession session,
Auth request) // <- custom object here, MyCustomAuth request
{
// ...
}
This works when each application is for a single tenant (a tenant/customer can build their own application and use that API key). Moving forward I want to build a multi-tenant mobile application. Thus the API key method cannot be used because I can't expect each user to type it in, hence I can't determine which tenant is using the application.
I wanted to alter the Auth object so that I could include the TenantId (provided by the user on login). However, I can't see how I can customize that object.
Is there anyway to customize that Auth object, or do I have to find an alternative solution?
You can't modify the built-in Authenticate Request DTO used, but you can use its Dictionary<string, string> Meta property to send additional metadata with the Authenticate request, e.g:
client.Post(new Authenticate {
...
Meta = new Dictionary<string,string> {
{"TenantId", tenantId},
}
}
Alternatively you can send additional info in the QueryString or HTTP Headers and access the IRequest with:
var tenantId = authService.Request.QueryString["TenantId"];

Enabling OAuth1 Support on a Jersey Jax-rs web service

I'd like to enable OAuth1 Provider support on my restful web service. Jersey supports this as described here Jersey OAuth1 Provider support.
I've been trying to register it as so:
public ApplicationConfig(){
super();
addRestResourceClasses(getMyResourceClasses());
register(new OAuth1ServerFeature(new DefaultOAuth1Provider(),"/oauth/access_token","/oauth/request_token"));
}
But, when I register the OAuth1ServerFeature, I get a 404 when trying to access my resources.
Can't seem to find any examples/tutorials implementing jersey oauth support anywhere!
Is there a simple component I can plug into my jax-rs service to enable oauth support?
I realise this thread is somewhat old - but having just got it work myself, I felt a reply was in order! Given time, I may even create a blog post with a fuller example. Be warned - this is not a short answer!
There is an absolute lack of examples on information on using the OAuth1 server (aka Provider) feature in Jersey - I can't remember a tech topic that revealed so little useful Google information. I almost passed on looking for another solution since it led me to think perhaps it didn't work. But, with some perseverance, I can say that not only is it usable, but it seems to work rather well. Plus of course, if you're already using Jersey for your REST API - you don't need any extra libs.
I am not an OAuth1 expert - and I'd strongly recommend some background reading for those attempting this. I am also assuming here you have Jersey working, understand things like ContainerRequestFilters, and also have some internal means to authorize users.
My examples also use the excellent JAX-RS OSGi connector - the only real difference is that where we use an OSGi bundle context to register the OAuth1 feature via an OSGI service, regular Jersey users will need to configure via their normal Application / Server config model.
Initialisation
You must create your OAuth1 feature - and give it a provider:
DefaultOAuth1Provider oap = new DefaultOAuth1Provider();
Feature oaFeature = new OAuth1ServerFeature(oap, "oauth1/request_token", "oauth1/access_token");
Don't forget to register oaFeature into Jersey!
The DefaultOAuth1Provider is entirely memory based - which was fine for us to start with. Many will want to persist access tokens for use across server restarts, which will require an extended subclass (or clean implementation)
Add in your Consumers Keys and Secrets
It took me a while to realise Consumers were not users but clients i.e. applications. The Jersey implementation will not work if you don't register keys and secrets for each consumer (aka client app) that wishes to connect
oap.registerConsumer("some-owner-id",
"abcdef" ,
"123456",
new MultivaluedHashMap<String,String> ());
You obviously would never hard-code these, and further would use some form of secure store for the secret (param 3).
If you do not add these you will not get any further.
OAuth protocol step 1 - get a request token
At this stage you are ready client side to get a request token - and here there is a perfectly good example on GitHub.
ConsumerCredentials consumerCredentials = new ConsumerCredentials("abcdef","123456");
//TODO - user proper client builder with real location + any ssl context
OAuth1AuthorizationFlow authFlow = OAuth1ClientSupport.builder(consumerCredentials)
.authorizationFlow(
"http://myhost:8080/myapi/oauth1/request_token",
"http://myhost:8080/myapi/oauth1/access_token",
"http://myhost:8080/myapi/oauth1/authorize")
.build();
String authorizationUri = authFlow.start();
System.out.println("Auth URI: " + authorizationUri);
Obviously you would change URLs to point to your server and - crucially - the client needs to use the same Conumer Key and Secret you registered in the server.
You will get back a response with an oauth_token string in it e.g.
http://myhost:8080/myapi/oauth/authorize?oauth_token=a1ec37598da
b47f6b9d770b1b23a5f99
OAuth protocol step 2 - authorize the user
As you will read in any article, actual user Authorization is outside of the scope of OAuth1 - at this stage you must invoke your servers auth process whatever that is.
However!!!! What is not outside the OAuth1 scope is what your server needs to do if the user authorizes successfully. You must tell your DefaultOAuth1Provider about the successful auth:
// Dummy code - make out like we're auth'd
Set<String> dummyRoles = new HashSet<> (Arrays.asList( new String[] { "my-role-1", "my-role-2" }));
DefaultOAuth1Provider.Token tok1 = getRequestToken("a1ec37598da
b47f6b9d770b1b23a5f99");
String verifier = authorizeToken(tok1, new Principal()
{
public String getName()
{
return "my-user";
}
},
dummyRoles);
System.out.println("***** verifier: " + verifier);
Note the request token string is that from step 1. Obviously a real implementation would pass a real Principal and set of roles for the authorized user.
Also, of course, printing out the verifier is not much use - you need to get that back to your client in some way, either via an independent channel or possibly as a header in the auth response - which maybe would need to be encrypted for added protection.
OAuth protocol step 3 - swap the request token for an access token
Once the client receives or has the verifier entered manually, it can finalize the process and swap the request token for an access token e.g.
String verifier = System.console().readLine("%s", "Verifier: ");
final AccessToken accessToken = authFlow.finish(verifier);
System.out.println("Access token: " + accessToken.getToken());
Again, not a realistic example - but it shows the process.
If your OAuth1Provider saves access tokens to some persistent store on the server, you can re-use any access token returned here on a future session without going through all the previous steps.
That's it - you then just need to make sure every request the client creates from this point on in the process makes use of that access token.

How to get the userIdentity of the user being logged out when using Worklight adapter based authentication

I am currently implementing an adapter based authentication for my Worklight application.
For the record, I am using Worklight version 5.0.6.1.
What I would like to do is, as it seems to be advised in the documentation, to perform some cleanup in the "logout" function of my authentication adapter.
Thus, inside the logout function being called automatically by the Worklight framework, I'd like to retrieve the userIdentity object holding the info about the user being logged out. I tried to achieve this by calling "WL.Server.getActiveUser()", but it does not seem to be possible to do this in the logout function.
I can see the following exception in the logs (WebSphere App Server 7):
[9/3/13 17:13:11:683 IST] 00000039 DataAccessSer 1 com.worklight.integration.services.impl.DataAccessServiceImpl invokeProcedureInternal Procedure 'onLogout' invocation failed. Runtime: Adapter 'onLogout' security test has no user realm.java.lang.RuntimeException: Adapter 'onLogout' security test has no user realm.
The idea behind this is that I want to call an external REST service that will perform some cleanup in a DB, and I need the mobile application userId to be passed as a parameter of this service.
Could someone please give some best practices in order to retrieve the identity of the user being logged out from inside the authentication adapter logout function?
Thanks.
User identity is destroyed by an underlying auth framework before Adapter.onLogout() is invoked. As a result when Adapter.onLogout() is called the user identity doesn't exist any more. Therefore WL.Server.getActiveUser() will return null or throw exception (in your case because it doesn't have any user realm defined, which is perfectly fine).
In case you still require data from userIdentity even AFTER underlying auth framework discards it (and this IS your case) you can save userIdentity in session state. However you need to remember that since you're manually storing it there - it is also your responsibility to wipe it once it is not required anymore.
So the adapter code would be something like:
/* global var, not inside of any function*/
var userIdentity = null;
function submitCredentials(user, pass){
if (/*validate credentials*/){
/* using previously created global var, not declaring it again */
userIdentity = {
userId:user,
displayName:user
};
WL.Server.setActiveUser("realm", userIdentity);
}
}
function onLogout(){
/* do your stuff with userIdentity object and then wipe it*/
userIdentity = null;
}
The main difference with regular adapter flow is that userIdentity object is not created in the scope of a submitCredentials() function but as a global variable, therefore it is a session scoped var.

spring-security: authorization without authentication

I'm trying to integrate Spring Security in my web application. It seems pretty easy to do as long as you integrate the whole process of authentication and authorization.
However, both authentication and authorization seem so coupled that it's being very time-consuming for me to understand how I could split these processes, and get authentication independently of authorization.
The authentication process is external to our system (based on single sign-on) and this cannot be modified. Nevertheless, once the user succeeds this process, it's loaded in the session, including roles.
What we are trying to achieve is to make use of this information for the authorization process of Spring Security, that's to say, to force it to get the roles from the user session instead of picking it up through the authentication-provider.
Is there any way to achieve this?
If your authentication is already done using an SSO service, then you should use one of spring security's pre-authentication filters. Then you can specify a UserDetails service (possibly custom) that will use the pre-authenticated user principle to populate the GrantedAuthority's
SpringSecurity includes several pre-authentication filters including J2eePreAuthenticatedProcessingFilter and RequestHeaderPreAuthenticatedProcessingFilter. If you can't find one that works for you, its also possible, and not that hard to write your own, provided you know where in the request your SSO implementation stuffs the data. (That depends on the implementation of course.)
Just implement the Filter interface and do something like this in the doFilter method:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// principal is set in here as a header or parameter. you need to find out
// what it's named to extract it
HttpServletRequest req = (HttpServletRequest) request;
if (SecurityContextHolder.getContext().getAuthentication() == null) {
// in here, get your principal, and populate the auth object with
// the right authorities
Authentication auth = doAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
Yes, it's possible. Spring Security (like most of the rest of Spring) is interface-driven so that you can plug in your own implementations selectively for different parts of the framework.
Update: Spring's authorisation and authentication mechanisms work together - the authentication mechanism will authenticate the user and insert various GrantedAuthority instances in the security context. These will then be checked by the authorisation machinery to allow/disallow certain operations.
Use nont's answer for the details on how to use pre-existing authentication. The details of how you get the details from your session (e.g. roles ) will of course depend on your specific setup. But if you put in the GrantedAuthority instances derived from the roles pre-populated in your session by your SSO system, you will be able to use them in your authorisation logic.
From the reference documentation (slightly edited, with my emphasis):
You can (and many users do) write
their own filters or MVC controllers
to provide interoperability with
authentication systems that are not
based on Spring Security. For example,
you might be using Container Managed
Authentication which makes the current
user available from a ThreadLocal or
JNDI location. Or you might work for a
company that has a legacy proprietary
authentication system, which is a
corporate "standard" over which you
have little control. In such
situations it's quite easy to get
Spring Security to work, and still
provide authorization capabilities.
All you need to do is write a filter
(or equivalent) that reads the
third-party user information from a
location, build an Spring
Security-specific Authentication
object, and put it onto the
SecurityContextHolder. It's quite easy
to do this, and it is a
fully-supported integration approach.
The server that handles the authentication should redirect the user to the application passing to it some kind of key (a token in CAS SSO). Then the application use the key to ask to the authentication server the username and roles associated. With this info create a security context that is passed to the authorization manager. This is a very simplified version of a SSO login workflow.
Take a look to CAS SSO and CAS 2 Architecture.
Tell me if you need more information.
we have had the same requirement where we had to use spring security for authorization purpose only. We were using Siteminder for authentication. You can find more details on how to use authorization part of spring security not authentication here at http://codersatwork.wordpress.com/2010/02/13/use-spring-security-for-authorization-only-not-for-authentication/
I have also added source code and test cases at http://code.google.com/p/spring-security-with-authorization-only/source/browse/
I am trying to understand CAS authentication with our own Authorization and was getting confused since the User object in Spring Security always expects the password to be filled in and we don't care about that in our scenario. After reading Surabh's post, it seems that the trick is to return a custom User object without the password filled in. I will try that out and see if it works in my case. Hopefully no other code in the chain will be expecting the Password in the User object.
I use the authorization by this:
Inject the authorization related bean into my own bean:
#Autowired
private AccessDecisionManager accessDecisionManager;
#Autowired
FilterSecurityInterceptor filterSecurityInterceptor;
Use this bean by this:
FilterInvocation fi = new FilterInvocation(rundata.getRequest(), rundata.getResponse(), new FilterChain() {
public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, ServletException {
// TODO Auto-generated method stub
}
});
FilterInvocationDefinitionSource objectDefinitionSource = filterSecurityInterceptor.getObjectDefinitionSource();
ConfigAttributeDefinition attr = objectDefinitionSource.getAttributes(fi);
Authentication authenticated = new Authentication() {
...........
public GrantedAuthority[] getAuthorities() {
GrantedAuthority[] result = new GrantedAuthority[1];
result[0] = new GrantedAuthorityImpl("ROLE_USER");
return result;
}
};
accessDecisionManager.decide(authenticated, fi, attr);
I too did spent lot of hours investigating on how to implement custom authorization without authentication.
The authentication process is external to our system (based on single sign-on).
I have done it, as mentioned below and it Works!!! (I am sure there are many other ways to it better, but this way just suits my scenario well enough)
Scenario : User is already authenticated by external system and all information needed for authorization is present in the request
1.
Security config need to be created, enabling global method security as below.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
class SpringWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
}
}
2.) Implement Spring PermissionEvaluator to authorize whether the request should be allowed or rejected
#Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
public boolean authorize(final String groups, final String role) {
boolean allowed = false;
System.out.println("Authorizing: " + groups + "...");
if (groups.contains(role)) {
allowed = true;
System.out.println(" authorized!");
}
return allowed;
};
#Override
public boolean hasPermission(final Authentication authentication, final Object groups, final Object role) {
return authorize((String) groups, (String) role);
};
#Override
public boolean hasPermission(final Authentication authentication, final Serializable targetId, final String targetType, final Object permission) {
return authorize((String) targetId, (String) permission);
};
}
3.) Add MethodSecurityConfig
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
return expressionHandler;
}
}
4.) Add #PreAuthorize in your controller as shown below. In this example, all the groups of the user are present in request header with key 'availableUserGroups'.
This is then passed on to the CustomPermissionEvaluator to verify authorization. Please note that spring automatically passes Authentication object to the method 'hasPermission'.
So in case if you want to load user and check using spring 'hasRole' method, then this can be used.
#PreAuthorize("hasPermission(#userGroups, 'ADMIN')")
#RequestMapping(value = "/getSomething")
public String getSomething(#RequestHeader(name = "availableUserGroups") final String userGroups) {
return "resource allowed to access";
}
Handling Other Scenarios :
1.) In scenario where you want to load the user before you can perform the authorization. You can use spring pre-authentication filters, and do it in a similar way.
Example link : http://www.learningthegoodstuff.com/2014/12/spring-security-pre-authentication-and.html