I am using SttpAPI to send http requests, for one of my requests I need to use mutual SSL and i'm failing to find instructions how to implement it.
My backend is HttpClientZioBackend and there is no documentation for customizing the SSL Context for this kind of backend.
I will appreciate any help or samples.
val httpClient: HttpClient = HttpClient.newBuilder().sslContext(ssl).build()
HttpClientZioBackend.usingClient(client = httpClient, ...)
Related
I have a server that supports oauth2.0 with authorization code grant. server uses a self-signed certificate which i have manually installed on my android device. I am using the standard oauth2 library https://pub.dev/packages/oauth2 to perform oauth flow. Now everything works fine until this step
var client = await grant.handleAuthorizationResponse(responseUrl.queryParameters);
Where client has to make a post request using code fetched during authorization to get an access token. (see example from https://pub.dev/packages/oauth2#authorization-code-grant)
here I get a
HandshakeException: Handshake error in client (OS Error:
E/flutter (11483): CERTIFICATE_VERIFY_FAILED: self signed certificate in certificate
Now I already know how to allow my certificate or completely bypass certificate check using a HttpClient object. problem is, HttpClient is part of the library dart._http which is under http.dart, but the Client object oauth2.dart uses is from http library under client.dart. even though they both seem to be http clients and support post methods only the former supports a custom SecurityContext. and there's no way apparently I can cast one into the other. I have a two part question:
has anyone had a similar experience with this OAuth2.0 library or know if I can make it work with my self-signed certificate at all?
my latest idea is to create a custom client class extending http.BaseClient. since I noticed OAuth2.0 only uses post method from the client object I am thinking of overriding this method and use a HttpClient object to perform the post request. however, post method from HttpClient only takes a Uri whereas the one from BaseClient takes in url, headers, body, and encoding. any idea how I can set those on HttpClient's request?
I've also looked into oauth2_client but it doesn't even support a custom http client and oauth_dio but that one only implements client credentials grant whereas my server only supports authorization code grant.
DART OPTION 1
Looks like Dart has its own root certificates. The preferred option is to avoid writing any security code. Instead in a development environment, configure your self signed host's root certificate as trusted by Dart, according to this guide.
DART OPTION 2
Looks like Dart also supports the C# certificate callback model, where there is a Bad Certificate Callback that you can override. Not sure if you have to subclass HttpClient to achieve this.
/* PSEUDOCODE */
bool callback(X509Certificate cert, String host, int port) {
// Don't allow any exceptions in production
if (currentEnvironment == "DEV" && host == "myhost.com") {
return true;
}
// Use system
return base.callback(cert, host, port)
}
MOBILE OAUTH RECOMMENDATIONS
I see you are trying a few different libraries to solve your SSL trust problem. So I thought I'd point out what I look for in a mobile OAuth library, in line with mobile security standards, where these are the key recommendations:
Use Authorization Code Flow (PKCE)
Login via the System Browser
Prefer HTTPS redirect URLs (claimed HTTPS schemes)
I would at least aim to use the correct flow as above. I'm always a bit wary of new tech stacks and their OAuth libraries, since they often don't implement the recommended behaviour.
The preferred library from a security viewpoint is probably Flutter AppAuth. I have often used AppAuth libraries with self signed certificates, but the AppAuth library comes with these challenges:
Login on a System Browser is tricky to make reliable
Could be quite a bit more work than your stakeholders want to pay for
User experience aspects may be different to what people are used to
The Flutter bridge may come with its own problems
APP AUTH RESOURCES OF MINE
When you get some time it may be worth browsing my blog posts and running my Swift / Kotlin code samples, to see if you think any of this behaviour would be useful to you:
Android Behaviour
Steps to run Android Code Sample
iOS Behaviour
Steps to run iOS Code Sample
Completing some previous answers, I discovered that it's possible to pass an http.Client as a named argument in the oauth2.AuthorizationCodeGrant constructor.
So I made one that hooks a badCertificateCallback where I can implement some rules to ignore certificate validation under some particular circumstances (like calling 10.0.2.2 from an emulator in a dev environment). I think it could go as far as looking at some X509Certificate attributes to make the decision.
bool _certificateCheck(X509Certificate cert, String host, int port) =>
host == '10.0.2.2';
http.Client devEmulatorClient() {
var ioClient = new HttpClient()
..badCertificateCallback = _certificateCheck;
return new IOClient(ioClient);
}
final grant = oauth2.AuthorizationCodeGrant(
_clientId, _authorizationEndpoint, _tokenEndpoint,
httpClient: devEmulatorClient());
And later whereas this call use to throw the certificate check exception, it is now accepting my dev self-signed certificate.
var client =
await grant.handleAuthorizationResponse(responseUrl.queryParameters);
Required imports:
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart';
import 'package:flutter_web_auth/flutter_web_auth.dart';
import 'package:oauth2/oauth2.dart' as oauth2;
Context:
I'm designing an API gateway for some microservices. The idea is:
I intercept all the incoming requests in a global interceptor, analyze them for accessibility based on some permissions and finally use a simple redirect to call the actual URL. For example, I'm doing the following at one place:
if(permAction && authorizationService.hasPermission(user, micro, permAction)) {
if(microName != "umm"){
log.info("Successfully Authorized. Forwarding request to: ${micro?.ipAddress}${req}")
redirect(url: "${micro?.ipAddress}${req}", params: params)
return false
}
log.info("Successfully Authorized. Forwarding request to: ${req}")
return true
}
For simple GET requests, it works fine.
Questions
I'm having some problems with this approach.
When I hit the API from the front end, through my gateway, it gives a 302 and finally gives a 200 whether the original API exists or not in the original microservice. It gives a 404 if directly hit. What's happening?
There is an error in the console saying preflight cors disabled. What is that? It wasn't there without redirection.
I want all the GET, PUT and POST requests to redirect to various microservices.As far as I searched, redirect in grails only support a GET request.
Is there any other (better) approach to achieve the same? Maybe RESTClient on the server side or something similar.
Some of these questions have been partially answered but not in this context. Any insights about the problems in the context would be great.
Complete interceptor file is given here. This repository contains the whole project for the Grails API gateway.
Update : 4:30 pm
Resolved question 2 by allowing OPTIONSin the Allowed-Methods on the server side.
Update: 11:12 pm
For number 4, I implemented a rest client at the gateway backend. It works fine as far as functionality is concerned. However, with rest client at the backend, client requests gateway, which in turn requests the microservice, gets the response and gives it back to the client. This sort of two-way involvement of gateway is costly. This also resolves question number 3 by the way.
Is there any other better solution for a gateway in some other language or technology which can provide robustness out of the box?
I am trying to create a few restful webservices that will add a bit functionality to the company cisco phones. The basic idea is simple, the users get a small client on which they need to enter login and password. When they have done so, their phone/phones are 'registered' to my restful service and they get added functions on their phone. When they log out, they get unregistered. To provide the extra functions (like adjusted caller information etc etc) I need the Cisco AXL API. This is a SOAP based API. I have generated the java classes using the wsdl already. When I make a testclient using the generated classes, all works fine.
But here comes the problem: When I try to run a soap request while my application is deployed on my Tomcat 7 container, it doesn't work anymore.
The problem seems to be the AXLAPIService, which hangs when executing the following piece of code:
#WebEndpoint(name = "AXLPort")
public AXLPort getAXLPort() {
return super.getPort(new QName("http://www.cisco.com/AXLAPIService/", "AXLPort"), AXLPort.class);
}
In other words, i am not getting a port for the soap request and it makes the tomcat crash i f you wait long enough.
I went googling. Somebody on some forum once had a problem because of an out of date stax version. I adjusted the stax version in my POM and tried again, to no help.
I also read somewhere that the underlaying javax.xml.ws.Service actually has an enumeration of ports, and when you do getPort(), you will get the most appropiate port. I then looked up the default port for SOAP and that would be 80, just like the port used for RESTful services. Could it be that the soap service would be wanting port 80, but that it can't have it because it is already in use?
So, to summarize my question:
can it be that my restful services consume the same port that my soap
request would want to use?
if not, what could be the problem then and how should I fix it?
As additional information, this is how the axl wsdl defines the service:
<service name="AXLAPIService">
<port binding="s0:AXLAPIBinding" name="AXLPort">
<soap:address location="https://CCMSERVERNAME:8443/axl/"/>
</port>
I was thinking about changing the soap port myself. Some googling tells me I should do that in the wsdl but I wouldn't really know how. There is post already here but I fail to see how binding another portname could help me out....
As with so many things involving Cisco Telephony and their Administrative XmL (AXL), I found a workaround instead of an actual answer. Since a problem never really leaves my mind, I spent the rest of yesterday trying to find a solution for getting information out of that AXL thing.
Any actual answers to the above questions are still welcome though.
The workaround I found is this: Since SOAP can be seen as a special http POST request, it should be possible to do a SOAP call using a REST framework such as Jersey. You just need some extra code to make it work. I used the 'SoapProvider' from the link and for those who are also wrestling with this, I'll add my code:
public void doSoapRequest() throws SOAPException, JAXBException{
ClientConfig config = new DefaultClientConfig();
config.getClasses().add(SoapProvider.class);
Client c = Client.create(config);
c.addFilter(new LoggingFilter());
c.addFilter(new HTTPBasicAuthFilter("user", "password"));
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
SOAPElement bodyElement = body.addChildElement(envelope.createName("getCCMVersion", "", "http://www.cisco.com/AXL/API/8.5"));
message.saveChanges();
WebResource service = c.resource("https://youraxlmachine:8443/axl/");
// POST the request
ClientResponse cr = service.type(MediaType.TEXT_XML).header("SOAPAction", "\"https://youraxlmachine:8443/axl/getCCMVersion\"").post(ClientResponse.class, message);
message = cr.getEntity(SOAPMessage.class);
JAXBContext ctx = JAXBContext.newInstance(GetCCMVersionRes.class);
Unmarshaller um = ctx.createUnmarshaller();
GetCCMVersionRes response = (um.unmarshal(message.getSOAPPart().getEnvelope().getBody().extractContentAsDocument(), GetCCMVersionRes.class)).getValue();
System.out.println("HERE COMES THE VERSION!");
System.out.println(response.getReturn().getComponentVersion().getVersion());
}
I have left as many things unchanged as I could, except for the company specific details. This code works for getting the CCM version.
WARNING: Depending on how you perform the request, you might get a different result for the same request. I'll explain:
I have implemented other AXL methods as well, such as getUser. Before I even coding the Jersey soap service, I tried everything with SOAPUI. So I setup the SOAPUI so I could do RESTful requests to the AXL server. Using my restful setup in SOAPUI, I get the same results as I when would do the standard SOAP calls using both SOAPUI and my first implementation of a soapclient in java.
But when I use the jersey client to do the same getUser request, some important fields are missing from the result. I have no clue what could have caused this. For the request getPhone, I dont even get a valid response. So be warned.
I am trying to access a REST web service using HTTP GET request.
For a example following URI provides Rest web service that return all the available parts for the given category.
http://localhost:8080/mycompany/parts/category
I want to authenticate/authorize users who are accessing above REST request in each time and I want to pass User authentication details (User Name and Token) with the HTTP Get Request.
Is there a possibility to cater to the above requirement in REST HTTP GET request (using HTTP header or query parameters)?
or
Is it better to use HTTP POST instead of HTTP GET?
Since you are getting information, you should use "Get". Here's the code that I use (it is Restlet based) for adding the oauth_token to the request...
import org.restlet.data.Reference;
import org.restlet.ext.oauth.OAuthUser;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
Reference commitsRef = new Reference(Consts.RESOURCE_BASE + "commitments/");
OAuthUser u = (OAuthUser) request.getClientInfo().getUser();
String token = u.getAccessToken();
ref.addQueryParameter("oauth_token", token);
ClientResource commitsResource = new ClientResource(getContext(), commitsRef);
Representation commitsRep = commitsResource.get();
As mentioned, this is Restlet based, but there is probably something similar in the framework you are using. (And if you are not using a framework, Restlet can make this easier).
if you are using restlet than good because restlet have rich api for rest framework
but without this if you want to authenticate than
you can do same thing with GET or POST
but send your credential data trough cookie
and read same cookie using #CookieParam from server side
in this way you can easily authenticate user.
Does anyone know of a way to set a proxy on httpclient that comes with the wcf web api? I just want to be able to monitor all the traffic with fiddler.
You don't need to set a proxy on HttpClient to use fiddler. If you are hosting on localhost then check these tips here http://www.fiddler2.com/fiddler/help/hookup.asp#Q-LocalTraffic.
if you really want to use a proxy then you can do it like this,
var clientHandler = new HttpClientHandler();
clientHandler.Proxy = new WebProxy(new Uri("http://..."));
var httpClient = new HttpClient(clientHandler);