How to customize the 401 in quarkus oidc? - java

I want to customize the 401/403 status code when access token is invalid in headers. I have create an exception mapper given below :
import io.quarkus.security.AuthenticationFailedException;
import org.jose4j.json.internal.json_simple.JSONObject;
import javax.annotation.Priority;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider//register as JAXRS provider
#Priority(1)
public class UnAuthorizedExceptionMapper implements ExceptionMapper<AuthenticationFailedException> {
#Override
public Response toResponse(AuthenticationFailedException exception) {
System.out.println("I m here:"+exception);
JSONObject ob=new JSONObject();
ob.put("errorCode",401);
ob.put("msg","Invalid access token");
return Response.status(Response.Status.UNAUTHORIZED)
.entity(ob.toJSONString())
.build();
}
}
But when I execute my code then above exception mapper is not executed instead following log is appearing in the console :
io.qua.oid.run.OidcProvider: (vert.x-eventloop-thread-1) Token verification has failed: The JWT is no longer valid - claim value.
How can I customize 401/403 status code msg in quarkus oidc.

Related

Return error in JSON body from webapplicationexception

I am trying to put my error message inside the body of an WebApplicationException.
I get the status code in postman but the body remains empty.
I have tried to add a mapper like this
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider
public class UnexpectedExceptionMapper implements ExceptionMapper<Exception> {
private static final transient ObjectMapper MAPPER = new ObjectMapper();
#Override
public Response toResponse(final Exception exception) {
ResponseBuilder builder = Response.status(Status.INTERNAL_SERVER_ERROR)
.entity(defaultJSON(exception))
.type(MediaType.APPLICATION_JSON);
return builder.build();
}
private String defaultJSON(final Exception exception) {
ErrorInfo errorInfo = new ErrorInfo(exception.getMessage(), exception.getMessage());
try {
return MAPPER.writeValueAsString(errorInfo);
} catch (JsonProcessingException e) {
return "{\"message\":\"An internal error occurred\"}";
}
}
}
But postman still tells me the body remains empty when I throw the error.
throw new Exception("test")
Is there a better way and why isn't this working?
The status gets updated but no body is passed with info in it.
The reason I need this is because I want to pass along the error that I get from another endpoint that I am calling.
EDIT:
I am also trying a simple Response version
but when i throw an error the entity is ignored as well
Response.status(Response.Status.UNAUTHORIZED).entity("Does not work").build();
Response.status(Response.Status.SEE_OTHER).entity("Does work").build();
I am throwing it like this for testing atm.
#POST
#Path("/place/actionid/{nr}")
#Produces("application/json")
public Response getActionIdForPlaceAssetOrder(#PathParam("nr") #Valid String nr, #Valid Body body) throws Exception {
Response response = Response.status(Response.Status.UNAUTHORIZED).entity("Some message here").build();
return response;
The above code throws the status but ignores the message. like i said any error message gets ignored. Is this maybe a jetty issue?
At the moment i would just be happy if i can get any message passed to postman.

React (CORS with Keycloak): no header set or header set twice, no OPTIONS call but HTTP status (cancelled)

I'm working on a react frontend using REST to communicate with a JAVA backend (Thorntail microservice), secured by Keycloak. Using GET and POST there's no problem, but trying to DELETE returns different errors depending on my settings. My default CorsFilter.java file has no settings:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
#Provider
public class CorsFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) {
}
}
My React REST function:
static deleteCourier = function (origin, courierNumber, endpoint, data, requestListener) {
keycloak.updateToken(5).then(function () {
const request = new XMLHttpRequest();
request.open("DELETE", HTTP_SCHEME + "://" + backendUrl + endpoint + "/" + origin + "/" + courierNumber);
request.setRequestHeader('Authorization', 'Bearer' + keycloak.token);
request.send();
}).catch(function () {
console.log('Failed to refresh the token, or the session has expired');
keycloak.init({onLoad: 'login-required'}).success(function (authenticated) {
if (authenticated) {
Rest.deleteCourier(origin, courierNumber, endpoint, data, requestListener);
} else {
console.log('kc not authenticated');
}
}).error(function () {
console.log('kc init failed');
});
}).then(function () {
Rest.updateData(data, requestListener, endpoint)
});
};
Calling the REST API of the backend:
import javax.ejb.EJB;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/device")
public class DeviceEndpoint {
#DELETE
#Path("/{origin}/{courierNumber}")
public Response delete(
#PathParam("origin") String origin,
#PathParam("courierNumber") String courierNumber) {
try {
deviceBF.removeCourier(origin, courierNumber);
} catch (PhoenixClientException e) {
return Response.ok(new WebApplicationException(e)).build();
}
return Response.ok().build();
}
}
In Keycloak "Web Origins" is set to "*".
Using this setup GET and POST work as intended. When trying to delete a device the network tab in my browser shows me as status: "(canceled)" and (Copied as fetch from Googles Chrome):
fetch("http://192.168.0.66:8282/auth/realms/customer/protocol/openid-connect/auth?response_type=code&client_id=mystique&redirect_uri=http%3A%2F%2F192.168.0.66%3A8080%2Fmystique%2Frest%2Fdevice%2Fdts%2Fd&state=22a1dba6-e12e-4852-8cba-0f86a4799d4f&login=true&scope=openid", {"credentials":"omit","referrer":"http://localhost:3000/","referrerPolicy":"no-referrer-when-downgrade","body":null,"method":"DELETE","mode":"cors"});
And the console output is:
Access to XMLHttpRequest at 'http://192.168.0.66:8080/mystique/rest/device/dts/d' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
GET and POST requests send OPTIONS request before the real request. In this case no such OPTIONS request is sent.
When i put a console output to my REST API I can see that it is not called. But the path should be ok. Tested the REST API via Postman and everything worked.
If I modify the CorsFilter.java file and add for example:
responseContext.getHeaders().add(
"Access-Control-Allow-Origin", "http://localhost:3000");
... the browser console shows to me:
Access to XMLHttpRequest at 'http://192.168.0.66:8080/mystique/rest/websocket-auth/token' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:3000, http://localhost:3000', but only one is allowed.
I've searched my code and also all my config files for the second entry and found nothing. I don't have any clue where it's from.
Adding the following to the CorsFilter.java seems to have no effect.
responseContext.getHeaders().add(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS");
In addition to the previously described behaviour the device, the courier is belonging to, is deleted. I'm not sure if this is corresponding to the problem with CORS or if it is a single problem. Changing a devices' data without deleting a courier (which is also done via REST) works as intended. But with the deletion of a courier before the update deletes my device.
I hope anybody can help me with this.

Catching JSON deserialization error on HTTP endpoint

I have a HTTP endpoint that receives and returns JSON content.
While testing for edge cases, I figured the endpoint returns the detailed content of the error when submitting a request with an unexpected JSON object.
In details, I expect a string and instead submit an object, e.g.:
I expect:
{
"myKey" : "someValue"
}
while I submit:
{
"myKey" : {}
}
When submitting that wrong content, what I expect my endpoint to return is:
a HTTP response with status code 400 and an empty content.
However, what I received was:
a HTTP response with status code 400 and the following content:
Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: org.apache.catalina.connector.CoyoteInputStream#5bcfg1cc; line: 1, column: 11] (through reference chain: com.example.MyRequest["myKey"])
I am using the RestEasy framework for my servlets and the Jackson library to serialize/deserialize JSON. I have tried to use an ExceptionMapper, to configure exception catching in my web.xml file, but I can apparently cannot catch that error and return an empty HTTP response.
How do I catch errors of JSON deserialization on my HTTP endpoint with RestEasy?
I am using below mapper and is working
In place of e.getLocalizedMessage() can use your message
package com.test.rest.service;
import java.util.Date;
import java.util.TimeZone;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider
public class DefaultExceptionHandler implements ExceptionMapper<Exception> {
public DefaultExceptionHandler() {
super();
TimeZone.setDefault(TimeZone.getTimeZone("IST"));
}
#Override
public Response toResponse(Exception e) {
// For simplicity I am preparing error xml by hand.
// Ideally we should create an ErrorResponse class to hold the error info.
StringBuilder response = new StringBuilder("<response>");
response.append("<status>ERROR</status>");
response.append("<message>"+e.getLocalizedMessage() + "</message>");
response.append("<time>" + new Date().toString() + "</time>");
response.append("</response>");
return Response.serverError().entity(response.toString()).type(MediaType.APPLICATION_XML).build();
}
}
My method :
#GET
#Path("consumeJSON")
#Consumes(MediaType.APPLICATION_JSON)
public String consumeJSON(Map<String, String> outputMap) {
return outputMap.get("Hello");
}

Spring boot REST Api JSON response - No serializer found for class org.json.JSONObject

I want to build a RestAPI and return a JSONObject as response.
Therefore I created this controller:
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api")
public class ApiController {
#Autowired
private AssemblyRepository assemblyRepo;
#RequestMapping(method=RequestMethod.POST)
public ResponseEntity<JSONObject> sendResponse(#RequestBody AppRequest request) {
//do some stuff with the data from requestBody
List<Assembly> assemblys = assemblyRepo.findAll();
JSONArray t_sub_objlist = new JSONArray();
for (Assembly a: assemblys) {
JSONObject object = new JSONObject();
object.put("obj_type", a.getObjType());
object.put("obj_key", a.getObjKey());
t_sub_objlist.put(object);
}
response.put("t_sub_objlist", t_sub_objlist);
return new ResponseEntity<JSONObject>(response, HttpStatus.OK);
}
}
When I print the response to the console everything looks good: {"t_sub_objlist":[{"obj_key":"MS","obj_type":"NT"},{"obj_key":"MV","obj_type":"NT"}]}
But when I want to access the API from outside, the first time after restarting the server, I get this error message:
There was an unexpected error (type=Not Acceptable, status=406).
Could not find acceptable representation
And if i try again then this message always comes up:
There was an unexpected error (type=Internal Server Error, status=500).
Could not write content: No serializer found for class org.json.JSONObject and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ); nested exception is com.fasterxml.jackson.databind.JsonMappingException
I don't know what I'm doing wrong here :)

Oauth implementation in REST

Hi I have implemanted one basic example of RESTful web services ,I am trying to implement Oauth client and Server (Provider) in my src folder of eclipse.
This is my OauthClient.java
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Path;
import com.sun.jersey.api.client.*;
import com.sun.jersey.oauth.client.OAuthClientFilter;
import com.sun.jersey.oauth.signature.OAuthParameters;
import com.sun.jersey.oauth.signature.OAuthSecrets;
import javax.ws.rs.core.*;
#Path("/OauthClient")
#RolesAllowed({"admin"})
public class OauthClient
{
#GET
#Path("/oauth_client")
#Produces(MediaType.TEXT_PLAIN)
public String oauthClient()
{
// establish the parameters that will be used to sign the request
OAuthParameters params = new OAuthParameters().consumerKey("hoge").signatureMethod("HMAC-SHA1").timestamp().nonce().version("1.1").token("sho1get");
// establish the secrets that will be used to sign the request
OAuthSecrets secrets = new OAuthSecrets().consumerSecret("testtest").tokenSecret("testtest");
Client client = Client.create();
// OAuth test server resource
WebResource resource = client.resource("http://localhost:8080/RestfulWS/rest/OauthServer/oauth_provider");
// if parameters and secrets remain static, filter can be added to each web resource
OAuthClientFilter filter = new OAuthClientFilter(client.getProviders(), params, secrets);
// filter added at the web resource level
resource.addFilter(filter);
System.out.println("==== Client =====");
// make the request (signing it in the process)
return resource.get(String.class);
}
}
and OauthServer.java is
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
//import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.oauth.server.OAuthServerRequest;
import com.sun.jersey.oauth.signature.OAuthParameters;
import com.sun.jersey.oauth.signature.OAuthSecrets;
import com.sun.jersey.oauth.signature.OAuthSignature;
import com.sun.jersey.oauth.signature.OAuthSignatureException;
#Path("/OauthServer")
#RolesAllowed({"admin"})
public class OauthServer {
#GET
#Path("/oauth_provider")
#Produces(MediaType.TEXT_PLAIN)
public String oauthProvider(#Context HttpContext context)
{
// wrap an existing request with server request
OAuthServerRequest request = new OAuthServerRequest(context.getRequest());
// baseline OAuth parameters for access to resource
OAuthParameters params = new OAuthParameters().readRequest(request);
// OAuth secrets to access resource
OAuthSecrets secrets = new OAuthSecrets().consumerSecret("hoge").tokenSecret("testtest");
// String timestamp = params.getTimestamp();
try {
/* The error occurs here. */
if (OAuthSignature.verify(request, params, secrets)) {
return "OK";
}
} catch (OAuthSignatureException e) {
// log.warning(e.getMessage());
// } catch (UniformInterfaceException e) {
//// log.warning(e.getMessage());
// } catch (Exception e) {
// log.warning(e.getMessage());
}
return "ERROR";
}
}
how to run this to achive Oauth authentication ,do we have to write some JSP? please suggest something.

Categories

Resources