SpringDoc Oauth2 Implicit flow nonce parameter required and auth fails - springdoc

I am able to enable the authorize button, but when I click on the authorize button, for oauth2 implicit flow it complains that the nonce parameter is missing.
The 'nonce' parameter is required for authorize requests with either the 'id_token' or 'token' response types
This is the code that I use to create the implicit flow
new OpenAPI()
.addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
.components(new Components()
.addSecuritySchemes(securitySchemeName,
new SecurityScheme()
.name(securitySchemeName)
.type(SecurityScheme.Type.OAUTH2)
.description("This API uses OAuth 2 with the implicit grant flow.")
.flows(new OAuthFlows()
.implicit(new OAuthFlow()
.authorizationUrl(authorizeUri)
.scopes(new Scopes()
.addString("read", "read")))
In Springfox to add the nonce parameter you create a bean for SecurityConfiguration and use the additionalQueryStringsParams() method.
SecurityConfiguration#additionalQueryStringParams()
where you would add nonce as the key and a random string in the value pair. I tried using the extensions on OAuthFlows and SecurityScheme, but it still doesn't seem to be working.
I am not sure what I am missing, any help is appreciated.

Ok I figured it out, there is no function to add extra query params. You just have to hardcode it into the string yourself so add it to the url.
.authorizaitonUrl(authroizeUri + "?nonce=\"asdf\"")

It is possible to configure a random nonce with the following entry in the properties application.yml file:
springdoc:
swagger-ui:
oauth:
additional-query-string-params:
nonce: ${random.uuid}
Or providing an equivalent org.springdoc.core.SwaggerUiOAuthProperties bean.
Tested with
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.13</version>
</dependency>

Related

I have installed OpenAPI 3 using springdoc, but the URL is strange. Can I change it to the expected value?

I have installed swagger-ui using the following artifacts in my Java project's pom file:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-security</artifactId>
<version>1.5.2</version>
</dependency>
I can view the swagger ui for my RESTful endpoints when I go to this URL
http://localhost:8081/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config
but not this link
http://localhost:8081/swagger-ui/index.html
Why is this? How can I change it back to the expected URL?
Firstly, the below dependency has nothing to do with the issue you are mentioning.
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-security</artifactId>
<version>1.5.2</version>
</dependency>
Now to answer your question.
The URL http://localhost:8081/swagger-ui/index.html comes from the Swagger-Core library upon which Springdoc is built and thus it will serve the default Petstore example. Though this page can be disabled using the below property in application.properties file.
springdoc.swagger-ui.disable-swagger-default-url=true
But that holds only for version v1.4.1 and above.
Why /v3/api-docs/swagger-config ?
Now in the URl http://localhost:8081/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config the query parameter configUrl specifies from where to fetch the Springdoc's Swagger-specific configuration file.
Now if you actually hit the URL http://localhost:8081/v3/api-docs/swagger-config, you'll get a config file that's similar to the one below
{
"configUrl": "/v3/api-docs/swagger-config",
"docExpansion": "none",
"urls": [
{
"url": "/v3/api-docs/default",
"name": "default"
}
],
"validatorUrl": ""
}
The urls object encompasses individual objects for each API-group. Each API group is shown in the Swagger-UI as shown below
Now, for each API group, these properties are used by Springdoc to render the Swagger-UI by fetching the OpenAPI specification file from the given url and the name is rendered as shown in the above image.
As for the docExpansion property, it's set to none as that setting was explicitly specified in my application.properties
Playing with Properties
As of Springdoc 1.6.0, the below property is blocked by default for security reasons. Please refer to the comment here for more info. Use springdoc.swagger-ui.queryConfigEnabled=true to enable it at own risk since 1.6.0.
Use the below property if you want to override the URL from where Springdoc's Swagger configuration file is fetched. Essentially the new URL should return a config file that's similar to the one returned from /v3/api-docs/swagger-config. Defaults to /v3/api-docs/swagger-config
springdoc.swagger-ui.configUrl=/mySpringdocConfig
Use the below property to override the URL where your Springdoc's Swagger-UI loads. Defaults to /swagger-ui.html, and that's why Swagger-UI is available at http://localhost:8081/swagger-ui.html
springdoc.swagger-ui.path=/mySwagger-UIPath
Conclusion
I don't think it's possible to get rid of the ?configUrl=/v3/api-docs/swagger-config from the URL, as it'll always be available and point to the location from where Sprinfdoc's configuration file will be fetched, which if not available will lead to an error in fetching the configuration file and thus make the Swagger-UI useless. Also, it's something you'll not want to get rid of as it's being used by the framework itself.
Refer here for a list of all the supported properties to customize the behavior of Springdoc.
You need to access the swagger ui at http://localhost:8081/swagger-ui.html instead of http://localhost:8081/swagger-ui/index.html which loads the pet store default swagger. You can check the work around at
https://github.com/springdoc/springdoc-openapi/issues/43
The url http://localhost:8081/swagger-ui.html will always get redirected to http://localhost:8081/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config.
You can customize with the following in application.properties
springdoc.api-docs.path=/api-docs
springdoc.swagger-ui.path=/swagger-ui-custom.html
Now the url http://localhost:8081/swagger-ui-custom.html will get redirected to http://localhost:8081/swagger-ui/index.html?configUrl=/api-docs/swagger-config.
Below configuration helped customising the paths:
springdoc.swagger-ui.path=docs/ui
springdoc.swagger-ui.url=/api/openapi.json
springdoc.swagger-ui.disable-swagger-default-url=true
/api is context path for application
In the browser, I can navigate to http://localhost:8080/docs/ui which further redirects to http://localhost:8080/docs/swagger-ui/index.html as the default behaviour (which seems to be unavoidable). However, configUrl is not present at the end of redirected url.
Dependency used:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.6</version>
</dependency>

Know intended RestController in Spring implementation of ResponseBodyAdvice

The Spring ResponseBodyAdvice allows for handling the JSON-Response. I want to write it to the logger but by this would also like to add the name of the RestController being invoked in this transaction.
Can anyone tell, whether the classname is somewhere available in the parameters of the instance of ResponseBodyAdvice.
Got it. The parameter "MethodParameter" supplied to the "beforeBodyWrite"-method contains the attribute "containingClass" which provides the name of the intended Controller (I found this with testing - did not find any docs about the use of this attribute until now). That will do for me

Xades4j - Unrecognized critical extension(s) in CRL

I'm using xades4j to verify a Signature. The CRL has two critical extensions (2.5.29.20, 2.5.29.35) and xades4j API reject the CRL.
Exception that throw xades4j API:
xades4j.providers.CannotBuildCertificationPathException: unable to find valid certification path to requested target
xades4j.providers.impl.PKIXCertificateValidationProvider.validate(PKIXCertificateValidationProvider.java:268)
xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:175)
How can I add an object identifier (OID) in xades4j?
TL;DR;: I don't think this is possible.
From the docs I was able to find, namely Java PKI Guide, additional OIDs can be handled using PKIXCertPathChecker. Currently, xades4j's PKIXCertificateValidationProvider doesn't include a way to register new checkers.
Do you know any other way this could be done?

Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String

I am using RestEasy client with jackson providers and getting the above error
clientside code is:
ClientRequest request = new ClientRequest(url);
request.accept(MediaType.APPLICATION_JSON);
ClientResponse<String> response = request.get(String.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
}
BufferedReader br =
new BufferedReader(new InputStreamReader(new ByteArrayInputStream(response.getEntity().getBytes())));
response.getEntity() is throwing ClientResponseFailure exception with the error being
Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String
My server side code is below:
#GET
#Path("/{itemId}")
#Produces(MediaType.APPLICATION_JSON)
public String item(#PathParam("itemId") String itemId) {
//custom code
return gson.toJSON(object);
}
You could try to add the following dependency to your maven pom.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>2.3.4.Final</version>
</dependency>
The problem actually is that RestEasy is unable to find the Jackson provider. I had to manually register it by the following code:
ResteasyProviderFactory instance=ResteasyProviderFactory.getInstance();
RegisterBuiltin.register(instance);
instance.registerProvider(ResteasyJacksonProvider.class);
Everything is working fine with this. But I am still unhappy with the solution as Resteasy is supposed to scan for the providers and register them automatically.
Client client = ClientBuilder.newBuilder().register(ResteasyJacksonProvider.class).build();
Just adding the line org.jboss.resteasy.plugins.providers.jackson.ResteasyJacksonProvider into META-INF/services/javax.ws.rs.ext.Providers file, solves the problem.
This file is included into resteasy-jackson-providers.jar but same file is also included into another jar, restasy-jaxrs.jar and for an executable jar file, that use both these jars, they are not merged !!
I had a similar problem and I realized that the problem was related with the version of resteasy-jackson-provider. I just moved from 3.0.4.Final to 3.0.5.Final and the problem disappeared.
Additionally I also realized that if I change the third line to the following the result was the expected with no need to change the dependencies.
Response response = request.get(Object.class).toString();
Things that had made work my code were that I added:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>&{resteasy.version}</version>
</dependency>
Beside this I don't know why but it seems that resteasy not initializing providers when client were created, this means that it required to init them manually:
ResteasyProviderFactory instance=ResteasyProviderFactory.getInstance();
RegisterBuiltin.register(instance);
instance.registerProvider(ResteasyJackson2Provider.class);
In general it's enough to run the client.
I don't know the full rationale behind that but we've hit the exact same problem (multiple times :P) and you need to change the MediaType to TEXT_PLAIN.
Or you can also let JAX-RS do the job for you: instead of doing gson.toJSON(object), simply return object and change your method signature to whatever class that is. JAX-RS (RESTEasy in your case) will automatically call Jackson (if it's properly configured) and serialize your object to json. Then on your client side you would request for that same class instead of String and everything should work on its own. I'm not particularly familiar with ClientRequest/Response so it might not work as I said; we use RESTEasy proxy functionality on the client side instead (see ProxyFactory). Nevertheless, JAX-RS/RESTEasy can do json serialize/deserialize automatically on the client side too so there's definetly a way.
If you really want to by-pass goodness of JAX-RS actually doing serialization for you (instead of manually calling GSON), use StreamingOutput (i.e. wrap outputter as StreamingOutput, return that object).

How do I get permitAll in Spring Security to NOT throw AuthenticationCredentialsNotFoundException in #Controller object?

I have a controller that has many actions and it specifies a default class-level #PreAuthorize annotation, but one of the actions I want to let anyone in (the "show" action).
#RequestMapping("/show/{pressReleaseId}")
#PreAuthorize("permitAll")
public ModelAndView show(#PathVariable long pressReleaseId) {
ModelAndView modelAndView = new ModelAndView(view("show"));
modelAndView.addObject("pressRelease",
sysAdminService.findPressRelease(pressReleaseId));
return modelAndView;
}
Unfortunately, Spring Security throws this exception:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:321)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:195)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64)
How can I get Spring Security to NOT throw this exception? I just want to let anyone in - non-authenticated users and authenticated users - Everyone.
My only solution is to put this method into another controller altogether with no #PreAuthorize at all... which will work, but that's stupid. I want to keep all my press release actions in the same controller.
Thanks for the help!
I guess you don't have anonymouse authentication filter enabled.
If you use namespace configuration and auto-config = "true", it should be enabled by default. If you don't use auto-config, you can enable anonymous filter as <security:anonymous />.
The normal way to allow anyone to access a controller method is to not annotate it with any Spring Security annotations; ie no #PreAuthorize no #Secured etc.
You should be able to simply remove #PreAuthorize from the show() method, any leave the annotation on the other methods in that controller. The other methods will remain secured regardless of what you do with the show() method.
The default changed with a newer version (version 2) of the spring security plugin. That means all controllers are secured by default (you need to login to see them). Removing #Secured will not work at all, it will still stay secured. You can change this default behaviour, but to me the new default behaviour makes sense because it is much more secure. And security is very important.
I only use
#Secured(['permitAll'])
for my controller, but it should also work for a method.