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;
Related
It is easy enough to set up basic authentication with JPA thanks to the related Quarkus tutorial.
But with the provided tutorial, when authentication fails, Quarkus sends a no-content 401 response, irrespective of whether the username provided in the request header exists (and the password did not match) or does not exist. I mean that Quarkus answers in exactly the same way (AFAICT) to curl -i -u "user:invalidpassword" localhost:8080/v0/users/me and curl -i -u "invaliduser:somepassword" localhost:8080/v0/users/me (assuming user is a valid username). I’d like these two answers to differ.
How can I tell Quarkus to send a different answer, when authentication fails, according to whether the username exists or does not exist? For example, send a 401 code with content “Invalid password” VS a 401 code with content “Unknown user”.
There are numerous discussions on SO about what codes one should send or whether one should indicate to the caller whether the username exists, and how to mitigate enumeration attacks in that case. But I could find no question about how to implement different answers with Quarkus.
I realize I could implement my own security interception and replace entirely the one provided by Quarkus, but I hope I do not need to go to such extreme solution: the Quarkus basic authentication facility is exactly what I need, apart from this little tweak.
I agree with Robert's comment; that said, if you'd like to customize the processing of the authentication failure (for example - provide a more informative secure log message, etc) then you can do it by disabling a proactive authentication and registering a custom JAX-RS ExceptionMapper:
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import io.quarkus.security.AuthenticationFailedException;
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFailedExceptionMapper implements ExceptionMapper<AuthenticationFailedException> {
#Context
UriInfo uriInfo;
#Override
public Response toResponse(AuthenticationFailedException exception) {
return Response.status(401).header("WWW-Authenticate", "Basic realm=\"Quarkus\"").build();
}
}
HTH
I get this error in my Ionic app when I run on my localhost with Chrome (ionic serve):
[Deprecation] getCurrentPosition() and watchPosition() no longer work on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS.
win # :8100/plugins/cordova-plugin-geolocation/www/android/geolocation.js:35
How to make it disappear ?
How to be sure there's no issue with a device ?
Try again using this plugin in your browser.
It allows to you request any site with ajax from any source, no matter http/https discrepancies and the like. Practically adds the Allow-Control-Allow-Origin: * header in the response.
Please keep in mind that this is a band-aid solution. Your server response has to actually have the 'Access-Control-Allow-Origin': '*' header, preferably with a more specific value than *.
Because switching to HTTPS can be painful or impossible depending on your architecture,
I found a workaround solution: you can use the Google Maps Geolocation API. Although it has usage limits, it does the job. You will need an browser API key, so don't forget to limit it's usage to your page hostname.
I use it as a fallback method to the getCurrentPosition() method if it fails. It allows me to make it work until I switch to HTTPS.
Here's the JSFiddles:
HTTP: getCurrentPosition() will fail and fall back to the API
HTTPS: getCurrentPosition() will succeed
When I try to access rest API using iPhone I have passed following parameter in URL and used both methods POST and GET but it displays Access Denied.
oauth_version="1.0",
oauth_signature_method="HMAC-SHA1",
oauth_nonce="B0dlzkfMWCAn0TJ",
oauth_timestamp="1366280813",
oauth_consumer_key="klkjylsiozbv6vfdsqtuheqo3kmqqzv2",
oauth_token="t9pefrwylmg7webyepsqepotlhzbytkp",
oauth_signature="NeOwbCLUPbIyF9ErnHoFQOl9%2Bwo%3D"
I have worked with REST Client plugin available for Firefox and Chrome, REST API is work well using REST Client plugin but not accessible in iPhone.
I am generating a random value for oauth_timestamp, oauth_signature and oauth_nonce then also REST API is displaying Access Denied.
Please provide suggestions.
//here final_sign is signature generated from following procedure.
$nonce = substr(md5(uniqid('nonce_', true)),0,16);
$temprealm="http://magentohost/api/rest/products";
$realm=urlencode($temprealm);
$oauth_version="1.0";
$oauth_signature_method="HMAC-SHA1";
$oauth_consumer_key="dfddfgdgdfgddf6qgygmyvw7e3";
$oauth_access_token="fdgdfgfdgdfg357gimpdnuejvcbtk51ni";
$oauth_method="GET";
$oauth_timestamp=time();
$algo="sha1";
$key="sb88hfdihyg25ipt1by559yzbj2m3861&s7uhaheu8nrx961oxg6uc3os4zgyc2tm"; //consumer secret & token secret //Both are used in generate signature
$data="oauth_consumer_key=".$oauth_consumer_key."&oauth_nonce=".$nonce."&oauth_signature_method=".$oauth_signature_method."&oauth_timestamp=".$oauth_timestamp."&oauth_token=".$oauth_access_token."&oauth_version=".$oauth_version;
$send_data=$oauth_method."&".$realm."&".urlencode($data);
$sign=hash_hmac($algo,$send_data,$key,1); // consumer key and token secrat used here
$fin_sign=base64_encode($sign);
echo $fin_sign;
From your question I understand that you use a random value for the signature and the nonce.
The latter would be fine, but a random signature would lead the receiver not to trust you as a legitimate client.
So, actually, you get the response you requested (;-)). But that does not solve your problem.
You have to generate a valid signature for the magento system.
I want users to be able to log in via HTTP Basic authentication modes.
The problem is that I also want them to be able to log out again - weirdly browsers just don't seem to support that.
This is considered to be a social-hacking risk - user leaves their machine unlocked and their browser open and someone else can easily visit the site as them. Note that just closing the browser-tab is not enough to reset the token, so it could be an easy thing for users to miss.
So I've come up with a workaround, but it's a total cludge:
1) Redirect them to a Logoff page
2) On that page fire a script to ajax load another page with dummy credentials:
$j.ajax({
url: '<%:Url.Action("LogOff401", new { id = random })%>',
type: 'POST',
username: '<%:random%>',
password: '<%:random%>',
success: function () { alert('logged off'); }
});
3) That should always return 401 the first time (to force the new credentials to be passed) and then only accept the dummy credentials:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOff401(string id)
{
// if we've been passed HTTP authorisation
string httpAuth = this.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(httpAuth) &&
httpAuth.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
// build the string we expect - don't allow regular users to pass
byte[] enc = Encoding.UTF8.GetBytes(id + ':' + id);
string expected = "basic " + Convert.ToBase64String(enc);
if (string.Equals(httpAuth, expected, StringComparison.OrdinalIgnoreCase))
{
return Content("You are logged out.");
}
}
// return a request for an HTTP basic auth token, this will cause XmlHttp to pass the new header
this.Response.StatusCode = 401;
this.Response.StatusDescription = "Unauthorized";
this.Response.AppendHeader("WWW-Authenticate", "basic realm=\"My Realm\"");
return Content("Force AJAX component to sent header");
}
4) Now the random string credentials have been accepted and cached by the browser instead. When they visit another page it will try to use them, fail, and then prompt for the right ones.
Note that my code examples are using jQuery and ASP.Net MVC, but the same thing should be possible with any technology stack.
There's another way to do this in IE6 and above:
document.execCommand("ClearAuthenticationCache");
However that clears all authentication - they log out of my site and they're logged out of their e-mail too. So that's out.
Is there any better way to do this?
I've seen other questions on this, but they're 2 years old - is there any better way now in IE9, FX4, Chrome etc?
If there is no better way to do this can this cludge be relied upon? Is there any way to make it more robust?
The short anser is:
There is no reliable procedure for achieving a "logoff" using
HTTP Basic or Digest authentication given current implemenations of basic auth.
Such authentication works by having the client add an Authorization header
to the request.
If for a certain resource the server is not satisfied with the credentials provided (e.g. if there are none), it will responde with a
"401 Unauthorized" status code and request authentication. For that purpose it will provide a WWW-Authenticate header with the response.
A client need not wait for a server requesting authentication.
It may simply provide an Authorization header based on some local
assumptions (e.g. cached information from the last successful attempt).
While your outlined approach on "clearing" out authentication info has a good chance of working with a wide range of clients (namely widespread browsers),
there is absolutely no guarantee that a nother client might be "smarter" and
simply discriminate proper authentication data for your "logout" page and any other pages of the target site.
You will recognize a similar "problem" with using client side certificate based authentication.
As long as there is no explicit support from clients you might fight on lost ground.
So, if "logoff" is a concern, move over to any session based authentication.
If you have access to the implementation of authentication on the server side you might be able implementing a functionality that will disregard authentication information presented with Authorization header (if still identical to what has been presented during current "session) on request of your application level code (or provide some "timout" after which any credentials will be re-requested), so that the client will ask the user for providing "new" credentials (performing a new login).
I have a use case where a UIWebView may need to connect with a web server secured with NTLM. I also have a use case where I already have the credentials to be passed. So instead of forcing the user to enter them, how do I perform the handshake with the UIWebView?
UPDATE:
Using this method here works well enough when you are doing simple GET requests, but utterly fails when doing POSTs, for the mere fact that it is doing a GET after it is posted.
The ASIHttpRequest and ASIWebPageRequest have the same problem. GET requests work wonders, but any POSTs just don't work. If only the world worked on just GET requests.
I have been able to use this method of including the username and password in the HTTP request string, but that is so grossly insecure as to defy reason for using it. Using a sniffer I am able to see the three-way handshake occur without any problems on both GET and POST requests.
You can set the default credential:
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost: _host
port: 80
protocol: #"http"
realm: _host
authenticationMethod:NSURLAuthenticationMethodNTLM];
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:[NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceForSession] forProtectionSpace:protectionSpace];
Now you can let your webviews do the request, and when it encounters your protenctionSpace it logs in using the given credentials
As of iOS 3.2 and 4.1, there is no public delegate for intercepting the NTLM challenge. There is, however, a private API that can be overriden to give proper support for this. Since this would put your application in danger of being rejected, I will forgo posting the code because it is of no worth for App Store development at the present time.
If you're willing to try some experimental code, you could use ASIWebPageRequest.
It would be a bit hacky, as you'd have to download the page content with ASIWebPageRequest, load it into a UIWebView, then capture any link clicks in the web view and repeat the process again (if the content at the URL requires authentication). Also, I think you'd have to manage your own history stack.
I don't think it would be easy, but it does seem doable, and it seems like it should work as long as the ASIWebPageRequest code isn't too buggy or limited.
UIWebView doesn't support authentication at all. Up to iPhone OS 3.1, you could add credentials to the central credential storage and UIWebView would at least work with basic authentication. But starting with iOS 4.0, I don't see any way to use authentication (except cookie or URL based forms authentication).