Spring MVC 3.1 How to access HttpSession in Custom Authentication Provider (which implements AuthenticationProvider) - authentication

My application calls a web service during the Authentication process (as shown in code below).
How can I save some information in HttpSession during this process?
This information like customer-account-number will be used in various other places in the application after the user is logged in.
Is it possible to pass HttpSession parameter to the MyServiceManager's static login method?
public class MyAuthenticationManager implements AuthenticationProvider {
#Override
public boolean supports(Class<? extends Object> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
#Override
public Authentication authenticate(Authentication authentication) {
//MyServiceManager.login - makes a call to web service
if(MyServiceManager.login(authentication.getName(), authentication.getCredentials().toString(), XXX_HTTP_SESSION_XXX))
{
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority> ();
authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
authorities.add(new GrantedAuthorityImpl("ROLE_SUPERVISOR"));
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(),authorities);
}
else
{
return null;
}
}
}

After breaking a lot of head on this issue, I was able to achive the objective using following work around.
Getting hold of session is really not feasible in following method
public Authentication authenticate(Authentication authentication)
I created a class
import java.security.Principal;
public class UserInfo implements Principal{
private String customerId;
private String accountNumber;
private String name;
}
The information which I wanted to store in session (like customerId, accountNumber etc), I saved it in userInfo object.
and this object was passed to UsernamePasswordAuthenticationToken
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
authorities.add(new GrantedAuthorityImpl("ROLE_SUPERVISOR"));
return new UsernamePasswordAuthenticationToken(**userInfo**, authentication.getCredentials(),authorities);
This information is readily available in the user's session using
(UserInfo)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
I home this is a good enough way to tackle the problem.

We can do this by:
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(false);
I recommend false as it is assumed that no one without valid session should be inside this method.

Related

Spring security - new access token

I have following issue.
I have multitenant system (with shared Database and shared Schema). Access token that is generated when user logs in contains information about tenantId. Idea is to allow logged user to change tenat
For example: My user works for 3 tenants(hospitals). When he is logged in, he should be able to change hospital.
So the main issues is how to generate new acces token for user that will contain updated tenantId.
It would be preferable that user doesnt have to provide password again (since he is already logged in), and that request to auth-server that he triggers would contain his current token (that will confirm that he is currently authenticated) and newTenandId.
Here is some custom code:
#Service
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private MessageSource validationMessageSource;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
SpringSecurityUserWithAdditionalData user = (SpringSecurityUserWithAdditionalData) userDetailsService.loadUserByUsername(username);
return checkPassword(user, password);
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private Authentication checkPassword(SpringSecurityUserWithAdditionalData user, String rawPassword) throws AuthenticationException {
try {
if (passwordEncoder.matches(rawPassword, user.getPassword())) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
return token;
} else {
throw new GeneralException(validationMessageSource.getMessage("security.authentication.NotValid", new Object[] {}, LocaleContextHolder.getLocaleContext().getLocale()));
}
} catch (Exception e) {
throw new BadCredentialsException(e.getMessage());
}
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration {
#Autowired
private CustomAuthenticationProvider authenticationProvider;
// #formatter:off
#Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
...
.and()
.logout()
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.and()
.formLogin()
.loginPage("/login")
.loginPage("/changeTenant")
.permitAll().and();
return http.build();
}
// #formatter:on
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
/**
* JWT koji je generisao authorization server sadrzi granted permissions (Spring ih naziva granted authorities) u okviru "scope" claim-a.
* Umesto njega cemo koristiti custom claim koji sam nazvao GlobalConstants.JWT_CLAIM_ROLA_LIST za specifikaciju rola koje ima authenticated korisnik.
* Spring koristi default instance JwtAuthenticationConverter koja ocekuje granted authorities u okviru "scope"/"scp" claim-a.
* Da bi koristili umesto standardno "scope" claim-a koristili claim GlobalConstants.JWT_CLAIM_ROLA_LIST override-ovan je JwtAuthenticationConverter.
*/
#Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName(GlobalConstants.JWT_CLAIM_ROLA_LIST); // override authorities claim-a
converter.setAuthorityPrefix(""); // eksplicitno definisemo nazive, bez podrazumevanih prefiksa (ROLE_ SCOPE_ i slicno)
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
#Bean
InitializingBean forcePostProcessor(BeanPostProcessor meterRegistryPostProcessor, MeterRegistry registry) {
return () -> meterRegistryPostProcessor.postProcessAfterInitialization(registry, "");
}
}
If you need any additional information, please say.
I tried adding custom fields to custom login form, that will have hidden tenantId field. But i could not manage to make it work.
The authentication process should be designed to return a list of all the tenants the user has access to, typically as a list of authorities.
Separately you need a back-end call (linked to the UI) that allows the user to choose the current tenant from the list of authorities returned in authn.
The value of the current tenant must be stored in the session.
If you really want to hack this via the auth roles, you could store the real auth token and generate your own token with only the current tenant. When user changes tenant they get a new token with the new tenant (obviously after checking against the real token)

.NET 5 Web API: Storing data per request

When getting a request in any action of any controller, I look at the jwt know which user is requesting and lookup the user in the database to get some user-data that I want to use throughout the application. E.g. which departments the user belongs to or the users preferred language.
Now I could create a object which wraps these information and send it down the layers and pass it to every method that likes to use some of this data. But I like the data to be available to every method throughout the application without passing it in every method. Like e.g. dependency injection (Seems to late at that point) or something else I can get access to that data quickly.
Any advice of how to handle it?
Try it with the Items property on the HttpContext. By using it you can store data during a single request. The only downside with this approach is that every service needs to have access to the HttpContext to read the values. Values can be added to the Items Dictionary as shown below
public class IndexModel : PageModel
{
//
public void OnGet()
{
HttpContext.Items.Add("Key", new RequestInfo { Key = "RequestKey" });
}
}
class RequestInfo
{
public string Key { get; set; }
}
You can then access the value by registering the IHttpContextAccessor to the IServiceCollection and then using Constructor injection to use the HttpContext in your service so that you can work with the Items Dictionary.
public class Service
{
private IHttpContextAccessor _htp;
public Service(IHttpContextAccessor http)
{
_htp = http;
}
public void Log()
{
Console.WriteLine(((RequestInfo)_htp.HttpContext.Items["Key"]).Key);
}
}

Logging user info and update operations from REST / EJB service

I need to log all update operations from my rest resources and store to a Database log table.
The idea is to store info like:
logged user
operation description about updated / saved entity
updated fields and reference key
My application is Java EE8 compatible, it uses REST / EJB and CDI stuff.
At first I thought of dealing all this stuff on EJB side, however the exposed services don't need to have logged user on the method signature, so adding it would result on a forcing..
Is there any way to send user information, that is normally retrieved by webrequest (we use a session token model authentication) and inject through EJB ?
If your session management is setup correctly, you can just inject the session context via:
#Resource
SessionContext sessionContext;
Then:
sessionContext.getCallerPrincipal().getName()
is your logged in user.
As mentioned before, SessionContext.getCallerPrincipal().getName() doesn't work as authentication mechanism does not provide it.
After some tries I found this:
On EJB side
#RequestScoped
public class UserInfo {
private String userId;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
if (this.userId != null) throw new UncheckedException("cannot infer userid twice");
this.userId = userId;
}
}
On REST side
#Inject
UserInfo userInfo;
void userAuthenticated(...) {
String userId = ... // get userid from access token through **WebRequest** object
userInfo.setUserId(userId);
}
Side note
I honestly would have preferred to inject userid on UserInfo constructor but I was not allowed doing this as WebRequest object does not belong to EJB context
Alternative way
Move all the logging process to REST side by using a Response filter.
Example code:
#Provider
public class LoggingFilter implements ContainerResponseFilter {
#Context
HttpServletRequest webRequest;
#Context
ResourceInfo resinfo;
#Inject
LoggingService loggingService;
#Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) {
final Method resourceMethod = resinfo.getResourceMethod();
if (resourceMethod.isAnnotationPresent(Loggable.class) && containerResponseContext.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
// get all method's info and log to database ...
}
}

OAuth: ASP.NET Web API User.Identity doesn't load claims set by authentication token provider

I am using OAuth bearer authentication, configured like this in Startup.cs:
OAuthBearerAuthenticationOptions oAuthBearerOptions =
new OAuthBearerAuthenticationOptions
{
AccessTokenProvider = new AccessTokenProvider(),
AuthenticationMode = AuthenticationMode.Active
};
app.UseOAuthBearerAuthentication(oAuthBearerOptions);
... where AccessTokenProvider is implemented as:
public class AccessTokenProvider : AuthenticationTokenProvider
{
public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
// Internal logic to get data needed for building identity...
// Create claims identity
ClaimsIdentity identity = new ClaimsIdentity(identityName);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, nameIdentifier));
// Add other claims
// Set claims identity
context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties()));
}
}
If I set a breakpoint at the end of ReceiveAsync, I can verify that the identity is built correctly (has claims) and that SetTicket is reached.
But when I try to access the identity from a Web API controller:
public abstract class BaseStorageController : ApiController
{
protected IStorageService StorageService;
protected BaseStorageController(IStorageServiceFactory storageServiceFactory)
{
StorageService = storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
}
}
... the list of claims on the identity is empty!
What can be causing this?
Side note: I don't know if this is related, but I am using Castle Windsor as an IOC container to inject dependencies into my controllers (in the above case, IStorageServiceFactory). The above seemed to work (claims were not empty) before I added that. However, I'm not using CW to manage anything related to authentication. Here is my CW installer for api controllers:
public class ApiControllerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<ApiController>().LifestylePerWebRequest());
}
}
I found the answer. It was not related to dependency injection/inversion of control. I'm not sure how I thought it was working prior to adding that.
The issue is similar to what is described here (but in my case the solution is different): User (IPrincipal) not avaliable on ApiController's constructor using Web Api 2.1 and Owin
Basically IPrincipal is not accessible from the constructor of the api controller, which is why there are no claims (the user is not yet authenticated). User.Identity is only accessible from the controller's actions, not the constructor. I changed my base controller implementation to the following to get around this issue:
public abstract class BaseStorageController : ApiController
{
private readonly IStorageServiceFactory _storageServiceFactory;
private IStorageService _storageService;
protected BaseStorageController(IStorageServiceFactory storageServiceFactory)
{
_storageServiceFactory = storageServiceFactory;
}
protected IStorageService StorageService
{
get
{
if (_storageService == null)
{
_storageService = _storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
}
return _storageService;
}
}
}
Since StorageService is only accessed from controller actions, User.Identity is authenticated and has claims populated by the time that the StorageService getter gets called.
Hope this helps someone!
protected IStorageService StorageService
{
get
{
if (_storageService == null)
{
_storageService = _storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
}
return _storageService;
}
}
this is not the best approach for implementing DI
It's much better to use constructor injection.
Check Constructor Injection in C#/Unity?
if you are not familliar with Unity, follow this link, very useful:
https://msdn.microsoft.com/en-us/library/dn223671(v=pandp.30).aspx
Regards

Adding RequestFilter data to Context (Request Scope), Retrieve in Service

I implemented Basic Auth for my services. Since ServiceStack's AuthFeature is strongly coupled with the session concept, I implemented a custom RequestFilter that performs stateless basic auth (credentials go in, on every request). Our auth strategy internally contemplates roles and permissions.
Besides authentication, we need to enforce authorization (e.g., user is manipulating a product that he owns). We are using FluentValidation, for all service validations.
Authorization validations include cross checking auth data with request parameters. Question is, where should I place the auth data produced in the BasicAuthRequestFilter? Should I key value pair it in cache, associating, for instance, RequestContext (or any other object that uniquely identifies the request scope) with an Authentication object?
I could just plug the AuthData in the Request Dto, which is available directly at the RequestFilter, however this would mess up our service contract design. We define dtos in a separate DLL, where only service input/output details are defined.
Any suggestions?
Thanks in advance.
I too use my own custom authentication mechanism and make custom role information available to my service. I do this by authenticating the request in a custom ServiceRunner which can then pass the information directly to my custom Service base. This ultimately means accessing information about the users permissions is exceptionally easy.
Create a custom ServiceRunner:
public class ServiceRunner<T> : ServiceStack.ServiceHost.ServiceRunner<T>
{
public ServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext)
{
}
public override object Execute(IRequestContext requestContext, object instance, T request)
{
// Check if the instance is of type AuthenticatedBase
var authenticatedBase = instance as AuthenticatedBase;
// If the request is not using the AuthenticatedBase, then allow it to run, as normal.
if(authenticatedBase == null)
return base.Execute(requestContext, instance, request);
/*
* Authentication required. Do you authorization check here.
* i.e.
* var authorization = requestContext.GetHeader("Authorization");
* bool authorised = ... some condition;
*/
/* You have access to your service base so if you injected the Db connection
* in you app config using IoC, then you can access the Db here.
* i.e.
* authenticatedBase.Db
*/
/*
* Not authorized?
* throw new UnauthorizedException();
*/
/*
* If authorized:
* Then simple set the details about their permissions
*/
authenticatedBase.AuthData = new AuthData { Id = 123, Roles = [], Username = "" };
// Pass back the authenticated base
return base.Execute(requestContext, authenticatedBase, request);
}
}
Configure you application to use it by adding this to your AppHost:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new ServiceRunner<TRequest>(this, actionContext);
}
Create a custom class to hold your auth data i.e. the user session information, such as:
public class AuthData
{
public int Id { get; set; }
public string Username { get; set; }
public int[] Roles { get; set; }
...
}
Then create a custom service base
public class AuthenticatedBase : Service
{
public AuthData AuthData { get; set; }
}
To then use the AuthData in the service is simply a case of extending AuthenticatedBase.
public class CustomerHandler : AuthenticatedBase
{
public object Get(ListCustomers request)
{
// You can access the AuthData now in the handler
var roles = AuthData.Role; // Check they have the role required to list customers
...
}
}
You are probably wondering why go to all the trouble of using the ServiceRunner over a RequestFilter but the main advantage is it gives direct access to the instance of the Service base, which isn't available to a RequestFilter.
The RequestFilters are run before the Service base is instantiated, so you can't populate it from there. See order of operations for more information.
By having access to the ServiceBase we can populate values (in this case AuthData) and we have access to our injected dependancies such as the database connection.
I hope you find this useful. You should be able to copy most of your existing RequestFilter into the service runner. If you need any further help with this just let me know.
Update to support Attributes:
Since you are unable to avoid using the attribute method to handle your authentication needs you can still use this method:
Continue doing your authentication and access filtering the way you were before.
In your existing authentication mechanism use req.Items.Add to set the AuthData i.e. Where req is your request object
req.Items.Add("AuthData", new AuthData { Username = "", Roles = [] ... });
Then access your AuthData item in your service base:
public class AuthenticatedBase : Service
{
public AuthData AuthData
{
get { return base.Request.Items["AuthData"] as AuthData; }
}
}