Understanding how ConcurrentDictionary works in WEB API - asp.net-web-api

I am trying to implement Refresh key repository (JWT). At the moment it looks like this:
public class AuthenticateController : ControllerBase
{
ConcurrentDictionary<string, string> refreshTokens = new ConcurrentDictionary<string, string>();
....
....
....
public async Task<IActionResult> Login()
{
...
GenerateRefreshToken()
...
}
public string GenerateRefreshToken(ApplicationUser user)
{
var randomNumber = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
var token = Convert.ToBase64String(randomNumber);
refreshTokens.AddOrUpdate(user.Id, token, (key, oldValue) => token);
return token;
}
}
public async Task<IActionResult> RefreshToken([FromBody] RefreshModel model)
{
....
refreshTokens.TryGetValue(user.Id, out string savedRefreshToken);
if (savedRefreshToken != model.RefreshToken)
throw new SecurityTokenException("Invalid refresh token");
...
}
(I removed the code that I thought was unnecessary to explain)
The user tries to log in, the information about the first update key is entered into the collection. I put a breakpoint, you can see that the information is entered:
After the JWT key has expired, the frontend sends a request to update the key. But the collection is empty. Why is this happening? I am definitely not clearing the collection before.
I think the problem may be that the web api processes each new request in isolation from each other.
How do I solve this?

Controllers are transient meaning they are newly instantiated every time a request comes in. So your dictionary is wiped out between requests.
To fix this, you'd made your dictionary static, like so:
private static readonly ConcurrentDictionary<string, string> refreshTokens =
new ConcurrentDictionary<string, string>();
Now it will live the lifetime of the application.

Related

Start a session from a given id in spring

How can I create a session in a spring mvc application from a given ID instead of a generated one?
I want to fixate the session.
The fixation will be started by a trusted ui service. This trusted service forwards all requests. Thus the fixation can't begin in browser. It is not intended to do it without this ui service.
Providing a HttpSessionIdResolver bean does not work, since it only changes the location in HTTP response. Eg Session ID in HTTP header after authorization.
Are there any solutions without creating a shadow session management?
Session is required for keycloak integration. Guess it's not possible to use keycloak in stateless mode.
Thanks in advance
It is possible to fixate a session with spring-session.
#Configuration
#EnableSpringHttpSession
public class SessionConfig {
#Bean
public HttpSessionIdResolver httpSessionIdResolver() {
return new HttpSessionIdResolver() {
public List<String> resolveSessionIds(HttpServletRequest request) {
final var sessionId = request.getHeader("X-SessionId");
request.setAttribute(SessionConfig.class.getName() + "SessionIdAttr", sessionId);
return List.of(sessionId);
}
public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {}
public void expireSession(HttpServletRequest request, HttpServletResponse response) {}
};
}
#Bean
public SessionRepository<MapSession> sessionRepository() {
return new MapSessionRepository(new HashMap<>()) {
#Override
public MapSession createSession() {
var sessionId =
(String)RequestContextHolder
.currentRequestAttributes()
.getAttribute(SessionConfig.class.getName()+"SessionIdAttr", 0);
final var session = super.createSession();
if (sessionId != null) {
session.setId(sessionId);
}
return session;
}
};
}
In #resolveSessionIds() you can read the ID and store for later use.
createSession() is called when no session has been found and a new one is required. Here you can create the session with previously remembered ID in MapSession#setId(String).
Even if is possible, IMO it is not a good idea. There might be other architectural solutions/problems.

Limiting Access to Endpoints by Method in Spring Boot

We are implementing a simple health check API our load balancers can call to help with the routing of requests. If the status of the application is "standby", requests should not be sent to it. Only the admins can set the state to "up" or "standby", but anyone (including the load balancers) can get the status of the application.
We are trying this with Spring Boot 2, but are having problems configuring security to grant anonymous access to just one of the routes. Consider the following controller:
#RestController
public class AppStatusController {
private static final String STATUS = "status";
String state = "standby";
private String getState() {
return state;
}
private Map<String, String> getStatusMap() {
Map<String, String> retval = new HashMap<>();
retval.put(STATUS, getState());
return retval;
}
// GET calls are public, all others require AuthN & AuthZ
#GetMapping(path = "/appstatus", produces = "application/json")
public Map<String, String> getStatus() {
return getStatusMap();
}
// Only those with the ADMIN role can POST to this endpoint
#PostMapping(path = "/appstatus", consumes = "application/json", produces = "application/json")
public Map<String, String> setStatus(#RequestBody Map state) {
// Validate and update the state
return getStatusMap();
}
}
There is only one endpoint, /appstatus, but one method is called with an HTTP GET and the other with an HTTP POST. We want calls to getStatus to be public, but allow Spring Security to control access to setStatus. One might expect an annotation such as #Anonymous or something similar to be applied to the getStatus() method but we can't seem to find one.
Some have suggested using a separate #Configuration class and setting up antMatchers but it's not clear how we can match on the HTTP method.
Does anyone have suggestions on how to configure Spring Security to allow public access to GET method requests but control access to other methods?
EDIT: We are trying to avoid any authentication on the getStatus() call. We can't store auth credentials in the health check probe and can't perform a login exchange. This is a simple GET request to see if the application is up and ready for operation.
Have you tried using Method Security Expressions?
It looks like this will do what you want:
// GET calls are public, all others require AuthN & AuthZ
#GetMapping(path = "/appstatus", produces = "application/json")
#PreAuthorize("permitAll")
public Map<String, String> getStatus() {
return getStatusMap();
}
// Only those with the ADMIN role can POST to this endpoint
#PostMapping(path = "/appstatus", consumes = "application/json", produces = "application/json")
#PreAuthorize("hasRole('ADMIN')")
public Map<String, String> setStatus(#RequestBody Map state) {
// Validate and update the state
return getStatusMap();
}
Note: I don't know what roles your admins have, so I used 'ADMIN' as a placeholder.

Freemaker template not render on first time

i am using freemaker with spring boot.
every thing is working fine expect one.
when i am sending request and returning to user
it is showing that resource not found.
but when i am restarting my server
and refresh my browser it is working fine.
here is my rest controller method
#GetMapping("/activation")
public ModelAndView activateUser(#RequestParam(value="token") String token) {
LOGGER.info("inside #class UserController #method activation entry..");
Map<String, Object> model = new HashMap<String, Object>();
String msg = userService.activateUser(token);
if(msg != null) {
model.put("error", true);
model.put("message", msg);
}else {
model.put("error", false);
model.put("message", UtilMessages.USER_ACTIVATION_SUCCESS);
}
return new ModelAndView("user-activation-response",model);
}
also i am using #RestController but it is also behaving same with #Controller.
and my application property configuration as.
spring.freemarker.template-loader-path: classpath:/templates
spring.freemarker.suffix: .ftl
also when i am sending mail it is working fine with java mail.

Migrating to Spring MVC 4

We are migrating our mvc code to Spring 4. Previously we had a a method formBackingObject which we converted to get method initForm.
But trouble is - in previous controller which was extending SimpleFormController, formBackingObject was getting called even before submit method. We have now removed SimpleFormController. But initForm is getting called only only once on page load. It doesn't get called before submit. And there is some custom logic of creating user object and adding to UserProfileForm object.
Have you faced similar issue.
Old code
protected Object formBackingObject(HttpServletRequest request) throws Exception {
final UserProfileForm userProfileForm = new UserProfileForm();
final String id = request.getParameter("id");
if (id != null && !id.trim().equals("")) {
final User user = authenticationServices.findUser(ServletRequestUtils.getLongParameter(request, "id"));
userProfileForm.setUser(user);
} else {
final User user = new User();
userProfileForm.setUser(user);
}
return userProfileForm;
}
new code
#RequestMapping(method = RequestMethod.GET)
public String initForm(HttpServletRequest request, ModelMap model) throws Exception{
final UserProfileForm userProfileForm = new UserProfileForm();
final String id = request.getParameter("id");
if (id != null && !id.trim().equals("")) {
final User user = authenticationServices.findUser(ServletRequestUtils.getLongParameter(request, "id"));
userProfileForm.setUser(user);
} else {
final User user = new User();
userProfileForm.setUser(user);
}
addToModel(request, model);
model.addAttribute("userProfileForm", userProfileForm);
return "user-management/user-profile";
}
Create a method annotated with #ModelAttribute to fill your model.
#ModelAttribute("userProfileForm");
public UserProfileForm formBackingObject(#RequestParam(value="id", required=false) Long id) throws Exception{
final UserProfileForm userProfileForm = new UserProfileForm();
if (id != null) {
final User user = authenticationServices.findUser(id);
userProfileForm.setUser(user);
} else {
final User user = new User();
userProfileForm.setUser(user);
}
return userProfileForm;
}
#RequestMapping(method = RequestMethod.GET)
public String initForm() {
return "user-management/user-profile";
}
This way you can also use the #RequestParam annotation instead of pulling out parameters yourself.
See the reference guide for more information on the subject.
Certain inter-module dependencies are now optional at the Maven POM level where they were once required. For example, spring-tx and its dependence on spring-context. This may result in ClassNotFoundErrors or other similar problems for users that have been relying on transitive dependency management to pull in affected downstream spring-* . To resolve this problem, simply add the appropriate missing jars to your build configuration.

Token based authorization implementation in MVC5

let me first set the stage with the cast of characters:
An MVC 5 application
A WebAPI
I need to implement a token based security to access #2 from #1.
What I have already:
Created a startup class in #2
public void Configuration(IAppBuilder app)
{
// token generation
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(8),
Provider = new MyServerProvider()
});
// token consumption
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
var httpConfiguration = new HttpConfiguration();
WebApiConfig.Register(new HttpConfiguration());
app.UseWebApi(httpConfiguration);
}
Standard code available everywhere on the internet.
This is the code in MyServerProvider also in #2
public class MyServerProvider: OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
await Task.FromResult(0);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName == "one" && context.Password == "two")
{
var id = new ClaimsIdentity(context.Options.AuthenticationType);
id.AddClaim(new Claim("name", context.UserName));
id.AddClaim(new Claim("role", "user"));
context.Validated(id);
}
else
{
context.Rejected();
}
await Task.FromResult(0);
}
}
And another class that provides the token also in #3
public class TokenProvider
{
public TokenResponse _tokenValue { get; set; }
public string _accessToken { get; set; }
public string GetToken(string tokenEndpoint, string userName, string password)
{
var client = new OAuth2Client(new Uri(tokenEndpoint));
var tokenResponse = client.RequestResourceOwnerPasswordAsync(userName, userName).Result;
_tokenValue = tokenResponse;
_accessToken = _tokenValue.AccessToken;
return _accessToken;
}
}
So far so good.
Q1. Now when a request from a controller hits the api or the api is
called form JavaScript, what happens?
Q2. Which method from the
above get's called?
Q3. What does GrantResourceOwnerCredentials do?
Q4. What does the context object in the above question has and how
does one add the userName and Password to it and how are claims store in a cookie?
Q5. If I have to store the token in a cookie and use it for subsequent requests, do I write
that code in OnActionExecuting method of the controller in #1?
This all may sound very specific but it isn't. I am trying to understand token based authentication from a real world scenario and I am new to this.
I have gone through the samples at ThinkTecture Github repo and they all do a good job in explaining them but I am stuck at implementing it.
Any help is greatly appreciated.
Regards.

Resources