I'm trying to setup an auth token authentication in the bean configuration of the CitrusEndpoints bean instead of use it in each request.
In my configuration class I've something like this:
#Bean
public HttpClient httpClient() throws Exception {
return CitrusEndpoints
.http()
.client()
.requestUrl(String.format("http://%s:%s", HOST, PORT))
.build();
}
I need to add an authentication in the bean configuration. Currently I'm setting it in each request
runner.given(http()
.client(httpClient)
.send()
.post(path)
.message()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer " + CORE_BE_TOKEN)
.body(channel.toString())
);
But I need to set it globally in the bean configuration.
The only examples I've found are using the basic auth but i need the bearer token auth
Related
I could not find a proper solution to my problem yet. I'm using Spring Security Oauth2 resource server to authenticate my requests. And that works fine. But when tested with different scenario it is found that spring security returns with 403 instead of 401 if there is no Authorization header present or if there is Authorization header present but the value doesn't begin with Bearer .
Spring Boot Starter - 2.6.7
Spring Boot Starter Security - 2.6.7
Spring Security Config & Web - 5.6.3
Spring Security Core - 5.3.19
Spring Boot Starter OAuth2 Resource Server - 2.6.7
Spring OAuth2 Resource Server - 5.6.3
I was referring to this answer and added below code for BearerTokenAuthenticationEntryPoint. The difference is I'm using introspection url instead jwt. But it doesn't help and that part doesn't get executed. If the Bearer token is present, then only it gets executed.
What am I missing here?
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class CustomResourceServerSecurityConfiguration {
#Value("${spring.security.oauth2.resourceserver.opaque-token.introspection-uri}")
String introspectionUri;
#Value("${spring.security.oauth2.resourceserver.opaque-token.client-id}")
String clientId;
#Value("${spring.security.oauth2.resourceserver.opaque-token.client-secret}")
String clientSecret;
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.opaqueToken(opaque -> opaque.introspectionUri(this.introspectionUri)
.introspectionClientCredentials(this.clientId, this.clientSecret))
.authenticationEntryPoint((request, response, exception) -> {
System.out.println("Authentication failed");
BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
delegate.commence(request, response, exception);
}))
.exceptionHandling(
(exceptions) -> exceptions.authenticationEntryPoint((request, response, exception) -> {
System.out.println("Authentication is required");
BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
delegate.commence(request, response, exception);
}));
return http.build();
}
}
If your scenario is that you get 403 for POST and 401 for GET if the Bearer token is missing it's related to csrf.
44.2.14 I get a 403 Forbidden when performing a POST
If an HTTP 403 Forbidden is returned for HTTP POST, but works for HTTP GET then the issue is most likely related to CSRF. Either provide the CSRF Token or disable CSRF protection (not recommended).
Here's source
and if you use JWT token then if doesn't have any ther additional requirements then you can disable this.
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated())
.csrf().disable()
return http.build();
}
4. Stateless Spring API
If our stateless API uses token-based authentication, such as JWT, we don't need CSRF protection, and we must disable it as we saw earlier
Source
With default conf, you should have a 302 (redirect to login) when authorization header is missing or invalid (malformed, expired, wrong issuer,...). If you have a 403, then you are facing another exception (CSRF, CORS or whatever). Set logging.level.org.sprngframework.security=DEBUG and carefully inspect the logs
To change this default behavior (401 instead of 302), do like it is done in those tutorials:
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
});
In samples, like the one for servlets and token introspection, which meets your exact use-case, you can even find unit tests which assert that http status are what you expect: 401 when not authorized and 403 when denied:
#Test
// security-context not set for this test => anonymous
void greetWitoutAuthentication() throws Exception {
api.get("/greet").andExpect(status().isUnauthorized());
}
#Test
#WithMockBearerTokenAuthentication(authorities = "ROLE_AUTHORIZED_PERSONNEL", attributes = #OpenIdClaims(sub = "Ch4mpy"))
void securedRouteWithAuthorizedPersonnelIsOk() throws Exception {
api.get("/secured-route").andExpect(status().isOk());
}
#Test
#WithMockBearerTokenAuthentication(authorities = "NOT_A_REQUIRED_ROLE")
void securedMethodWithoutAuthorizedPersonnelIsForbidden() throws Exception {
api.get("/secured-method").andExpect(status().isForbidden());
}
Off course, those tests pass...
I have the same problem ,I just don't understand why the security team return 403 in default AuthenticationEntryPoint when there is not token in the request header.
My solution is custom AuthenticationEntryPoint:
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Log logger = LogFactory.getLog(CustomAuthenticationEntryPoint.class);
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
logger.debug("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
I'm building a app that has to communicate with a REST service that is secured using OAuth2 with grant type client_credentials, the catch is that the /oauth/token endpoint is expecting a custom header, for simplification, let's call it "Custom-Header".
My problem is that there is no example or trace in the documentation in how to accomplish this.
My code is as follows
ClientRegistration client = ClientRegistration
.withRegistrationId(authUser)
.tokenUri(authUrl)
.clientId(authUser)
.clientSecret(authPassword)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.scope(authScope)
.build();
ReactiveClientRegistrationRepository clientRegistrations =
new InMemoryReactiveClientRegistrationRepository(client);
ServerOAuth2AuthorizedClientExchangeFilterFunction oauthFilter =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauthFilter.setDefaultClientRegistrationId(authUser);
this.webClient = WebClient.builder()
.filter(oauthFilter)
.defaultHeaders(httpHeaders -> {
httpHeaders.add(CUSTOM_HEADER, customHeader);;
})
.build();
As you can see, I'm setting the custome header in the WebClient, but it doesn't reach the oauth filter.
Any help will be appreciated since I've been going back and forth for two days now.
Finally my solution was to reimplment the OAuth2ExchangeFilterFunction and implement my own CustomWebClientCredentialsTokenResponseClient
In CustomWebClientCredentialsTokenResponseClient I added the custom headers that the provider is expecting and in the method createDefaultAuthorizedClientManager() I had to create an instance of the CustomWebClientCredentialsTokenResponseClient I created.
I'm building an API Gateway which uses Spring Webflux, Spring Cloud Gateway, Spring Cloud Security & Okta for OAuth2.
Here's my RouteLocator, through which I can call my Foo Microservice.
#Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder, TokenRelayGatewayFilterFactory filterFactory) {
return builder.routes()
.route("foo", r ->
r.path("/foo")
.filters(f -> f
.rewritePath("/foo", "/api/v1/foo")
.filter(filterFactory.apply()))
.uri("lb://foo-service")
)
.build();
}
This works perfectly fine.
However, since I need to aggregate the results of different microservices, let's say Foo and Bar, I'm creating a load balanced Spring WebClient bean that I can use to make http calls:
#Bean
#LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
How can I configure the WebClient to pass the Token on every request the same way as the TokenRelayGatewayFilterFactory does in the RouteLocator?
EDIT:
Here's my updated WebClient bean:
#Bean
#LoadBalanced
public WebClient.Builder webClientBuilder(ReactiveClientRegistrationRepository clientRegistrations,
ServerOAuth2AuthorizedClientRepository authorizedClients) {
var oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
oauth.setDefaultOAuth2AuthorizedClient(true);
oauth.setDefaultClientRegistrationId("okta");
return WebClient
.builder()
.filter(oauth);
}
Now it seems like it is working on the Chrome browser. After logging in on Okta I can access /foo on Foo microservice through Http GET. Although when I try an Http POST on /foo through Postman, (while adding the Authorization header), I'm getting a 302 response that redirects me to an Okta html page.
Funnily enough, using the RouteLocator I don't get any redirection and both GET and POST work through Postman. The redirection seems to be happening only when using WebClient.
Any idea why?
EDIT #2:
My Security config file:
#Configuration
#EnableWebFluxSecurity
class SecurityConfig {
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange().anyExchange().authenticated()
.and()
.oauth2Login()
.and()
.oauth2ResourceServer().jwt()
.and()
.and().build();
}
#Bean
CorsWebFilter corsWebFilter(){
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.setAllowedOrigins(List.of("*"));
corsConfig.setMaxAge(3600L);
corsConfig.addAllowedMethod("*");
corsConfig.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfig);
return new CorsWebFilter(source);
}
}
Take a look at ServletOAuth2AuthorizedClientExchangeFilterFunction (or the reactive equivalent)
This video covers it in more detail: https://youtu.be/v2J32nd0g24?t=2168
I'm trying to implement login functionality using angular and spring boot.
Im following spring tutorial https://spring.io/guides/tutorials/spring-security-and-angular-js/
But in my case my angular project is hosted on localhost:4200 and spring on localhost:8080
Now im sending a '/user' request to spring server. My angular code look like:
const headers = new HttpHeaders(credentials ? {
authorization: 'Basic ' + btoa(credentials.username + ':' + credentials.password)
} : {});
this.http.get('http://localhost:8080'+'/user', { headers: headers }).subscribe(response => {
if (response['name']) {
this.authenticated = true;
} else {
this.authenticated = false;
}
return callback && callback();
});
Now because of CORS it sends OPTIONS request which is successful with status 200.
After this it is not sending actual GET request which should send credentials as Angular $http is sending OPTIONS instead of PUT/POST
My spring code looks like this:
#CrossOrigin(origins = "*", maxAge = 3600, allowedHeaders={"x-auth-token", "x-requested-with", "x-xsrf-token"})
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
#Configuration
#Order(SecurityProperties.DEFAULT_FILTER_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().cors().and().authorizeRequests().antMatchers("/index.html", "/", "/home", "/login").permitAll()
.anyRequest().authenticated().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
Also i have added Spring Security in pom file.
Can you please help to find out why GET request is not triggered after OPTIONS request.
Root Cause:
In your spring security, you allowed only three types of request headers, i.e. allowedHeaders={"x-auth-token", "x-requested-with", "x-xsrf-token"}
But, in your http.GET request you are doing the basic authentication using header name as authorization which is rejected by Spring Security. So, the spring responses the allowed headers to your browser (i.e. preflight request with OPTIONS method) and your browser sees that.. Ooops.. authorization header is not allowed so I can't proceed with original GET call.
Solution:
Allow authorization header also in your spring security. It should work.!!
I am facing issue with Token Relay while communicating from one ResourceServer to another ResourceServer.
My AuthServer is based on Dave Sayer's sample and this is the application.yml for resource server1.
security:
user:
password: none
oauth2:
client:
accessTokenUri: http://localhost:9999/uaa/oauth/token
userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
clientId: trusted
clientSecret: secret
The config is very similar in resource server2, except that it is using a different clientId
Here is how i am creating the OAuth2RestTemplate in resource server1.
#LoadBalanced
#Bean
#Autowired
public OAuth2RestTemplate loadBalancedOauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails details) {
return new OAuth2RestTemplate(details, oauth2ClientContext);
}
This call requires JWT OAuth2 Token Relay, but its not happening probably.
#GetMapping("/test-relay")
public String fetchMyProfile2() {
final ResponseEntity<String> forEntity = oauthRestTemplate.getForEntity("http://my-oauth/users/me", String.class);
final String body = forEntity.getBody();
System.out.println("body = " + body);
return body;
}
This is the exception i get while invoking this endpoint /test-relay from Postman RestClient. I am specifying JWT Token in Authorization Header while making the call.
org.springframework.security.oauth2.client.resource.UserRedirectRequiredException: A redirect is required to get the users approval
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.getRedirectForAuthorization(AuthorizationCodeAccessTokenProvider.java:359)
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:205)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:148)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:121)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:648)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128)
I am using Spring Boot 1.5.2/3. My Resource Server is a UI Server as well, and this call works fine if i use Web Browser to hit the url.
UPDATE-1
This issue only happens for Resource Server that is a UI server too i.e. with #EnableOAuth2Sso annotation present on it. For a pure Resource Server that does not have #EnableOAuth2Sso, token relay works perfectly fine.
You might be affected by the bug I reported https://github.com/spring-cloud/spring-cloud-security/issues/123. See if this workaround helps :
#Configuration
public class WorkaroundConfig extends WebMvcConfigurerAdapter {
#Autowired
#Qualifier("tokenRelayRequestInterceptor")
HandlerInterceptor handlerInterceptor;
#Override
public void addInterceptors (InterceptorRegistry registry) {
registry.addInterceptor(handlerInterceptor);
}
}