How to create a WebClient-object in a spring application with oauth2 - spring-security-oauth2

I'm developing a spring application (client) that is secured with an OAuth2 provider. This application should do some REST calls to another spring application (resource server). For performing the REST calls, I will use spring's WebClient.
I therefore try to create a bean of type WebClient as can be found in several blogs.
#Configuration
public class AppConfig {
#Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId("myprovider");
return WebClient.builder().filter(oauth).build();
}
}
When starting the application, I get the following error:
The following candidates were found but could not be injected:
- Bean method 'clientRegistrationRepository' in 'ReactiveOAuth2ClientAutoConfiguration' not loaded because NoneNestedConditions 1 matched 0 did not; NestedCondition on ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition.ServletApplicationCondition found 'session' scope
Action:
Consider revisiting the entries above or defining a bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository' in your configuration.
As several websites recommend exactly this code for generating a WebClient instance when using OAuth2 authentication, I'm wondering what I'm doing wrong?
Do you have any suggestions for me?
Thanks.

I got the same issue. I changed the code as provided in the video : https://www.youtube.com/watch?v=1N-xwmoN83w&t=1569s and that worked
#Bean
public WebClient webClient(ClientRegistrationRepository clientRegistrationRepository , OAuth2AuthorizedClientRepository authorizedClientRepository) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServletOAuth2AuthorizedClientExchangeFilterFunction (clientRegistrationRepository , authorizedClientRepository);
return WebClient.builder().apply(oauth.oauth2Configuration()).build();
}
Hope that helps.

Related

StrictHttpFirewall in spring security 4.2 vs spring MVC #MatrixVariable

Having upgraded to spring security 4.2.4 I discovered that StrictHttpFirewall is now the default.
Unfortunately it doesn't play well with spring MVC #MatrixVariable since ";" are not allowed anymore.
How to get around that?
Example:
#GetMapping(path = "/{param}")
public void example(#PathVariable String param,
#MatrixVariable Map<String, String> matrix) {
//...
}
This could be called like this:
mockMvc.perform(get("/someparam;key=value"))
And the matrix map would be populated.
Now spring security blocks it.
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlacklistedUrls(StrictHttpFirewall.java:140)
I could use a custom HttpFirewall that would allow semicolons.
Is there a way to use #MatrixVariable without using forbidden characters?
BTW: the javadoc is incorrect https://docs.spring.io/autorepo/docs/spring-security/4.2.x/apidocs/index.html?org/springframework/security/web/firewall/StrictHttpFirewall.html
Since:
5.0.1
I guess it was backported?
You can dilute the default spring security firewall using your custom defined instance of StrictHttpFirewall (at your own risk)
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
firewall.setAllowSemicolon(true);
return firewall;
}
And then use this custom firewall bean in WebSecurity (Spring boot does not need this change)
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
// #formatter:off
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
...
}
That shall work with Spring Security 4.2.4+, but of-course that brings some risks!
As mentioned by Крис in a comment if you prefer to use a XML approach, you can add the following part to your securityContext.xml (or whatever your spring-security related xml-config is called):
<bean id="allowSemicolonHttpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall">
<property name="allowSemicolon" value="true"/>
</bean>
<security:http-firewall ref="allowSemicolonHttpFirewall"/>
The <bean> part defines a new StrictHttpFirewall bean with the id allowSemicolonHttpFirewall which is then set as default http-firewall in the <security> tag by referencing the id.
I used combination of following two
https://stackoverflow.com/a/48636757/6780127
https://stackoverflow.com/a/30539991/6780127
First one resolved the
The request was rejected because the URL contained a potentially malicious String ";"
Second one Resolved the
Spring MVC Missing matrix variable
As I am using Spring Security with Spring Web I had to do both And the issue is now Resolved.
I found using #MatrixVariable Following Pattern is useful. First in Url {num} has to be mentioned to use it as #MatrixVariable
#RequestMapping(method = RequestMethod.GET,value = "/test{num}")
#ResponseBody
public ResponseEntity<String> getDetail(#MatrixVariable String num){
return new ResponseEntity<>("test"+num, HttpStatus.OK);
}

Auto-start ASP.NET application hosting WebAPI instance

We're having an odd issue with a WebAPI application hosted by another ASP.NET webapp. The WebAPI controllers are all mapped with Ninject but the ASP.NET host site does not use Ninject.
The issue is that any requests to any of the WebAPI controllers fail with a Ninject error and HTTP 500:
"An error occurred when trying to create a controller of type 'MyObjectsController'. Make sure that the controller has a parameterless public constructor."
However, once even a single request to the main webapp is made (such as opening the login page) then the WebAPI calls all work as expected. The WebAPI is registered and initialized as part of the Application_Start global event. The start event is triggered regardless of whether the first request comes in under the WebAPI or the webapp so it's not bypassing the global startup when coming through the WebAPI before the main app. The WebAPI registration is pretty standard stuff:
GlobalConfiguration.Configure(AddressOf WebApiConfig.Register)
And the Register function itself is nothing unusual:
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*", "X-Pagination");
//To allow cross-origin credentials in Web API
cors.SupportsCredentials = true;
config.EnableCors(cors);
// To disable host-level authentication inside the Web API pipeline and "un-authenticates" the request.
config.SuppressHostPrincipal();
config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthBearerOptions.AuthenticationType));
// Web API routes
var constraintResolver = new DefaultInlineConstraintResolver();
constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));
//constraintResolver.ConstraintMap.Add("NonEmptyFolderIds", typeof(NonEmptyFolderIdsConstraint));
config.MapHttpAttributeRoutes(constraintResolver);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
The NinjectConfig is also pretty standard:
public static class NinjectConfig
{
/// <summary>
/// THe kernel of Ninject
/// </summary>
public static Lazy<IKernel> CreateKernel = new Lazy<IKernel>(() =>
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
RegisterServices(kernel);
return kernel;
});
private static void RegisterServices(KernelBase kernel)
{
kernel.Bind<IMyObjectRepository>().To<MyObjectRepository>().InRequestScope();
...
}
}
An example of the DI usage (again, very basic and standard) is:
public class MyObjectRepository : IMyObjectRepository
{
private readonly IMyOtherObjectRepository _objectRepository;
...
public MyObjectRepository(IMyOtherObjectRepository objectRepository)
{
_objectRepository = objectRepository;
...
}
...
}
We want to avoid the requirement of the initial request to the webapp before the WebAPI is available for requests but nothing seems to be getting us towards a solution.
We initially tried out the IIS preloading/app initialization by setting Start Mode to AlwaysRunning and Start automatically to True in the AppPool config. We also enabled preloadEnabled to true and then added the applicationInitialization config section to the web.config such as the following:
<system.webServer>
...
<applicationInitialization>
<add initializationPage="login.aspx" />
</applicationInitialization>
...
</system.webServer>
However, none of these changes and variations of made any difference to the behavior of the WebAPI. We've scoured the web for more help but are at somewhat of a loss as pretty much everything we've come across points to setting the Start Mode, Start Automatically, preloadEnabled, and applicationInitialization and then it will magically work but that's definitely not our experience.
Does anyone have suggestions or ideas?
Install Ninject integration for WebApi nuget package. It creates a file which initializes Ninject on startup. Here is the doc.

Spring Security: Why is my custom AccessDecisionVoter not invoked

I'm trying to do URL authorization using a custom AccessDecisionVoter. I don't get any errors and debugging shows that my voter is picked up at start up. However, at runtime, the vote method is not called, thus allowing every authenticated user full access.
Note that, I don't need method security. I'm also not using XML config. That rules out every example ever posted on the internet regarding this topic.
#Configuration
#EnableWebSecurity
#EnableWebMvc
#ComponentScan
#Order(-10)
public class HttpSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${trusted_ports}")
private List<Integer> trustedPorts;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private ServiceIdAwareVoter serviceIdAwareVoter;
RequestMatcher requestMatcher = new OrRequestMatcher(
// #formatter:off
new AntPathRequestMatcher("/**", GET.name()),
new AntPathRequestMatcher("/**", POST.name()),
new AntPathRequestMatcher("/**", DELETE.name()),
new AntPathRequestMatcher("/**", PATCH.name()),
new AntPathRequestMatcher("/**", PUT.name())
// #formatter:on
);
#Override
protected UserDetailsService userDetailsService() {
return userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preAuthProvider());
auth.authenticationProvider(authProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.
httpBasic().and().
authorizeRequests().anyRequest().fullyAuthenticated().
accessDecisionManager(accessDecisionManager()).and().
csrf().disable().
logout().disable().
exceptionHandling().and().
sessionManagement().sessionCreationPolicy(STATELESS).and().
anonymous().disable().
addFilterAfter(preAuthFilter(), X509AuthenticationFilter.class).
addFilter(authFilter());
// #formatter:on
}
AccessDecisionManager accessDecisionManager() {
return new UnanimousBased(ImmutableList.of(serviceIdAwareVoter));
}
Filter preAuthFilter() throws Exception {
PreAuthenticationFilter preAuthFilter = new PreAuthenticationFilter(trustedPorts);
preAuthFilter.setAuthenticationManager(super.authenticationManager());
return preAuthFilter;
}
PreAuthenticatedAuthenticationProvider preAuthProvider() {
PreAuthenticatedAuthenticationProvider preAuthProvider = new PreAuthenticatedAuthenticationProvider();
UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> userDetailsServiceWrapper = new UserDetailsByNameServiceWrapper<>();
userDetailsServiceWrapper.setUserDetailsService(userDetailsService());
preAuthProvider.setPreAuthenticatedUserDetailsService(userDetailsServiceWrapper);
return preAuthProvider;
}
Filter authFilter() throws Exception {
AppIdAppKeyAuthenticationFilter authFilter = new AppIdAppKeyAuthenticationFilter(requestMatcher);
authFilter.setAuthenticationFailureHandler(new ExceptionStoringAuthenticationFailureHandler());
authFilter.setAuthenticationSuccessHandler(new UrlForwardingAuthenticationSuccessHandler());
authFilter.setAuthenticationManager(authenticationManagerBean());
return authFilter;
}
AuthenticationProvider authProvider() {
AppIdAppKeyAuthenticationProvider authProvider = new AppIdAppKeyAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
return authProvider;
}
Background:
After hours of debugging, I found out the root cause of the problem, which is really deep. Part of it is due to the fact that the Spring Security Java config is very poorly documented (for which I've opened a JIRA ticket). Theirs, as well as most online, examples are copy-pasted from XML config whereas the world has stopped using Spring XML config since probably 2010. Another part is due to the fact that REST service security is an afterthought in the Spring Security design and they don't have first-class support for protecting applications that don't have a login page, error page and the usual view layer. Last but not the least is that there were several (mis)configurations in my app which all came together and created a perfect storm of mind-boggling complexity.
Technical Context:
Using the authorizeRequests() configures a ExpressionUrlAuthorizationConfigurer which ultimately sets up a UnanimousBased AccessDecisionManager with a WebExpressionVoter. This AccessDecisionManager is called from the FilterSecurityInterceptor if the authentication succeeds (obviously there's no point in authorization if the user fails authentication in the first place).
Issues:
In my AbstractAnnotationConfigDispatcherServletInitializer subclass, which is basically the Java version of the web.xml, I'd configured filters not to intercept forward requests. I'm not going to go into the why here. For the interested, here's an example of how it's done:
private Dynamic registerCorsFilter(ServletContext ctx) {
Dynamic registration = ctx.addFilter("CorsFilter", CorsFilter.class);
registration.addMappingForUrlPatterns(getDispatcherTypes(), false, "/*");
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes() {
return (isAsyncSupported() ? EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC)
: EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
}
If you take the DispatcherType.FORWARD out of the dispatcher types set, the registered filter doesn't kick in for that kind of request.
The authFilter shown in my question extended from UsernamePasswordAuthenticationFilter and had an AuthenticationSuccessHandler which forwarded the request to the destination URL after successful authentication. The default Spring implementation uses a SavedRequestAwareAuthenticationSuccessHandler which does a redirect to a webpage, which is unwanted in the context of a REST app.
Due to the above 2 reasons, the FilterSecurityInterceptor was not invoked after successful authentication which in turn, skipped the authorization chain causing the issue in my original post.
Fix:
Get rid of custom dispatcher configuration from web app initializer.
Don't do forward, or redirect, from AuthenticationSuccessHandler. Just let the request take it's natural course.
The custom voter has a vote method that looks as follows:
public int vote(Authentication authentication, FilterInvocation fi,
Collection<ConfigAttribute> attributes) {
}
The attributes in my case, as shown in my original post, is the string expression fullyAuthenticated. I didn't use it for authorization as I already knew the user to have been authenticated through the various filters in the authentication flow.
I hope this serves as documentation for all those souls who're suffering from the lack of documentation in Spring Security Java config.
Your config is saying that you are allowing access to fully authenticated users right here:
authorizeRequests().anyRequest().fullyAuthenticated().
You are telling Spring Security to grant access to any request as long as they are fully authenticated. What's you're goal? How are you trying to restrict access, by a role/permission? I'm guessing it's something that you are dictating inside your custom voter bean?
Usually the voter bean comes into play when you have conflicting security levels, for example, here you say that that all requests have full access but if your code hits a method with method level security like this (not a very real-world example):
#PreAuthrorize("permitNone")
public void someMethod{
...
}
You're going to have voters come into play because your java security config is saying "grant access to everyone" (voting yes to access) but this method annotation is "grant access to no one" (voting no to access).
In your case, there's nothing to vote on, you are granting everyone access.

How to specify credentials from a Java Web Service in PTC Windchill PDMLink

I am currently investigating the possibility of using a Java Web Service (as described by the Info*Engine documentation of Windchill) in order to retrieve information regarding parts. I am using Windchill version 10.1.
I have successfully deployed a web service, which I consume in a .Net application. Calls which do not try to access Windchill information complete successfully. However, when trying to retrieve part information, I get a wt.method.AuthenticationException.
Here is the code that runs within the webService (The web service method simply calls this method)
public static String GetOnePart(String partNumber) throws WTException
{
WTPart part=null;
RemoteMethodServer server = RemoteMethodServer.getDefault();
server.setUserName("theUsername");
server.setPassword("thePassword");
try {
QuerySpec qspec= new QuerySpec(WTPart.class);
qspec.appendWhere(new SearchCondition(WTPart.class,WTPart.NUMBER,SearchCondition.LIKE,partNumber),new int[]{0,1});
// This fails.
QueryResult qr=PersistenceHelper.manager.find((StatementSpec)qspec);
while(qr.hasMoreElements())
{
part=(WTPart) qr.nextElement();
partName = part.getName();
}
} catch (AuthenticationException e) {
// Exception caught here.
partName = e.toString();
}
return partName;
}
This code works in a command line application deployed on the server, but fails with a wt.method.AuthenticationException when performed from within the web service. I feel it fails because the use of RemoteMethodServer is not what I should be doing since the web service is within the MethodServer.
Anyhow, if anyone knows how to do this, it would be awesome.
A bonus question would be how to log from within the web service, and how to configure this logging.
Thank you.
You don't need to authenticate on the server side with this code
RemoteMethodServer server = RemoteMethodServer.getDefault();
server.setUserName("theUsername");
server.setPassword("thePassword");
If you have followed the documentation (windchill help center), your web service should be something annotated with #WebServices and #WebMethod(operationName="getOnePart") and inherit com.ptc.jws.servlet.JaxWsService
Also you have to take care to the policy used during deployment.
The default ant script is configured with
security.policy=userNameAuthSymmetricKeys
So you need to manage it when you consume your ws with .Net.
For logging events, you just need to call the log4j logger instantiated by default with $log.debug("Hello")
You can't pre-authenticate server side.
You can write the auth into your client tho. Not sure what the .Net equivilent is, but this works for Java clients:
private static final String USERNAME = "admin";
private static final String PASSWORD = "password";
static {
java.net.Authenticator.setDefault(new java.net.Authenticator() {
#Override
protected java.net.PasswordAuthentication getPasswordAuthentication() {
return new java.net.PasswordAuthentication(USERNAME, PASSWORD.toCharArray());
}
});
}

Customize HTTP codes and error message in JBoss AS 7

can anyone tell me how i can customize http codes and reasonphrase in JBoss AS 7?
basically i have a REST webservice that returns a nonstandard status code '499' with reasonphrase 'app error'
In standalone.xml, I set the org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER to true under systemproperties, but AS still overrides the HTTP error message.
There seems to be a mistake in JBoss documentation, the correct property name is:
org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER
So in the standalone you should have something like this:
<system-properties>
<property name="org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER" value="true"/>
</system-properties>
I assume that the REST service is interpretted using RestEasy.
That provides a nice feature of injecting a HTTP response object using #Context:
The #Context annotation allows you to inject instances of javax.ws.rs.core.HttpHeaders, javax.ws.rs.core.UriInfo, javax.ws.rs.core.Request, javax.servlet.HttpServletRequest, javax.servlet.HttpServletResponse, javax.servlet.ServletConfig, javax.servlet.ServletContext, and javax.ws.rs.core.SecurityContext objects.
#Path("/")
public class MyService {
#Context org.jboss.resteasy.spi.HttpResponse response;
#GET #Path("/") public void myMethod(){
response.sendError(499, "The file was censored by NSA.")
}
}
But maybe you should rather consider using a proprietary HTTP header:
response.getOutputHeaders().putSingle("X-MyApp-Error",
"499 Our server is down and admin is on holiday. Mañana.");

Resources