Vikas Parihar
Vikas Parihar

Reputation: 59

Spring Webflux WebClient : Does it supports digest based authentication?

I have just looked around all spring 5 documentation but did not find anything related to WebClient supports digest based authentication out of the box. Is there any workaround to use webClient and still call digest based secured APIs?

Upvotes: 5

Views: 2169

Answers (3)

Niklas Eldberger
Niklas Eldberger

Reputation: 225

So this is quite late but I just faced the same problem when trying to integrate my reactive Spring Boot application and WebClient with Atlas API.

The gist below solved it for me. I had to download the git repo (https://github.com/vzhn/netty-http-authenticator) and build it by myself as the maven artifact published 1.5 was not enough to get the gist stuff to work but when that was done it was possible to make digest auth work with WebClient.

https://gist.github.com/sebphil/e3773a87e5bf197193deec5c9512bd2d

Upvotes: 0

Vikas Parihar
Vikas Parihar

Reputation: 59

The temporary solution to get digest based authentication working with webClient is given below, until spring community fixes this.

public Mono<String> getDigestCallByUserId(String userId) {

    String url = serviceUrl + uri;

    return webClient.get()
        .uri(url)
        .exchange()
        .flatMap(resp -> {

            if (resp.statusCode().equals(HttpStatus.UNAUTHORIZED)) {
                return getSectionsByUserId(userId, resp.headers().header(Constants.HEADER_AUTHENTICATE).get(0), url, uri);
            } else {
                return resp.bodyToMono(String.class);
            }
        });
}


public Mono<String> getDigestCallByUserId(String userId, String digestAuthResponse, String url, String uri) {

    return webClient.get()
        .uri(url)
        .header(Constants.HEADER_AUTHORIZATION, getDigestAuthHeader(digestAuthResponse, username, password, uri, HttpMethod.GET.toString()))
        .retrieve()
        .bodyToMono(String.class)
        .log(LOGGER.getName(), Level.SEVERE, SignalType.ON_ERROR, SignalType.CANCEL);
}



 /**
 * @param values
 * @returns the appended string for the given list of strings
 */
public static String getStringsAppended(String... values) {
    StringBuilder sb = new StringBuilder();
    for (String s : values) {
        sb.append(s);
    }
    return sb.toString();
}

/**
 * @param digestChallenge
 * @param username
 * @param password
 * @param uri
 * @param httpMethod
 * @returns the Digest based authorization header
 */
public static String getDigestAuthHeader(String digestChallenge, String username, String password, String uri,
        String httpMethod) {
    Pattern digestChallengePattern = Pattern.compile(Constants.PATTERN_DIGEST_CHALLENGE);
    Matcher m = digestChallengePattern.matcher(digestChallenge);
    if (m.matches()) {
        String realm = m.group(Constants.DIGEST_AUTH_REALM);
        String qop = m.group(Constants.DIGEST_AUTH_QOP);
        String nonce = m.group(Constants.DIGEST_AUTH_NONCE);
        String clientNonce = DigestScheme.createCnonce();
        String nonceCount = String.format("%08x", 1);

        String ha1 = hash(getStringsAppended(username, Constants.COLON, realm, Constants.COLON, password));
        String ha2 = hash(getStringsAppended(httpMethod, Constants.COLON, uri));
        String response = hash(getStringsAppended(ha1, Constants.COLON, nonce, Constants.COLON, nonceCount,
                Constants.COLON, clientNonce, Constants.COLON, qop, Constants.COLON, ha2));

        return getStringsAppended(Constants.DIGEST, StringUtils.SPACE, Constants.DIGEST_AUTH_USERNAME,
                Constants.EQUAL, Constants.DOUBLE_QUOTE, username, Constants.DOUBLE_QUOTE, Constants.COMMA,
                Constants.DIGEST_AUTH_REALM, Constants.EQUAL, Constants.DOUBLE_QUOTE, realm, Constants.DOUBLE_QUOTE,
                Constants.COMMA, Constants.DIGEST_AUTH_NONCE, Constants.EQUAL, Constants.DOUBLE_QUOTE, nonce,
                Constants.DOUBLE_QUOTE, Constants.COMMA, Constants.DIGEST_AUTH_URI, Constants.EQUAL,
                Constants.DOUBLE_QUOTE, uri, Constants.DOUBLE_QUOTE, Constants.COMMA,
                Constants.DIGEST_AUTH_RESPONSE, Constants.EQUAL, Constants.DOUBLE_QUOTE, response,
                Constants.DOUBLE_QUOTE, Constants.COMMA, Constants.DIGEST_AUTH_QOP, Constants.EQUAL,
                Constants.DOUBLE_QUOTE, qop, Constants.DOUBLE_QUOTE, Constants.COMMA, Constants.DIGEST_AUTH_NC,
                Constants.EQUAL, Constants.DOUBLE_QUOTE, nonceCount, Constants.DOUBLE_QUOTE, Constants.COMMA,
                Constants.DIGEST_AUTH_CNONCE, Constants.EQUAL, Constants.DOUBLE_QUOTE, clientNonce,
                Constants.DOUBLE_QUOTE);
    }
    return StringUtils.EMPTY;
}

Upvotes: -1

Brian Clozel
Brian Clozel

Reputation: 59211

No, there is no such support at the moment.

You could look at implementing your own client support by implementing a org.springframework.web.reactive.function.client.ExchangeFunction.

Alternatively (and because rolling your own support might be difficult), you could create a new issue on the Spring Security project and see of the community is interested in such a feature.

Upvotes: 1

Related Questions