WSO2 API Manager - Handle Authentication failure with Custom Authenticator - authentication

I'm implementing a custom Authentication Handler for wso2-am following this guide https://docs.wso2.com/display/AM190/Writing+Custom+Handlers
But it's not clear how to handle the case when my authentication handler returns false. The sample code of the handleRequest is
public boolean handleRequest(MessageContext messageContext) {
try {
if (authenticate(messageContext)) {
return true;
}
} catch (APISecurityException e) {
e.printStackTrace();
}
return false;
}
If I try calling an API with valid credentials everything goes well (the method returns true) and I get an "HTTP 200 OK" response. If I try with invalid credentials the method returns false but I get an HTTP 202 ACCEPTED" response. I would like to receive another response code (for example 400). How do I handle this authentication failure path?
Thank you.

Ideally you need to call handleAuthFaliure method with message context.
private void handleAuthFailure(MessageContext messageContext, APISecurityException e)
When you call that method please create APISecurityException object(as listed below) and pass it. Then error code and error messages will be picked from there and automatically send error to client(by default we return 401 for security exceptions and if defined then send defined error code and message).
public class APISecurityException extends Exception {
private int errorCode;
public APISecurityException(int errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public APISecurityException(int errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}
Hope this will work for you.
Thanks
sanjeewa.

Related

How do I hook into micronaut server on error handling from a filter?

For any 4xx or 5xx response given out by my micronaut server, I'd like to log the response status code and endpoint it targeted. It looks like a filter would be a good place for this, but I can't seem to figure out how to plug into the onError handling
for instance, this filter
#Filter("/**")
class RequestLoggerFilter: OncePerRequestHttpServerFilter() {
companion object {
private val log = LogManager.getLogger(RequestLoggerFilter::class.java)
}
override fun doFilterOnce(request: HttpRequest<*>, chain: ServerFilterChain): Publisher<MutableHttpResponse<*>>? {
return Publishers.then(chain.proceed(request), ResponseLogger(request))
}
class ResponseLogger(private val request: HttpRequest<*>): Consumer<MutableHttpResponse<*>> {
override fun accept(response: MutableHttpResponse<*>) {
log.info("Status: ${response.status.code} Endpoint: ${request.path}")
}
}
}
only logs on a successful response and not on 4xx or 5xx responses.
How would i get this to hook into the onError handling?
You could do the following. Create your own ApplicationException ( extends RuntimeException), there you could handle your application errors and in particular how they result into http error codes. You exception could hold the status code as well.
Example:
class BadRequestException extends ApplicationException {
public HttpStatus getStatus() {
return HttpStatus.BAD_REQUEST;
}
}
You could have multiple of this ExceptionHandler for different purposes.
#Slf4j
#Produces
#Singleton
#Requires(classes = {ApplicationException.class, ExceptionHandler.class})
public class ApplicationExceptionHandler implements ExceptionHandler<ApplicationException, HttpResponse> {
#Override
public HttpResponse handle(final HttpRequest request, final ApplicationException exception) {
log.error("Application exception message={}, cause={}", exception.getMessage(), exception.getCause());
final String message = exception.getMessage();
final String code = exception.getClass().getSimpleName();
final ErrorCode error = new ErrorCode(message, code);
log.info("Status: ${exception.getStatus())} Endpoint: ${request.path}")
return HttpResponse.status(exception.getStatus()).body(error);
}
}
If you are trying to handle Micronaut native exceptions like 400 (Bad Request) produced by ConstraintExceptionHandler you will need to Replace the beans to do that.
I've posted example here how to handle ConstraintExceptionHandler.
If you want to only handle responses itself you could use this mapping each response code (example on #Controller so not sure if it works elsewhere even with global flag:
#Error(status = HttpStatus.NOT_FOUND, global = true)
public HttpResponse notFound(HttpRequest request) {
<...>
}
Example from Micronaut documentation.
Below code I used for adding custom cors headers in the error responses, in doOnError you can log errors
#Filter("/**")
public class ResponseCORSAdder implements HttpServerFilter {
#Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
return this.trace(request)
.switchMap(aBoolean -> chain.proceed(request))
.doOnError(error -> {
if (error instanceof MutableHttpResponse<?>) {
MutableHttpResponse<?> res = (MutableHttpResponse<?>) error;
addCorsHeaders(res);
}
})
.doOnNext(res -> addCorsHeaders(res));
}
private MutableHttpResponse<?> addCorsHeaders(MutableHttpResponse<?> res) {
return res
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "OPTIONS,POST,GET")
.header("Access-Control-Allow-Credentials", "true");
}
private Flowable<Boolean> trace(HttpRequest<?> request) {
return Flowable.fromCallable(() -> {
// trace logic here, potentially performing I/O
return true;
}).subscribeOn(Schedulers.io());
}
}

ASP.NET Core: global handling for showing exception message in current page

In my application I have a set of errors that are "expected" and thus they shouldn't redirect the user to a full error page when they occur. For example, I would like to show the error message in a red box above the current page.
I can easily catch an error on a model action and show the message on the current view, but I was wondering how to do it globally.
I tried a custom middleware with TempData and a filter with ModelState, but can't wrap my head around how to actually pass the error data back to the current page.
This works for a single model (setting the error data in TempData):
public async Task<IActionResult> OnPost() {
try {
// methodThatThrows
}
catch (ApplicationError e) {
TempData["Error"] = e.Message;
return RedirectToPage("Current_Page");
}
return RedirectToPage("Other_Page");
}
For some reason, this doesn't work (in a global middleware), as TempData is empty when the redirect completes. Also setting the middleware doesn't really work with showing the other, more critical errors in the normal error page:
public class ApplicationErrorMiddleware {
private readonly RequestDelegate _next;
private readonly ITempDataDictionaryFactory _tempFactory;
public ApplicationErrorMiddleware(RequestDelegate next, ITempDataDictionaryFactory tempFactory) {
_next = next;
_tempFactory = tempFactory;
}
public async Task InvokeAsync(HttpContext httpContext) {
try {
await _next(httpContext);
}
catch (ApplicationError ex) {
HandleError(httpContext, ex);
}
}
private void HandleError(HttpContext context, ApplicationError error) {
var tempData = _tempFactory.GetTempData(context);
tempData.Add("Error", error.Message);
context.Response.Redirect(context.Request.Path);
}
}
By the tip of #hiiru, I went through a wild goose chase through the configuration options to find a working solution.
My issue was a missing call from the middleware HandleError-method:
private void HandleError(HttpContext context, ApplicationError error) {
var tempData = _tempFactory.GetTempData(context);
tempData.Add("Error", error.Message);
tempData.Save(); // this call was missing
context.Response.Redirect(context.Request.Path);
}
After popping that in there, the tempdata is sent with the redirection back to the original page. Note that this is using the default cookie-based temp data, so no specific configuration is needed.
Now, this works, but it might not be the best way to do this.

Lagom http status code / header returned as json

I have a sample where I make a client request to debug token request to the FB api, and return the result to the client.
Depending on whether the access token is valid, an appropriate header should be returned:
#Override
public ServerServiceCall<LoginUser, Pair<ResponseHeader, String>> login() {
return this::loginUser;
}
public CompletionStage<Pair<ResponseHeader, String>> loginUser(LoginUser user) {
ObjectMapper jsonMapper = new ObjectMapper();
String responseString = null;
DebugTokenResponse.DebugTokenResponseData response = null;
ResponseHeader responseHeader = null;
try {
response = fbClient.verifyFacebookToken(user.getFbAccessToken(), config.underlying().getString("facebook.app_token"));
responseString = jsonMapper.writeValueAsString(response);
} catch (ExecutionException | InterruptedException | JsonProcessingException e) {
LOG.error(e.getMessage());
}
if (response != null) {
if (!response.isValid()) {
responseHeader = ResponseHeader.NO_CONTENT.withStatus(401);
} else {
responseHeader = ResponseHeader.OK.withStatus(200);
}
}
return completedFuture(Pair.create(responseHeader, responseString));
}
However, the result I get is:
This isn't really what I expected. What I expect to receive is an error http status code of 401, and the json string as defined in the code.
Not sure why I would need header info in the response body.
There is also a strange error that occurs when I want to return a HeaderServiceCall:
I'm not sure if this is a bug, also I am a bit unclear about the difference between a ServerServiceCall and HeaderServiceCall.
Could someone help?
The types for HeaderServiceCall are defined this way:
interface HeaderServiceCall<Request,Response>
and
CompletionStage<Pair<ResponseHeader,Response>> invokeWithHeaders(RequestHeader requestHeader,
Request request)
What this means is that when you define a response type, the return value should be a CompletionStage of a Pair of the ResponseHeader with the response type.
In your code, the response type should be String, but you have defined it as Pair<ResponseHeader, String>, which means it expects the return value to be nested: CompletionStage<Pair<ResponseHeader,Pair<ResponseHeader, String>>>. Note the extra nested Pair<ResponseHeader, String>.
When used with HeaderServiceCall, which requires you to implement invokeWithHeaders, you get a compilation error, which indicates the mismatched types. This is the error in your screenshot above.
When you implement ServerServiceCall instead, your method is inferred to implement ServiceCall.invoke, which is defined as:
CompletionStage<Response> invoke()
In other words, the return type of the method does not expect the additional Pair<ResponseHeader, Response>, so your implementation compiles, but produces the incorrect result. The pair including the ResponseHeader is automatically serialized to JSON and returned to the client that way.
Correcting the code requires changing the method signature:
#Override
public HeaderServiceCall<LoginUser, String> login() {
return this::loginUser;
}
You also need to change the loginUser method to accept the RequestHeader parameter, even if it isn't used, so that it matches the signature of invokeWithHeaders:
public CompletionStage<Pair<ResponseHeader, String>> loginUser(RequestHeader requestHeader, LoginUser user)
This should solve your problem, but it would be more typical for a Lagom service to use domain types directly and rely on the built-in JSON serialization support, rather than serializing directly in your service implementation. You also need to watch out for null values. You shouldn't return a null ResponseHeader in any circumstances.
#Override
public ServerServiceCall<LoginUser, Pair<ResponseHeader, DebugTokenResponse.DebugTokenResponseData>> login() {
return this::loginUser;
}
public CompletionStage<Pair<ResponseHeader, DebugTokenResponse.DebugTokenResponseData>> loginUser(RequestHeader requestHeader, LoginUser user) {
try {
DebugTokenResponse.DebugTokenResponseData response = fbClient.verifyFacebookToken(user.getFbAccessToken(), config.underlying().getString("facebook.app_token"));
ResponseHeader responseHeader;
if (!response.isValid()) {
responseHeader = ResponseHeader.NO_CONTENT.withStatus(401);
} else {
responseHeader = ResponseHeader.OK.withStatus(200);
}
return completedFuture(Pair.create(responseHeader, response));
} catch (ExecutionException | InterruptedException | JsonProcessingException e) {
LOG.error(e.getMessage());
throw e;
}
}
Finally, it appears that fbClient.verifyFacebookToken is a blocking method (it doesn't return until the call completes). Blocking should be avoided in a Lagom service call, as it has the potential to cause performance issues and instability. If this is code you control, it should be written to use a non-blocking style (that returns a CompletionStage). If not, you should use CompletableFuture.supplyAsync to wrap the call in a CompletionStage, and execute it in another thread pool.
I found this example on GitHub that you might be able to adapt: https://github.com/dmbuchta/empty-play-authentication/blob/0a01fd1bd2d8ef777c6afe5ba313eccc9eb8b878/app/services/login/impl/FacebookLoginService.java#L59-L74

how to return something from a Ninject Interceptor

I have written a common validator as part of Ninject interceptor. My requirement is that I have to return a response object, just like how any service method in my project returns, for consistency sake. By returning a response object also helps me to send back an appropriate validation message when the validator fails. How do I do that in the interceptor? I understood that the Intercept() returns nothing. I tried throwing an exception but I don't know where to catch it. Can someone help me?
public void Intercept(IInvocation invocation)
{
var validationFails = false;
if (validationFails)
{
// return an object
// response.ErrorMessage = "Validation Error"
// Or throw exception, but where should I catch it
throw new Exception(statusMessage);
}
else
{
invocation.Proceed();
}
}
Assign the ReturnValue and don't call Proceed when validation fails.
public class MyRequestHandler
{
Response ProcessRequest(string input) { return new Response(); }
}
public MyValidationInterceptor : IInterceptor
{
public void Intercept( IInvocation invocation )
{
if (NeedsValidation(invocation.Method) &&
!IsValidRequest((string)invocation.Arguments[0]))
{
invocation.ReturnValue =
new Response { ErrorMessage = "Validation Error" };
return;
}
invocation.Proceed();
}
}
I had to hook up my interceptor to business layer methods, instead of service methods, and am able to return proper return value as part of my response.

vaadin: getting user logindata from external page

i wrote a normal html login form, that forwards to a vaadin project, where i want to receive the username and password and check if its valid. but i have problems getting this request.
when i add a requesthandler in the init() method of my UI class, i can only get the request data after the second call of the vaadin page (because at the first call of init, the hander ist not added yet)
#Override
protected void init(VaadinRequest vaadinRequest) {
setContent(new MainComponent());
VaadinSession.getCurrent().addRequestHandler(
new RequestHandler() {
#Override
public boolean handleRequest(VaadinSession vaadinSession, VaadinRequest vaadinRequest, VaadinResponse vaadinResponse) throws IOException {
String username = vaadinRequest.getParameter("username");
return false;
}
});
so i tried to overwrite the VaadinServlet method doPost, but it does not get triggered. when i overwrite the methode service(HttpServletRequest request, HttpServletResponse response), this method is triggered a serval times for each request, so also not a good place to get just the userdata.
so whats the right way to solve this problem?
i dont't know if this is the best solution, but at least it works. maybe this helps someone.
here a short explanation what i do. i retrieve the posted username and password from the post values of my plain html login formular from another url and see if it is existing in the database. if it exists, it returns the result, otherwise the value ERROR.
i extended the VaadinServlet and overwrote the method service like this
#Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
super.service(request, response);
String username = request.getParameter("username");
if(username != null) { // called several times, only set when username is returned, otherwise the value remains "error"
String password = request.getParameter("password");
this.result = getResult(username, Encrypter.encryp(password));
}
}
and this is inside my class extended from UI
#Override
protected void init(VaadinRequest vaadinRequest) {
MyServlet myServlet = (MyServlet) VaadinServlet.getCurrent();
String result = myServlet.getResult();
if(result .equals(MyServlet.ERROR)){ // check if the result set in the servlet is valid, otherwise forward to the loginpage
goToLogin();
myServlet.resetResult();
return;
}
myServlet.resetResult();
...
}
To whom it may concern - obtaining request and response in Vaadin 8 (which might be also available in Vaadin 7):
VaadinServletRequest vsRequest = (VaadinServletRequest) VaadinService.getCurrentRequest ();
HttpServletRequest httpServletRequest = vsRequest.getHttpServletRequest ();
VaadinServletResponse vsResponse = (VaadinServletResponse) VaadinService.getCurrentResponse ();
HttpServletResponse httpServletResponse = vsResponse.getHttpServletResponse ();
You can read the request parameter directly through the VaadinRequest object that's passed into init():
#Override
protected void init(VaadinRequest vaadinRequest) {
setContent(new MainComponent());
String username = vaadinRequest.getParameter("username");
}
It work for me perfect:
User is my simple class with username, name etc.
setting logged user in session:
public void setLoggedUser(User loggedUser) {
this.loggedUser = loggedUser;
getUI().getSession().getSession().setAttribute("loggedUser", loggedUser);
}
reading user:
loggedUser = (User) getUI().getSession().getSession().getAttribute("loggedUser"); //return null if not logged in