Create a session scoped bean in spring security - spring-mvc

I need to create a POJO class that will store information pertaining to a user. In spring security, my authorities table has an extra column of instituionIds, it is a CSV string that will be needed in various DAO calls. I need to set the values of this class, which will be needed when querying the database.
#Component
#Scope("session")
public class InstitutionList {
private String institutionList = "";
public String getInstitutionList() {
return institutionList;
}
public void setInstitutionList(String institutionList) {
this.institutionList = institutionList;
}
}
I need to use this in my custom UserDetailsService implementation
#Transactional
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
private #Autowired ACSAdminUsersService acsAdminUsersService;
private #Autowired ACSAdminAuthoritiesService acsAdminAuthoritiesService;
private String[] authority;
public ACSAdminUsers getUserByAdminUsername(String username) {
logger.info("Getting user by username");
ACSAdminUsers user = acsAdminUsersService.getUserByAdminUsername(username);
if(user!=null) acsAdminUsersService.addLogInInfo(username);
return user;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ACSAdminUsers user = getUserByAdminUsername(username);
logger.info("Username is : " + username);
logger.info("user is : " + user);
authority = acsAdminAuthoritiesService.getAuthoritiesForRole(user.getRole());
logger.info("User role is : " + authority);
if(authority == null) {
throw new UsernameNotFoundException("User : "+username+" has no authorities." );
}else {
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new org.springframework.security.core.userdetails.User(username,user.getAdmin_pass(),
true,accountNonExpired,credentialsNonExpired,
accountNonLocked,
getAuthorities(username));
}
}
public Collection<? extends GrantedAuthority> getAuthorities(String username) {
List<GrantedAuthority> authList = null;
authList=new ArrayList<GrantedAuthority>();
for(int i = 0; i < authority.length; i++) {
SimpleGrantedAuthority s = new SimpleGrantedAuthority(this.authority[i]);
authList.add(s);
}
return authList;
}
}
In the above java class I need to query the database and fetch, along with authorities, institutionIds which need to be used in queries throughout the application.

You could create your own InstitutionContextHolder and use a threadlocal to store the object. In this way you can acces this object in every class you want. You can take a look at the SecurityContextHolder for examples.

Related

Testing login api Spring Security with Postman

I am Working on a spring boot project for an e-commerce website, As a beginner, I try to add spring security in it so the problem is when I try to test my rest login API using postman I have a status code 200 and the body is always the default login page of spring security. I will be thankful for any advice or any solution.
Here is my user class :
public class User implements Serializable {
private static final long serialVersionUID = -2800960695811489984L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String firstName;
private String lastName;
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false)
private String address;
#Column(nullable = false, unique = true)
private String email;
private String password;
private boolean isEnabled;
#Column(name = "role" , nullable = false)
#Enumerated(EnumType.STRING)
private Role role;
Here is my Role enum :
public enum Role {
USER ,ADMIN
}
MyUserDetails Class :
public class MyUserDetails implements UserDetails {
String ROLE_PREFIX ="ROLE_";
private String email;
private String password;
private boolean active;
private Role role;
public MyUserDetails(User user) {
super();
this.email = user.getEmail();
this.password = user.getPassword();
this.active = user.isEnabled();
this.role = role;
}
public MyUserDetails(String email, String password, boolean enabled, Role role) {
super();
}
public static MyUserDetails create(User user) {
return new MyUserDetails(user.getEmail(), user.getPassword() ,user.isEnabled(), user.getRole());
}
Here is MyUserDetailsService :
#Service
#ToString
public class MyUserDetailsService implements UserDetailsService {
UserRepository userRepository;
#Autowired
public MyUserDetailsService(UserRepository userRepository) {
super();
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
if (email == null || email.isEmpty()) {
throw new UsernameNotFoundException("email is Empty");
}
User user = userRepository.findByEmail(email);
if (user != null) {
return user.toCurrentUserDetails();
}
throw new UsernameNotFoundException( email + "is not found !!!");
}
}
Here is my RestController :
#CrossOrigin(origins = "*")
#RestController
#RequestMapping("/home")
public class HomeController {
private AuthenticationManager authenticationManager;
private MyUserDetailsService userDetailsService;
private UserRepository userRepository;
private Jwt jwtUtil;
#Autowired
public HomeController(AuthenticationManager authenticationManager, MyUserDetailsService userDetailsService
, UserRepository userRepository, Jwt jwtUtil) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
}
#PostMapping("/signin")
public ResponseEntity<ServerResp> addUser(#RequestBody User user) {
ServerResp response = new ServerResp();
try {
if (Validator.isUserEmpty(user)) {
response.setStatus(ResponseCode.BAD_REQUEST_CODE);
response.setMessage(ResponseCode.BAD_REQUEST_MESSAGE);
} else if (!Validator.isValidEmail(user.getEmail())) {
response.setStatus(ResponseCode.BAD_REQUEST_CODE);
response.setMessage(ResponseCode.INVALID_EMAIL_FAIL_MSG);
} else {
user.setRole(Role.USER);
user.setEnabled(true);
User reg = userRepository.save(user);
response.setStatus(ResponseCode.SUCCESS_CODE);
response.setMessage(ResponseCode.CUST_REG);
}
} catch (Exception e) {
response.setStatus(ResponseCode.FAILURE_CODE);
response.setMessage(e.getMessage());
}
return new ResponseEntity<ServerResp>(response, HttpStatus.ACCEPTED);
}
#PostMapping("/login")
public ResponseEntity<ServerResp> authentification(#RequestBody HashMap<String, String> credential) {
final String email = credential.get(WebConstants.USER_EMAIL);
final String password = credential.get(WebConstants.USER_PASSWORD);
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(email, password));
} catch (BadCredentialsException e) {
throw new UserNotFoundException(email);
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(email);
final String jwt = jwtUtil.generateToken(userDetails);
ServerResp resp = new ServerResp();
resp.setStatus(ResponseCode.SUCCESS_CODE);
resp.setMessage(ResponseCode.SUCCESS_MESSAGE);
resp.setAUTH_TOKEN(jwt);
return new ResponseEntity<ServerResp>(resp, HttpStatus.OK);
}
}
Here is my Security Configuration Class :
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private MyUserDetailsService userDetailsService;
private JwtFilter jwtFilter;
#Autowired
DataSource datasource;
#Autowired
public SecurityConfiguration(MyUserDetailsService userDetailsService, JwtFilter jwtFilter) {
this.userDetailsService = userDetailsService;
this.jwtFilter = jwtFilter;
}
public SecurityConfiguration(boolean disableDefaults, MyUserDetailsService userDetailsService, JwtFilter jwtFilter) {
super(disableDefaults);
this.userDetailsService = userDetailsService;
this.jwtFilter = jwtFilter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/resources/**", "/static/**", "/public/**").permitAll()
.antMatchers("/home/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**")
.hasRole("USER")
.anyRequest().authenticated().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.formLogin()
//.loginPage("/home/login")
.usernameParameter("email")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.permitAll();
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
// TODO Auto-generated method stub
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Here is my user in the database ( MySQL):
user in database
And finally, this is the result of the test in postman :
Test in Postman
Try sending your credentials using Authorization tab in postman, select authorization type to basic Auth.
I do the following steps with my Spring Boot app that has Spring Security. Note than my set up is most likely different from yours but the key here is the csfr token.
These are the steps I am following to do the login in Postman:
Start my Server
Open a chrome tab and go to developer tools and then the Network tab.
In the Network tab, I go to the DOC tab.
I then enter the url: http://localhost:9005/mywarehouse and press enter
This takes me to the Login Form where I then click on Create User
After the user is created I am taken back to the login form and login with the new user credentials.
In Postman, I submit a GET request such as http://localhost:9005/mywarehouse/products which returns the HTML of my login form instead of JSON.
Now back in chrome developer tab on the Doc tab I go to the last entry which is my login and I right click on it and select "Copy as cURL (bash)"
I then go back into Postman and click on the Import button and select "raw text" and then paste.
I then click on Continue and then Import. This opens up a new tab with a POST request and I click on send.
This returns HTML code. I locate the csfr token and copy it and then go to the Body tab and replace the csfr token that is currently there with the one returned from the HTML.
I then resend the POST request created by the Import and then I go back to my original GET request tab and resend and this time I get the JSON response I was expecting.
I have faced this same issue and I got a solution.
In postman go to the settings --> In General section --> turn off the automatically follow redirects.

How can i capture record key and value when there is a DeserializationException while consuming a message from kafka topic?

I'm using spring boot 2.1.7.RELEASE and spring-kafka 2.2.8.RELEASE.And I'm using #KafkaListener annotation to create a consumer and I'm using all default settings for the consumer.And I'm using below configuration as specified in the Spring-Kafka documentation.
// other props
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer2.class);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer2.class);
props.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, StringDeserializer.class);
props.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, AvroDeserializer.class.getName());
return new DefaultKafkaConsumerFactory<>(props);
Now, I've implemented my custom SeekToCurrentErrorHandler by extending SeekToCurrentErrorHandler as per the below thread but the record value is coming as null and the record key is not in a readable format. Please suggest me how can i get the record key and value?
How to capture the exception and message key when using ErrorHandlingDeserializer2 to handle exceptions during deserialization
Here is my custom SeekToCurrentErrorHandler code
#Component
public class MySeekToCurrentErrorHandler extends SeekToCurrentErrorHandler {
private final MyDeadLetterRecoverer deadLetterRecoverer;
#Autowired
public MySeekToCurrentErrorHandler(MyDeadLetterRecoverer deadLetterRecoverer) {
super(-1);
this.deadLetterRecoverer = deadLetterRecoverer;
}
#Override
public void handle(Exception thrownException, List<ConsumerRecord<?, ?>> data, Consumer<?, ?> consumer, MessageListenerContainer container) {
if (thrownException instanceof DeserializationException) {
//Improve to support multiple records
DeserializationException deserializationException = (DeserializationException) thrownException;
deadLetterRecoverer.accept(data.get(0), deserializationException);
ConsumerRecord<?, ?>. consumerRecord = data.get(0);
sout(consumerRecord.key());
sout(consumerRecord.value());
} else {
//Calling super method to let the 'SeekToCurrentErrorHandler' do what it is actually designed for
super.handle(thrownException, data, consumer, container);
}
}
}
If the key fails deserialization, the original byte[] can be obtained by calling getData() on the exception.
Similarly, if the value fails deserialization, use getData() to get the original data.
The DeadLetterPublishingRecoverer does this (since 2.3).
You can tell which of the key or value failed by calling isKey() on the exception.
EDIT
I was wrong, the key and value are available if the value or key failed.
This is written with Boot 2.3.4:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
SeekToCurrentErrorHandler errorHandler(ProducerFactory<String, String> pf) {
Map<String, Object> configs = new HashMap<>(pf.getConfigurationProperties());
configs.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
ProducerFactory<byte[], byte[]> bytesPF = new DefaultKafkaProducerFactory<>(configs);
KafkaOperations<byte[], byte[]> template = new KafkaTemplate<>(bytesPF);
return new SeekToCurrentErrorHandler(new DeadLetterPublishingRecoverer(template),
new FixedBackOff(1000, 5));
}
#KafkaListener(id = "so64597061", topics = "so64597061",
properties = {
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG
+ ":org.springframework.kafka.support.serializer.ErrorHandlingDeserializer",
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG
+ ":org.springframework.kafka.support.serializer.ErrorHandlingDeserializer",
ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS
+ ":com.example.demo.Application$FailSometimesDeserializer",
ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS
+ ":com.example.demo.Application$FailSometimesDeserializer"
})
public void listen(String val, #Header(name = KafkaHeaders.RECEIVED_MESSAGE_KEY) String key) {
System.out.println(key + ":" + val);
}
#KafkaListener(id = "so64597061.dlt", topics = "so64597061.DLT",
properties = {
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG
+ ":org.apache.kafka.common.serialization.ByteArrayDeserializer",
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG
+ ":org.apache.kafka.common.serialization.ByteArrayDeserializer"
})
public void dltListen(byte[] val, #Header(name = KafkaHeaders.RECEIVED_MESSAGE_KEY, required = false) byte[] key) {
String keyStr = key != null ? new String(key) : null;
String valStr = val != null ? new String(val) : null;
System.out.println("DLT:" + keyStr + ":" + valStr);
}
#Bean
public ApplicationRunner runner(KafkaTemplate<String, String> template) {
return args -> {
template.send("so64597061", "foo", "bar");
template.send("so64597061", "fail", "keyFailed");
template.send("so64597061", "valueFailed", "fail");
};
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("so64597061").partitions(1).replicas(1).build();
}
#Bean
public NewTopic dlt() {
return TopicBuilder.name("so64597061.DLT").partitions(1).replicas(1).build();
}
public static class FailSometimesDeserializer implements Deserializer<byte[]> {
#Override
public void configure(Map<String, ?> configs, boolean isKey) {
}
#Override
public byte[] deserialize(String topic, byte[] data) {
return data;
}
#Override
public void close() {
}
#Override
public byte[] deserialize(String topic, Headers headers, byte[] data) {
String string = new String(data);
if ("fail".equals(string)) {
throw new RuntimeException("fail");
}
return data;
}
}
}
spring.kafka.consumer.auto-offset-reset=earliest
foo:bar
DLT:fail:keyFailed
DLT:valueFailed:fail

ASP.NET Core 2.1 How to pass variables to TypeFilter

I have created this typefilter that is supposed to take 2 variables in order for it to send to a method that is linked to the filter. However, I am unable to attach my 2 variables for it to run.
public class RolesFilterAttribute : TypeFilterAttribute
{
public RolesFilterAttribute() : base(typeof(RolesFilterAttributeImpl))
{
}
private class RolesFilterAttributeImpl : IActionFilter
{
private readonly ValidateRoleClient validateRoleClient;
private string Role;
private string SecretKey;
public RolesFilterAttributeImpl(string Role, string SecretKey, ValidateRoleClient validateRoleClient)
{
this.validateRoleClient = validateRoleClient;
this.Role = Role;
this.SecretKey = SecretKey;
}
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.HttpContext.Request.Cookies["Token"] != null || context.HttpContext.Request.Cookies["RefreshToken"] != null)
{
TokenViewModel tvm = new TokenViewModel
{
Token = context.HttpContext.Request.Cookies["Token"],
RefreshToken = context.HttpContext.Request.Cookies["RefreshToken"]
};
ValidateRoleViewModel vrvm = new ValidateRoleViewModel
{
Role = Role,
SecretKey = SecretKey,
Token = tvm
};
validateRoleClient.ValidateRole(vrvm);
}
}
public void OnActionExecuting(ActionExecutingContext context)
{
throw new NotImplementedException();
}
}
}
This is how I declare the filter and it compiles fine. However, I am not able to pass the required variables which are SecretKey and Role through it. Is my typefilter declared correctly?
[TypeFilter(typeof(RolesFilterAttribute))]
public IActionResult About()
{
return View();
}
Taken from the official documentation
[TypeFilter(typeof(AddHeaderAttribute),
Arguments = new object[] { "Author", "Steve Smith (#ardalis)" })]
public IActionResult Hi(string name)
{
return Content($"Hi {name}");
}

org.springframework.security.core.userdetails.User cannot be cast to MyUserDetails (Junit 5)

I am testing web application using Junit 5, every thing working fine except
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
line in controller class while running test case above code of line return object of
org.springframework.security.core.userdetails.User instead of MyUserDetails (While running application in spring boot return object is MyUserDetails and code works well)
My Test case class
#ExtendWith(SpringExtension.class)
#WebMvcTest(VendorController.class)
#AutoConfigureMockMvc
class VendorControllerTest {
#Autowired
private MockMvc mvc;
#Test
void test() throws Exception {
mvc.perform(MockMvcRequestBuilders.post("/vendor/user/getVendorList").with(csrf().asHeader())
.param("iDisplayStart","0")
.param("iDisplayLength", "10")
.param("iSortCol_0", "0")
.param("sSortDir_0", "asc")
.param("sSearch","")
.with(user("username").password("password").roles("2"))).andExpect(status().isOk());
}
}
What code I am missing so that it is return org.springframework.security.core.userdetails.User object while running test case using Junit 5 but working properly if I run project through spring boot.
Adding UserDetailsService implementation code
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserSvc userSvc;
#Transactional(readOnly=true)
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
com.esociety.entity.UserEntity userEntity = userSvc.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(userEntity.getUserType());
return buildUserForAuthentication(userEntity, authorities);
}
private User buildUserForAuthentication(com.esociety.entity.UserEntity userEntity, List<GrantedAuthority> authorities) {
MyUserDetails myUserDetails = new MyUserDetails (userEntity.getUsername(), userEntity.getPassword(), userEntity.isEnabled(), userEntity.isAccountNonExpired(), userEntity.isCredentialsNonExpired(),userEntity.isAccountNonLocked(),authorities,userEntity.getUserId(),userEntity.getSocietyId(),userEntity.getUserType(),userEntity.getFirstName(),userEntity.getMiddleName(),userEntity.getLastName());
return myUserDetails;
}
private List<GrantedAuthority> buildUserAuthority(String userType) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
setAuths.add(new SimpleGrantedAuthority(userType));
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
}
MyUserDetails class
public class MyUserDetails extends User {
public MyUserDetails(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities, Long userId,
Long societyId, String userType, String firstName,
String middleName, String lastName) {
super(username, password, enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, authorities);
this.userId = userId;
this.societyId = societyId;
this.userType = userType;
this.firstName = firstName;
this.middleName = middleName;
this.lastName = lastName;
}
// getter setter goes here
}
The problem is you are using SecurityMockMvcRequestPostProcessors.user(String username) RequestPostProcessor which will create and return a org.springframework.security.core.userdetails.User which is an implementation of UserDetails but not the MyUserDetails that you want.
Instead you can use SecurityMockMvcRequestPostProcessors.user(UserDetails user) which allows to submit your own implementation of UserDetails, in this case MyUserDetails that you want.
Change your test as following:
#Test
void test() throws Exception {
mvc.perform(MockMvcRequestBuilders.post("/vendor/user/getVendorList").with(csrf().asHeader())
.param("iDisplayStart","0")
.param("iDisplayLength", "10")
.param("iSortCol_0", "0")
.param("sSortDir_0", "asc")
.param("sSearch","")
.with(user(new MyUserDetails ("username", "password", true, true, true,true, Arrays.asList(new SimpleGrantedAuthority("2")),1,1,USER_TYPE,"FirstName","MiddleName","LastName")))).andExpect(status().isOk());
}
Reference Spring Security Source code

Authentication Failed: No AuthenticationProvider

I have two levels of access in the application: for everyone and only for authorized.
I'm login as a registered user,
but if I try to request data that is protected I get an error:
Authentication Failed: No AuthenticationProvider found for
com.company.security.tokenAuth.TokenAuthentication
My TokenAuthentication class:
public class TokenAuthentication extends AbstractAuthenticationToken {
private static final long serialVersionUID = -4021530026682433724L;
private UserDetails principal;
private String token;
public TokenAuthentication(String token) {
super(new HashSet<>());
this.token = token;
}
public TokenAuthentication(String token, Collection<? extends GrantedAuthority> authorities,
boolean isAuthenticated, UserDetails principal) {
super(authorities);
this.principal = principal;
this.setAuthenticated(isAuthenticated);
}
#Override
public Object getCredentials() {
return null;
}
#Override
public UserDetails getPrincipal() {
return principal;
}
public String getToken() {
return token;
}
}
My TokenAuthenticationProvider class:
#Component
public class TokenAuthenticationProvider implements AuthenticationProvider {
private TokenService tokenService;
private AccountDetailsService accountService;
public TokenAuthenticationProvider(TokenService tokenService, AccountDetailsService accountService) {
this.tokenService = tokenService;
this.accountService = accountService;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication instanceof TokenAuthentication) {
return processAuthentication((TokenAuthentication) authentication);
} else {
authentication.setAuthenticated(false);
return authentication;
}
}
#Override
public boolean supports(Class<?> aClass) {
return aClass.equals(TokenAuthentication.class);
}
private TokenAuthentication processAuthentication(TokenAuthentication authentication) {
try {
Account token = tokenService.parseToken(authentication.getToken());
Set<GrantedAuthority> authorities = new HashSet<>();
authorities.add(new SimpleGrantedAuthority(token.getRole().name()));
return new TokenAuthentication(authentication.getToken(), authorities,
true, new AccountDetails((Account) accountService.loadUserByUsername(token.getEmail())));
} catch (ValidationException e) {
throw new AuthenticationServiceException("Invalid token");
} catch (Exception e) {
throw new AuthenticationServiceException("Token corrupted");
}
}
}
What is my problem?
Thank you for your help.
I found the answer.
I changed my authentication according to the project by reference https://github.com/oharsta/spring-jwt/tree/50f130ee5d63d746cc9d7adf2f0d8f085327a84a
And fixed role, since I have only one user and one role in the form of the enum. And during authentication, the list of roles is used.
After solving this problem, everything worked.

Resources