Repository: wso2/product-microgateway
Issue: Add www-authenticate header to the authentication failed responses

### Describe your problem(s)
$subject.

### Describe your solution
<!-- Describe the feature/improvement -->

### How will you implement it
<!-- If you like to suggest an approach or a design -->

---
### Optional Fields
#### Related Issues:
<!-- Any related issues from this/other repositories-->

#### Suggested Labels:
<!--Only to be used by non-members-->

#### Suggested Assignees:
<!--Only to be used by non-members-->


Changed files:
integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testCases/jwtValidator/JwtTestCase.java
enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/Authenticator.java
enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/JWTAuthenticator.java
enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java
enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java
enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/UnsecuredAPIAuthenticator.java
enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/InternalAPIKeyAuthenticator.java

diff --git a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java
index 0f6fc723d..2ae145572 100644
--- a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java
+++ b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java
@@ -61,6 +61,7 @@ public class APIConstants {
     public static final String API_SECURITY_API_KEY = "api_key";
     public static final String API_SECURITY_MUTUAL_SSL_MANDATORY = "mutualssl_mandatory";
     public static final String API_SECURITY_OAUTH_BASIC_AUTH_API_KEY_MANDATORY = "oauth_basic_auth_api_key_mandatory";
+    public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
 
     public static final String BEGIN_CERTIFICATE_STRING = "-----BEGIN CERTIFICATE-----\\n";
     public static final String END_CERTIFICATE_STRING = "-----END CERTIFICATE-----";
diff --git a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java
index 933735907..e667d34d9 100644
--- a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java
+++ b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java
@@ -111,6 +111,10 @@ public class AuthFilter implements Filter {
         if (!canAuthenticated) {
             FilterUtils.setUnauthenticatedErrorToContext(requestContext);
         }
+        //set WWW_AUTHENTICATE header to error response
+        requestContext.addResponseHeaders(APIConstants.WWW_AUTHENTICATE, getAuthenticatorsChallengeString() +
+                ", error=\\"invalid_token\\"" +
+                ", error_description=\\"The provided token is invalid\\"");
         return false;
     }
 
@@ -187,4 +191,14 @@ public class AuthFilter implements Filter {
             }   
         }
     }
+
+    private String getAuthenticatorsChallengeString() {
+        StringBuilder challengeString = new StringBuilder();
+        if (authenticators != null) {
+            for (Authenticator authenticator : authenticators) {
+                challengeString.append(authenticator.getChallengeString()).append(" ");
+            }
+        }
+        return challengeString.toString().trim();
+    }
 }
diff --git a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/Authenticator.java b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/Authenticator.java
index 0cccbddc7..f39fa2219 100644
--- a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/Authenticator.java
+++ b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/Authenticator.java
@@ -30,5 +30,14 @@ public interface Authenticator {
 
     AuthenticationContext authenticate(RequestContext requestContext) throws APISecurityException;
 
+    /**
+     * Returns a string representation of the authentication challenge imposed by this
+     * authenticator. In case of an authentication failure this value will be sent back
+     * to the API consumer in the form of a WWW-Authenticate header.
+     *
+     * @return A string representation of the authentication challenge
+     */
+    String getChallengeString();
+
     int getPriority();
 }
diff --git a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/InternalAPIKeyAuthenticator.java b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/InternalAPIKeyAuthenticator.java
index 5feafb9d2..412f58fa6 100644
--- a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/InternalAPIKeyAuthenticator.java
+++ b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/InternalAPIKeyAuthenticator.java
@@ -191,6 +191,11 @@ public class InternalAPIKeyAuthenticator implements Authenticator {
                 APISecurityConstants.API_AUTH_GENERAL_ERROR, APISecurityConstants.API_AUTH_GENERAL_ERROR_MESSAGE);
     }
 
+    @Override
+    public String getChallengeString() {
+        return "";
+    }
+
     public static JSONObject validateAPISubscription(String apiContext, String apiVersion, JWTClaimsSet payload,
                                                      String[] splitToken, boolean isOauth)
             throws APISecurityException {
diff --git a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/JWTAuthenticator.java b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/JWTAuthenticator.java
index 38ea1b06c..45f81601d 100644
--- a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/JWTAuthenticator.java
+++ b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/JWTAuthenticator.java
@@ -211,6 +211,11 @@ public class JWTAuthenticator implements Authenticator {
 
     }
 
+    @Override
+    public String getChallengeString() {
+        return "Bearer realm=\\"Choreo Connect\\"";
+    }
+
     private String retrieveAuthHeaderValue(RequestContext requestContext) {
         Map<String, String> headers = requestContext.getHeaders();
         String authHeader = requestContext.getMatchedAPI().getAPIConfig().getAuthHeader().toLowerCase();
diff --git a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/UnsecuredAPIAuthenticator.java b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/UnsecuredAPIAuthenticator.java
index b846b7e6e..40c801c78 100644
--- a/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/UnsecuredAPIAuthenticator.java
+++ b/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/UnsecuredAPIAuthenticator.java
@@ -44,6 +44,11 @@ public class UnsecuredAPIAuthenticator implements Authenticator {
         return FilterUtils.generateAuthenticationContext(requestContext);
     }
 
+    @Override
+    public String getChallengeString() {
+        return "";
+    }
+
     @Override public int getPriority() {
         return -20;
     }
diff --git a/integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testCases/jwtValidator/JwtTestCase.java b/integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testCases/jwtValidator/JwtTestCase.java
index d162dcf63..f6847b46b 100644
--- a/integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testCases/jwtValidator/JwtTestCase.java
+++ b/integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testCases/jwtValidator/JwtTestCase.java
@@ -65,6 +65,8 @@ public class JwtTestCase {
 
         Assert.assertNotNull(response);
         Assert.assertEquals(response.getResponseCode(), HttpStatus.SC_OK,"Response code mismatched");
+        Assert.assertFalse(response.getHeaders().containsKey("www-authenticate"),
+                "\\"www-authenticate\\" is available");
     }
 
     @Test(description = "Test to check the JWT auth validate invalida signature token")
@@ -78,6 +80,8 @@ public class JwtTestCase {
 
         Assert.assertNotNull(response);
         Assert.assertEquals(response.getResponseCode(), HttpStatus.SC_UNAUTHORIZED, "Response code mismatched");
+        Assert.assertTrue(response.getHeaders().containsKey("www-authenticate"),
+                "\\"www-authenticate\\" is not available");
     }
 
     @Test(description = "Test to check the JWT auth validate expired token")
@@ -92,5 +96,7 @@ public class JwtTestCase {
         Assert.assertNotNull(response);
         Assert.assertEquals(response.getResponseCode(), HttpStatus.SC_UNAUTHORIZED,
                 "Response code mismatched");
+        Assert.assertTrue(response.getHeaders().containsKey("www-authenticate"),
+                "\\"www-authenticate\\" is not available");
     }
 }