Added NextNonceInterceptor to check the `Authentication-Info` header in HTTP Digest Access Authentication, extracting the `nextnonce` parameter when present and storing it in `HttpClientContext`. This supports compliance with RFC 7616, enhancing client authentication continuity. (#596)
This commit is contained in:
parent
28c3ea0fd2
commit
40d6ba4ee5
|
@ -68,6 +68,7 @@ import org.apache.hc.core5.http.protocol.HttpContext;
|
||||||
import org.apache.hc.core5.net.PercentCodec;
|
import org.apache.hc.core5.net.PercentCodec;
|
||||||
import org.apache.hc.core5.util.Args;
|
import org.apache.hc.core5.util.Args;
|
||||||
import org.apache.hc.core5.util.CharArrayBuffer;
|
import org.apache.hc.core5.util.CharArrayBuffer;
|
||||||
|
import org.apache.hc.core5.util.TextUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -253,6 +254,16 @@ public class DigestScheme implements AuthScheme, Serializable {
|
||||||
if (this.paramMap.get("nonce") == null) {
|
if (this.paramMap.get("nonce") == null) {
|
||||||
throw new AuthenticationException("missing nonce");
|
throw new AuthenticationException("missing nonce");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context != null) {
|
||||||
|
final HttpClientContext clientContext = HttpClientContext.cast(context);
|
||||||
|
final String nextNonce = clientContext.getNextNonce();
|
||||||
|
if (!TextUtils.isBlank(nextNonce)) {
|
||||||
|
this.paramMap.put("nonce", nextNonce);
|
||||||
|
clientContext.setNextNonce(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return createDigestResponse(request);
|
return createDigestResponse(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -202,6 +202,23 @@ public class HttpClientContext extends HttpCoreContext {
|
||||||
private Object userToken;
|
private Object userToken;
|
||||||
private RequestConfig requestConfig;
|
private RequestConfig requestConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the {@code nextnonce} value provided by the server in an HTTP response.
|
||||||
|
* <p>
|
||||||
|
* In the context of HTTP Digest Access Authentication, the {@code nextnonce} parameter
|
||||||
|
* is used by the client in subsequent requests to ensure one-time or session-bound usage
|
||||||
|
* of nonce values, enhancing security by preventing replay attacks.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This field is set by an interceptor or other component that processes the server's
|
||||||
|
* response containing the {@code Authentication-Info} header. Once used, this value
|
||||||
|
* may be cleared from the context to avoid reuse.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 5.5
|
||||||
|
*/
|
||||||
|
private String nextNonce;
|
||||||
|
|
||||||
public HttpClientContext(final HttpContext context) {
|
public HttpClientContext(final HttpContext context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
@ -449,6 +466,28 @@ public class HttpClientContext extends HttpCoreContext {
|
||||||
this.exchangeId = exchangeId;
|
this.exchangeId = exchangeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the stored {@code nextnonce} value.
|
||||||
|
*
|
||||||
|
* @return the {@code nextnonce} parameter value, or {@code null} if not set
|
||||||
|
* @since 5.5
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public String getNextNonce() {
|
||||||
|
return nextNonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@code nextnonce} value directly as an instance attribute.
|
||||||
|
*
|
||||||
|
* @param nextNonce the nonce value to set
|
||||||
|
* @since 5.5
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setNextNonce(final String nextNonce) {
|
||||||
|
this.nextNonce = nextNonce;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal adaptor class that delegates all its method calls to a plain {@link HttpContext}.
|
* Internal adaptor class that delegates all its method calls to a plain {@link HttpContext}.
|
||||||
* To be removed in the future.
|
* To be removed in the future.
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hc.client5.http.protocol;
|
||||||
|
|
||||||
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
|
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||||
|
import org.apache.hc.core5.http.EntityDetails;
|
||||||
|
import org.apache.hc.core5.http.FormattedHeader;
|
||||||
|
import org.apache.hc.core5.http.Header;
|
||||||
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
|
import org.apache.hc.core5.http.HttpResponseInterceptor;
|
||||||
|
import org.apache.hc.core5.http.message.ParserCursor;
|
||||||
|
import org.apache.hc.core5.http.protocol.HttpContext;
|
||||||
|
import org.apache.hc.core5.util.Args;
|
||||||
|
import org.apache.hc.core5.util.CharArrayBuffer;
|
||||||
|
import org.apache.hc.core5.util.TextUtils;
|
||||||
|
import org.apache.hc.core5.util.Tokenizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code NextNonceInterceptor} is an HTTP response interceptor that extracts the {@code nextnonce}
|
||||||
|
* parameter from the {@code Authentication-Info} header of an HTTP response. This parameter is used
|
||||||
|
* in HTTP Digest Access Authentication to provide an additional nonce value that the client is expected
|
||||||
|
* to use in subsequent authentication requests. By retrieving and storing this {@code nextnonce} value,
|
||||||
|
* the interceptor facilitates one-time nonce implementations and prevents replay attacks by ensuring that
|
||||||
|
* each request/response interaction includes a fresh nonce.
|
||||||
|
* <p>
|
||||||
|
* If present, the extracted {@code nextnonce} value is stored in the {@link HttpContext} under the attribute
|
||||||
|
* {@code auth-nextnonce}, allowing it to be accessed in subsequent requests. If the header does not contain
|
||||||
|
* the {@code nextnonce} parameter, no context attribute is set.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>This implementation adheres to the HTTP/1.1 specification, particularly focusing on the {@code Digest}
|
||||||
|
* scheme as defined in HTTP Digest Authentication, and parses header tokens using the {@link Tokenizer}
|
||||||
|
* utility class for robust token parsing.</p>
|
||||||
|
*
|
||||||
|
* <p>In the context of HTTP Digest Access Authentication, the {@code nextnonce} parameter is
|
||||||
|
* a critical part of the security mechanism, designed to mitigate replay attacks and enhance mutual
|
||||||
|
* authentication security. It provides the server with the ability to set and enforce single-use or
|
||||||
|
* session-bound nonces, prompting the client to use the provided {@code nextnonce} in its next request.
|
||||||
|
* This setup helps secure communication by forcing new cryptographic material in each transaction.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>This interceptor is stateless and thread-safe, making it suitable for use across multiple
|
||||||
|
* threads and HTTP requests. It should be registered with the HTTP client to enable support
|
||||||
|
* for advanced authentication mechanisms that require tracking of nonce values.</p>
|
||||||
|
*
|
||||||
|
* @since 5.5
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Contract(threading = ThreadingBehavior.STATELESS)
|
||||||
|
public class NextNonceInterceptor implements HttpResponseInterceptor {
|
||||||
|
|
||||||
|
public static final HttpResponseInterceptor INSTANCE = new NextNonceInterceptor();
|
||||||
|
|
||||||
|
private final Tokenizer tokenParser = Tokenizer.INSTANCE;
|
||||||
|
|
||||||
|
|
||||||
|
private static final String AUTHENTICATION_INFO_HEADER = "Authentication-Info";
|
||||||
|
|
||||||
|
private static final Tokenizer.Delimiter TOKEN_DELIMS = Tokenizer.delimiters('=', ',');
|
||||||
|
private static final Tokenizer.Delimiter VALUE_DELIMS = Tokenizer.delimiters(',');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the HTTP response and extracts the {@code nextnonce} parameter from the
|
||||||
|
* {@code Authentication-Info} header if available, storing it in the provided {@code context}.
|
||||||
|
*
|
||||||
|
* @param response the HTTP response containing the {@code Authentication-Info} header
|
||||||
|
* @param entity the response entity, ignored by this interceptor
|
||||||
|
* @param context the HTTP context in which to store the {@code nextnonce} parameter
|
||||||
|
* @throws NullPointerException if either {@code response} or {@code context} is null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void process(final HttpResponse response, final EntityDetails entity, final HttpContext context) {
|
||||||
|
Args.notNull(response, "HTTP response");
|
||||||
|
Args.notNull(context, "HTTP context");
|
||||||
|
|
||||||
|
final Header header = response.getFirstHeader(AUTHENTICATION_INFO_HEADER);
|
||||||
|
if (header != null) {
|
||||||
|
final String nextNonce;
|
||||||
|
if (header instanceof FormattedHeader) {
|
||||||
|
final CharSequence buf = ((FormattedHeader) header).getBuffer();
|
||||||
|
final ParserCursor cursor = new ParserCursor(((FormattedHeader) header).getValuePos(), buf.length());
|
||||||
|
nextNonce = parseNextNonce(buf, cursor);
|
||||||
|
} else {
|
||||||
|
final CharSequence headerValue = header.getValue();
|
||||||
|
final ParserCursor cursor = new ParserCursor(0, headerValue.length());
|
||||||
|
nextNonce = parseNextNonce(headerValue, cursor);
|
||||||
|
}
|
||||||
|
if (!TextUtils.isBlank(nextNonce)) {
|
||||||
|
HttpClientContext.castOrCreate(context).setNextNonce(nextNonce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the {@code Authentication-Info} header content represented by a {@link CharArrayBuffer}
|
||||||
|
* to extract the {@code nextnonce} parameter.
|
||||||
|
*
|
||||||
|
* @param buffer the {@link CharArrayBuffer} containing the value of the {@code Authentication-Info} header
|
||||||
|
* @param cursor the {@link ParserCursor} used to navigate through the buffer content
|
||||||
|
* @return the extracted {@code nextnonce} parameter value, or {@code null} if the parameter is not found
|
||||||
|
*/
|
||||||
|
private String parseNextNonce(final CharSequence buffer, final ParserCursor cursor) {
|
||||||
|
while (!cursor.atEnd()) {
|
||||||
|
final String name = tokenParser.parseToken(buffer, cursor, TOKEN_DELIMS);
|
||||||
|
if ("nextnonce".equals(name)) {
|
||||||
|
cursor.updatePos(cursor.getPos() + 1);
|
||||||
|
return tokenParser.parseValue(buffer, cursor, VALUE_DELIMS);
|
||||||
|
}
|
||||||
|
if (!cursor.atEnd()) {
|
||||||
|
cursor.updatePos(cursor.getPos() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ import org.apache.hc.client5.http.auth.ChallengeType;
|
||||||
import org.apache.hc.client5.http.auth.CredentialsProvider;
|
import org.apache.hc.client5.http.auth.CredentialsProvider;
|
||||||
import org.apache.hc.client5.http.auth.MalformedChallengeException;
|
import org.apache.hc.client5.http.auth.MalformedChallengeException;
|
||||||
import org.apache.hc.client5.http.auth.StandardAuthScheme;
|
import org.apache.hc.client5.http.auth.StandardAuthScheme;
|
||||||
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||||
import org.apache.hc.core5.http.ContentType;
|
import org.apache.hc.core5.http.ContentType;
|
||||||
import org.apache.hc.core5.http.HeaderElement;
|
import org.apache.hc.core5.http.HeaderElement;
|
||||||
|
@ -928,4 +929,62 @@ class TestDigestScheme {
|
||||||
Assertions.assertNotNull(table.get("rspauth"));
|
Assertions.assertNotNull(table.get("rspauth"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNextNonceUsageFromContext() throws Exception {
|
||||||
|
final HttpRequest request = new BasicHttpRequest("GET", "/");
|
||||||
|
final HttpHost host = new HttpHost("somehost", 80);
|
||||||
|
final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
|
||||||
|
.add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final HttpClientContext context = HttpClientContext.create();
|
||||||
|
context.setNextNonce("sampleNextNonce"); // Set `nextNonce` in the context
|
||||||
|
|
||||||
|
final DigestScheme authscheme = new DigestScheme();
|
||||||
|
final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"initialNonce\"";
|
||||||
|
final AuthChallenge authChallenge = parse(challenge);
|
||||||
|
authscheme.processChallenge(authChallenge, context);
|
||||||
|
|
||||||
|
// Simulate credentials validation and generate auth response
|
||||||
|
authscheme.isResponseReady(host, credentialsProvider, context);
|
||||||
|
final String authResponse = authscheme.generateAuthResponse(host, request, context);
|
||||||
|
|
||||||
|
// Validate that `sampleNextNonce` (from context) is used in place of the initial nonce
|
||||||
|
final Map<String, String> paramMap = parseAuthResponse(authResponse);
|
||||||
|
Assertions.assertEquals("sampleNextNonce", paramMap.get("nonce"), "The nonce should match 'auth-nextnonce' from the context.");
|
||||||
|
|
||||||
|
// Verify that `auth-nextnonce` was removed from context after use
|
||||||
|
Assertions.assertNull(context.getAttribute("auth-nextnonce"), "The next nonce should be removed from the context after use.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNoNextNonceUsageFromContext() throws Exception {
|
||||||
|
final HttpRequest request = new BasicHttpRequest("GET", "/");
|
||||||
|
final HttpHost host = new HttpHost("somehost", 80);
|
||||||
|
final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
|
||||||
|
.add(new AuthScope(host, "realm1", null), "username", "password".toCharArray())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final HttpClientContext context = HttpClientContext.create();
|
||||||
|
|
||||||
|
// Initialize DigestScheme without setting any `nextNonce`
|
||||||
|
final DigestScheme authscheme = new DigestScheme();
|
||||||
|
final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"initialNonce\"";
|
||||||
|
final AuthChallenge authChallenge = parse(challenge);
|
||||||
|
authscheme.processChallenge(authChallenge, context);
|
||||||
|
|
||||||
|
// Simulate credentials validation and generate auth response
|
||||||
|
authscheme.isResponseReady(host, credentialsProvider, context);
|
||||||
|
final String authResponse = authscheme.generateAuthResponse(host, request, context);
|
||||||
|
|
||||||
|
// Validate that the nonce in the response matches the initial nonce, not a nextNonce
|
||||||
|
final Map<String, String> paramMap = parseAuthResponse(authResponse);
|
||||||
|
Assertions.assertEquals("initialNonce", paramMap.get("nonce"), "The nonce should match the initial nonce provided in the challenge.");
|
||||||
|
|
||||||
|
// Verify that the context does not contain any `nextNonce` value set
|
||||||
|
Assertions.assertNull(context.getAttribute("auth-nextnonce"), "The context should not contain a nextNonce attribute.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hc.client5.http.protocol;
|
||||||
|
|
||||||
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
|
import org.apache.hc.core5.http.message.BasicHeader;
|
||||||
|
import org.apache.hc.core5.http.message.BasicHttpResponse;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class TestNextNonceInterceptor {
|
||||||
|
|
||||||
|
private static final String AUTHENTICATION_INFO_HEADER = "Authentication-Info";
|
||||||
|
|
||||||
|
private NextNonceInterceptor interceptor;
|
||||||
|
private HttpClientContext context;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
interceptor = new NextNonceInterceptor();
|
||||||
|
context = HttpClientContext.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNoAuthenticationInfoHeader() {
|
||||||
|
final HttpResponse response = new BasicHttpResponse(200);
|
||||||
|
|
||||||
|
interceptor.process(response, null, context);
|
||||||
|
|
||||||
|
Assertions.assertNull(context.getNextNonce(),
|
||||||
|
"Context should not contain nextnonce when the header is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAuthenticationInfoHeaderWithoutNextNonce() {
|
||||||
|
final HttpResponse response = new BasicHttpResponse(200);
|
||||||
|
response.addHeader(new BasicHeader(AUTHENTICATION_INFO_HEADER, "auth-param=value"));
|
||||||
|
|
||||||
|
interceptor.process(response, null, context);
|
||||||
|
|
||||||
|
Assertions.assertNull(context.getNextNonce(),
|
||||||
|
"Context should not contain nextnonce when it is missing in the header value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAuthenticationInfoHeaderWithNextNonce() {
|
||||||
|
final HttpResponse response = new BasicHttpResponse(200);
|
||||||
|
response.addHeader(new BasicHeader(AUTHENTICATION_INFO_HEADER, "nextnonce=\"10024b2308596a55d02699c0a0400fb4\",qop=auth,rspauth=\"0386df3cb9effdf08c9e00ab955827f3\",cnonce=\"21558090\",nc=00000001"));
|
||||||
|
|
||||||
|
interceptor.process(response, null, context);
|
||||||
|
|
||||||
|
Assertions.assertEquals("10024b2308596a55d02699c0a0400fb4", context.getNextNonce(),
|
||||||
|
"Context should contain the correct nextnonce value when it is present in the header");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testMultipleAuthenticationInfoHeaders() {
|
||||||
|
final HttpResponse response = new BasicHttpResponse(200);
|
||||||
|
response.addHeader(new BasicHeader(AUTHENTICATION_INFO_HEADER, "auth-param=value")); // First header without nextnonce
|
||||||
|
response.addHeader(new BasicHeader(AUTHENTICATION_INFO_HEADER, "nextnonce=\"10024b2308596a55d02699c0a0400fb4\",qop=auth,rspauth=\"0386df3cb9effdf08c9e00ab955827f3\",cnonce=\"21558090\",nc=00000001")); // Second header with nextnonce
|
||||||
|
|
||||||
|
interceptor.process(response, null, context);
|
||||||
|
|
||||||
|
// Since only the first header is processed, `auth-nextnonce` should not be set in the context
|
||||||
|
Assertions.assertNull(context.getNextNonce(),
|
||||||
|
"Context should not contain nextnonce if it's not in the first Authentication-Info header");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAuthenticationInfoHeaderWithEmptyNextNonce() {
|
||||||
|
final HttpResponse response = new BasicHttpResponse(200);
|
||||||
|
response.addHeader(new BasicHeader(AUTHENTICATION_INFO_HEADER, "nextnonce=\"\",qop=auth,rspauth=\"0386df3cb9effdf08c9e00ab955827f3\",cnonce=\"21558090\",nc=00000001"));
|
||||||
|
|
||||||
|
interceptor.process(response, null, context);
|
||||||
|
|
||||||
|
Assertions.assertNull(context.getNextNonce(),
|
||||||
|
"Context should not contain nextnonce if it is empty in the Authentication-Info header");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue