KEYCLOAK-836 Added OsgiJaxrsBearerTokenFilterImpl to be used in fuse
This commit is contained in:
parent
a94ab5883d
commit
07fd8ae9d7
6 changed files with 197 additions and 32 deletions
|
@ -70,6 +70,13 @@
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.osgi</groupId>
|
||||||
|
<artifactId>org.osgi.core</artifactId>
|
||||||
|
<version>4.3.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.keycloak.adapters.AdapterDeploymentContext;
|
||||||
import org.keycloak.adapters.AdapterUtils;
|
import org.keycloak.adapters.AdapterUtils;
|
||||||
import org.keycloak.adapters.AuthChallenge;
|
import org.keycloak.adapters.AuthChallenge;
|
||||||
import org.keycloak.adapters.AuthOutcome;
|
import org.keycloak.adapters.AuthOutcome;
|
||||||
|
import org.keycloak.adapters.AuthenticatedActionsHandler;
|
||||||
import org.keycloak.adapters.BearerTokenRequestAuthenticator;
|
import org.keycloak.adapters.BearerTokenRequestAuthenticator;
|
||||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
@ -43,17 +44,17 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
|
|
||||||
private String keycloakConfigFile;
|
private String keycloakConfigFile;
|
||||||
private String keycloakConfigResolverClass;
|
private String keycloakConfigResolverClass;
|
||||||
private volatile boolean started;
|
protected volatile boolean started;
|
||||||
|
|
||||||
protected AdapterDeploymentContext deploymentContext;
|
protected AdapterDeploymentContext deploymentContext;
|
||||||
|
|
||||||
// TODO: Should also handle stop lifecycle for de-registration
|
// TODO: Should also somehow handle stop lifecycle for de-registration
|
||||||
protected NodesRegistrationManagement nodesRegistrationManagement;
|
protected NodesRegistrationManagement nodesRegistrationManagement;
|
||||||
protected UserSessionManagement userSessionManagement = new EmptyUserSessionManagement();
|
protected UserSessionManagement userSessionManagement = new EmptyUserSessionManagement();
|
||||||
|
|
||||||
public void setKeycloakConfigFile(String configFile) {
|
public void setKeycloakConfigFile(String configFile) {
|
||||||
this.keycloakConfigFile = configFile;
|
this.keycloakConfigFile = configFile;
|
||||||
start();
|
attemptStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKeycloakConfigFile() {
|
public String getKeycloakConfigFile() {
|
||||||
|
@ -66,7 +67,25 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
|
|
||||||
public void setKeycloakConfigResolverClass(String keycloakConfigResolverClass) {
|
public void setKeycloakConfigResolverClass(String keycloakConfigResolverClass) {
|
||||||
this.keycloakConfigResolverClass = keycloakConfigResolverClass;
|
this.keycloakConfigResolverClass = keycloakConfigResolverClass;
|
||||||
|
attemptStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
// INITIALIZATION AND STARTUP
|
||||||
|
|
||||||
|
protected void attemptStart() {
|
||||||
|
if (started) {
|
||||||
|
throw new IllegalStateException("Filter already started. Make sure to specify just keycloakConfigResolver or keycloakConfigFile but not both");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInitialized()) {
|
||||||
start();
|
start();
|
||||||
|
} else {
|
||||||
|
log.fine("Not yet initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isInitialized() {
|
||||||
|
return this.keycloakConfigFile != null || this.keycloakConfigResolverClass != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void start() {
|
protected void start() {
|
||||||
|
@ -75,30 +94,20 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keycloakConfigResolverClass != null) {
|
if (keycloakConfigResolverClass != null) {
|
||||||
Class<?> clazz;
|
Class<? extends KeycloakConfigResolver> resolverClass = loadResolverClass();
|
||||||
try {
|
|
||||||
clazz = getClass().getClassLoader().loadClass(keycloakConfigResolverClass);
|
|
||||||
} catch (ClassNotFoundException cnfe) {
|
|
||||||
// Fallback to tccl
|
|
||||||
try {
|
|
||||||
clazz = Thread.currentThread().getContextClassLoader().loadClass(keycloakConfigResolverClass);
|
|
||||||
} catch (ClassNotFoundException cnfe2) {
|
|
||||||
throw new RuntimeException("Unable to find resolver class: " + keycloakConfigResolverClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
KeycloakConfigResolver resolver = (KeycloakConfigResolver) clazz.newInstance();
|
KeycloakConfigResolver resolver = resolverClass.newInstance();
|
||||||
log.info("Using " + resolver + " to resolve Keycloak configuration on a per-request basis.");
|
log.info("Using " + resolver + " to resolve Keycloak configuration on a per-request basis.");
|
||||||
this.deploymentContext = new AdapterDeploymentContext(resolver);
|
this.deploymentContext = new AdapterDeploymentContext(resolver);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Unable to instantiate resolver " + clazz);
|
throw new RuntimeException("Unable to instantiate resolver " + resolverClass);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (keycloakConfigFile == null) {
|
if (keycloakConfigFile == null) {
|
||||||
throw new IllegalArgumentException("You need to specify either keycloakConfigResolverClass or keycloakConfigFile in configuration");
|
throw new IllegalArgumentException("You need to specify either keycloakConfigResolverClass or keycloakConfigFile in configuration");
|
||||||
}
|
}
|
||||||
InputStream is = readConfigFile();
|
InputStream is = loadKeycloakConfigFile();
|
||||||
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
|
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
|
||||||
deploymentContext = new AdapterDeploymentContext(kd);
|
deploymentContext = new AdapterDeploymentContext(kd);
|
||||||
log.info("Keycloak is using a per-deployment configuration loaded from: " + keycloakConfigFile);
|
log.info("Keycloak is using a per-deployment configuration loaded from: " + keycloakConfigFile);
|
||||||
|
@ -108,7 +117,20 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
started = true;
|
started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected InputStream readConfigFile() {
|
protected Class<? extends KeycloakConfigResolver> loadResolverClass() {
|
||||||
|
try {
|
||||||
|
return (Class<? extends KeycloakConfigResolver>)getClass().getClassLoader().loadClass(keycloakConfigResolverClass);
|
||||||
|
} catch (ClassNotFoundException cnfe) {
|
||||||
|
// Fallback to tccl
|
||||||
|
try {
|
||||||
|
return (Class<? extends KeycloakConfigResolver>)Thread.currentThread().getContextClassLoader().loadClass(keycloakConfigResolverClass);
|
||||||
|
} catch (ClassNotFoundException cnfe2) {
|
||||||
|
throw new RuntimeException("Unable to find resolver class: " + keycloakConfigResolverClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InputStream loadKeycloakConfigFile() {
|
||||||
if (keycloakConfigFile.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
|
if (keycloakConfigFile.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
|
||||||
String classPathLocation = keycloakConfigFile.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
|
String classPathLocation = keycloakConfigFile.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
|
||||||
log.fine("Loading config from classpath on location: " + classPathLocation);
|
log.fine("Loading config from classpath on location: " + classPathLocation);
|
||||||
|
@ -135,11 +157,13 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// REQUEST HANDLING
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void filter(ContainerRequestContext request) throws IOException {
|
public void filter(ContainerRequestContext request) throws IOException {
|
||||||
SecurityContext securityContext = getRequestSecurityContext(request);
|
SecurityContext securityContext = getRequestSecurityContext(request);
|
||||||
JaxrsHttpFacade facade = new JaxrsHttpFacade(request, securityContext);
|
JaxrsHttpFacade facade = new JaxrsHttpFacade(request, securityContext);
|
||||||
if (handlePreauth(request, facade)) {
|
if (handlePreauth(facade)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,12 +174,12 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
bearerAuthentication(facade, request, resolvedDeployment);
|
bearerAuthentication(facade, request, resolvedDeployment);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean handlePreauth(ContainerRequestContext request, JaxrsHttpFacade facade) {
|
protected boolean handlePreauth(JaxrsHttpFacade facade) {
|
||||||
PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
|
PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
|
||||||
if (handler.handleRequest()) {
|
if (handler.handleRequest()) {
|
||||||
// Response might be already finished if error was sent
|
// Send response now (if not already sent)
|
||||||
if (!facade.isResponseFinished()) {
|
if (!facade.isResponseFinished()) {
|
||||||
request.abortWith(facade.getResponseBuilder().build());
|
facade.getResponse().end();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -175,9 +199,9 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
facade.getResponse().setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
|
facade.getResponse().setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send response now
|
// Send response now (if not already sent)
|
||||||
if (!facade.isResponseFinished()) {
|
if (!facade.isResponseFinished()) {
|
||||||
request.abortWith(facade.getResponseBuilder().build());
|
facade.getResponse().end();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -187,6 +211,7 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
propagateSecurityContext(facade, request, resolvedDeployment, bearer);
|
propagateSecurityContext(facade, request, resolvedDeployment, bearer);
|
||||||
|
handleAuthActions(facade, resolvedDeployment);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void propagateSecurityContext(JaxrsHttpFacade facade, ContainerRequestContext request, KeycloakDeployment resolvedDeployment, BearerTokenRequestAuthenticator bearer) {
|
protected void propagateSecurityContext(JaxrsHttpFacade facade, ContainerRequestContext request, KeycloakDeployment resolvedDeployment, BearerTokenRequestAuthenticator bearer) {
|
||||||
|
@ -239,6 +264,16 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
|
||||||
return request.getSecurityContext();
|
return request.getSecurityContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void handleAuthActions(JaxrsHttpFacade facade, KeycloakDeployment deployment) {
|
||||||
|
AuthenticatedActionsHandler authActionsHandler = new AuthenticatedActionsHandler(deployment, facade);
|
||||||
|
if (authActionsHandler.handledRequest()) {
|
||||||
|
// Send response now (if not already sent)
|
||||||
|
if (!facade.isResponseFinished()) {
|
||||||
|
facade.getResponse().end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We don't have any sessions to manage with pure jaxrs filter
|
// We don't have any sessions to manage with pure jaxrs filter
|
||||||
private static class EmptyUserSessionManagement implements UserSessionManagement {
|
private static class EmptyUserSessionManagement implements UserSessionManagement {
|
||||||
|
|
||||||
|
|
|
@ -136,8 +136,9 @@ public class JaxrsHttpFacade implements HttpFacade {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void end() {
|
public void end() {
|
||||||
// For now doesn't need to be supported
|
javax.ws.rs.core.Response response = responseBuilder.build();
|
||||||
throw new IllegalStateException("Not supported yet");
|
requestContext.abortWith(response);
|
||||||
|
responseFinished = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,8 +169,4 @@ public class JaxrsHttpFacade implements HttpFacade {
|
||||||
public boolean isResponseFinished() {
|
public boolean isResponseFinished() {
|
||||||
return responseFinished;
|
return responseFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
public javax.ws.rs.core.Response.ResponseBuilder getResponseBuilder() {
|
|
||||||
return responseFacade.responseBuilder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package org.keycloak.jaxrs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Priority;
|
||||||
|
import javax.ws.rs.Priorities;
|
||||||
|
import javax.ws.rs.container.PreMatching;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||||
|
import org.keycloak.constants.GenericConstants;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant of JaxrsBearerTokenFilter, which can be used to properly use resources from current osgi bundle
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
@PreMatching
|
||||||
|
@Priority(Priorities.AUTHENTICATION)
|
||||||
|
public class OsgiJaxrsBearerTokenFilterImpl extends JaxrsBearerTokenFilterImpl {
|
||||||
|
|
||||||
|
private final static Logger log = Logger.getLogger("" + JaxrsBearerTokenFilterImpl.class);
|
||||||
|
|
||||||
|
private BundleContext bundleContext;
|
||||||
|
|
||||||
|
public BundleContext getBundleContext() {
|
||||||
|
return bundleContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBundleContext(BundleContext bundleContext) {
|
||||||
|
this.bundleContext = bundleContext;
|
||||||
|
attemptStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isInitialized() {
|
||||||
|
return super.isInitialized() && bundleContext != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<? extends KeycloakConfigResolver> loadResolverClass() {
|
||||||
|
String resolverClass = getKeycloakConfigResolverClass();
|
||||||
|
try {
|
||||||
|
return (Class<? extends KeycloakConfigResolver>) bundleContext.getBundle().loadClass(resolverClass);
|
||||||
|
} catch (ClassNotFoundException cnfe) {
|
||||||
|
log.warning("Not able to find class from bundleContext. Fallback to current classloader");
|
||||||
|
return super.loadResolverClass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InputStream loadKeycloakConfigFile() {
|
||||||
|
String keycloakConfigFile = getKeycloakConfigFile();
|
||||||
|
if (keycloakConfigFile.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
|
||||||
|
|
||||||
|
// Load from classpath of current bundle
|
||||||
|
String classPathLocation = keycloakConfigFile.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
|
||||||
|
log.fine("Loading config from classpath on location: " + classPathLocation);
|
||||||
|
|
||||||
|
URL cfgUrl = bundleContext.getBundle().getResource(classPathLocation);
|
||||||
|
if (cfgUrl == null) {
|
||||||
|
log.warning("Not able to find configFile from bundleContext. Fallback to current classloader");
|
||||||
|
return super.loadKeycloakConfigFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return cfgUrl.openStream();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new RuntimeException(ioe);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return super.loadKeycloakConfigFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import org.junit.Test;
|
||||||
import org.junit.rules.ExternalResource;
|
import org.junit.rules.ExternalResource;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.TokenIdGenerator;
|
import org.keycloak.TokenIdGenerator;
|
||||||
|
import org.keycloak.adapters.CorsHeaders;
|
||||||
import org.keycloak.constants.AdapterConstants;
|
import org.keycloak.constants.AdapterConstants;
|
||||||
import org.keycloak.adapters.HttpClientBuilder;
|
import org.keycloak.adapters.HttpClientBuilder;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
|
@ -227,6 +228,52 @@ public class JaxrsFilterTest {
|
||||||
Assert.assertTrue(getRep.getHasJaxrsAppRole());
|
Assert.assertTrue(getRep.getHasJaxrsAppRole());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCors() {
|
||||||
|
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
|
Map<String,String> initParams = new TreeMap<String,String>();
|
||||||
|
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak.json");
|
||||||
|
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send OPTIONS request
|
||||||
|
Response optionsResp = client.target(JAXRS_APP_URL).request()
|
||||||
|
.header(CorsHeaders.ORIGIN, "http://localhost:8081")
|
||||||
|
.options();
|
||||||
|
Assert.assertEquals("true", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||||
|
Assert.assertEquals("http://localhost:8081", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
|
optionsResp.close();
|
||||||
|
|
||||||
|
// Retrieve token
|
||||||
|
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
|
||||||
|
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
|
||||||
|
|
||||||
|
// Send GET request with token but bad origin
|
||||||
|
Response badOriginResp = client.target(JAXRS_APP_URL).request()
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||||
|
.header(CorsHeaders.ORIGIN, "http://evil.org")
|
||||||
|
.get();
|
||||||
|
Assert.assertEquals(403, badOriginResp.getStatus());
|
||||||
|
badOriginResp.close();
|
||||||
|
|
||||||
|
// Send GET request with token and good origin
|
||||||
|
Response goodResp = client.target(JAXRS_APP_URL).request()
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, authHeader)
|
||||||
|
.header(CorsHeaders.ORIGIN, "http://localhost:8081")
|
||||||
|
.get();
|
||||||
|
Assert.assertEquals(200, goodResp.getStatus());
|
||||||
|
Assert.assertEquals("true", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||||
|
Assert.assertEquals("http://localhost:8081", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||||
|
JaxrsTestResource.SimpleRepresentation getRep = goodResp.readEntity(JaxrsTestResource.SimpleRepresentation.class);
|
||||||
|
Assert.assertEquals("get", getRep.getMethod());
|
||||||
|
goodResp.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPushNotBefore() {
|
public void testPushNotBefore() {
|
||||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
"auth-server-url": "http://localhost:8081/auth",
|
"auth-server-url": "http://localhost:8081/auth",
|
||||||
"ssl-required" : "external",
|
"ssl-required" : "external",
|
||||||
"bearer-only": true
|
"bearer-only": true,
|
||||||
|
"enable-cors": true
|
||||||
}
|
}
|
Loading…
Reference in a new issue