Spring boot Authorization server redirection issue to client after successful authentication via Facebook - spring-boot

I am trying to setup a spring boot Authorizaiton server which will have the internal user login and OAuth2 with facebook. I am facing the below issues -
If I make my Authorization server SessionCreationPolicy.STATELESS then after successful authentication from facebook control get stuck in Authorization server itself (Its not returning to the my client application while if SessionCreationPolicy.IF_REQUIRED then control returns to my client app).
When I am using SessionCreationPolicy.IF_REQUIRED then control returns and I can do a authorization_code flow but the jwt token generated by spring-security-jwt gives me only user_name information in token which is facebook user's id (not even name).
My local user authentication code works fine with code flow and I can customize my token with custom token enhancer and add other properties also but when I try to customize facebook principal object to my custom user I get error that string can not be casted to custom user object.
Please refer to the repo for details - dev repo
I am using below code for setup/jwt generation -
#EnableOAuth2Client // for Oauth setup
// jwt enhancer which gives me error when principal is converted to custom user
class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
Authentication auth = authentication.getUserAuthentication();
/* additionalInfo.put("email", ((CustomPrincipal)auth.getPrincipal()).getEmail());
additionalInfo.put("roles", ((CustomPrincipal)auth.getPrincipal()).getRoles());
additionalInfo.put("id", ((CustomPrincipal)auth.getPrincipal()).getId());*/
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
//SSO filter i am using -
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
client.getResource().getUserInfoUri(), client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(tokenServices);
// filter.setAuthenticationSuccessHandler(authenticationHandler);
return filter;
}
Any help is appreciated.
Thanks!

I was able to get an explanation for 2nd and 3rd point-
Since after the authentication is successful from Facebook; Spring boot authorization server stores authentication object as below format -
{
"authorities": [
{
"authority": "ROLE_USER"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "xyzxyzxyzxyzxyz",
"tokenValue": "xyzxyzxyzxyzxyz",
"tokenType": "bearer",
"decodedDetails": null
},
"authenticated": true,
"userAuthentication": {
"authorities": [
{
"authority": "ROLE_USER"
}
],
"details": {
"id": "xyzxyzxyzxyzxyz",
"name": "xyzxyzxyzxyzxyz",
"email": "xyzxyzxyzxyzxyz"
},
"authenticated": true,
"principal": "xyzxyzxyzxyzxyz",
"credentials": "N/A",
"name": "xyzxyzxyzxyzxyz"
},
"principal": "xyzxyzxyzxyzxyz",
"oauth2Request": {
"clientId": "xyzxyzxyzxyzxyz",
"scope": [],
"requestParameters": {},
"resourceIds": [],
"authorities": [],
"approved": true,
"refresh": false,
"redirectUri": null,
"responseTypes": [],
"extensions": {},
"grantType": null,
"refreshTokenRequest": null
},
so when I was casting my principal to custom principal I was getting the error since in above model principal is just a string.
Note - I still have no idea how i can customize the above authentication object to my customuser object.

Related

Customize response parameters of oauth response

I am currently working on a project that is using JDBCTokenStore. By default it return
{
"access_token": "<somedata>",
"token_type": "bearer",
"expires_in": <time>,
"scope": "read write"
}
Is there a way to customize this response?
I tried using TokenEnhancer, but that saves information in DB which is not what I want.
I just want to customize the response and send it back to end user (add an additional parameter like state of the user which changes dynamically based on some criteria).
{
"access_token": "<somedata>",
"token_type": "bearer",
"expires_in": <time>,
"scope": "read write"
"state": <true or false>
}
Any help here would be highly appreciated.
You can customize the token response using a custom TokenEnhancer implementation.
public class CustomTokenEnhancer implements TokenEnhancer {
private TokenEnhancer delegate;
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
OAuth2AccessToken result = delegate.enhance(accessToken, authentication);
result.getAdditionalInformation().put("custom_response_claim", "value");
return result;
}
}
Adding parameters to OAuth2AccessToken.additionalInformation map would be processed as additional parameters in token response.

How to log spring-webflux WebClient request + response details (bodies, headers, elasped_time)?

Basically, I want to log a request/response informations in one log containing bodies/headers with a Spring WebClient.
With Spring RestTemplate we can do it with a ClientHttpRequestInterceptor. I find about ExchangeFilterFunction for Spring WebClient but haven't managed to do something similar in a clean way. We can use this filter and log the request AND THEN the response but I need both on the same log trace.
Moreover, I haven't managed to get the response body with ExchangeFilterFunction.ofResponseProcessor method.
I expect a log like this (current implementation working with a ClientHttpRequestInterceptor) with all the informations I need:
{
"#timestamp": "2019-05-14T07:11:29.089+00:00",
"#version": "1",
"message": "GET https://awebservice.com/api",
"logger_name": "com.sample.config.resttemplate.LoggingRequestInterceptor",
"thread_name": "http-nio-8080-exec-5",
"level": "TRACE",
"level_value": 5000,
"traceId": "e65634ee6a7c92a7",
"spanId": "7a4d2282dbaf7cd5",
"spanExportable": "false",
"X-Span-Export": "false",
"X-B3-SpanId": "7a4d2282dbaf7cd5",
"X-B3-ParentSpanId": "e65634ee6a7c92a7",
"X-B3-TraceId": "e65634ee6a7c92a7",
"parentId": "e65634ee6a7c92a7",
"method": "GET",
"uri": "https://awebservice.com/api",
"body": "[Empty]",
"elapsed_time": 959,
"status_code": 200,
"status_text": "OK",
"content_type": "text/html",
"response_body": "{"message": "Hello World!"}"
}
Does anyone manage to do something like this with Spring WebClient ? Or how would one proceed to track request/reponses issue with a Spring WebClient ?
You can use filter(), something like this:
this.webClient = WebClient.builder().baseUrl("your_url")
.filter(logRequest())
.filter(logResponse())
.build();
private ExchangeFilterFunction logRequest() {
return (clientRequest, next) -> {
log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
clientRequest.headers()
.forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
return next.exchange(clientRequest);
};
}
private ExchangeFilterFunction logResponse() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
log.info("Response: {}", clientResponse.headers().asHttpHeaders().get("property-header"));
return Mono.just(clientResponse);
});
}
I don't think you can call .bodyToMono twice (once in the filter and then again where you use the client), so you might not be able to log that in the filter. As for the other details...
The WebClient config:
#Configuration
class MyClientConfig {
#Bean
fun myWebClient(): WebClient {
return WebClient
.builder()
.baseUrl(myUrl)
.filter(MyFilter())
.build()
}
}
The filter:
class MyFilter : ExchangeFilterFunction {
override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> {
return next.exchange(request).flatMap { response ->
// log whenever you want here...
println("request: ${request.url()}, response: ${response.statusCode()}")
Mono.just(response)
}
}
}

Serializing a request for a JSON PATCH with Jackson

I'm using Java Spring Boot as a gateway to an API with a PATCH endpoint that uses JSON Patch. Is it possible to use Jackson to serialize the JSON Patch document if there are different types? For example, if I want my JSON Patch document to have 3 operations where the values are of different type, is it possible for Jackson to serialize each operation in 3 different ways?
[
{
"op": "replace",
"path": "/name",
"value": "foo bar"
},
{
"op": "replace",
"path": "/tags",
"value": [
"done",
"complete"
]
},
{
"op": "replace",
"path": "/age",
"value": 25
},
]
I'm currently using a #RequestBody annotation to deserialize the request I receive from my frontend application.
// Controller
#PatchMapping(
path = "/images/{imageId}",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<ResponseEntity<String>> updateImage(
#RequestBody #NotBlank List<UpdateOp> request) {
return imageService.updateImage(request);
}
// Image Service
public Mono<ResponseEntity<String>> updateImage(List<UpdateOp> request) {
...
.body(BodyInserters.fromObject(objMapper.writeValueAsBytes(request)))
...
}
I'm a Spring Boot noob so open to suggestions and alternate solutions.

Spring boot using Postman when making a POST with foreign key is returning null

I am trying to make a post request using POSTMAN with Spring Boot
The relation between User and Role is (ManyToOne).
Why does role returns this: ("role":null)
POSTMAN VIEW:
{
"name": "usertest",
"lastname": "usertest",
"email": "usertest#gmail.com",
"role": {
"id": 1
}
}
POSTMAN OUTPUT:
{
"id": 29,
"name": "usertest",
"lastname": "usertest",
"email": "usertest#gmail.com",
"role": {
"id": 1,
"role": null
}
}
CONTROLLER:
#PostMapping("user")
public ResponseEntity<User> addUser(#RequestBody User user){
try {
userService.save(user);
HttpHeaders httpHeaders = new HttpHeaders();
return ResponseEntity.status(HttpStatus.CREATED)
.headers(httpHeaders)
.body(user);
}
catch (Exception e){
e.printStackTrace();
return null;
}
}
ENTITY USER:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, updatable = false)
private Role role;
You are mapping the input request body to a User object and persisting it into DB by calling userService.save(user) and you are NOT re-initializing user property with the persisted entity reference. So, it is a plain POJO, not a JPA managed entity. That's why the "role" property is still null.
You could return the persistent user from userService.save(user) method and return that from the Controller method. Also. you need to take care of loading Role inside User as it is a LAZY property.

How to use ResourceOwnerPasswordCredentialsGrant with swagger ui

I am using swagger, swagger ui with spring rest api to get a platform for testing/documenting the API, so I need to get oAuth2 authorisation working in swagger ui, I am using password grant with the authorisation server, so I had to use ResourceOwnerPasswordCredentialsGrant from the package springfox.documentation.servicewhich has a single parameter to its constructor, namely, the token url, I am setting that to the token endpoint in my authorisation server, but unfortunately, it does not persist token url and shows that as null in the authorisation window as follows:
I could not find any example to use this particular type of grant with swagger ui, any help is much appreciated.
This is my configuration
public Docket oauth() {
return new Docket(DocumentationType.SWAGGER_2).groupName("oauth")
.securitySchemes(Arrays.asList(userOAuthScheme())).securityContexts(Arrays.asList(securityContext()))
.select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any())
.paths(not(ant("/admin/**")))
.paths(not(ant("/admin.json")))
.paths(not(ant("/error/**")))
.paths(not(ant("/exception/**")))
.paths(not(ant("/ping/**"))).build();
}
private OAuth userOAuthScheme() {
List<AuthorizationScope> authorizationScopeList = new ArrayList<AuthorizationScope>();
GrantType grantType = new ResourceOwnerPasswordCredentialsGrant("http://localhost:8080/authServer/oauth/token");
return new OAuth("oauth2", authorizationScopeList, Arrays.asList(grantType));
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.any()).build();
}
#Bean
public SecurityConfiguration securityInfo() {
return new SecurityConfiguration("myClientId", "myClientSecret", "", "", "", ApiKeyVehicle.HEADER, "",
" ");
}
private List<SecurityReference> defaultAuth() {
final AuthorizationScope[] authorizationScopes = new AuthorizationScope[0];
return Arrays.asList(new SecurityReference("oauth2", authorizationScopes));
}
On the Swagger screen take care in the "Setup client authentication" section
Type: Basic auth/ Request Body
It depends on your implementation, in my case works Basic auth.
I dont use scopes but you can add it on
AuthorizationScope[] authorizationScopes
List<AuthorizationScope> authorizationScopeList

Resources