spring-boot-starter-graphql: getting 404 for /graphql endpoint - spring-boot

I am using the new Spring for Graphql in my Spring Boot application. My problem is, that the server always responds 404 when making a POST request to the /graphql endpoint.
These are my gradle dependencies
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-graphql'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.flywaydb:flyway-core'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
I followed the steps in this tutorial to create the controllers (formerly known as resolvers, I actually changed all my old Graphql resolvers to the new #QueryMapping and #MutationMapping annotations)
This is the code for my StepController
#Slf4j
#Controller
#RequiredArgsConstructor
public class StepController {
private final StepService stepService;
private final TokenService tokenService;
#QueryMapping
public ArrayList<Step> getSteps(String templateId) {
return stepService.getSteps(templateId);
}
#MutationMapping
public Step createStep(StepInput input, DataFetchingEnvironment env) {
return stepService.createStep(input, tokenService.getUserId(env));
}
}
I am also using WebSecurity in my project, changed my deprecated WebSecurityConfigurerAdapter to the more modern way of doing this by creating a Bean inside a SecurityConfig.class
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors()
.configurationSource(request -> {
var cors = new CorsConfiguration();
cors.setAllowedOrigins(List.of("http://localhost:4200", "electron://altair"));
cors.setAllowedMethods(List.of("GET", "POST", "DELETE", "OPTIONS"));
cors.setAllowedHeaders(List.of("*"));
cors.setAllowCredentials(true);
return cors;
})
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/graphql").permitAll()
.antMatchers(HttpMethod.GET, "/stomp/**").permitAll()
.anyRequest().authenticated()
.and()
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
My GraphQl Schema is structured in resources/graphql in a folder structure, I had no problem with this before I switched to the new Spring Boot Graphql
UPDATE To be on the safe side: I deleted the whole folder structure and replaced it with one schema.graphqls file that has just the Login mutation and a query that is never used, just to avoid the Query needed compiler error
type Mutation {
login(email: String!, password: String!): Login!
}
type Login {
token: String
}
// unused, just to make the compiler happy
type Query {
getLogin: Login
}
application.properties
spring.banner.location=classpath:logo.txt
spring.main.banner-mode=console
spring.output.ansi.enabled=ALWAYS
spring.main.allow-bean-definition-overriding=true
spring.mail.host=mail.blablabla
spring.mail.port=587
spring.mail.username=no-reply#blablabla
spring.mail.password=blablalba
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=false
spring.profiles.include=prod,dev
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL95Dialect
spring.jpa.hibernate.ddl-auto=validate
spring.servlet.multipart.max-file-size=512KB
I hope I provided enough information as to how my project is set up. I would really appreciate any help that can solve this issue. I searched the whole evening yesterday, finally now posting this question here. I hope someone can help me. Thanks

Please add this property in application.properties file and try again.
spring.graphql.graphiql.enabled=true

Related

How can I unit test AuthControllerIntegrationTest and mock JwtDecoder in Spock correctly?

I have a spring security Book store api which is working as expected.
My Security Config class look like this.
#Configuration
public class SecurityConfiguration {
#Bean
JwtDecoder jwtDecoder () {
return JwtDecoders.fromIssuerLocation("https://jwt-issuer-location")
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.antMatchers(HttpMethod.GET, '/api/auth/**')
.antMatchers(HttpMethod.GET, '/api/books').authenticated()
.antMatchers(HttpMethod.GET, '/api/users').authenticated()
.anyRequest().authenticated()
.and().httpBasic()
)
.oauth2ResourceServer().jwt()
return http.build();
}
#Bean
InMemoryUserDetailsManagerService() {
UserDetails user = User.builder().username("bob").password("password123").roles("api").build()
return new InMemoryUserDetailsManager(user)
}
}
For controller integrations like returning the list of users and books, I have used #MockBean for JwtDecoder like following
#MockBean
JwtDecoder jwtDecoder
then the test works.
However, for my integration test for authentication, it does not work in the same way. The integration looks like this
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutocConfigureMockMvc
class AuthController extends Specification {
MockMvc mockMvc
#Autowired
WebApplicationContext context
def setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build()
}
def "401 Unauthorized Exception"() {
given:
String url = "/api/books"
when:
ResultActions res = this.mockMvc.perform(get(url))
then:
response.andExpect(status().is4xxClientError())
}
}
For some reason, this auth controller integration test tries to make a http request for verifying the token sent by the user. the url is passed to jwtDecoders.fromIssuerLocation function.
I believe that is not an ideal way of make a unit test. It is not supposed to make any request to 3rd party
And since this is just the unit test, I passed some random url (I do not want to pass actual url for security purpose and push it. For the production app, I am pulling the actual url from environment variable).
Because of that, when mockMvc.perform runs, it throws I/O error on GET request for .....
I do not understand why other integration works by MockBean annotation on JwtDecoder. But it does not resolve AuthControllerIntegrationTest error.
Can someone tell I how I should mock the JwtDecoder instead?
Thank you in advance :)
I have tried adding MockBean on JwtDecoder but it does not work for AuthControllerIntegrationTest.
I would like to know how to mock the JwtDecoder correctly or maybe how to mock the JwtDecoders.fromIssuerLocation. But I do not know how to mock and return what.

Spring Security CORS Blocked By Policy

This should be easy, but of course since it's Spring Security, it's not.
I am attempting to access a relatively simple api running as a Spring Boot application from an Angular application. Angular makes the calls to the API just fine, but the backend blocks the request due to CORS policy:
I added the following to my Security configuration:
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://localtest.me:4200","http://localtest.me:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
#Profile("dev")
public SecurityFilterChain devFilterChain(HttpSecurity http) throws Exception {
// define a custom filter, irrelevant to question
// #formatter:off
http
.addFilterAfter(filter, ConcurrentSessionFilter.class)
.authorizeRequests()
.antMatchers("/path1","/path2","/logout").permitAll()
.anyRequest().authenticated()
.and()
.cors();
// #formatter:on
return http.build();
}
This STILL does not prevent the CORS policy block.
I've also tried putting various iterations of #CrossOrigin (with and without origins argument):
on the Controller class
on the endpoint method itself
Am I making a simple error causing this?
Edit: I added breakpoints to Spring's CorsFilter, and they are not being hit.
Edit: Adding screenshot by request:
try to add this at the head ( beggining of your controller)
#CrossOrigin(origins = "http://localhost:{youy_angular_application_port}")
public class YourRestController {
}
Not the proudest and most beautiful solution, but some months ago, I also needed to expose some endpoints to my frontend, so my angular application could send requests to them.
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/user").allowedOrigins("http://localhost:4200");
registry.addMapping("/post").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/").allowedOrigins("http://localhost:4200");
registry.addMapping("/user/{id}").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/{id}").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/user").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/user/").allowedOrigins("http://localhost:4200");
registry.addMapping("/post/user/{id}").allowedOrigins("http://localhost:4200");
registry.addMapping("/user/").allowedOrigins("http://localhost:4200");
}
};
}
The bean can get implemented where ever, since its a bean. In my case I implemented it in the MainApplication.java class.
Okay, here's what happened.
At end-of-day the day before yesterday, some numbskull checked in a change to application.properties changing the context-root of the application.
The application was no longer being served at http://localtest.me:8000/api , it was being servered at http://localtest.me:8000/appname/api.
Effectively, I had a 404 error as much as I had a CORS error. Chrome didn't tell me that the path didn't exist, it just kept telling me it was blocked.

How to validate tokens received from authorization server

I have a oauth flow in my project.
I retrieve in the front-end a jwt token and add it to each request in the authorization header.
Now I need to validate said token and verify the signature in my back-end which is a kotlin spring boot app.
I know how to validate the token with the jjwt library but I don't understand where the validation is done.
I have a certificate to validate the tokens with and just want to let the requests with a valid token to be treated.
I saw online that some people do it with a OncePerRequestFilter that they add to their SecurityConfiguration but I don't understand what's going on and how it works.
I tried searching for tutorials online but many of them make a backend that's both the authorization server and resource server. I just want the backend to be the resource server that checks with the certificate if the token is valid and treats the request if it is. How can I do that ?
For now this is my SecurityConfiguration :
package com.renaulttrucks.transfertprotocolbackend.security.config
import org.springframework.beans.factory.annotation.Value
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
#EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
#Value("\${security.enabled}")
val securityEnabled : Boolean? = false
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
if(!securityEnabled!!) {
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.and()
.csrf().disable()
.formLogin().disable()
} else {
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.and()
.csrf().disable()
.formLogin().disable()
}
}
}
Spring Security supports resource servers out-of-the-box when including the correct dependencies and configuration.
As #sdoxsee mentioned, there is a Spring Security sample that outlines how to create a resource server with a public key; however, I'll briefly summarize it here, though you can find more detail in the Spring Security reference.
First, you need to add the appropriate dependency. If you are a Spring Boot application, then you can add:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
Second, you either specify your key as a Boot property:
spring:
security:
oauth2:
resourceserver:
jwt:
public-key-location: classpath:my-key.pub
or, you configure a JwtDecoder with your public key directly:
#Configuration
class SecurityConfig {
#Value("${public.key.property}") val key : RSAPublicKey;
#Bean
fun jwtDecoder() : JwtDecoder {
return NimbusJwtDecoder.withPublicKey(this.key).build();
}
}
Either the Boot property or the JwtDecoder #Bean will introduce a filter automatically into the filter chain called BearerTokenAuthenticationFilter, so you don't need to create your own.

SpringBoot2 OAuth2 AuthorizationServer's Login Page is shown infinitely loop after login

I'm implemention OAuth2 System with Spring Boot 2.1.4 and Spring Security OAuth2.
I want to separate All Components Client, ResourceServer, AuthorizationServer)
so i create 3 projects each git repository.
in Client, I requested protected URL.
and Spring Security redirected me to Authorization Server's /oauth/authorize, and i redirected to Authorization Server's login page.
i tried login, and success.
and i redirected to my Client and redirected again to AuthorizationServer's login page agian. (infinitely loop)
following is my 3 Components(Client, AuthorizationServer, ResourceServer)' Configuration.
Client
gradle
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'java'
}
...
dependencies {
...
/** Spring Security **/
implementation 'org.springframework.boot:spring-boot-starter-security'
// https://mvnrepository.com/artifact/org.springframework.security.oauth.boot/spring-security-oauth2-autoconfigure
compile group: 'org.springframework.security.oauth.boot', name: 'spring-security-oauth2-autoconfigure', version: '2.1.4.RELEASE'
// https://mvnrepository.com/artifact/org.springframework.security/spring-security-oauth2-client
compile group: 'org.springframework.security', name: 'spring-security-oauth2-client', version: '5.1.5.RELEASE'
}
application.yml
...
spring:
security:
oauth2:
client:
provider:
teemo:
authorizationUri: http://localhost:8082/oauth/authorize
tokenUri: http://localhost:8082/oauth/token
userInfoUri: http://localhost:8081/me
registration:
teemo:
clientId: foo
clientSecret: bar
provider: teemo
authorizationGrantType: authorization_code
redirectUri: http://localhost:8080/oauth2/authorization/teemo
...
WebSecurityConfigurerAdapter config
#Configuration
#EnableOAuth2Client
public class WebSecurityConfigurerAdapterImpl extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll()
.anyRequest().authenticated()
.and().oauth2Login();
}
}
AuthorizationServer
gradle
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'java'
}
...
dependencies {
...
/** Spring Security **/
implementation 'org.springframework.boot:spring-boot-starter-security'
// https://mvnrepository.com/artifact/org.springframework.security.oauth.boot/spring-security-oauth2-autoconfigure
compile group: 'org.springframework.security.oauth.boot', name: 'spring-security-oauth2-autoconfigure', version: '2.1.4.RELEASE'
...
}
application.yml
spring:
security:
user:
name: user
password: user
...
WebSecurityConfigurerAdapter config
#Configuration
public class WebSecurityConfigurerAdapterImpl extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/oauth/authorize", "/oauth/token", "/login**").permitAll()
.and().formLogin().permitAll();
}
}
AuthorizationServerConfigurerAdapter config
#Component
#EnableAuthorizationServer
public class AuthorizationServerConfigurerAdapterImpl extends AuthorizationServerConfigurerAdapter {
private AuthenticationManager authenticationManager;
public AuthorizationServerConfigurerAdapterImpl(AuthenticationConfiguration authenticationConfiguration) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("foo")
.secret("bar")
//.authorities("USER")
.authorizedGrantTypes("authorization_code", "implicit", "refresh_token")
.autoApprove(true)
.redirectUris("http://localhost:8080/oauth2/authorization/teemo")
.scopes("read");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
}
ResourceServer
gradle
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'java'
}
...
dependencies {
...
/** Spring Security **/
implementation 'org.springframework.boot:spring-boot-starter-security'
// https://mvnrepository.com/artifact/org.springframework.security.oauth.boot/spring-security-oauth2-autoconfigure
compile group: 'org.springframework.security.oauth.boot', name: 'spring-security-oauth2-autoconfigure', version: '2.1.4.RELEASE'
// https://mvnrepository.com/artifact/org.springframework.security/spring-security-oauth2-resource-server
//compile group: 'org.springframework.security', name: 'spring-security-oauth2-resource-server', version: '5.1.5.RELEASE'
}
application.yml
...
security:
oauth2:
resource:
token-info-uri: http://localhost:8082/oauth/check_token
ResourceServerConfigurerAdapter config
#Configuration
#EnableResourceServer
public class ResourceServerConfigurerAdapterImpl extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/me").access("#oauth2.hasScope('read')");
}
#Primary
#Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("http://localhost:8082/oauth/check_token");
tokenService.setClientId("foo");
tokenService.setClientSecret("bar");
return tokenService;
}
}
and following is screenshot for infinitely looping after login.
infinitely image
how can i fix this? and i'm spring boot & security newbie.
There are several problems with your code. Among them:
You need to have a specific context path for each project (because the session tracking is cookie based and each session cookie must have a unique path)
There is no /me path mapping on resource server
The redirectUri should have the format <client_url>/login/oauth2/code/<provider_name>
I have made a pull request for each or your projects that fixed the issues.
For an example of OAUTH2 with String Boot take a look here
If you find something unclear with the modifications I have made, feel free to ask me.

Spring's basic security overwriting app's configuration

I have a Spring Boot 2 app with Spring security, as follow:
#SpringBootApplication(exclude = [(SecurityAutoConfiguration::class)])
class UntappdCqrsApplication
fun main(args: Array<String>) {
runApplication<UntappdCqrsApplication>(*args)
}
and the configuration class
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
class TokenConfiguration(
val jwtTokenProvider: JwtTokenProvider
) : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/users/signup").permitAll()
.anyRequest().authenticated()
http.apply(JwtTokenConfigurer(jwtTokenProvider));
}
}
There are two endpoints: POST users/signup and GET users/test.
According to my configuration, /signup should not require authentication and /test should, but both endpoints are accessible without any authentication.
If I add #EnableWebSecurity in my TokenConfiguration class, Spring now generates a default password and both endpoints are now protected.
I think I'm missing something here, but I have no idea what
You haven't provided the source or imports for your JwtTokenProvider or JwtTokenConfigurer classes, but it seems likely that your JwtTokenProvider is throwing an unchecked exception or even directly sending a response on authentication failure. This will prevent permitAll() from ever being triggered.
See my response to a similar question:
https://stackoverflow.com/a/46086769/873590

Resources