Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide default implementation for Basic / Bearer authentication #2143

Closed
dherges opened this issue Dec 22, 2015 · 18 comments
Closed

Provide default implementation for Basic / Bearer authentication #2143

dherges opened this issue Dec 22, 2015 · 18 comments

Comments

@dherges
Copy link

dherges commented Dec 22, 2015

Hi,

we all know that authentication can be handled using Authenticators and/or Interceptors. Would it be useful to provide default implementations for Basic auth and Bearer token auth. Guess this could be common tasks that are performed by many client applications.

Impl for interceptor could look like this, passing the credentials in the constructor ... the authenticator could use something of a Callback interface so that the application can provide its credentials

wdyt?

import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import java.io.IOException;

public class BasicAuthenticationInterceptor implements Interceptor {

    private final String userName;
    private final String password;

    public BasicAuthenticationInterceptor(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        final Request request = chain.request()
                .newBuilder()
                .addHeader("Authorization", Credentials.basic(userName, password))
                .build();

        return chain.proceed(request);
    }

}
import com.squareup.okhttp.Authenticator;
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import java.io.IOException;
import java.net.Proxy;
import java.net.URLEncoder;

/** Inspired by https://github.com/square/okhttp/wiki/Recipes */
public class BasicAuthenticationAuthenticator implements Authenticator {

    public interface Callback {

        String username();

        String password();
    }

    private final Callback callback;
    private final int maxRetries;

    public BasicAuthenticationAuthenticator(Callback callback) {
        this(callback, 3);
    }

    public BasicAuthenticationAuthenticator(Callback callback, int maxRetries) {
        this.callback = callback;
        this.maxRetries = maxRetries;
    }

    @Override
    public Request authenticate(Proxy proxy, Response response) throws IOException {
        if (responseCount(response) >= maxRetries) {
            return null; // If we've failed 3 times, give up.
        }

        final String username = URLEncoder.encode(callback.username());
        final String password = URLEncoder.encode(callback.password());
        final String credential = "Basic " + Credentials.basic(username, password);
        if (credential.equals(response.request().header("Authorization"))) {
            return null; // If we already failed with these credentials, don't retry.
        }

        return response.request().newBuilder()
                .header("Authorization", credential)
                .build();
    }

    @Override
    public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
        return null;
    }

    private int responseCount(Response response) {
        int result = 1;
        while ((response = response.priorResponse()) != null) {
            result++;
        }

        return result;
    }

}
@swankjesse
Copy link
Collaborator

I’m somewhat reluctant to include this. I expect credentials to not usually be available as a simple string, but instead stored encrypted somewhere. Or used to prompt the user. I'm going to close this, but I could be convinced.

@npomfret
Copy link

I have a react-native app that really needs this. It makes use of a java library that has a REST API which needs basic auth (user name and password in the url) -> https://user:pass@localhost:8888/...

There's no option to provide the auth as a header when supplying an image with one of these urls.

@swankjesse
Copy link
Collaborator

For basic auth you can just set the header manually. See the Credential class to build a basic auth header.

@npomfret
Copy link

Thanks. The user of this is the Facebook Fresco api, so I'll have to try and get that changed. Out of interest what's the reason for not wanting to include it?

@swankjesse
Copy link
Collaborator

Do Chrome and Firefox still implement that? As far as I can tell this behavior is obsolete.

@npomfret
Copy link

They do.

The reason I need it is that I'm making use of an http service that requires an auth header, even for loading image content. And there's no way I can specify anything about the transport layer I'm using. I'm writing in javascript (react-native), which calls react-native java code, which calls the Facebook fresco api, which calls OkHttp. :(

@swankjesse
Copy link
Collaborator

They do

Which versions? How'd you confirm that they support this?

I used whatever version I have on my Mac and I entered a URL like http://user:pass@localhost/ into the address bar and didn't observe an auth header in either my dev tools or Charles proxy.

@npomfret
Copy link

npomfret commented Apr 26, 2016

Hmm, not sure why you're not seeing it. Here's what I see when I hit my server with this URL in chrome

http://admin:pass@localhost:5984/pomochat_5425c828-faa6-41f4-b574-74a322429e60/54fa8607-7ccf-4f2e-a74b-11cbda1c7af3/cad43e2d-4375-4f26-8a48-8923ba11749a

screen shot 2016-04-26 at 13 48 32

Note the Authorisation header in the request

@npomfret
Copy link

npomfret commented Apr 26, 2016

I've dug a little deeper and it not obvious from the Chrome output whats happening here.

From the source code of this server I'm using I can see that if the Authorisation header is not present it returns a 401 along with "WWW-Authenticate", "Basic realm=\"blah\"". I guess the browser responds to this by making a 2nd request with the header added, or something like that. It's strange that chrome isn't showing it though.

Found this:

A summary of basic authentication goes like this :

client makes a request for a webpage
server responds with an error, requesting authentication
client retries request - with authentication details encoded in request
server checks details and sends the page requested, or another error

@swankjesse
Copy link
Collaborator

Ahh, so the authentication isn’t pre-emptive. I missed that.

@swankjesse
Copy link
Collaborator

Can you hook up an OkHttp interceptor?

@npomfret
Copy link

I'm not familiar with the code base but I could try. Actually, there's one right at the top of this discussion. Will that work?

@swankjesse
Copy link
Collaborator

Yup!

@npomfret
Copy link

npomfret commented Jun 8, 2016

Hey @swankjesse - did this ever get implemented?

@npomfret
Copy link

Hi - did this get implemented???

@swankjesse
Copy link
Collaborator

No.

@npomfret
Copy link

Any reason why? Its part of the http spec, no?

@swankjesse
Copy link
Collaborator

https://tools.ietf.org/html/rfc7230#appendix-A.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants