Thread Local remove() in Spring Boot webflux - java

I have a Web Filter that sets an object in a ThreadLocal attribute and I'm trying to understand how/when this Thread local should be cleaned-up (ThreadLocal.remove()) to avoid the exception "User context already initiated." that happens because it is being retrieved from the Spring Boot Thread Pool with the previous values set.
I'm using Spring Webflux.
Where can I hook this SecurityAuthorizationContext.clean() call?
public class SecurityAuthorizationContext
{
private static final ThreadLocal<PrivilegeHolder> userContext = new ThreadLocal<>();
private final List<String> roles;
private SecurityAuthorizationContext(List<String> roles)
{
this.roles = roles;
}
public static void create(List<String> roles)
{
if (nonNull(userContext.get()))
{
log.error("User context already initiated.");
throw new AuthorizationException("User context already initiated.");
}
PrivilegeHolder privilegeHolder = new PrivilegeHolder();
userContext.set(privilegeHolder);
// example of privileges retrieved from database by the user roles
privilegeHolder.add(INSERT);
privilegeHolder.add(DELETE);
}
public static void clean()
{
userContext.remove();
}
public static boolean hasInsertPrivilege()
{
return userContext.get().hasPrivilege(INSERT);
}
public static boolean hasDeletePrivilege()
{
return userContext.get().hasPrivilege(DELETE);
}
}
public class AuthorizationFilter implements OrderedWebFilter
{
private static final String USER_ROLES = "user-roles";
#Override
public int getOrder()
{
return SecurityWebFiltersOrder.AUTHORIZATION.getOrder();
}
#Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain)
{
ServerHttpRequest request = serverWebExchange.getRequest();
HttpHeaders headers = request.getHeaders();
List<String> roles = headers.get(USER_ROLES);
SecurityAuthorizationContext.create(roles);
return webFilterChain.filter(serverWebExchange);
}
}
#Configuration
#EnableWebFluxSecurity
#EnableTransactionManagement
public class ApplicationConfiguration
{
#Autowired
private AuthorizationFilter authorizationFilter;
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http)
{
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/**").permitAll()
.and()
.addFilterAt(authorizationFilter, AUTHORIZATION)
.build();
}
}
UPDATE: Long story short ... I just want to extract something from request headers and make it available to all the stack without passing it as parameter.

So, better to use reactor context instead of ThreadLocal, here you can read about: https://projectreactor.io/docs/core/release/reference/#context

Related

WebClient ExchangeFilterFunction caches value from ThreadLocal

I faced with the following issue while using Spring MVC with ThreadLocal and WebClient from Webflux.
My task is to:
Intercept the user's request to my application and get all the headers from it and save it in ThreadLocal.
After that, when my application makes a call to another service through the WebClient, intercept this request in ExchangeFilterFunction and supplement it with the Authorization header from p.1.
When I finish processing the user's request, I clear the context.
I use my custom class "RequestContext" to store headers in ThreadLocal:
public class RequestContext {
private HttpHeaders requestHeaders;
private String jwt;
private static final String BEARER_PREFIX = "Bearer ";
public RequestContext(HttpHeaders httpHeaders) {
this.requestHeaders = httpHeaders;
if (Objects.nonNull(httpHeaders)) {
init();
}
}
private void init() {
if (Objects.nonNull(requestHeaders)) {
extractJwt();
}
}
private void extractJwt() {
var jwtHeader = requestHeaders.getFirst(HttpHeaders.AUTHORIZATION);
if (StringUtils.isNotBlank(jwtHeader) && jwtHeader.startsWith(BEARER_PREFIX)) {
jwt = jwtHeader.substring(7);
}
}
}
I use my custom clas "RequestContextService" to deal with ThreadLocal:
public class RequestContextService {
private static final ThreadLocal<RequestContext> CONTEXT = new InheritableThreadLocal<>();
public void init(RequestContext requestContext) {
if (Objects.isNull(CONTEXT.get())) {
CONTEXT.set(requestContext);
} else {
log.error("#init: Context init error");
}
}
public RequestContext get() {
return CONTEXT.get();
}
public void clear() {
CONTEXT.remove();
}
}
My app is a WebMvc app. To complete step 1, I intercept the request with an HandlerInterceptor and set all headers to Threadlocal.
public class HeaderInterceptor implements HandlerInterceptor {
private final RequestContextService requestContextService;
#Override
public boolean preHandle(#NonNull HttpServletRequest request,
#NonNull HttpServletResponse response,
#NonNull Object handler) {
if (Objects.equals(request.getDispatcherType(), DispatcherType.REQUEST)) {
var headers = new ServletServerHttpRequest(request).getHeaders();
requestContextService.init(new RequestContext(headers));
}
return true;
}
#Override
public void afterCompletion(#NonNull HttpServletRequest request, #NonNull HttpServletResponse response,
#NonNull Object handler, Exception ex) {
requestContextService.clear();
}
}
As you can see, after every request I call "requestContextService.clear()" method to clear ThreadLocal.
To perform step two, I use the ExchangeFilterFunction, where I turn to the threadlocal and get the title from there.
public class SamlExchangeFilterFunction implements ExchangeFilterFunction {
private final RequestContextService requestContextService;
private static final ClientResponse UNAUTHORIZED_CLIENT_RESPONSE =
ClientResponse.create(HttpStatus.UNAUTHORIZED).build();
#Override
public #NotNull Mono<ClientResponse> filter(#NotNull ClientRequest request, #NotNull ExchangeFunction next) {
var jwt = requestContextService.get().getJwt();
if (StringUtils.isNoneBlank(jwt)) {
var clientRequest = ClientRequest.from(request)
.headers(httpHeaders -> httpHeaders.set(SAML_HEADER_NAME, jwt))
.build();
return next.exchange(clientRequest);
}
return Mono.just(UNAUTHORIZED_CLIENT_RESPONSE);
}
}
The problem is that the SamlExchangeFilterFunction works correctly only once.
On the first request to the application, everything works as it should. But with further requests with different authorization headers, the ExchangeFilterFunction seems to cache the value from the first request and substitutes it despite the fact that the threadlocal itself contains a completely different meaning of Authorization header.

Spring Boot OAuth2 without web environment on Feign

I'm using Spring Boot 2.3.4 and I need to call an external web service that needs the oauth2 authentication.
Currently I've achieved that in this way using feign
Client
#FeignClient(name = "myClient", value = "myClient", url = "${app.my.client.apiUrl}", configuration = MyClientConfiguration.class)
public interface MyClient {
#GetMapping(value = "/api/my-url", consumes = "application/json")
String getSomeData();
}
Client Configuration
public class MyClientConfiguration {
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final ClientRegistrationRepository clientRegistrationRepository;
public MyClientConfiguration(OAuth2AuthorizedClientService oAuth2AuthorizedClientService, ClientRegistrationRepository clientRegistrationRepository) {
this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
this.clientRegistrationRepository = clientRegistrationRepository;
}
#Bean
public RequestInterceptor requestInterceptor() {
ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId("my-client");
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build());
return new OAuthClientCredentialsRestTemplateInterceptor(authorizedClientManager, clientRegistration);
}
}
OAuth Interceptor
public class OAuthClientCredentialsRestTemplateInterceptor implements RequestInterceptor {
private static final String BEARER_HEADER_NAME = "Bearer";
private final OAuth2AuthorizedClientManager manager;
private final Authentication emptyPrincipal;
private final ClientRegistration clientRegistration;
public OAuthClientCredentialsRestTemplateInterceptor(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) {
this.manager = manager;
this.clientRegistration = clientRegistration;
this.emptyPrincipal = createEmptyPrincipal();
}
#Override
public void apply(RequestTemplate requestTemplate) {
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistration.getRegistrationId()).principal(emptyPrincipal).build();
OAuth2AuthorizedClient client = manager.authorize(oAuth2AuthorizeRequest);
if (client == null)
throw new IllegalStateException("Cannot retrieve a valid client for registration " + clientRegistration.getRegistrationId());
requestTemplate.header(HttpHeaders.AUTHORIZATION, BEARER_HEADER_NAME + " " + client.getAccessToken().getTokenValue());
}
private Authentication createEmptyPrincipal() {
return new Authentication() {
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptySet();
}
#Override
public Object getCredentials() {
return null;
}
#Override
public Object getDetails() {
return null;
}
#Override
public Object getPrincipal() {
return this;
}
#Override
public boolean isAuthenticated() {
return false;
}
#Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
#Override
public String getName() {
return clientRegistration.getClientId();
}
};
}
}
properties
spring:
security:
oauth2:
client:
registration:
microsoft:
client-id: ******
client-secret: ******
scope: ******
authorization-grant-type: client_credentials
provider: my-client
provider:
my-client:
token-uri: ******
app:
my-client:
apiUrl: https://my-url.com
feign:
hystrix:
enabled: false
client:
config:
default:
connect-timeout: 3000
In another project I need the same BUT it's a spring boot application without the web environment, and I've the following error
bean of type 'org.springframework.security.oauth2.client.OAuth2AuthorizedClientService' that could not be found.
How can I solve this situation?
Is it possible to use the oauth2 auto-configuration in an environment without a tomcat (or similar)?
You need to configure a separate class and tell spring IOC that this is stateless session.
#Configuration
#EnableWebSecurity
public class SecurityConfigForOauth extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic().disable().formLogin().disable().logout().disable().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
Check my response on how to get client_credentials working on https://stackoverflow.com/a/65741386/698471, there is no direct dependency on spring-boot-starter-web
Not the solution, only a workaround to convert the microservice into a micro-application: let the job run at configuration time during application startup by either using no batch property or setting spring.batch.job.enabled=true and then terminating the process.
#SpringBootApplication
public class MyBootApp implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(MyBootApp.class, args);
}
//Runs after #Configuration classes are finished synchronously including batch jobs.
#Override
public void run(String... args) throws Exception {
System.exit(0);
}
}
It seems that RestTemplate and WebClient are using spring-web configuration files for autowiring stuff needed for OAuth interceptors.

Spring Boot: Configure custom MethodSecurityExpressionOperations?

I am trouble shooting a spring boot security configuration that I once had working, but now is not recognizing my custom definitions. My goal was to protect all of our Services with method level security in Spring with custom annotations.
When I start the service my CustomMethodSecurityConfig is instantiated and does call createExpressionHandler(), but when I make the request to the service it does not call createSecurityExpressionRoot(...) on my CustomMethodSecurityExpressionHandler, but on the DefaultWebSecurityExpressionHandler.
I appreciate any insights anyone may be able to provide as to why Spring Security is not recognizing my expressions defined in my CustomMethodSecurityExpressionRoot.
Here is a snippet of my GlobalMethodSecurityConfiguration class
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class CustomMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
private final MyService1 myService1;
private final MyService2 myService2;
private final MyService3 myService3;
#Autowired
public CustomMethodSecurityConfig(MyService1 myService1, MyService2 myService2,
MyService3 myService3) {
this.myService1 = myService1;
this.myService2 = myService2;
this.myService3 = myService3;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
CustomMethodSecurityExpressionHandler expressionHandler =
new CustomMethodSecurityExpressionHandler(myService1, myService2, myService3);
expressionHandler.setPermissionEvaluator(permissionEvaluator());
return expressionHandler;
}
}
Here is a snippet of my DefaultMethodSecurityExpressionHandler class
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private final MyService1 myService1;
private final MyService2 myService2;
private final MyService3 myService3;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
public CustomMethodSecurityExpressionHandler(MyService1 myService1, MyService2 myService2,
MyService3 myService3) {
this.myService1 = myService1;
this.myService2 = myService2;
this.myService3 = myService3;
}
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication,
myService1,
myService2,
myService3);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
Here is the snippet of my SecurityExpressionRoot, this is where I am defining my SpEL expressions which I use in annotations on my Services. I have only included a simplified, isUser as an example. What these methods do isn't important, but the fact that they are visible.
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot
implements MethodSecurityExpressionOperations {
private Object filterObject;
private Object returnObject;
private MyService1 myService1;
private MyService2 myService2;
private MyService3 myService3;
public CustomMethodSecurityExpressionRoot(
Authentication authentication,
MyService1 myService1,
MyService2 myService2,
MyService3 myService3) {
super(authentication);
this.myService1 = myService1;
this.myService2 = myService2;
this.myService3 = myService3;
}
#Override
public Object getFilterObject() {
return this.filterObject;
}
#Override
public Object getReturnObject() {
return this.returnObject;
}
#Override
public void setFilterObject(Object obj) {
this.filterObject = obj;
}
#Override
public void setReturnObject(Object obj) {
this.returnObject = obj;
}
#Override
public Object getThis() {
return this;
}
//All custom SpEL methods
public boolean isUser(Long userId) {
SecurityUser user = (SecurityUser) this.getPrincipal();
return user.getUserId() == userId;
}
...
}
And finally here is a snippet of my WebSecurityConfigurerAdapter which is used in tandem, it verifies the external authentication token from our UAA server.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
prePostEnabled = true,
proxyTargetClass = true)
public class ServiceSecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenCheckService _tokenCheckService;
#Autowired
ServiceSecurityConfig(TokenCheckService tokenCheckService) {
_tokenCheckService = tokenCheckService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new TokenAuthenticationProvider(_tokenCheckService));
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/api/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.anonymous()
.disable()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new UnAuthorizedEntryPoint())
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().authenticated();
http.addFilterBefore(new AuthenticationTokenFilter(), BasicAuthenticationFilter.class);
}
}
Edit:
I seem to think it is an issue with my WebDecisionVoters being overridden during intialization. If I have a breakpoint in the Affirmative constructor
AffirmativeBased(List<AccessDecisionVoter<? extends Object>> decisionVoters)
I can see AffirmativeBased being instantiated with 3 decision voters, one of which is a PreInvocationAuthorizationAdviceVoter, which contains a reference to my expression handler. I believe this is being created by bean instantiation of the methodSecurityInterceptor.
When I continue the breakpoint I again hit the same Affirmative based constructor, but with only one decision voter, a WebExperssionVoter with a reference to an instance of DefaultWebSecurityExpressionHandler. I believe this is being created by bean instantiation of the springSecurityFilterChain.
I was able to resolve this issue by following the steps in Custom SecurityExpression with Service. The issue appears to have been with my autowired services that were separate from security. MyService1, MyService2, and MyService3 causing the issues and removing them allowed security to work.
Any additional services must be set in createSecurityExpressionRoot of the class that extends DefaultMethodSecurityExpressionHandler.
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
// Other initialization
root.setMyService1(applicationContext.getBean(MyService1.class));
root.setMyService2(applicationContext.getBean(MyService2.class));
root.setMyService3(applicationContext.getBean(MyService3.class));
return root;
}

Disable WebSession creation when using spring-security with spring-webflux

I am running a stateless spring-boot application with a rest api and want to disable the creation of WebSessions as described https://www.baeldung.com/spring-security-session
I have created my own WebSessionManager that does not store the session.
#Bean
public WebSessionManager webSessionManager() {
return new WebSessionManager() {
#Override
#NonNull
public Mono<WebSession> getSession(#NonNull final ServerWebExchange exchange) {
return Mono.just(new WebSession() {
#Override
#NonNull
public String getId() {
return "";
}
#Override
#NonNull
public Map<String, Object> getAttributes() {
return new HashMap<>();
}
#Override
public void start() {
}
#Override
public boolean isStarted() {
return true;
}
#Override
#NonNull
public Mono<Void> changeSessionId() {
return Mono.empty();
}
#Override
#NonNull
public Mono<Void> invalidate() {
return Mono.empty();
}
#Override
#NonNull
public Mono<Void> save() {
return Mono.empty();
}
#Override
public boolean isExpired() {
return false;
}
#Override
#NonNull
public Instant getCreationTime() {
return Instant.now();
}
#Override
#NonNull
public Instant getLastAccessTime() {
return Instant.now();
}
#Override
public void setMaxIdleTime(#NonNull final Duration maxIdleTime) {
}
#Override
#NonNull
public Duration getMaxIdleTime() {
return Duration.ofMinutes(1);
}
});
}
};
}
It works but I wonder if there is a better way to not create a session.
The Issue #6552: Session Creation Policy with Webflux Security is going to be fixed by Spring team.
The problem is that the request cache is being invoked for every request to see if there is a value saved to replay and thus the WebSession is being looked up for every request. Since the WebSession is being looked up with an invalid session id, Spring WebFlux invalidates the SESSION cookie. ~ rwinch
Solution suggested by DarrenJiang1990 is:
.and().securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
The security context in a WebFlux application is stored in a ServerSecurityContextRepository. Its WebSessionServerSecurityContextRepository implementation, which is used by default, stores the context in session. Configuring a NoOpServerSecurityContextRepository instead would make our application stateless
You can track the progress of patching in Issue #7157 ServerRequestCacheWebFilter causes WebSession to be read every request.
I've disabled WebSessionManager by the following trick
#Bean
public WebSessionManager webSessionManager() {
// Emulate SessionCreationPolicy.STATELESS
return exchange -> Mono.empty();
}
All other solutions didn't help for me.
Use the: NoOpServerSecurityContextRepository intended for this purpose.
#Configuration
#EnableWebFluxSecurity
#ComponentScan(value = {"my.package.security"})
public class SpringSecurityConfig2 {
#Autowired private MyHeaderExchangeMatcher myHeaderExchangeMatcher;
#Autowired private MyReactiveAuthenticationManager myReactiveAuthenticationManager;
#Autowired private MyTokenAuthenticationConverter myTokenAuthenticationConverter;
#Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) {
http.httpBasic().disable().formLogin().disable().csrf().disable().logout().disable();
http...
.addFilterAt(webFilter(), SecurityWebFiltersOrder.AUTHORIZATION)
...;
return http.build();
}
#Bean
public AuthenticationWebFilter webFilter() {
AuthenticationWebFilter authenticationWebFilter =
new AuthenticationWebFilter(myReactiveAuthenticationManager);
authenticationWebFilter.setServerAuthenticationConverter(myTokenAuthenticationConverter);
authenticationWebFilter.setRequiresAuthenticationMatcher(myHeaderExchangeMatcher);
// NoOpServerSecurityContextRepository is used to for stateless sessions so no session or state is persisted between requests.
// The client must send the Authorization header with every request.
NoOpServerSecurityContextRepository sessionConfig = NoOpServerSecurityContextRepository.getInstance();
authenticationWebFilter.setSecurityContextRepository(sessionConfig);
return authenticationWebFilter;
}
}

Unauthorized error oauth2 client from database

I've got problem with error "Unauthorized" in my app. I'm using Spring Security and oauth2. My Clients and Users are store in database. I have got error 401 in PostMan when I start to use Client from database. Client is saving in database but still I've got error when I want to get token access from localhost:8080/oauth/token. Below is my source :
AuthorizationServerConfig :
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
private CustomClientDetailsService customClientDetailsService;
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(customClientDetailsService);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.authenticationManager(authenticationManager);
}
}
This is my CustomClientDetails :
public class CustomClientDetails implements ClientDetails {
final static Logger log = LoggerFactory.getLogger(CustomClientDetailsService.class);
private static final long serialVersionUID = 6602529451366778198L;
private Clients clients;
public CustomClientDetails(final Clients clients){
this.clients = clients;
}
#Override
public String getClientId() {
return clients.getClientId();
}
#Override
public Set<String> getResourceIds() {
final Set<String> resourcesIds = new HashSet<String>();
resourcesIds.add(clients.getResourceIds());
return resourcesIds;
}
#Override
public boolean isSecretRequired() {
return true;
}
#Override
public String getClientSecret() {
return clients.getClientSecret();
}
#Override
public boolean isScoped() {
return true;
}
#Override
public Set<String> getScope() {
final Set<String> scopes = new HashSet<String>();
scopes.add(clients.getScope());
return scopes;
}
#Override
public Set<String> getAuthorizedGrantTypes() {
final Set<String> authorizedGrantTypes = new HashSet<String>();
authorizedGrantTypes.add(clients.getAuthorizedGrantTypes());
return authorizedGrantTypes;
}
#Override
public Set<String> getRegisteredRedirectUri() {
final Set<String> registeredRedirectUris = new HashSet<String>();
registeredRedirectUris.add(clients.getWebServerRedirectUri());
return registeredRedirectUris;
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
final Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(clients.getAuthorities()));
return authorities;
}
#Override
public Integer getAccessTokenValiditySeconds() {
return clients.getAccessTokenValidity();
}
#Override
public Integer getRefreshTokenValiditySeconds() {
return clients.getRefreshTokenValidity();
}
#Override
public boolean isAutoApprove(String s) {
return false;
}
#Override
public Map<String, Object> getAdditionalInformation() {
final Set<String> additionalInformation = new HashSet<String>();
additionalInformation.add(clients.getAdditionalInformation());
return null;
}
This is a CustomClientDetailsService :
public class CustomClientDetailsService implements ClientDetailsService {
#Autowired
private ClientsRepository clientsRepository;
#Autowired
private CustomClientDetails customClientDetails;
#Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
Clients client = clientsRepository.findByClientId(clientId);
final CustomClientDetails customClientDetails = new CustomClientDetails(client);
return customClientDetails;
}
And error from PostMan :
{
"timestamp": "2019-02-20T09:32:15.479+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/oauth/token"
}
You should provide a client_id and a client_secret in postman, in the authorization section, you can set a Basic Auth.
In the username field, put your client_id and in the password, put your client_secret.
"Unauthorized" at "/oauth/token" probably means you didn't provide HTTP Basic Auth credentials in request headers. As far as I recall this endpoint is secured by default with login and password stored in oauth_client_details entity. Look for client_id + client_secret pair and provide it to Postman with Authorization->Basic Auth settings.

Categories

Resources