KEYCLOAK-1780 documentation + Generic client authentication screen
This commit is contained in:
parent
fbf62b2a63
commit
be8394158f
15 changed files with 319 additions and 15 deletions
|
@ -200,7 +200,7 @@ Forms Subflow - ALTERNATIVE
|
|||
</orderedlist>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<section id="auth_spi_walkthrough">
|
||||
<title>Authenticator SPI Walk Through</title>
|
||||
<para>
|
||||
In this section, we'll take a look at the Authenticator interface. For this, we are going to implement an authenticator
|
||||
|
@ -502,7 +502,7 @@ public class SecretQuestionAuthenticatorFactory implements AuthenticatorFactory,
|
|||
bean as well.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<section id="adding_authenticator">
|
||||
<title>Adding Authenticator to a Flow</title>
|
||||
<para>
|
||||
Adding an Authenticator to a flow must be done in the admin console.
|
||||
|
@ -865,4 +865,140 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor
|
|||
authenticator.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="client_authentication">
|
||||
<title>Authentication of clients</title>
|
||||
<para>Keycloak actually supports pluggable authentication for <ulink url="http://openid.net/specs/openid-connect-core-1_0.html">OpenID Connect</ulink>
|
||||
client applications. Authentication of client (application) is used under the hood by the <link linkend="adapter-config">Keycloak adapter</link>
|
||||
during sending any backchannel requests to the Keycloak server (like the request for exchange code to access token after
|
||||
successful authentication or request to refresh token). But the client authentication can be also used directly by you during
|
||||
<link linkend="direct-access-grants">Direct Access grants</link> or during <link linkend="service-accounts">Service account</link> authentication.
|
||||
</para>
|
||||
<section>
|
||||
<title>Default implementations</title>
|
||||
<para>
|
||||
Actually Keycloak has 2 builtin implementations of client authentication:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Traditional authentication with client_id and client_secret</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This is default mechanism mentioned in the <ulink url="http://openid.net/specs/openid-connect-core-1_0.html">OpenID Connect</ulink>
|
||||
or <ulink url="http://tools.ietf.org/html/rfc6749">OAuth2</ulink> specification and Keycloak supports it since it's early days.
|
||||
The public client needs to include <literal>client_id</literal> parameter with it's ID in the POST request (so it's defacto not authenticated)
|
||||
and the confidential client needs to include <literal>Authorization: Basic</literal> header with
|
||||
the clientId and clientSecret used as username and password.
|
||||
</para>
|
||||
<para>
|
||||
For the public/javascript clients, you
|
||||
don't need to add anything into your keycloak.json configuration file. For the confidential (server) clients, you need to add something like this:
|
||||
<programlisting><![CDATA[
|
||||
"credentials": {
|
||||
"secret": "mysecret"
|
||||
}
|
||||
]]></programlisting>
|
||||
|
||||
where the <literal>mysecret</literal> needs to be replaced with the real value of client secret. You can obtain it from client admin console.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Authentication with signed JWT</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This is based on the <ulink url="https://tools.ietf.org/html/draft-jones-oauth-jwt-bearer-03">JWT Bearer Token Profiles for OAuth 2.0</ulink> specification.
|
||||
The client/adapter generates the <ulink url="https://tools.ietf.org/html/rfc7519">JWT</ulink> and signs it with his private key.
|
||||
The Keycloak then verifies the signed JWT with the client's public key and authenticates client based on it.
|
||||
</para>
|
||||
<para>
|
||||
To achieve this, you need those steps:
|
||||
<itemizedlist>
|
||||
<listitem>Your client needs to have private key and Keycloak needs to have client public key. This can be either:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
Generated in Keycloak admin console - In this case, Keycloak will generate pair of keys and it will save
|
||||
public key and certificate in it's DB. The keystore file with the private key will be downloaded and you need to save it
|
||||
in the location accessible to your client application
|
||||
</listitem>
|
||||
<listitem>
|
||||
Uploaded in Keycloak admin console - This option is useful if you already has existing private key of your client.
|
||||
In this case, you just need to upload the public key and certificate to the Keycloak server.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
In both cases, the private key is not saved in Keycloak DB, but it's owned exclusively by your client. The Keycloak DB has just public key.
|
||||
</listitem>
|
||||
<listitem>
|
||||
As second step, you need to use the configuration like this in your <literal>keycloak.json</literal> adapter configuration:
|
||||
<programlisting><![CDATA[
|
||||
"credentials": {
|
||||
"jwt": {
|
||||
"client-keystore-file": "classpath:keystore-client.jks",
|
||||
"client-keystore-type": "JKS",
|
||||
"client-keystore-password": "storepass",
|
||||
"client-key-password": "keypass",
|
||||
"client-key-alias": "clientkey",
|
||||
"token-expiration": 10
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
The <literal>client-keystore-file</literal> is the location of the keystore file, which is either on classpath
|
||||
(for example if bundled in the WAR itself) or somewhere on the filesystem. Other options specify type of keystore and password of keystore itself
|
||||
and of the private key. Last option <literal>token-expiration</literal> is the expiration of JWT in seconds. The token needs to be valid
|
||||
just for single request, so 10 seconds is usually sufficient.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
<para>
|
||||
See the demo example and especially the <literal>examples/preconfigured-demo/service-account</literal>
|
||||
for the example application showing service accounts authentication with both clientId+clientSecret and with signed JWT.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Implement your own client authenticator</title>
|
||||
<para>
|
||||
For plug your own client authenticator, you need to implement few interfaces on both client (adapter) and server side.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Client side</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Here you need to implement <literal>org.keycloak.adapters.authentication.ClientCredentialsProvider</literal> and put the implementation either to:
|
||||
<itemizedlist>
|
||||
<listitem>your WAR file into WEB-INF/classes . But in this case, the implementation can be used just for this single WAR application</listitem>
|
||||
<listitem>Some JAR file, which will be added into WEB-INF/lib of your WAR</listitem>
|
||||
<listitem>Some JAR file, which will be used as jboss module and configured in jboss-deployment-structure.xml of your WAR.</listitem>
|
||||
</itemizedlist>
|
||||
In all cases, you also need to create the file <literal>META-INF/services/org.keycloak.adapters.authentication.ClientCredentialsProvider</literal>
|
||||
either in the WAR or in your JAR.
|
||||
</para>
|
||||
<para>
|
||||
You also need to configure your clientCredentialsProvider in <literal>keycloak.json</literal> . See the javadoc for more details.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Server side</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Here you need to implement <literal>org.keycloak.authentication.ClientAuthenticatorFactory</literal> and
|
||||
<literal>org.keycloak.authentication.ClientAuthenticator</literal> . You also need to add the file
|
||||
<literal>META-INF/services/org.keycloak.authentication.ClientAuthenticatorFactory</literal> with the name of the implementation classes.
|
||||
See <link linkend="auth_spi_walkthrough">authenticators</link> for more details.
|
||||
</para>
|
||||
<para>
|
||||
Finally you need to configure admin console . You need to create new client authentication flow and define execution
|
||||
with your authenticator (you can also add the builtin authenticators and configure requirements etc)
|
||||
and finally configure Clients binding . See <link linkend="adding_authenticator">Adding Authenticator</link> for more details.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
|
@ -30,7 +30,9 @@
|
|||
an access token for. You must also pass along the "client_id" of the client you are creating
|
||||
an access token for. This "client_id" is the Client Id specified in admin console (not it's id from DB!). Depending on
|
||||
whether your client is <link linkend='access-types'>"public" or "confidential"</link>, you may also have to pass along
|
||||
it's client secret as well. Finally you need to pass "grant_type" parameter with value "password" .
|
||||
it's client secret as well. We support pluggable client authentication, so alternatively you can use other form of client credentials like signed JWT assertion.
|
||||
See <link linkend="client_authentication">Client Authentication</link> section for more details. Finally you need to pass "grant_type"
|
||||
parameter with value "password" .
|
||||
</para>
|
||||
<para>
|
||||
For public client's, the POST invocation requires form parameters that contain the username,
|
||||
|
@ -71,6 +73,10 @@ Pragma: no-cache
|
|||
</programlisting>
|
||||
|
||||
</para>
|
||||
<para>As mentioned above, we support also other means of authenticating clients. In adition to default client_id and client secret,
|
||||
we also have signed JWT assertion by default. There is possibility to use any other form of client authentication implemented by you. See <link linkend="client_authentication">Client Authentication</link>
|
||||
section for more details.
|
||||
</para>
|
||||
<para>
|
||||
Here's a Java example using Apache HTTP Client and some Keycloak utility classes.:
|
||||
<programlisting><![CDATA[
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
|
||||
<para>
|
||||
The REST URL to invoke on is <literal>/{keycloak-root}/realms/{realm-name}/protocol/openid-connect/token</literal>.
|
||||
Invoking on this URL is a POST request and requires you to post the clientId and clientSecret of the client in <literal>Authorization: Basic</literal> header.
|
||||
Later we want to add more mechanisms for authenticating clients. You also need to use parameter <literal>grant_type=client_credentials</literal> as per OAuth2 specification.
|
||||
Invoking on this URL is a POST request and requires you to post the client credentials. By default, client credentials are
|
||||
represented by clientId and clientSecret of the client in <literal>Authorization: Basic</literal> header, but you can also
|
||||
authenticate client with signed JWT assertion or any other custom mechanism for client authentication. See
|
||||
<link linkend="client_authentication">Client Authentication</link> section for more details. You also need to use parameter <literal>grant_type=client_credentials</literal> as per OAuth2 specification.
|
||||
</para>
|
||||
<para>
|
||||
For example the POST invocation to retrieve service account can look like this:
|
||||
|
|
|
@ -106,7 +106,7 @@ public abstract class ProductServiceAccountServlet extends HttpServlet {
|
|||
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
|
||||
|
||||
// Add client credentials according to the method configured in keycloak.json file
|
||||
// Add client credentials according to the method configured in keycloak-client-secret.json or keycloak-client-signed-jwt.json file
|
||||
Map<String, String> reqHeaders = new HashMap<>();
|
||||
Map<String, String> reqParams = new HashMap<>();
|
||||
ClientCredentialsProviderUtils.setClientCredentials(deployment, reqHeaders, reqParams);
|
||||
|
|
|
@ -666,6 +666,21 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'ClientSignedJWTCtrl'
|
||||
})
|
||||
.when('/realms/:realm/clients/:client/credentials/:provider', {
|
||||
templateUrl : resourceUrl + '/partials/client-credentials-generic.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
client : function(ClientLoader) {
|
||||
return ClientLoader();
|
||||
},
|
||||
clientConfigProperties: function(PerClientAuthenticationConfigDescriptionLoader) {
|
||||
return PerClientAuthenticationConfigDescriptionLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ClientGenericCredentialsCtrl'
|
||||
})
|
||||
.when('/realms/:realm/clients/:client/credentials/client-jwt/:keyType/import/:attribute', {
|
||||
templateUrl : resourceUrl + '/partials/client-credentials-jwt-key-import.html',
|
||||
resolve : {
|
||||
|
|
|
@ -93,6 +93,39 @@ module.controller('ClientSignedJWTCtrl', function($scope, $location, realm, clie
|
|||
};
|
||||
});
|
||||
|
||||
module.controller('ClientGenericCredentialsCtrl', function($scope, $location, realm, client, clientConfigProperties, Client, Notifications) {
|
||||
|
||||
console.log('ClientGenericCredentialsCtrl invoked');
|
||||
|
||||
$scope.realm = realm;
|
||||
$scope.client = angular.copy(client);
|
||||
$scope.clientConfigProperties = clientConfigProperties;
|
||||
$scope.changed = false;
|
||||
|
||||
$scope.$watch('client', function() {
|
||||
if (!angular.equals($scope.client, client)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.save = function() {
|
||||
|
||||
Client.update({
|
||||
realm : realm.realm,
|
||||
client : client.id
|
||||
}, $scope.client, function() {
|
||||
$scope.changed = false;
|
||||
client = $scope.client;
|
||||
Notifications.success("Client authentication configuration has been saved to the client.");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.client = angular.copy(client);
|
||||
$scope.changed = false;
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('ClientIdentityProviderCtrl', function($scope, $location, $route, realm, client, Client, $location, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.client = angular.copy(client);
|
||||
|
|
|
@ -418,6 +418,15 @@ module.factory('AuthenticationConfigDescriptionLoader', function(Loader, Authent
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('PerClientAuthenticationConfigDescriptionLoader', function(Loader, PerClientAuthenticationConfigDescription, $route, $q) {
|
||||
return Loader.query(PerClientAuthenticationConfigDescription, function () {
|
||||
return {
|
||||
realm: $route.current.params.realm,
|
||||
provider: $route.current.params.provider
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('ExecutionIdLoader', function($route) {
|
||||
return function() { return $route.current.params.executionId; };
|
||||
});
|
||||
|
|
|
@ -1255,6 +1255,12 @@ module.factory('AuthenticationConfigDescription', function($resource) {
|
|||
provider: '@provider'
|
||||
});
|
||||
});
|
||||
module.factory('PerClientAuthenticationConfigDescription', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/authentication/per-client-config-description/:provider', {
|
||||
realm : '@realm',
|
||||
provider: '@provider'
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('AuthenticationConfig', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/authentication/config/:config', {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
|
||||
<li>{{client.clientId}}</li>
|
||||
</ol>
|
||||
|
||||
<kc-tabs-client></kc-tabs-client>
|
||||
|
||||
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageClients">
|
||||
<fieldset>
|
||||
<kc-provider-config realm="realm" config="client.attributes" properties="clientConfigProperties"></kc-provider-config>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
|
||||
<button kc-save data-ng-disabled="!changed">Save</button>
|
||||
<button kc-reset data-ng-disabled="!changed">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
|
@ -9,14 +9,14 @@ import org.keycloak.adapters.KeycloakDeployment;
|
|||
* (codeToToken exchange, refresh token or backchannel logout) . You can also use it in your application during direct access grants or service account request
|
||||
* (See the service-account example from Keycloak demo for more info)
|
||||
*
|
||||
* When you implement this SPI on the adapter (application) side, you also need to implement {@link org.keycloak.authentication.ClientAuthenticator} on the server side,
|
||||
* When you implement this SPI on the adapter (application) side, you also need to implement org.keycloak.authentication.ClientAuthenticator on the server side,
|
||||
* so your server is able to authenticate client
|
||||
*
|
||||
* You must specify a file
|
||||
* META-INF/services/org.keycloak.adapters.authentication.ClientCredentialsProvider in the WAR that this class is contained in (or in the JAR that is attached to the WEB-INF/lib or as jboss module
|
||||
* if you want to share the implementation among more WARs). This file must have the fully qualified class name of all your ClientAuthenticatorFactory classes
|
||||
*
|
||||
* NOTE: The SPI is not finished and method signatures are still subject to change in future versions (for example to support usecase for
|
||||
* NOTE: The SPI is not finished and method signatures are still subject to change in future versions (for example to support
|
||||
* authentication with client certificate)
|
||||
*
|
||||
* @see ClientIdAndSecretCredentialsProvider
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package org.keycloak.authentication;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
|
@ -29,4 +32,12 @@ public interface ClientAuthenticatorFactory extends ProviderFactory<ClientAuthen
|
|||
*/
|
||||
boolean isConfigurablePerClient();
|
||||
|
||||
/**
|
||||
* List of config properties for this client implementation. Those will be shown in admin console in clients credentials tab and can be configured per client.
|
||||
* Applicable only if "isConfigurablePerClient" is true
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<ProviderConfigProperty> getConfigPropertiesPerClient();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.authentication.authenticators.client;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -157,6 +158,12 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
|
|||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigPropertiesPerClient() {
|
||||
// This impl doesn't use generic screen in admin console, but has it's own screen. So no need to return anything here
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.authentication.authenticators.client;
|
|||
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -174,6 +175,12 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
|
|||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigPropertiesPerClient() {
|
||||
// This impl doesn't use generic screen in admin console, but has it's own screen. So no need to return anything here
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
|
|
|
@ -877,17 +877,40 @@ public class AuthenticationManagementResource {
|
|||
rep.setProperties(new LinkedList<ConfigPropertyRepresentation>());
|
||||
List<ProviderConfigProperty> configProperties = factory.getConfigProperties();
|
||||
for (ProviderConfigProperty prop : configProperties) {
|
||||
ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
|
||||
propRep.setName(prop.getName());
|
||||
propRep.setLabel(prop.getLabel());
|
||||
propRep.setType(prop.getType());
|
||||
propRep.setDefaultValue(prop.getDefaultValue());
|
||||
propRep.setHelpText(prop.getHelpText());
|
||||
ConfigPropertyRepresentation propRep = getConfigPropertyRep(prop);
|
||||
rep.getProperties().add(propRep);
|
||||
}
|
||||
return rep;
|
||||
}
|
||||
|
||||
private ConfigPropertyRepresentation getConfigPropertyRep(ProviderConfigProperty prop) {
|
||||
ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
|
||||
propRep.setName(prop.getName());
|
||||
propRep.setLabel(prop.getLabel());
|
||||
propRep.setType(prop.getType());
|
||||
propRep.setDefaultValue(prop.getDefaultValue());
|
||||
propRep.setHelpText(prop.getHelpText());
|
||||
return propRep;
|
||||
}
|
||||
|
||||
|
||||
@Path("per-client-config-description/{providerId}")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public List<ConfigPropertyRepresentation> getPerClientConfigDescription(@PathParam("providerId") String providerId) {
|
||||
this.auth.requireView();
|
||||
ConfigurableAuthenticatorFactory factory = CredentialHelper.getConfigurableAuthenticatorFactory(session, providerId);
|
||||
ClientAuthenticatorFactory clientAuthFactory = (ClientAuthenticatorFactory) factory;
|
||||
List<ProviderConfigProperty> perClientConfigProps = clientAuthFactory.getConfigPropertiesPerClient();
|
||||
List<ConfigPropertyRepresentation> result = new LinkedList<>();
|
||||
for (ProviderConfigProperty prop : perClientConfigProps) {
|
||||
ConfigPropertyRepresentation propRep = getConfigPropertyRep(prop);
|
||||
result.add(propRep);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Path("config")
|
||||
@POST
|
||||
@NoCache
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.testsuite.forms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -25,6 +26,25 @@ public class PassThroughClientAuthenticator extends AbstractClientAuthenticator
|
|||
AuthenticationExecutionModel.Requirement.REQUIRED
|
||||
};
|
||||
|
||||
private static final List<ProviderConfigProperty> clientConfigProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName("passthroughauth.foo");
|
||||
property.setLabel("Foo Property");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
property.setHelpText("Foo Property of this authenticator, which does nothing");
|
||||
clientConfigProperties.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName("passthroughauth.bar");
|
||||
property.setLabel("Bar Property");
|
||||
property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
|
||||
property.setHelpText("Bar Property of this authenticator, which does nothing");
|
||||
clientConfigProperties.add(property);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticateClient(ClientAuthenticationFlowContext context) {
|
||||
ClientModel client = context.getRealm().getClientByClientId(clientId);
|
||||
|
@ -60,7 +80,7 @@ public class PassThroughClientAuthenticator extends AbstractClientAuthenticator
|
|||
|
||||
@Override
|
||||
public boolean isConfigurablePerClient() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,6 +98,11 @@ public class PassThroughClientAuthenticator extends AbstractClientAuthenticator
|
|||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigPropertiesPerClient() {
|
||||
return clientConfigProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
|
|
Loading…
Reference in a new issue