[KEYCLOAK-7162] - Expose WWW-Authenticate Header when using CORS
This commit is contained in:
parent
527d6ca4d8
commit
c3d297dd05
5 changed files with 87 additions and 3 deletions
|
@ -102,6 +102,14 @@ public class AuthenticatedActionsHandler {
|
|||
KeycloakSecurityContext securityContext = facade.getSecurityContext();
|
||||
String origin = facade.getRequest().getHeader(CorsHeaders.ORIGIN);
|
||||
String exposeHeaders = deployment.getCorsExposedHeaders();
|
||||
|
||||
if (deployment.getPolicyEnforcer() != null) {
|
||||
if (exposeHeaders != null) {
|
||||
exposeHeaders += ",";
|
||||
}
|
||||
exposeHeaders += "WWW-Authenticate";
|
||||
}
|
||||
|
||||
String requestOrigin = UriUtils.getOrigin(facade.getRequest().getURI());
|
||||
log.debugv("Origin: {0} uri: {1}", origin, facade.getRequest().getURI());
|
||||
if (securityContext != null && origin != null && !origin.equals(requestOrigin)) {
|
||||
|
|
|
@ -26,6 +26,14 @@
|
|||
"clientRoles": {
|
||||
"realm-management": [ "realm-admin" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username": "service-account-cors-database-service",
|
||||
"enabled": true,
|
||||
"serviceAccountClientId": "cors-database-service",
|
||||
"clientRoles": {
|
||||
"cors-database-service" : ["uma_protection"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
|
@ -54,6 +62,49 @@
|
|||
"webOrigins": [
|
||||
"http://localhost:8080"
|
||||
]
|
||||
},
|
||||
{
|
||||
"clientId": "cors-database-service",
|
||||
"secret" : "secret",
|
||||
"enabled": true,
|
||||
"baseUrl": "http://localhost:8080/cors-database/products",
|
||||
"redirectUris": [
|
||||
"http://localhost:8080/cors-database/*"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://localhost:8080"
|
||||
],
|
||||
"authorizationServicesEnabled" : true,
|
||||
"authorizationSettings" : {
|
||||
"allowRemoteResourceManagement" : false,
|
||||
"policyEnforcementMode" : "ENFORCING",
|
||||
"resources" : [ {
|
||||
"name" : "Default Resource",
|
||||
"uri" : "/*",
|
||||
"type" : "default"
|
||||
} ],
|
||||
"policies" : [ {
|
||||
"name" : "Default Policy",
|
||||
"description" : "A policy that grants access only for users within this realm",
|
||||
"type" : "js",
|
||||
"logic" : "POSITIVE",
|
||||
"decisionStrategy" : "AFFIRMATIVE",
|
||||
"config" : {
|
||||
"code" : "// by default, grants any permission associated with this policy\n$evaluation.grant();\n"
|
||||
}
|
||||
}, {
|
||||
"name" : "Default Permission",
|
||||
"description" : "A permission that applies to the default resource type",
|
||||
"type" : "resource",
|
||||
"logic" : "POSITIVE",
|
||||
"decisionStrategy" : "UNANIMOUS",
|
||||
"config" : {
|
||||
"defaultResourceType" : "default",
|
||||
"applyPolicies" : "[\"Default Policy\"]"
|
||||
}
|
||||
} ],
|
||||
"scopes" : [ ]
|
||||
}
|
||||
}
|
||||
],
|
||||
"clientScopeMappings": {
|
||||
|
|
|
@ -47,6 +47,7 @@ public class ProductService {
|
|||
rtn.add("ipod");
|
||||
|
||||
response.addHeader("X-Custom1", "some-value");
|
||||
response.addHeader("WWW-Authenticate", "some-value");
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,11 @@
|
|||
"bearer-only" : true,
|
||||
"ssl-required": "external",
|
||||
"enable-cors": true,
|
||||
"cors-exposed-headers": "X-Custom1"
|
||||
"cors-exposed-headers": "X-Custom1",
|
||||
"credentials": {
|
||||
"secret": "secret"
|
||||
},
|
||||
"policy-enforcer": {
|
||||
"enforcement-mode": "DISABLED"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,15 @@
|
|||
|
||||
package org.keycloak.testsuite.adapter.example.cors;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployer;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
|
@ -53,6 +57,9 @@ public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdap
|
|||
public static final String AUTH_SERVER_HOST = "localhost-auth";
|
||||
private static String hostBackup;
|
||||
|
||||
@ArquillianResource
|
||||
private Deployer deployer;
|
||||
|
||||
@Page
|
||||
@JavascriptBrowser
|
||||
private AngularCorsProductTestApp jsDriverAngularCorsProductPage;
|
||||
|
@ -66,7 +73,7 @@ public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdap
|
|||
return exampleDeployment(AngularCorsProductTestApp.CLIENT_ID);
|
||||
}
|
||||
|
||||
@Deployment(name = CorsDatabaseServiceTestApp.DEPLOYMENT_NAME)
|
||||
@Deployment(name = CorsDatabaseServiceTestApp.DEPLOYMENT_NAME, managed = false)
|
||||
private static WebArchive corsDatabaseServiceExample() throws IOException {
|
||||
return exampleDeployment(CorsDatabaseServiceTestApp.CLIENT_ID);
|
||||
}
|
||||
|
@ -74,7 +81,17 @@ public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdap
|
|||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(
|
||||
loadRealm(new File(EXAMPLES_HOME_DIR + "/cors/cors-realm.json")));
|
||||
loadRealm(new File(TEST_APPS_HOME_DIR + "/cors/cors-realm.json")));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
deployer.deploy(CorsDatabaseServiceTestApp.DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
@After
|
||||
public void onAfter() {
|
||||
deployer.undeploy(CorsDatabaseServiceTestApp.DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
static{
|
||||
|
@ -100,6 +117,7 @@ public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdap
|
|||
waitUntilElement(jsDriverAngularCorsProductPage.getOutput()).text().contains("ipad");
|
||||
waitUntilElement(jsDriverAngularCorsProductPage.getOutput()).text().contains("ipod");
|
||||
waitUntilElement(jsDriverAngularCorsProductPage.getHeaders()).text().contains("\"x-custom1\":\"some-value\"");
|
||||
waitUntilElement(jsDriverAngularCorsProductPage.getHeaders()).text().contains("\"www-authenticate\":\"some-value\"");
|
||||
|
||||
jsDriverAngularCorsProductPage.loadRoles();
|
||||
waitUntilElement(jsDriverAngularCorsProductPage.getOutput()).text().contains("user");
|
||||
|
|
Loading…
Reference in a new issue