I'm tring to configure ad OAUTH2 provider with grails based on plugin grails-spring-security-oauth2-provider but upgraded to pring-Security-OAuth M6.
I was able to register clients and get authorization code using /oauth/authorize endpoint.
But I have a problem when I try to obtain access token, it seems it can't return json.
I call the access token endpoint with curl
curl -k -i -H "Accept: application/json" "https://graph.mysite.it/oauth/token?client_id=testApp&client_secret=testAppSecret&response_type=token&grant_typization_code&code=OJD7xf&redirect_uri=https%3A%2F%2Fgraph.mysiste.it%2Fxme"
And server reply with HttpMediaTypeNotAcceptableException Could not find acceptable representation.
Searching on google I have tried adding in resources.xml mvc:annotation-driven to
let spring register json jackson convertor, at this point the call return with HTTP/1.1 406 Not Acceptable "The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers"
Going into spring security oauth source I reached this controller TokenEndpoint.java
Debugging here I see the token is correctly generated.
Using groovy console I have tried manually colling jackson converter ad it worked:
import org.codehaus.jackson.map.ObjectMapper
import org.springframework.security.oauth2.common.OAuth2AccessToken
def mapper = new ObjectMapper()
def token = new OAuth2AccessToken('fdkshdlfhklsahdklfhksaldfkl')
mapper.writeValueAsString(token)
The json is correctly printed, so I can exclude a problem in jackson configuration.
The spring mvc controller is mapped in grails with
"/oauth/token"(uri:"/oauth/token.dispatch")
Where the problem is? Why grails can't return the json?
This is my Dependecy report
I faced the same problem, but declared annotationHandlerAdapter configuration in resource.groovy this way and it works:
import org.springframework.http.converter.ByteArrayHttpMessageConverter
import org.springframework.http.converter.FormHttpMessageConverter
import org.springframework.http.converter.StringHttpMessageConverter
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter
import org.springframework.http.converter.xml.SourceHttpMessageConverter
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
beans = {
annotationHandlerAdapter(RequestMappingHandlerAdapter){
messageConverters = [
new StringHttpMessageConverter(writeAcceptCharset: false),
new ByteArrayHttpMessageConverter(),
new FormHttpMessageConverter(),
new SourceHttpMessageConverter(),
new MappingJacksonHttpMessageConverter()
]
}
}
Solved.
In Spring the converters are declared in bean RequestMappingHandlerAdapter that was configured in grails core in file ControllersGrailsPlugin.
Declaring in resource.xml has no effect.
To add converters I have redefined the bean in a doWithSpring method of my grails plugin
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
import org.springframework.http.converter.StringHttpMessageConverter
import org.springframework.http.converter.ByteArrayHttpMessageConverter
import org.springframework.http.converter.FormHttpMessageConverter
import org.springframework.http.converter.xml.SourceHttpMessageConverter
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter
class MyGrailsPlugin {
def doWithSpring = {
annotationHandlerAdapter(RequestMappingHandlerAdapter){
messageConverters = [
new StringHttpMessageConverter(writeAcceptCharset: false),
new ByteArrayHttpMessageConverter(),
new FormHttpMessageConverter(),
new SourceHttpMessageConverter(),
new MappingJacksonHttpMessageConverter()
]
}
}
}
Related
I'm currently working on a senior project and we decided to use Spring Webflux for our backend and Google as our OAuth2.0 provider. I am currently trying to run some integration tests using Spock and Groovy on some endpoints that are secured behind OAuth2.0 authentication. The endpoint does not use the Authentication principal for anything, is just not supposed to be accessed by someone who isn't authenticated. However, reading the Spring documentation and I came across the method for a webTestClient to use a mock open id connect login in which I might not need to do mock the entire OAuth2 process, however, this is giving me a HTTP 302 status
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = "spring.main.web-application-type=reactive")
class UserControllerITSpec extends Specification {
#Autowired
ReactiveWebApplicationContext context
#Autowired
ApplicationContext applicationContext
#Autowired
WebTestClient client
#Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig()
.port(8077))
def setup() {
client = WebTestClient.bindToApplicationContext(context)
.configureClient()
.build()
}
def "getAllUsers is successful"() {
given: "a request"
def request = client.mutateWith(mockOidcLogin()).get().uri("/api/v2/users/getAllUsers")
stubFor(post("/graphql/")
.withHeader("Authorization", equalTo("F9v4MUqdQuWAh3Wqxe11mteqPfPedUqp78VaQNJt8DSt"))
.withHeader("content-type", equalTo("application/json"))
.withHeader("accept", equalTo("application/json"))
.withRequestBody(equalTo("""{
"query": "query { list_UserItems { _UserItems { _id email displayName appointments } } }",
"variables": null,
"operationName": null
}"""))
.willReturn(aResponse()
.withStatus(200)
.withBodyFile("vendiaResponses/getAllUsersResponse.json")))
stubFor(get("/oauth2/authorization/wiremock")
.willReturn(status(200)))
when: "the request is sent"
def response = request.exchange()
then: "an OK status is returned"
response.expectStatus()
.isOk()
}
}
Is my understanding of the mutateWith(mockOidcLogin()) method incorrect?
I'd consider using webEnvironment = SpringBootTest.WebEnvironment.MOCK
Are you sure about the Authentication implementation you have at runtime? (you can have Authentication auth auto-magically injected as #Controller method parameter and inspect it with your favorite debugger or log its class)
Isn't there anything of help in those samples: https://github.com/ch4mpy/spring-addons/tree/master/samples (look at those containing webflux in their name and focus on test classes ending with ItegrationTest in their name like this one)
If their isn't a test annotation for your spring Authentication type in the repo yet, open an issue. I might consider implement it.
I am getting below error for spring-security using Spring Boot 2.7.3
[or-http-epoll-1] o.s.s.w.s.a.AuthenticationWebFilter:
Authentication failed: An error occurred while attempting to decode the Jwt:
class com.nimbusds.jose.JWEHeader cannot be cast to class
com.nimbusds.jose.JWSHeader (com.nimbusds.jose.JWEHeader and
com.nimbusds.jose.JWSHeader are in unnamed module of loader
org.springframework.boot.loader.LaunchedURLClassLoader #43da41e)
I suspect the following cast inside this method is causing the error:
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.jwt.JwtValidators
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders
#Bean
fun jwtDecoder(properties: OAuth2ResourceServerProperties): ReactiveJwtDecoder {
val issuerUri = properties.jwt.issuerUri
val jwtDecoder = ReactiveJwtDecoders.fromOidcIssuerLocation(issuerUri) as NimbusReactiveJwtDecoder
val audienceValidator: OAuth2TokenValidator<Jwt> = AudienceValidator(audience)
val withIssuer: OAuth2TokenValidator<Jwt> = JwtValidators.createDefaultWithIssuer(issuerUri)
val withAudience: OAuth2TokenValidator<Jwt> = DelegatingOAuth2TokenValidator(withIssuer, audienceValidator)
jwtDecoder.setJwtValidator(withAudience)
return jwtDecoder
}
We're using Auth0 as a provider.
Turns out we were sending an Opaque Access token according to Auth0. We needed to specify the audience on our ApiProvider for React Frontend:
<Auth0Provider
domain={domain}
clientId={clientId}
redirectUri={window.location.origin}
onRedirectCallback={onRedirectCallback}
audience={auth0Audience}
>
Also needed to disable RDBAC as we're not using Scoped APIs:
Lastly jwt.io was a great tool in testing our JWT tokens. We were expecting RS256 Algorithm typed tokens, but were getting DIR types when getting opaque tokens when testing against it.
I have the following code:
case class X(s: String)
#RequestMapping(path = Array("/tagReads"), produces = Array("application/json"))
def tagReads(#RequestParam(value = "tagId") tagId:String): X = {
val response = X("Hello")
println(response)
response
}
curl -H "Accept: application/json localhost:8080/tagReads?tagId="1234" results in exactly what I would expect being println-ed in the spring boot app, but the response I get is {}.
If I change the return to just "Hello" and the type to String then I get "Hello" returned when I curl.
I don't like that I'm getting empty JSON the rest of the time. I am using spring-boot-starter-web:2.1.6-RELEASEand this is all wrapped in an #RestController annotated class.
There is nothing useful logged to the Spring Boot application console.
I'm stuck - all the examples I've seen suggest this should 'just work' - so any help is much appreciated
In order to support Scala Case Classes serialization, try to add the jackson-module-scala as your dependency and register the Scala module with the Jackson ObjectMapper e.g.
val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)
I'm trying to integrate Swagger 2 into my API, which is implemented with CXF newest version: 3.2.7.
I tried lots of tutorials, the CXF official documentation, others too (e.g. Spring Boot, Apache CXF, Swagger under JAX-RS).
The swagger official website does not help for me. The swagger OpenAPI 2.0 authentication doc is not working, neighter the OpenAPI 3.0.
It is not working with component schemes of Open API 3.0.0, so i stayed with the apiKeyDefinition.
The one, which is working now can be found in this thread, in the answer of #Naoj:
CXF Swagger2Feature adding securityDefinitions
With this solution the swagger ui appeared and also the Autorize button is showing.
I fill the authentication form, and after that, I try to send requests with the swagger-ui. The problem is, that the Authorization header does not appear in the request, so I got 401 response.
In the pom:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description-swagger</artifactId>
<version>3.2.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>3.20.1</version>
</dependency>
My swagger configuration looks like this:
#Configuration
public class SwaggerConfig {
#Bean
#ConfigurationProperties("swagger")
public Swagger2Feature swagger() {
return new ExtendedSwagger2Feature();
}
#Bean
#DependsOn("jaxRsServer")
public ServletContextInitializer initializer() {
return servletContext -> {
BeanConfig scanner = (BeanConfig) ScannerFactory.getScanner();
Swagger swagger = scanner.getSwagger();
servletContext.setAttribute("swagger", swagger);
};
}
}
The extended swagger feature is the following:
#Provider(value = Provider.Type.Feature, scope = Provider.Scope.Server)
public class ExtendedSwagger2Feature extends Swagger2Feature {
#Override
protected void addSwaggerResource(Server server, Bus bus) {
super.addSwaggerResource(server, bus);
BeanConfig config = (BeanConfig) ScannerFactory.getScanner();
Swagger swagger = config.getSwagger();
swagger.securityDefinition("Bearer", new ApiKeyAuthDefinition("authorization", In.HEADER));
}
}
I try to configure Bearer JWT token based authentication. My application.yml contains the following:
swagger:
basePath: /rest
title: Backend Application
description: Swagger documentation of Backend Application REST services
license:
licenceUrl:
contact:
resourcePackage: my.resource.package
scan: true
apiKeyAuthDefinition:
name: Authorization
in: header
type: http
I import the SwaggerConfig into my #SpringBootApplication class like this:
#Import(SwaggerConfig.class)
It is working, as i see, swagger appeared and the title and description field is filled with the properties of my yml.
What am I missing? Any suggestions would be appretiated.
Thanks in advance.
You can simplify your code and remove ExtendedSwagger2Feature and the initializer(). Modify your swagger() method as follows and the output will be the same:
#Bean
#ConfigurationProperties("swagger")
public Swagger2Feature swagger() {
Swagger2Feature swagger2Feature = new Swagger2Feature();
swagger2Feature.setSecurityDefinitions(Collections.singletonMap("bearerAuth",
new ApiKeyAuthDefinition("Authorization", In.HEADER)));
return swagger2Feature;
}
The reason for the token not being added to your request, is that securityDefinitions is just a declaration for the available schemes. You need to apply it to the operation by adding (to your TestResource interface):
#Api( authorizations = #Authorization( value = "bearerAuth" ))
You will notice a lock icon next to the operation in Swagger UI. Currently it's not there.
Btw. you should use the newer OpenApiFeature instead of the old Swagger2Feature. Happy to answer questions, if you have problems with it.
I am running Grails 3.0.11 and want to create Swagger-documentation for my REST endpoints. I added the SwaggyDoc-plugin to the dependencies in my build.gradle script by adding:
compile "org.grails.plugins:swaggydoc:0.26.0".
In IntelliJ I see the Swaggydoc-dependency added to my list of libraries.
After starting my Grails-application via the grails run-app command and opening my application by entering http://localhost:8080/api/ I get an 404 error telling the page does not exist.
Do I need to configure something more or to run something special to generate documentation? I already tried to open a ticket in the Git-project and contacting the author with no succes.
Update1: there seems to be a Grails 3-plugin (found at Versioneye?) which I added:
compile "org.grails.plugins:swaggydoc-grails3:0.26.0"
It does work half, by default some sort of Pet-demo is visible and it is failing on constraints in a domain and enums. Doesn't seem to work very well actually.
Update2: As pointed out by Dilip Krishnan I tried to use SpringFox, first I added the dependencies to my Gradle build file:
compile("io.springfox:springfox-swagger2:2.3.1")
compile("io.springfox:springfox-swagger-ui:2.3.1")
Then I added a new class called ApiDocumentationConfiguration with the following code:
#Configuration
#EnableSwagger2
public class ApiDocumentationConfiguration {
#Bean
public Docket documentation() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
#Bean
public UiConfiguration uiConfig() {
return UiConfiguration.DEFAULT;
}
private ApiInfo metadata() {
return new ApiInfoBuilder()
.title("My awesome API")
.description("Some description")
.version("1.0")
.contact("my-email#domain.org")
.build();
}
}
My Grails resources file contains the following code:
beans = {
apiDocumentationConfiguration(ApiDocumentationConfiguration)
}
Last step was starting the application and trying to load the end point which shows the Swagger front end:
http://localhost:8080/swagger-ui.html
It behind the scenes tries to load an other end point (containing the JSON I guess?) which loads
http://localhost:8080/v2/api-docs
This does show JSON data and I get end points for things like a basic error controller, health mvc, metrics mvc et cetera. But not my own annotated user controller which is annotated like follows:
#Api(value = "users", description = "Endpoint for user management")
class UserController {
// GET all users
#ApiOperation(value = "doStuff", nickname = "doStuff", response = User.class)
def index() {
respond User.list()
}
}
Seems I am almost there, but still missing something, is my annotation wrong or doesn't it scan my controllers?
Update3: in contact with one of the authors of SpringFox (Dilip Krishnan) to add support for Grails 3+ to SpringFox, see ticket. The reason it doesn't currently work is because SpringFox looks at MVC annotation, an adapter needs to be written to retrieve the endpoints from the controllers in Grails.
I have successfully used swaggydocs in both 2.4.x projects and 3.1.4.
In order to make it work in 3.x (tested on 3.1.4) you have to add
compile "org.grails.plugins:swaggydoc-grails3:0.26.0"
to gradle dependencies section. That makes swaggy available in your project.
Then add annotations to your controllers
#Api("test methods")
class TestController {
#ApiOperation(value = "some method description")
#ApiResponses([
#ApiResponse(code = 405, message = "Bad method. Only POST is allowed"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 400, message = "Invalid request json")
])
def testGetMethod() {
render([status: "OK"] as JSON)
}
Then mark your methods allowedMethods as follows
class TestController {
static allowedMethods = [testGetMethod: "GET", testPostMethod: "POST"]
NOTE this is really important - otherwise swaggy will mark every your method as GET. Swaggy doesn't respect neither httpMethod in ApiOperation annotation nor http method in url mappings.
Finally add your controller to urlmappings as swaggy checks url mappings to look for URLS. Note camelCase!
//NOTE small camelCase
//swaggy won't see urls correctly if you start controller name with capital letter
"/api/test/"(controller: "test", action: "testGetMethod")
"/api/test/"(controller: "test", action: "testPostMethod")
You can also add some api info in application.yml
swaggydoc:
contact: rafal#pydyniak.pl
description: sample swaggy app
You can find sample app (with dummy methods but point was to make swaggy work) at my github https://github.com/RafalPydyniak/Swaggy-example.
Also there are some more docs on how to use this api on http://rahulsom.github.io/swaggydoc/. I just wanted to show you how to install it (as it's quite tricky to make everything work)
Hope it helps!
I followed the same 2 steps:
1) add swagger2 dependencies
2) provide configuation
The problem is that sprinfox does not scan grails url mappings (see https://github.com/springfox/springfox/issues/1169#issuecomment-252259284)
To fix that I added standard spring annotations:
#Controller()
#RequestMapping(value="/path")
on the controller and
#RequestMapping(value="/resource")
on the method. After that sprinfox started to pick up my documentation.