Is there any way to override #AuthenticationPrincipal in spring boot app? - spring-security

I need to implement communication among micro services (requests that are already authenticated). I am going to pass userId in request headers. These userId I want to be accessible by #AuthenticationPrincipal annotation, like that:
public ResponseEntity somemethod(#AuthenticationPrincipal Long userId)
{
....
}
Is there any way to do it?

The #AuthenticationPrincipal annotation is really a shorthand for:
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
You can set the same with a custom filter with code like:
Authentication authentication = new PreAuthenticatedAuthenticationToken(userId, null);
SecurityContextHolder.getContext().setAuthentication(authentication);

Related

How to get the id_token outside a controller in Asp.net Core MVC?

How can I get the id_token returned by OpenIdConnect provider outside of a controller.
Inside the controller, this gives me the token:
string idToken = await HttpContext.GetTokenAsync("id_token");
I need to get the token inside a service in a similar fashion. How can I do this?
P.S: I have already tried to use HttpContextAccessor but it does not return anything.
Using Session to store data/value, which can be used in different place within your project.
//store vaule
Session["idToken"] = await HttpContext.GetTokenAsync("id_token");
//Call session
var x = Session["idToken"];
//empty or clean Session value
Session["idToken"] = null;
It seems GetTokenAsync() is an extension method to the httpContext. If this is true, then you could add the HttpContextAccessor in your startup / top level program.cs (depending if you are on .net 5 or 6)
builder.Services.AddHttpContextAccessor();
//or
services.AddHttpContext();
Then in your class, you can go the well known DI route:
public class MyService : IMyService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyService(IHttpContextAccessor httpContextAccessor) =>
_httpContextAccessor = httpContextAccessor;
public async Task DoSomething() {
var idtoken = await _httpContextAccessor.HttpContext.GetTokenAsync("id_token");
}
}
Documentation: https://learn.microsoft.com/de-de/aspnet/core/fundamentals/http-context?view=aspnetcore-6.0
As a variation on Marco's answer, you usually work with tokens in a filter / middleware, then inject claims into your service classes:
A website would use the ID token claims as the Claims Principal
A web API would use the access token claims as the Claims Principal
You can make the ClaimsPrincipal injectable at startup, and note that this is a per-request dependency:
this.services.AddScoped(ctx => ctx.GetService<IHttpContextAccessor>().HttpContext.User);
This enables you to inject the ClaimsPrincipal class into a Service class as in this API code of mine, so that it doesn't need to know about HTTP, just the claims. In my example the service class is also request scoped, and applies some claims based authorization rules.

Spring Security 4 - Get Authorization mappings

Assuming I have a Spring Application whose requests are being authorized with http.authorizeRequests() and #PreAuthorize
Is there a way to get these mappings programmatically?
Is there an api within Spring where I can retrieve which urls are authorized for the currently authenticated user?
I dont know if there are better ways similar to we pass request header like #RequestHeader("Content-Type")... i would love to see that ...
#PreAuthorize("authorizeAccess(#user,#contentType,)")
public Idea somemethod(#RequestBody User user, #RequestHeader("Content-Type") String contentType )
{..}
But below is definitely an option.
Assuming you know how to write overrride or write your own methods to use in spring security (customised class extending SecurityExpressionRoot). If so if you have a method i.e..authorizeAccess
public boolean authorizeAccess(String mapping)
{
// use mapping to authorize
.. do something
}
Then, we can have all urls in a constant file..
package org.somepackage;
public interface URLMappings {
public static String ADMIN = "/admin";
}
Then pass these urls as constants ..
#RequestMapping(value = URLMappings.ADMIN, method = RequestMethod.POST)
#PreAuthorize("authorizeAccess(T(org.somepackage.URLMappings).ADMIN)")

Spring Security Rest

I'm having a set of Sping Data Repositories which are all exposed over Rest by using Spring-data-rest project. Now I want to secure the HTTP, so that only registered users can access the http://localhost:8080/rest/ So for this purpose I add #Secured(value = { "ROLE_ADMIN" }) to all the repositories and I also enable the security by specifying the
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
So now what happens is I go to the rest and it's all good - i'm asked to authenticate. Next thing I do is I go to my website (which uses all the repositories to access the database) but my request fails with
nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
which is correct because i'm browsing my website as anonymous user.
So my question is: is there a way to provide method authentication for the REST layer only? To me it sounds like a new annotation is needed (something like #EnableRestGlobalMethodSecurity or #EnableRestSecurity)
I don't know if this will solve your problem, however I managed to get something similar, working for me by creating an event handler for my specific repository, and then used the #PreAuthorize annotation to check for permissions, say on beforeCreate. For example:
#RepositoryEventHandler(Account.class)
public class AccountEventHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
#PreAuthorize("isAuthenticated() and (hasRole('ROLE_USER'))")
#HandleBeforeCreate
public void beforeAccountCreate(Account account) {
logger.debug(String.format("In before create for account '%s'", account.getName()));
}
#PreAuthorize("isAuthenticated() and (hasRole('ROLE_ADMIN'))")
#HandleBeforeSave
public void beforeAccountUpdate(Account account) {
logger.debug(String.format("In before update for account '%s'", account.getName()));
//Don't need to add anything to this method, the #PreAuthorize does the job.
}
}

Integrating SignalR with existing Authorization

I've been working on a way of integrating SignalR Authorization Attributes with a custom authorization provider (called MVCAuthorization) I went down a few rabbit holes of trying to recreate an Authorization provider for hubs specifically, but that turned out to be far too complicated. So I was wondering, how I can integrate my existing Controller and Action Authorization with my SignalR Hubs and methods?
I figured out that you can retrieve an IAuthorization provider.
If you treat you hub as a controller, and your methods as your actions, all you have to do is create a SignalR Attribute that implements IAuthorizeHubConnection and IAuthorizeHubMethodInvocation
public class HubAuthorizeAttribute : Attribute, IAuthorizeHubConnection,IAuthorizeHubMethodInvocation
{
public virtual bool AuthorizeHubConnection(HubDescriptor hubDescriptor, Microsoft.AspNet.SignalR.IRequest request)
{
IAuthorizationProvider authorizationProvider = DependencyResolver.Current.GetService<IAuthorizationProvider>();
return authorizationProvider.IsAuthorizedController(hubDescriptor.Name);
}
public virtual bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext)
{
IAuthorizationProvider authorizationProvider = DependencyResolver.Current.GetService<IAuthorizationProvider>();
return authorizationProvider.IsAuthorizedAction(hubIncomingInvokerContext.MethodDescriptor.Hub.Name, hubIncomingInvokerContext.MethodDescriptor.Name);
}
}
Then all you have to do is put the attribute on your hub or any methods you want authorized
[HubAuthorize]
public class Message : Hub
{
public void Send(string message)
{
}
}
You should override the existing methods in the pipeline
Check authorize in SignalR attribute
http://www.asp.net/signalr/overview/signalr-20/security/hub-authorization
Overriding AuthorizeHubMethodInvocation will allow you to authorize the request while overriding UserAuthorized with allow you to authenticate (you can check the user's roles etc.
Have your HubAuthorizeAttribute inherit from AuthorizeAttribute and allow the constructor to take in a list of roles
Here's a simple example on how to handle roles http://www.jasonwatmore.com/post/2014/02/18/ASPNET-Web-API-2-Enum-Authorize-Attribute.aspx

Cross platform authentication using ASP.NET Web API

How do I even begin coding authentication using ASP.NET Web API so it is cross-platform to support desktop, mobile and web? I'd read of some methods of doing RESTful authentication, such as using tokens in the header.
Are there any example projects out there that utilizes this method?
Questions:
If not how do I fix the [Authorize] attribute to read the token?
How do I generate this token? I dont think i can use formsauthentication because that uses cookies.
How do I handle the actual authorization, do the client send raw password and username then I generate the token or is there some other way?
How do I handle when my website is using it? I heard this is handled differently than when an app is using it, such as getting the domain and authorizing it.
I think tokens would be a solid way to go. Forms authentication is based on cookies for the web. Not the most idea situation for all non browser clients though.
What I'd suggest is creating a custom AuthorizationFilterAttribute and overriding the OnAuthorization method. In that method, you could check for the existence of a token that you've issued to the client after they've supplied valid credentials. You can use this attribute on any method or controller you want validated. Here's a sample you might reference
public class AuthorizeTokenAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext != null)
{
if (!AuthorizeRequest(actionContext.ControllerContext.Request))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = actionContext.ControllerContext.Request };
}
return;
}
}
private bool AuthorizeRequest(System.Net.Http.HttpRequestMessage request)
{
bool authorized = false;
if (request.Headers.Contains(Constants.TOKEN_HEADER))
{
var tokenValue = request.Headers.GetValues("TOKEN_HEADER");
if (tokenValue.Count() == 1) {
var value = tokenValue.FirstOrDefault();
//Token validation logic here
//set authorized variable accordingly
}
}
return authorized;
} }
TOKEN_HEADER is just a string representing an HTTP header that the client should pass back for authenticated requests.
So let's walk through it
Client requests secure data
Client is not authorized, return a response with an Unauthorized status code
Client sends credentials to authenticate, which should be secured via HTTPS
Once validated, client receives a token via an HTTP header, or whatever works for you
Client tries requesting secure data again, this time attached the token to the request
The AuthorizeTokenAttribute will validate the token and allow the action to execute.
Also, check this post by John Petersen. Making your ASP.NET Web API’s secure
There are lots of ways to authenticate users for a REST service. Using tokens is possible but just using Basic Authentication is even simpler and about as standard and cross platform as you can go.
Don't confuse authorization with authentication. The [Authorize] attribute is all about authorization but only after a user has been authenticated using some other mechanism. Authorization is completely useless without doing proper authentication first.
The best resource to check is Dominick Baier who is an expert on the subject.
I use a very simple approach:
define an access profile with its unique accessId and accessKey (e.g. MD5 hashed GUID value)
store such access profile in database
every request (GET/POST/etc.) must supply accessId, queryHash (MD5 hash value represents the query) and signature (MD5 hash value of queryHash + accessKey). Of course the client needs keep the accessKey in a secure place!!!
server gets the request will check the accessId and the signature using the same calculation algorithm to reject or grant the access (authenticate)
further authorization can be done on request type basis utilizing the access profile
the service with this approach using the new ASP.NET MVC web API can serve whatever type of client: browser/javascript and native(desktop or mobile) etc.
U can use ActionFilterAttribute and override the OnActionExecuting method.
Later on register this filter in global.cs to apply this filter for all the actions like this in Application Start method
var config = GlobalConfiguration.Configuration;
config.Filters.Add(new CustomAuthAttribute ());
{
namespace Customss
{
Public class CustomAuthAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// To inforce HTTPS if desired , else comment out the code
if (!String.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
{
Content = new StringContent("HTTPS Required")
};
return;
}
// get toekn from the header
var userToken = actionContext.Request.Headers.GetValues("UserToken");
// Customer Logic to check the validity of the token.
// U can have some DB logic to check , custom STS behind or some loca cache used to compare the values
}
}
}
}

Resources