Unable to find the group information of the logged in user -JWT token OKTA - spring-boot

I am new to Okta so apologies if my questions are not clear.
So what I want to do is basically parse the JWT token generated by
okta and extract the group information of the logged in user
associated with it.
I am under the impression that this information should be there in the
OidcUser object. I do see user name/email id / token validity etc
information inside this object. Unfortunately I can't see group id
which I need for further processing.
#RequestMapping("/")
public String hello(#AuthenticationPrincipal OidcUser user){
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : user.getClaims().entrySet()) {
sb.append(entry.getKey() + ":" + entry.getValue().toString());
sb.append("\n");
}
sb.append("|");
sb.append(user.getClaims());
return sb.toString();
}
Here is my okta plugin inside spring boot
okta.oauth2.issuer=https://dev-XXXXXXXX.okta.com/oauth2/default
okta.oauth2.client-id=XXXXXXXXXX
okta.oauth2.client-secret=XXXXXXXXXXXX
I am wondering if my approach is proper and what more I need to do to extract User group from Okta JWT token.

To get user groups you need to make an additional request to /userinfo endpoint, assuming you requested groups scope during /authorize call.
Please see a guide here
Not exactly spring-boot response, but it's always beneficial to know how things work under-the-hood

Related

Spring security match Principal info with request param

So I'm trying to validate my endpoint without IFs and such things, only with PreFilter and I basically have the following
#GetMapping(value = "/books/{userId}")
#PreFilter("hasRole('ADMIN') or principal.userId == pathVar.userId")
public List<Books> getBooks(#PathVariable("userId" String userId) {
//do sth
}
My problem is getting the PreFilter to work, or actually to match the userId from the authenticated principal with the path variable so that each user can request only his own books.
Yes I know that I can remove the path variable and just do principal.getUserId() inside the method, but that means that I will need to have an if user is admin.
Okay after quite a few tries I have finally found out how to do it. In case anyone is interested:
#PreAuthorize("hasRole('ADMIN') or #userId == principal.userId")
This way if you are the admin you can get the books of any user, and if you are a user you can get only your own.

What is a good way to pass additional information to the http response when issuing access token with Owin middleware in Asp Net Web Api?

I am using Owin middleware to implement token-based security for my application. When issuing the access token to the client I would also like to pass User Id along with the token, so that, the client application will know the User Id and will be able to call GetUserById (one of the methods inside UserController) in order to show the user his starting page. The best solution I could come up with so far is just adding User Id to the response header. Take a look at my OAuthAuthorizationServerProvider class, in GrantResourceOwnerCredentialsmethod I am adding User Id to the header, using context.Response.Headers.Add("User-Id", new string[] { "1" })
Here is the implementation of my OAuthAuthorizationServerProviderclass
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
//The actual credential check will be added later
if (context.UserName=="user" && context.Password=="user")
{
identity.AddClaim(new Claim("Id", "1"));
context.Validated(identity);
//Here I am adding User Id to the response header
context.Response.Headers.Add("User-Id", new string[] { "1" });
}
else
{
context.SetError("invalid_grant","The credentials provided are not valid");
return;
}
}
}
Basically the client then will have to read User-Id from the header. Is this a good solution I came up with or there is a better one? Also what if I want to pass the whole User object with all its properties to the response is it possible and how to do this?
Since you store the ID already in the claims, why don't you just decode your token on the client and read out the user id like that? Passing it through the headers could allow tampering with it (security).
Have a look on how you could achieve to decode your token and read the claims. This is a c# example https://contos.io/peeking-inside-your-jwt-tokens-using-c-bf6a729d06c8 but this could also be done even through javascript.
This, assuming you use the JWT-format as token (was not specified in your initial question).
Bad way to store UserID as a response header back to client side. This is a huge security concern.
The best way would be to store it as a Claims.
It is very easy to achieve this and get back the claims in the client side.
In your controller, call this and remember to reference
using Microsoft.AspNet.Identity;
var userId = User.Identity.GetUserId();

WebAPI 2 secure routing parameter

I have a situation where I don't want to pass logged-in userid in API route parameter.
For example there is a WebAPI [GET or POST] endpoint 'api/getUserDetails/{id}'. This id is passed to another layer to get the user data.
public RestResult getUserDetails(int uID)
{
int uID = getFromHeaderCookie();
var userDetails = someService.GetUserDetail(uID);
return userDetails;
}
Here id is passed as route parameter and by changing the Id value anyone can see the other users data.
So now I want that the url should be like 'api/getUserDetails' and I am reading Id from header Cookie saved at the time of login. using ActionFilters I can manage to get the Id.
public RestResult getUserDetails( )
{
int uID = getFromHeaderCookie();
var userDetails = someService.GetUserDetail(uID);
return userDetails;
}
This is working but not looking good as there are other methods where I want to secure UserId.
Is there any way to remove int uID = getFromHeaderCookie(); and use some custom model binder to inject userID in controller?
Thanks in advance
You can try Oauth, and get user info from ClaimsIdentity, just need to add Authorization(such as Bearer AccessToken) when request url.

Spring Security - Further Restricting Methods

I have an application that is authorizing an OAuth2 token with Spring Security. Using the #PreAuthorize tag, I can easily make sure that a user has a permission before allowing them to access a method:
#PreAuthorize("#oauth2.hasScope('account.read')")
public void getAccount(int accountId);
{
//return account
}
This works great at restricting users without the account.read permission from accessing this method.
The only problem is now any user with this permission can access any account. I want to restrict the users to only access their own account. I'm sure this is a common scenario. How do other applications deal with this?
So, the question here - how would system know if account belongs to user?
The answer would be that you are probably storing User<->Account relationship in the database. The most simple solution would be to do the check right in your method:
#PreAuthorize("#oauth2.hasScope('account.read')")
public Account getAccount(int accountId) {
// get account from db
Account account = repository.findById(accountId);
// you will need a little helper to get your User from
//Spring SecurityContextHolder or whatever there for oauth2
User user = securityManager.getCurrentUser();
if (account.belongs(user)) {
return account;
} else {
throw new UnathorizedException("User is not authorized to view account");
}
}
Upd. one of possible improvements may be to first get the user, get id from it and do a repository.findByIdAndUserId(accountId, userId) or somthing like that. (or even repositoryFindByIdAndUser(accountId, user))

Spring security, bypass login page after changing password

I'm relatively new to Java/Spring/MVC. I hope someone can shine some light on this, because this problem is about to ruin yet another weekend without getting solved.
At the moment I'm working on a project, implementing a couple of RFC's. Each RFC has it's own 'challenges', ranging from changing hibernate queries and stored procedures to minor textual changes and adding extra fields to forms.
The problem I am now facing has (I think) to do with Spring security.
The present situation:
To login to the application, user is presented by a login page (/user/login.jsp). After entering a username and password, user is sent to welcome page (/welcome/hello.jsp).
If a user forgets their password, they use a link that will sent an email providing a link to a page (/welcome/newPassword?code=xxx) where a new password can be entered. Entering the Submit-button will do some validation, save the password in the database and displays a 'password changed' message on the page.
To enter the application, user will now have to login with the new password.
What the customer wants:
After submitting the new password, the user should be automatically logged in with the new password and redirected to /welcome/hello.jsp
I searched the web, but couldn't find the scenario I was looking for (or maybe didn't recognize it!).
What I thought would do the trick:
I have this method in WelcomeController which presently handles the new password:
#RequestMapping(method = RequestMethod.POST)
public void newPassword(Model model, HttpSession httpSession, ModelAttribute(PASSWORD) Passwords password, BindingResult result) {
logger.debug("Posting password for new password");
ScreenMessage message = null;
//
try {
// errors from initBinder - back to page
if (result.hasErrors()) {
return;
}
// Validate input: on error go back to form
passwordValidator.validate(password, result);
if (result.hasErrors()) {
return;
}
// Save password
passwordService.createNewOwnPassword(result, password);
if (result.hasErrors()) {
return;
}
// No errors
message = new ScreenMessage();
message.setText(CHANGED);
message.setArg(THIS_PASSWORD, 0);
model.addAttribute(MESSAGE, message);
httpSession.setAttribute(ACTION_READ, Boolean.TRUE);
// Errors are fatal.
} catch (Exception e) {
logger.error("ERROR-CHANGE-password for user: " + password.getUser().getLoginName(), e);
result.reject( ERROR_FATAL, new Object[] { password}, ERROR_FATAL);
} finally {
model.addAttribute(PASSWORD, password);
}
}
I thought I could simply change this method to have it return a ModelAndView and use redirect:
ModelAndView mav = new ModelAndView("redirect:/welcome/hello", (ModelMap) model);
return mav;
But: It keeps sending me to /user/login, exactly the page I don't want to go to...
Extra information that could be handy:
As far as I can tell the application is using Spring 2.5.
(I hope relevant) Part of my security-applicationContext.xml:
<!-- always-use-default-target="true" -->
<security:form-login login-page="/dyn/user/login"
login-processing-url="/dyn/user/j_spring_security_check"
default-target-url="/dyn/welcome/hello"
always-use-default-target="true"
authentication-failure-handler-ref="exceptionTranslationRouting" />
<security:access-denied-handler error-page="/dyn/welcome/hello" />
<security:logout logout-success-url="/dyn/user/logout" invalidate-session="true" />
Is my idea of redirecting to /welcome/hello too simple or am I just missing something small?
Firstly, you'd better move your logic from web controller (I don't like password validation) to component dedicated to password change. This will allow you to logically separate parts of your application. Filter maybe? (more later) Or special service?
Secondly, take a look at the source code of Spring Security. From my experience, I can tell you that any customization in Spring Security is only possible if you understand its architecture (especially filter chain). I would recommend taking look at class AbstractAuthenticationProcessingFilter.
It's responsible for authenticating users when they provide username and password in your application. Take a look at the method AbstractAuthenticationProcessingFilter.successfulAuthentication.
This line of that method sets security context which makes the user logged in:
SecurityContextHolder.getContext().setAuthentication(authResult);
Reference authResult is of type Authentication. It is an interface implemented by for instance UsernamePasswordAuthenticationToken. Take a look at UsernamePasswordAuthenticationFilter.attemptAuthentication method for example of how to construct such an object.
Going back to filter solution:
I personally would think about implementing my own filter that would extend AbstractAuthenticationProcessingFilter. It would be responsible for password change. That way you don't have to think about things like session change on successful authentication. You just need to inject it in you XML.
To sum up,
classes that are recommended to look at:
AbstractAuthenticationProcessingFilter
UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationFilter
You should read Spring Security Documentation, especially parts about architecture, filter chain and its role in authenticating users.
http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#overall-architecture

Resources