You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/05/03 14:58:08 UTC

[1/8] syncope git commit: [SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser

Repository: syncope
Updated Branches:
  refs/heads/2_0_X 13f442a92 -> f29e60f62
  refs/heads/master 064b7efac -> 797fd1cbd


http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
new file mode 100644
index 0000000..65fb816
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.Cacheable;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.jpa.entity.resource.AbstractItem;
+
+@Entity
+@Table(name = JPAOIDCProviderItem.TABLE)
+@Cacheable
+public class JPAOIDCProviderItem extends AbstractItem implements OIDCProviderItem {
+
+    public static final String TABLE = "OIDCProviderItem";
+
+    private static final long serialVersionUID = -6903418265811089724L;
+
+    @ManyToOne
+    private JPAOIDCProvider op;
+
+    /**
+     * (Optional) classes for Item transformation.
+     */
+    @ElementCollection(fetch = FetchType.EAGER)
+    @Column(name = "transformerClassName")
+    @CollectionTable(name = TABLE + "_Transformer",
+            joinColumns =
+            @JoinColumn(name = "oidcProviderItemItem_id", referencedColumnName = "id"))
+    private List<String> transformerClassNames = new ArrayList<>();
+
+    @Override
+    public List<String> getTransformerClassNames() {
+        return transformerClassNames;
+    }
+
+    @Override
+    public OIDCProvider getOP() {
+        return op;
+    }
+
+    @Override
+    public void setOP(final OIDCProvider op) {
+        checkType(op, JPAOIDCProvider.class);
+        this.op = (JPAOIDCProvider) op;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCUserTemplate.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCUserTemplate.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCUserTemplate.java
new file mode 100644
index 0000000..54eea72
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCUserTemplate.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
+import org.apache.syncope.core.persistence.jpa.entity.resource.AbstractAnyTemplate;
+
+@Entity
+@Table(name = JPAOIDCUserTemplate.TABLE, uniqueConstraints =
+        @UniqueConstraint(columnNames = { "op_id" }))
+public class JPAOIDCUserTemplate extends AbstractAnyTemplate implements OIDCUserTemplate {
+
+    public static final String TABLE = "OIDCUserTemplate";
+
+    private static final long serialVersionUID = 3964321047520954968L;
+
+    @ManyToOne
+    private JPAOIDCProvider op;
+
+    @Override
+    public OIDCProvider getOP() {
+        return op;
+    }
+
+    @Override
+    public void setOP(final OIDCProvider op) {
+        checkType(op, JPAOIDCProvider.class);
+        this.op = (JPAOIDCProvider) op;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/pom.xml b/ext/oidcclient/pom.xml
new file mode 100644
index 0000000..ff10b6d
--- /dev/null
+++ b/ext/oidcclient/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-ext</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client</name>
+  <description>Apache Syncope Ext: OIDC Client</description>
+  <groupId>org.apache.syncope.ext</groupId>
+  <artifactId>syncope-ext-oidcclient</artifactId>
+  <packaging>pom</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+  </properties>
+ 
+
+  <modules>
+    <module>common-lib</module>
+    <module>rest-api</module>
+    <module>logic</module>
+    <module>rest-cxf</module>
+    <module>persistence-api</module>
+    <module>persistence-jpa</module>
+    <module>agent</module>
+    <module>client-console</module>
+    <module>client-enduser</module>
+    <module>provisioning-api</module>
+    <module>provisioning-java</module>
+  </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/provisioning-api/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-api/pom.xml b/ext/oidcclient/provisioning-api/pom.xml
new file mode 100644
index 0000000..99b7a9c
--- /dev/null
+++ b/ext/oidcclient/provisioning-api/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Provisioning API</name>
+  <description>Apache Syncope Ext: OIDC Client Provisioning API</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-provisioning-api</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-provisioning-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-persistence-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/OIDCProviderActions.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/OIDCProviderActions.java b/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/OIDCProviderActions.java
new file mode 100644
index 0000000..40ea6a8
--- /dev/null
+++ b/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/OIDCProviderActions.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.provisioning.api;
+
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.lib.to.UserTO;
+
+public interface OIDCProviderActions {
+
+    UserTO beforeCreate(UserTO input, OIDCLoginResponseTO loginResponse);
+
+    UserTO afterCreate(UserTO input, OIDCLoginResponseTO loginResponse);
+
+    UserPatch beforeUpdate(UserPatch input, OIDCLoginResponseTO loginResponse);
+
+    UserTO afterUpdate(UserTO input, OIDCLoginResponseTO loginResponse);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCProviderDataBinder.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCProviderDataBinder.java b/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCProviderDataBinder.java
new file mode 100644
index 0000000..acea05d
--- /dev/null
+++ b/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCProviderDataBinder.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+
+public interface OIDCProviderDataBinder {
+
+    OIDCProvider create(OIDCProviderTO op);
+
+    OIDCProvider update(OIDCProvider op, OIDCProviderTO opTO);
+
+    OIDCProviderTO getOIDCProviderTO(OIDCProvider op);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-java/pom.xml b/ext/oidcclient/provisioning-java/pom.xml
new file mode 100644
index 0000000..8738e5a
--- /dev/null
+++ b/ext/oidcclient/provisioning-java/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Provisioning Java</name>
+  <description>Apache Syncope Ext: OIDC Client Provisioning Java</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-provisioning-java</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-provisioning-java</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-provisioning-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultOIDCProviderActions.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultOIDCProviderActions.java b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultOIDCProviderActions.java
new file mode 100644
index 0000000..e6b060c
--- /dev/null
+++ b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultOIDCProviderActions.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.provisioning.java;
+
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.core.provisioning.api.OIDCProviderActions;
+
+public class DefaultOIDCProviderActions implements OIDCProviderActions {
+
+    @Override
+    public UserTO beforeCreate(final UserTO input, final OIDCLoginResponseTO loginResponse) {
+        return input;
+    }
+
+    @Override
+    public UserTO afterCreate(final UserTO input, final OIDCLoginResponseTO loginResponse) {
+        return input;
+    }
+
+    @Override
+    public UserPatch beforeUpdate(final UserPatch input, final OIDCLoginResponseTO loginResponse) {
+        return input;
+    }
+
+    @Override
+    public UserTO afterUpdate(final UserTO input, final OIDCLoginResponseTO loginResponse) {
+        return input;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
new file mode 100644
index 0000000..6cfa235
--- /dev/null
+++ b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import java.text.ParseException;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.syncope.common.lib.SyncopeClientCompositeException;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
+import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.OIDCEntityFactory;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.provisioning.api.IntAttrName;
+import org.apache.syncope.core.provisioning.api.data.OIDCProviderDataBinder;
+import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
+import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.spring.BeanUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class OIDCProviderDataBinderImpl implements OIDCProviderDataBinder {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCProviderDataBinder.class);
+
+    private static final String[] ITEM_IGNORE_PROPERTIES = { "key", "purpose" };
+
+    @Autowired
+    private AnyTypeDAO anyTypeDAO;
+
+    @Autowired
+    private OIDCProviderDAO oidcOPDAO;
+
+    @Autowired
+    private OIDCEntityFactory entityFactory;
+
+    @Autowired
+    private IntAttrNameParser intAttrNameParser;
+
+    @Override
+    public OIDCProvider create(final OIDCProviderTO opTO) {
+        return update(entityFactory.newEntity(OIDCProvider.class), opTO);
+
+    }
+
+    private void populateItems(
+            final OIDCProviderTO opTO,
+            final OIDCProvider op,
+            final AnyTypeClassTO allowedSchemas) {
+
+        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
+        SyncopeClientException invalidMapping =
+                SyncopeClientException.build(ClientExceptionType.InvalidMapping);
+        SyncopeClientException requiredValuesMissing =
+                SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
+
+        for (ItemTO itemTO : opTO.getItems()) {
+            if (itemTO == null) {
+                LOG.error("Null {}", ItemTO.class.getSimpleName());
+                invalidMapping.getElements().add("Null " + ItemTO.class.getSimpleName());
+            } else if (itemTO.getIntAttrName() == null) {
+                requiredValuesMissing.getElements().add("intAttrName");
+                scce.addException(requiredValuesMissing);
+            } else {
+                IntAttrName intAttrName = null;
+                try {
+                    intAttrName = intAttrNameParser.parse(itemTO.getIntAttrName(), AnyTypeKind.USER);
+                } catch (ParseException e) {
+                    LOG.error("Invalid intAttrName '{}' specified, ignoring", itemTO.getIntAttrName(), e);
+                }
+
+                if (intAttrName == null || intAttrName.getSchemaType() == null && intAttrName.getField() == null) {
+                    LOG.error("'{}' not existing", itemTO.getIntAttrName());
+                    invalidMapping.getElements().add("'" + itemTO.getIntAttrName() + "' not existing");
+                } else {
+                    boolean allowed = true;
+                    if (intAttrName.getSchemaType() != null
+                            && intAttrName.getEnclosingGroup() == null
+                            && intAttrName.getRelatedAnyObject() == null) {
+                        switch (intAttrName.getSchemaType()) {
+                            case PLAIN:
+                                allowed = allowedSchemas.getPlainSchemas().contains(intAttrName.getSchemaName());
+                                break;
+
+                            case DERIVED:
+                                allowed = allowedSchemas.getDerSchemas().contains(intAttrName.getSchemaName());
+                                break;
+
+                            case VIRTUAL:
+                                allowed = allowedSchemas.getVirSchemas().contains(intAttrName.getSchemaName());
+                                break;
+
+                            default:
+                        }
+                    }
+
+                    if (allowed) {
+                        // no mandatory condition implies mandatory condition false
+                        if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
+                                ? "false" : itemTO.getMandatoryCondition())) {
+
+                            SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
+                                    ClientExceptionType.InvalidValues);
+                            invalidMandatoryCondition.getElements().add(itemTO.getMandatoryCondition());
+                            scce.addException(invalidMandatoryCondition);
+                        }
+
+                        OIDCProviderItem item = entityFactory.newEntity(OIDCProviderItem.class);
+                        BeanUtils.copyProperties(itemTO, item, ITEM_IGNORE_PROPERTIES);
+                        item.setOP(op);
+                        item.setPurpose(MappingPurpose.NONE);
+                        if (item.isConnObjectKey()) {
+                            if (intAttrName.getSchemaType() == SchemaType.VIRTUAL) {
+                                invalidMapping.getElements().
+                                        add("Virtual attributes cannot be set as ConnObjectKey");
+                            }
+                            if ("password".equals(intAttrName.getField())) {
+                                invalidMapping.getElements().add(
+                                        "Password attributes cannot be set as ConnObjectKey");
+                            }
+
+                            op.setConnObjectKeyItem(item);
+                        } else {
+                            op.add(item);
+                        }
+                    } else {
+                        LOG.error("'{}' not allowed", itemTO.getIntAttrName());
+                        invalidMapping.getElements().add("'" + itemTO.getIntAttrName() + "' not allowed");
+                    }
+                }
+            }
+        }
+
+        if (!invalidMapping.getElements().isEmpty()) {
+            scce.addException(invalidMapping);
+        }
+        if (scce.hasExceptions()) {
+            throw scce;
+        }
+    }
+
+    @Override
+    public OIDCProvider update(final OIDCProvider op, final OIDCProviderTO opTO) {
+        op.setAuthorizationEndpoint(opTO.getAuthorizationEndpoint());
+        op.setClientID(opTO.getClientID());
+        op.setClientSecret(opTO.getClientSecret());
+        op.setName(opTO.getName());
+        op.setIssuer(opTO.getIssuer());
+        op.setJwksUri(opTO.getJwksUri());
+        op.setTokenEndpoint(opTO.getTokenEndpoint());
+        op.setUserinfoEndpoint(opTO.getUserinfoEndpoint());
+        op.setHasDiscovery(opTO.getHasDiscovery());
+        op.setCreateUnmatching(opTO.isCreateUnmatching());
+        op.setUpdateMatching(opTO.isUpdateMatching());
+
+        if (opTO.getUserTemplate() == null) {
+            op.setUserTemplate(null);
+        } else {
+            OIDCUserTemplate userTemplate = op.getUserTemplate();
+            if (userTemplate == null) {
+                userTemplate = entityFactory.newEntity(OIDCUserTemplate.class);
+                userTemplate.setAnyType(anyTypeDAO.findUser());
+                userTemplate.setOP(op);
+                op.setUserTemplate(userTemplate);
+            }
+            userTemplate.set(opTO.getUserTemplate());
+        }
+
+        op.getItems().clear();
+        AnyTypeClassTO allowedSchemas = new AnyTypeClassTO();
+        for (AnyTypeClass anyTypeClass : anyTypeDAO.findUser().getClasses()) {
+            allowedSchemas.getPlainSchemas().addAll(
+                    CollectionUtils.collect(anyTypeClass.getPlainSchemas(),
+                            EntityUtils.<PlainSchema>keyTransformer()));
+            allowedSchemas.getDerSchemas().addAll(
+                    CollectionUtils.collect(anyTypeClass.getDerSchemas(),
+                            EntityUtils.<DerSchema>keyTransformer()));
+            allowedSchemas.getVirSchemas().addAll(
+                    CollectionUtils.collect(anyTypeClass.getVirSchemas(),
+                            EntityUtils.<VirSchema>keyTransformer()));
+        }
+        populateItems(opTO, op, allowedSchemas);
+
+        op.getActionsClassNames().clear();
+        op.getActionsClassNames().addAll(opTO.getActionsClassNames());
+
+        return oidcOPDAO.save(op);
+    }
+
+    private void populateItems(final OIDCProvider op, final OIDCProviderTO opTO) {
+        for (OIDCProviderItem item : op.getItems()) {
+            ItemTO itemTO = new ItemTO();
+            itemTO.setKey(item.getKey());
+            BeanUtils.copyProperties(item, itemTO, ITEM_IGNORE_PROPERTIES);
+            itemTO.setPurpose(MappingPurpose.NONE);
+
+            if (itemTO.isConnObjectKey()) {
+                opTO.setConnObjectKeyItem(itemTO);
+            } else {
+                opTO.add(itemTO);
+            }
+        }
+    }
+
+    @Override
+    public OIDCProviderTO getOIDCProviderTO(final OIDCProvider op) {
+        OIDCProviderTO opTO = new OIDCProviderTO();
+
+        opTO.setKey(op.getKey());
+        opTO.setAuthorizationEndpoint(op.getAuthorizationEndpoint());
+        opTO.setClientID(op.getClientID());
+        opTO.setClientSecret(op.getClientSecret());
+        opTO.setName(op.getName());
+        opTO.setIssuer(op.getIssuer());
+        opTO.setJwksUri(op.getJwksUri());
+        opTO.setTokenEndpoint(op.getTokenEndpoint());
+        opTO.setUserinfoEndpoint(op.getUserinfoEndpoint());
+        opTO.setHasDiscovery(op.getHasDiscovery());
+        opTO.setCreateUnmatching(op.isCreateUnmatching());
+        opTO.setUpdateMatching(op.isUpdateMatching());
+
+        if (op.getUserTemplate() != null) {
+            opTO.setUserTemplate((UserTO) op.getUserTemplate().get());
+        }
+
+        populateItems(op, opTO);
+
+        opTO.getActionsClassNames().addAll(op.getActionsClassNames());
+
+        return opTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/rest-api/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-api/pom.xml b/ext/oidcclient/rest-api/pom.xml
new file mode 100644
index 0000000..7f71fb0
--- /dev/null
+++ b/ext/oidcclient/rest-api/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client REST API</name>
+  <description>Apache Syncope Ext: OIDC Client REST API</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.common</groupId>
+      <artifactId>syncope-common-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>      
+      <version>${project.version}</version>
+    </dependency>    
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!-- Generating javadoc JAR artifact for usage with CXF's WADL generator (for core) -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-javadocs</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCClientService.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCClientService.java b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCClientService.java
new file mode 100644
index 0000000..5585502
--- /dev/null
+++ b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCClientService.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.rest.api.service;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.Authorization;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.OIDCConstants;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+
+/**
+ * REST operations for OpenID Connect Clients.
+ */
+@Api(tags = "OIDCClients", authorizations = {
+    @Authorization(value = "BasicAuthentication")
+    ,
+    @Authorization(value = "Bearer") })
+@Path("oidcclient/clients")
+public interface OIDCClientService extends JAXRSService {
+
+    /**
+     * Generates OpenID Connect authentication request for the Provider matching the provided op.
+     *
+     * @param redirectURI redirect URI
+     * @param op OpenID Connect Provider
+     * @return OpenID Connect authentication request
+     */
+    @POST
+    @Path("loginRequest")
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    OIDCLoginRequestTO createLoginRequest(
+            @QueryParam(OIDCConstants.REDIRECT_URI) String redirectURI,
+            @QueryParam(OIDCConstants.OP) String op);
+
+    /**
+     * Uses the provided authorization code to go through the OpenID Connect tokens process and finally creates JWT for
+     * the matching user, if found.
+     *
+     * @param redirectURI redirect URI
+     * @param authorizationCode authorization code generated by the remote OpenID Connect Provider
+     * @param op OpenID Connect Provider
+     * @return JWT for the matching user plus attributes returned in the response
+     */
+    @POST
+    @Path("login")
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    OIDCLoginResponseTO login(
+            @QueryParam(OIDCConstants.REDIRECT_URI) String redirectURI,
+            @QueryParam("authorizationCode") String authorizationCode,
+            @QueryParam(OIDCConstants.OP) String op);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
new file mode 100644
index 0000000..b8ee7c7
--- /dev/null
+++ b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.rest.api.service;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.ResponseHeader;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.PUT;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.HttpHeaders;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+/**
+ * REST operations for OpenID Connect Providers.
+ */
+@Api(tags = "OIDCProviders", authorizations = {
+    @Authorization(value = "BasicAuthentication"),
+    @Authorization(value = "Bearer") })
+@Path("oidcclient/providers")
+public interface OIDCProviderService extends JAXRSService {
+
+    /**
+     * Returns the list of available OIDCProviderActions implementations.
+     *
+     * @return the list of available OIDCProviderActions implementations
+     */
+    @GET
+    @Path("actionsClasses")
+    @Produces({ MediaType.APPLICATION_JSON })
+    Set<String> getActionsClasses();
+
+    /**
+     * Returns a list of all defined OIDC Providers.
+     *
+     * @return list of all defined OIDC Providers
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    List<OIDCProviderTO> list();
+
+    /**
+     * Returns the OIDC Provider with matching key, if available.
+     *
+     * @param key OIDC Provider's key
+     * @return OIDC Providers with matching key, if available
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    OIDCProviderTO read(@PathParam("key") String key);
+
+    /**
+     * Creates a new OIDC Provider.
+     *
+     * @param oidcProviderTO OpenID Connect Provider configuration to be stored
+     * @return Response object featuring Location header of created OIDC Provider
+     */
+    @ApiResponses(
+            @ApiResponse(code = 201,
+                    message = "SecurityQuestion successfully created", responseHeaders = {
+                @ResponseHeader(name = RESTHeaders.RESOURCE_KEY, response = String.class,
+                        description = "UUID generated for the entity created")
+                ,
+                @ResponseHeader(name = HttpHeaders.LOCATION, response = String.class,
+                        description = "URL of the entity created") }))
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    Response create(OIDCProviderTO oidcProviderTO);
+
+    /**
+     * Creates a new OIDC Provider by using its Discovery Document.
+     *
+     * @param oidcProviderTO OpenID Connect Provider configuration to be stored  
+     * @return Response object featuring Location header of created OIDC Provider
+     */
+    @ApiResponses(
+            @ApiResponse(code = 201,
+                    message = "SecurityQuestion successfully created", responseHeaders = {
+                @ResponseHeader(name = RESTHeaders.RESOURCE_KEY, response = String.class,
+                        description = "UUID generated for the entity created")
+                ,
+                @ResponseHeader(name = HttpHeaders.LOCATION, response = String.class,
+                        description = "URL of the entity created") }))
+    @POST
+    @Path("fromDiscovery")
+    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    Response createFromDiscovery(OIDCProviderTO oidcProviderTO);
+
+    /**
+     * Updates the OIDC Provider with matching key.
+     *
+     * @param oidcProviderTO OpenID Connect Provider configuration to be stored
+     */
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = "key", paramType = "path", dataType = "string", value = "OIDC Provider's key") })
+    @ApiResponses(
+            @ApiResponse(code = 204, message = "Operation was successful"))
+    @PUT
+    @Path("{key}")
+    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void update(@NotNull OIDCProviderTO oidcProviderTO);
+
+    /**
+     * Deletes the OIDC Provider with matching key.
+     *
+     * @param key OIDC Provider key
+     */
+    @ApiResponses(
+            @ApiResponse(code = 204, message = "Operation was successful"))
+    @DELETE
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void delete(@PathParam("key") String key);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/rest-cxf/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-cxf/pom.xml b/ext/oidcclient/rest-cxf/pom.xml
new file mode 100644
index 0000000..701093b
--- /dev/null
+++ b/ext/oidcclient/rest-cxf/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client REST CXF</name>
+  <description>Apache Syncope Ext: OIDC Client REST CXF</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-rest-cxf</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-logic</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCClientServiceImpl.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCClientServiceImpl.java b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCClientServiceImpl.java
new file mode 100644
index 0000000..74c14b9
--- /dev/null
+++ b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCClientServiceImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.apache.syncope.core.logic.OIDCClientLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+
+@Service
+public class OIDCClientServiceImpl extends AbstractServiceImpl implements OIDCClientService {
+
+    @Autowired
+    private OIDCClientLogic logic;
+
+    @Override
+    public OIDCLoginRequestTO createLoginRequest(final String redirectURI, final String op) {
+        return logic.createLoginRequest(redirectURI, op);
+    }
+
+    @Override
+    public OIDCLoginResponseTO login(final String redirectURI, final String authorizationCode, final String op) {
+        return logic.login(redirectURI, authorizationCode, op);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
new file mode 100644
index 0000000..7af4366
--- /dev/null
+++ b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.apache.syncope.core.logic.OIDCProviderLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class OIDCProviderServiceImpl extends AbstractServiceImpl implements OIDCProviderService {
+
+    @Autowired
+    private OIDCProviderLogic logic;
+
+    @Override
+    public Set<String> getActionsClasses() {
+        return logic.getActionsClasses();
+    }
+
+    @Override
+    public Response create(final OIDCProviderTO oidcProviderTO) {
+        String created = logic.create(oidcProviderTO);
+
+        URI location = uriInfo.getAbsolutePathBuilder().path(created).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created).
+                build();
+    }
+
+    @Override
+    public Response createFromDiscovery(final OIDCProviderTO oidcProviderTO) {
+        String created = logic.createFromDiscovery(oidcProviderTO);
+
+        URI location = uriInfo.getAbsolutePathBuilder().path(created).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created).
+                build();
+    }
+
+    @Override
+    public List<OIDCProviderTO> list() {
+        return logic.list();
+
+    }
+
+    @Override
+    public OIDCProviderTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public void update(final OIDCProviderTO oidcProviderTO) {
+        logic.update(oidcProviderTO);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/pom.xml
----------------------------------------------------------------------
diff --git a/ext/pom.xml b/ext/pom.xml
index 73a5585..fb86dfd 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -93,6 +93,7 @@ under the License.
     <module>swagger-ui</module>
     <module>saml2sp</module>
     <module>scimv2</module>
+    <module>oidcclient</module>
   </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
index e206ff7..98d4504 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
@@ -30,7 +30,7 @@ import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.to.SAML2RequestTO;
 import org.apache.syncope.common.rest.api.service.SAML2SPService;
 
-@WebServlet(name = "login", urlPatterns = { "/saml2sp/login" })
+@WebServlet(name = "saml2spLogin", urlPatterns = { "/saml2sp/login" })
 public class Login extends AbstractSAML2SPServlet {
 
     private static final long serialVersionUID = 968480296813639041L;

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/console-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/console-reference/pom.xml b/fit/console-reference/pom.xml
index e4f2405..4fff371 100644
--- a/fit/console-reference/pom.xml
+++ b/fit/console-reference/pom.xml
@@ -75,6 +75,12 @@ under the License.
     </dependency>
 
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/console-reference/src/main/resources/oidcclient-agent.properties
----------------------------------------------------------------------
diff --git a/fit/console-reference/src/main/resources/oidcclient-agent.properties b/fit/console-reference/src/main/resources/oidcclient-agent.properties
new file mode 100644
index 0000000..1d53d49
--- /dev/null
+++ b/fit/console-reference/src/main/resources/oidcclient-agent.properties
@@ -0,0 +1,26 @@
+# 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.
+conf.directory=${conf.directory}
+
+anonymousUser=${anonymousUser}
+anonymousKey=${anonymousKey}
+
+scheme=http
+host=localhost
+port=9080
+rootPath=/syncope/rest/
+useGZIPCompression=true

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/console-reference/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/fit/console-reference/src/main/webapp/WEB-INF/web.xml b/fit/console-reference/src/main/webapp/WEB-INF/web.xml
index 890e5b0..1dc41d1 100644
--- a/fit/console-reference/src/main/webapp/WEB-INF/web.xml
+++ b/fit/console-reference/src/main/webapp/WEB-INF/web.xml
@@ -53,6 +53,16 @@ under the License.
     <param-value>../wicket/bookmarkable/org.apache.syncope.client.console.pages.SAML2SPSelfReg</param-value>
   </context-param>
   
+  <!-- OIDC Client Parameters -->
+  <context-param>
+    <param-name>oidcclient.login.success.url</param-name>
+    <param-value>../wicket/bookmarkable/org.apache.syncope.client.console.pages.OIDCClientLogin</param-value>
+  </context-param>
+  <context-param>
+    <param-name>oidcclient.login.error.url</param-name>
+    <param-value>../wicket/bookmarkable/org.apache.syncope.client.console.pages.Login</param-value>
+  </context-param>
+
   <!-- SESSION TIMEOUT (MINUTES)-->
   <session-config>
     <session-timeout>30</session-timeout>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/console-reference/src/test/resources/rebel.xml
----------------------------------------------------------------------
diff --git a/fit/console-reference/src/test/resources/rebel.xml b/fit/console-reference/src/test/resources/rebel.xml
index cfd69c9..876c58f 100644
--- a/fit/console-reference/src/test/resources/rebel.xml
+++ b/fit/console-reference/src/test/resources/rebel.xml
@@ -34,6 +34,10 @@ under the License.
     </dir>
     <dir name="${basedir}/../../ext/scimv2/client-console/target/classes">
     </dir>
+    <dir name="${basedir}/../../ext/oidcclient/agent/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/client-console/target/classes">
+    </dir>
   </classpath>
 
   <web>
@@ -57,6 +61,10 @@ under the License.
       <dir name="${basedir}/../../ext/scimv2/client-console/target/classes">
       </dir>
     </link>
+    <link target="/">
+      <dir name="${basedir}/../../ext/oidcclient/client-console/target/classes">
+      </dir>
+    </link>
   </web>
 
 </application>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 798a6e9..0b719e8 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -191,6 +191,18 @@ under the License.
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>          
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
       <scope>test</scope>
@@ -1075,6 +1087,17 @@ under the License.
         </dependency>
         
         <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+        
+        <dependency>
           <groupId>org.apache.syncope.ext.scimv2</groupId>
           <artifactId>syncope-ext-scimv2-rest-cxf</artifactId>
           <version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 3595886..5d24bd8 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -83,6 +83,8 @@ import org.apache.syncope.common.rest.api.service.ReportService;
 import org.apache.syncope.common.rest.api.service.ResourceService;
 import org.apache.syncope.common.rest.api.service.GroupService;
 import org.apache.syncope.common.rest.api.service.MailTemplateService;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
 import org.apache.syncope.common.rest.api.service.RealmService;
 import org.apache.syncope.common.rest.api.service.ReconciliationService;
 import org.apache.syncope.common.rest.api.service.RelationshipTypeService;
@@ -247,6 +249,10 @@ public abstract class AbstractITCase {
     protected static SAML2SPService saml2SpService;
 
     protected static SAML2IdPService saml2IdPService;
+    
+    protected static OIDCClientService oidcClientService;
+
+    protected static OIDCProviderService oidcProviderService;
 
     protected static SCIMConfService scimConfService;
 
@@ -318,6 +324,8 @@ public abstract class AbstractITCase {
         camelRouteService = adminClient.getService(CamelRouteService.class);
         saml2SpService = adminClient.getService(SAML2SPService.class);
         saml2IdPService = adminClient.getService(SAML2IdPService.class);
+        oidcClientService = adminClient.getService(OIDCClientService.class);
+        oidcProviderService = adminClient.getService(OIDCProviderService.class);
         scimConfService = adminClient.getService(SCIMConfService.class);
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/core-reference/src/test/java/org/apache/syncope/fit/OIDCClientDetector.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/OIDCClientDetector.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/OIDCClientDetector.java
new file mode 100644
index 0000000..b08342e
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/OIDCClientDetector.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.fit;
+
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OIDCClientDetector {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCClientDetector.class);
+
+    private static Boolean ENABLED;
+
+    public static boolean isOIDCClientAvailable() {
+        synchronized (LOG) {
+            if (ENABLED == null) {
+                try {
+                    new SyncopeClientFactoryBean().
+                            setAddress(AbstractITCase.ADDRESS).
+                            setContentType(SyncopeClientFactoryBean.ContentType.XML).
+                            create(new AnonymousAuthenticationHandler(
+                                    AbstractITCase.ANONYMOUS_UNAME, AbstractITCase.ANONYMOUS_KEY)).
+                            getService(OIDCProviderService.class).list();
+                    ENABLED = true;
+                } catch (Exception e) {
+                    // ignore
+                    ENABLED = false;
+                }
+            }
+        }
+        return ENABLED;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCClientITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCClientITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCClientITCase.java
new file mode 100644
index 0000000..33a9d44
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCClientITCase.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.fit.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.BasicAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.apache.syncope.fit.OIDCClientDetector;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class OIDCClientITCase extends AbstractITCase {
+
+    private static SyncopeClient anonymous;
+
+    private static SyncopeClient admin;
+
+    @BeforeClass
+    public static void setup() {
+        anonymous = new SyncopeClientFactoryBean().
+                setAddress(ADDRESS).
+                create(new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
+
+        admin = new SyncopeClientFactoryBean().
+                setAddress(ADDRESS).
+                create(new BasicAuthenticationHandler(ADMIN_UNAME, ADMIN_PWD));
+    }
+
+    @BeforeClass
+    public static void createOIDCProviderWithoutDiscovery() throws Exception {
+        if (!OIDCClientDetector.isOIDCClientAvailable()) {
+            return;
+        }
+
+        assertTrue(oidcProviderService.list().isEmpty());
+
+        OIDCProviderTO oidcProviderTO = new OIDCProviderTO();
+        oidcProviderTO.setAuthorizationEndpoint("AuthorizationEndpoint");
+        oidcProviderTO.setClientID("ClientID");
+        oidcProviderTO.setClientSecret("ClientSecret");
+        oidcProviderTO.setIssuer("https://accounts.google.com");
+        oidcProviderTO.setJwksUri("JwksUri");
+        oidcProviderTO.setName("Google");
+        oidcProviderTO.setTokenEndpoint("TokenEndpoint");
+        oidcProviderTO.setUserinfoEndpoint("UserinfoEndpoint");
+
+        admin.getService(OIDCProviderService.class).create(oidcProviderTO);
+    }
+
+    @AfterClass
+    public static void clearProviders() throws Exception {
+        if (!OIDCClientDetector.isOIDCClientAvailable()) {
+            return;
+        }
+
+        for (OIDCProviderTO op : oidcProviderService.list()) {
+            oidcProviderService.delete(op.getKey());
+        }
+    }
+
+    @Test
+    public void createLoginRequest() {
+        Assume.assumeTrue(OIDCClientDetector.isOIDCClientAvailable());
+
+        OIDCLoginRequestTO loginRequest = anonymous.getService(OIDCClientService.class).
+                createLoginRequest("http://localhost:9080/syncope-console/oidcclient/code-consumer", "Google");
+
+        assertNotNull(loginRequest);
+        assertEquals("http://localhost:9080/syncope-console/oidcclient/code-consumer", loginRequest.getRedirectURI());
+        assertNotNull(loginRequest.getProviderAddress());
+        assertNotNull(loginRequest.getClientId());
+        assertNotNull(loginRequest.getResponseType());
+        assertNotNull(loginRequest.getScope());
+        assertNotNull(loginRequest.getState());
+    }
+
+    @Test
+    public void setProviderMapping() {
+        Assume.assumeTrue(OIDCClientDetector.isOIDCClientAvailable());
+
+        OIDCProviderTO ssoCircle = IterableUtils.find(oidcProviderService.list(), new Predicate<OIDCProviderTO>() {
+
+            @Override
+            public boolean evaluate(final OIDCProviderTO object) {
+                return "Google".equals(object.getName());
+            }
+        });
+        assertNotNull(ssoCircle);
+        assertFalse(ssoCircle.isCreateUnmatching());
+        assertNull(ssoCircle.getUserTemplate());
+        assertFalse(ssoCircle.getItems().isEmpty());
+        assertNotNull(ssoCircle.getConnObjectKeyItem());
+        assertNotEquals("fullname", ssoCircle.getConnObjectKeyItem().getIntAttrName());
+        assertNotEquals("given_name", ssoCircle.getConnObjectKeyItem().getExtAttrName());
+
+        ssoCircle.setCreateUnmatching(true);
+
+        UserTO userTemplate = new UserTO();
+        userTemplate.setRealm("'/'");
+        ssoCircle.setUserTemplate(userTemplate);
+
+        ssoCircle.getItems().clear();
+        ItemTO keyMapping = new ItemTO();
+        keyMapping.setIntAttrName("fullname");
+        keyMapping.setExtAttrName("given_name");
+        ssoCircle.setConnObjectKeyItem(keyMapping);
+
+        oidcProviderService.update(ssoCircle);
+
+        ssoCircle = oidcProviderService.read(ssoCircle.getKey());
+        assertTrue(ssoCircle.isCreateUnmatching());
+        assertEquals(userTemplate, ssoCircle.getUserTemplate());
+        assertEquals("fullname", ssoCircle.getConnObjectKeyItem().getIntAttrName());
+        assertEquals("given_name", ssoCircle.getConnObjectKeyItem().getExtAttrName());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/core-reference/src/test/resources/rebel.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/rebel.xml b/fit/core-reference/src/test/resources/rebel.xml
index e740552..975ded0 100644
--- a/fit/core-reference/src/test/resources/rebel.xml
+++ b/fit/core-reference/src/test/resources/rebel.xml
@@ -103,6 +103,23 @@ under the License.
     </dir>
     <dir name="${basedir}/../../ext/scimv2/logic/target/classes">
     </dir>
+    <dir name="${basedir}/../../ext/oidcclient/common-lib/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/logic/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/persistence-api/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/persistence-jpa/target/classes">
+      <exclude name="org/apache/syncope/core/persistence/jpa/entity/**"/>
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/provisioning-api/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/provisioning-java/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/rest-api/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/rest-cxf/target/classes">
+    </dir>
   </classpath>
 
   <web>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/enduser-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/enduser-reference/pom.xml b/fit/enduser-reference/pom.xml
index 61ddfd9..2cb3b8e 100644
--- a/fit/enduser-reference/pom.xml
+++ b/fit/enduser-reference/pom.xml
@@ -68,6 +68,12 @@ under the License.
     </dependency>
 
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/enduser-reference/src/main/resources/oidcclient-agent.properties
----------------------------------------------------------------------
diff --git a/fit/enduser-reference/src/main/resources/oidcclient-agent.properties b/fit/enduser-reference/src/main/resources/oidcclient-agent.properties
new file mode 100644
index 0000000..1d53d49
--- /dev/null
+++ b/fit/enduser-reference/src/main/resources/oidcclient-agent.properties
@@ -0,0 +1,26 @@
+# 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.
+conf.directory=${conf.directory}
+
+anonymousUser=${anonymousUser}
+anonymousKey=${anonymousKey}
+
+scheme=http
+host=localhost
+port=9080
+rootPath=/syncope/rest/
+useGZIPCompression=true

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml b/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml
index 7f61a36..81a7651 100644
--- a/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml
+++ b/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml
@@ -53,6 +53,16 @@ under the License.
     <param-value>../wicket/bookmarkable/org.apache.syncope.client.enduser.pages.SAML2SPSelfReg</param-value>
   </context-param>
 
+  <context-param>
+    <param-name>oidcclient.login.success.url</param-name>
+    <param-value>../wicket/bookmarkable/org.apache.syncope.client.enduser.pages.OIDCClientLogin</param-value>
+  </context-param>
+  <context-param>
+    <param-name>oidcclient.login.error.url</param-name>
+    <param-value>../wicket/bookmarkable/org.apache.syncope.client.enduser.pages.HomePage</param-value>
+  </context-param>
+
+
   <!-- SESSION TIMEOUT (MINUTES)-->
   <session-config>
     <session-timeout>30</session-timeout>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/fit/enduser-reference/src/test/resources/rebel.xml
----------------------------------------------------------------------
diff --git a/fit/enduser-reference/src/test/resources/rebel.xml b/fit/enduser-reference/src/test/resources/rebel.xml
index da15397..d544b8d 100644
--- a/fit/enduser-reference/src/test/resources/rebel.xml
+++ b/fit/enduser-reference/src/test/resources/rebel.xml
@@ -30,6 +30,10 @@ under the License.
     </dir>
     <dir name="${basedir}/../../ext/saml2sp/client-enduser/target/classes">
     </dir>
+    <dir name="${basedir}/../../ext/oidcclient/agent/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/client-enduser/target/classes">
+    </dir>
   </classpath>
 
   <web>
@@ -45,6 +49,10 @@ under the License.
       <dir name="${basedir}/../../ext/saml2sp/client-enduser/target/classes">
       </dir>
     </link>
+    <link target="/">
+      <dir name="${basedir}/../../ext/oidcclient/client-enduser/target/classes">
+      </dir>
+    </link>
   </web>
 
 </application>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 302cbb9..8a11868 100644
--- a/pom.xml
+++ b/pom.xml
@@ -636,6 +636,16 @@ under the License.
           </exclusion>
         </exclusions>
       </dependency>
+      <dependency>
+        <groupId>org.apache.cxf</groupId>
+        <artifactId>cxf-rt-rs-security-sso-oidc</artifactId>
+        <version>${cxf.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.cxf</groupId>
+        <artifactId>cxf-rt-rs-extension-providers</artifactId>
+        <version>${cxf.version}</version>
+      </dependency>
       <!-- /CXF -->
 
       <!-- Swagger -->      

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/src/main/asciidoc/reference-guide/concepts/extensions.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/concepts/extensions.adoc b/src/main/asciidoc/reference-guide/concepts/extensions.adoc
index ae749ce..4b709b9 100644
--- a/src/main/asciidoc/reference-guide/concepts/extensions.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/extensions.adoc
@@ -116,6 +116,39 @@ This extension adds features to all components and layers that are available, an
 <<customization-extensions,new extensions>>.
 ====
 
+==== OpenID Connect Client 
+
+This extension can be leveraged to provide http://openid.net/connect/[OpenID Connect^]-based
+https://en.wikipedia.org/wiki/Single_sign-on[Single Sign-On^] access to the <<admin-console-component>>,
+the <<enduser-component>> or any other Java EE application dealing with the <<core>>.
+
+Once installed, one or more OpenID Providers can be created either from 
+the http://openid.net/specs/openid-connect-discovery-1_0.html[discovery document^] if it is supported or from inserting 
+manually the required attributes, in any case the client_id and the secret_id from the OAuth 2.0 credential and the issuer 
+are required.
+After configuration the OpenID provider, the http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[Authorization Code Flow^]
+is going to be implemented in order to reach the user information to be used by Syncope to match the internal users.
+
+
+[NOTE]
+.Extension Sources
+====
+The source code of this extension is available from the Apache Syncope
+ifeval::["{snapshotOrRelease}" == "release"]
+https://github.com/apache/syncope/tree/syncope-{docVersion}/ext/oidcclient[source tree^]
+endif::[]
+ifeval::["{snapshotOrRelease}" == "snapshot"]
+https://github.com/apache/syncope/tree/2_0_X/ext/oidcclient[source tree^]
+endif::[]
+.
+====
+
+[TIP]
+====
+This extension adds features to all components and layers that are available, and can be taken as reference when creating
+<<customization-extensions,new extensions>>.
+====
+
 ==== Elasticsearch
 
 [WARNING]

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
index 4c46ef1..2e2541b 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
@@ -351,6 +351,25 @@ Setup a <<keystore,keystore>> and place it under the <<properties-files-location
 the content of `core/src/main/resources/saml2sp-logic.properties` accordingly.
 
 [discrete]
+===== Enable the <<openid-connect-client>> extension
+
+Add the following dependencies to `core/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+  <version>${syncope.version}</version>
+</dependency>
+<dependency>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+[discrete]
 ===== Enable the <<elasticsearch>> extension
 
 [WARNING]
@@ -478,6 +497,23 @@ Add the following dependencies to `console/pom.xml`:
 Copy `console/src/main/resources/all/saml2sp-agent.properties` to `console/src/main/resources/saml2sp-agent.properties`.
 
 [discrete]
+===== Enable the <<openid-connect-client>> extension
+
+Add the following dependencies to `console/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+Copy `console/src/main/resources/all/oidcclient-agent.properties` to `console/src/main/resources/oidcclient-agent.properties`.
+
+
+[discrete]
 ===== Enable the <<SCIM>> extension
 
 Add the following dependencies to `console/pom.xml`:
@@ -516,6 +552,22 @@ Add the following dependencies to `enduser/pom.xml`:
 
 Copy `enduser/src/main/resources/all/saml2sp-agent.properties` to `enduser/src/main/resources/saml2sp-agent.properties`.
 
+[discrete]
+===== Enable the <<openid-connect-client>> extension
+
+Add the following dependencies to `enduser/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+Copy `enduser/src/main/resources/all/oidcclient-agent.properties` to `enduser/src/main/resources/oidcclient-agent.properties`.
+
 [[customization-enduser-i18n]]
 ===== i18n 
 


[4/8] syncope git commit: [SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser

Posted by il...@apache.org.
[SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/f29e60f6
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/f29e60f6
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/f29e60f6

Branch: refs/heads/2_0_X
Commit: f29e60f62f12bc7ec77ce6de4d2674db466a8f4f
Parents: 13f442a
Author: dayash <di...@tirasa.net>
Authored: Wed May 2 09:50:13 2018 +0200
Committer: dayash <di...@tirasa.net>
Committed: Thu May 3 16:16:14 2018 +0200

----------------------------------------------------------------------
 archetype/pom.xml                               |  31 ++
 .../archetype-resources/console/pom.xml         |   6 +
 .../resources/archetype-resources/core/pom.xml  |  11 +
 .../archetype-resources/enduser/pom.xml         |   6 +
 .../resources/META-INF/resources/app/index.html |   1 +
 .../resources/META-INF/resources/app/js/app.js  |  27 +-
 .../app/js/controllers/LoginController.js       |   2 +
 .../app/js/services/oidcProviderService.js      |  42 ++
 .../META-INF/resources/app/views/self.html      |   8 +
 deb/console/pom.xml                             |  13 +
 deb/console/src/deb/control/conffiles           |   2 +
 deb/enduser/pom.xml                             |  13 +
 deb/enduser/src/deb/control/conffiles           |   2 +
 deb/enduser/src/deb/control/preinst             |   1 +
 ext/oidcclient/agent/pom.xml                    | 126 ++++++
 .../ext/oidcclient/agent/CodeConsumer.java      |  92 ++++
 .../syncope/ext/oidcclient/agent/Constants.java |  40 ++
 .../syncope/ext/oidcclient/agent/Login.java     |  89 ++++
 .../oidcclient/agent/OIDCClientAgentSetup.java  |  77 ++++
 .../resources/oidcclient/loginError.jsp         |  35 ++
 .../resources/oidcclient/loginSuccess.jsp       |  33 ++
 .../main/resources/META-INF/web-fragment.xml    |  26 ++
 .../main/resources/oidcclient-agent.properties  |  26 ++
 ext/oidcclient/client-console/pom.xml           |  83 ++++
 .../client/console/pages/OIDCClient.java        |  68 +++
 .../client/console/pages/OIDCClientLogin.java   |  68 +++
 .../panels/OIDCProvidersDirectoryPanel.java     | 291 +++++++++++++
 .../console/panels/OIDCSSOLoginFormPanel.java   | 109 +++++
 .../console/rest/OIDCProviderRestClient.java    |  59 +++
 .../wizards/OIDCProviderMappingPanel.java       | 104 +++++
 .../wizards/OIDCProviderWizardBuilder.java      | 290 +++++++++++++
 .../client/console/pages/OIDCClient.html        |  37 ++
 .../client/console/pages/OIDCClient.properties  |  17 +
 .../console/pages/OIDCClient_it.properties      |  17 +
 .../console/pages/OIDCClient_pt_BR.properties   |  17 +
 .../console/pages/OIDCClient_ru.properties      |  17 +
 .../panels/OIDCProvidersDirectoryPanel.html     |  23 +
 .../OIDCProvidersDirectoryPanel.properties      |  30 ++
 .../OIDCProvidersDirectoryPanel_it.properties   |  30 ++
 ...OIDCProvidersDirectoryPanel_pt_BR.properties |  30 ++
 .../OIDCProvidersDirectoryPanel_ru.properties   |  30 ++
 .../console/panels/OIDCSSOLoginFormPanel.html   |  25 ++
 .../OIDCProviderWizardBuilder$Mapping.html      |  23 +
 .../wizards/OIDCProviderWizardBuilder$OP.html   |  28 ++
 .../OIDCProviderWizardBuilder$OP.properties     |  24 ++
 .../OIDCProviderWizardBuilder$OPContinue.html   |  33 ++
 ...CProviderWizardBuilder$OPContinue.properties |  27 ++
 ...oviderWizardBuilder$OPContinue_it.properties |  27 ++
 ...derWizardBuilder$OPContinue_pt_BR.properties |  27 ++
 ...oviderWizardBuilder$OPContinue_ru.properties |  27 ++
 .../OIDCProviderWizardBuilder$OP_it.properties  |  24 ++
 ...IDCProviderWizardBuilder$OP_pt_BR.properties |  24 ++
 .../OIDCProviderWizardBuilder$OP_ru.properties  |  24 ++
 ext/oidcclient/client-enduser/pom.xml           |  83 ++++
 .../client/enduser/pages/OIDCClientLogin.java   |  63 +++
 .../resources/OIDCProvidersResource.java        |  72 ++++
 ext/oidcclient/common-lib/pom.xml               |  56 +++
 .../syncope/common/lib/OIDCConstants.java       |  45 ++
 .../common/lib/to/OIDCLoginRequestTO.java       |  91 ++++
 .../common/lib/to/OIDCLoginResponseTO.java      | 140 +++++++
 .../syncope/common/lib/to/OIDCProviderTO.java   | 229 ++++++++++
 .../common/lib/types/OIDCClientEntitlement.java |  57 +++
 ext/oidcclient/logic/pom.xml                    |  76 ++++
 .../syncope/core/logic/OIDCClientLogic.java     | 418 +++++++++++++++++++
 .../syncope/core/logic/OIDCProviderLogic.java   | 177 ++++++++
 ...ClientClassPathScanImplementationLookup.java |  78 ++++
 .../core/logic/init/OIDCClientLoader.java       |  55 +++
 .../model/OIDCProviderDiscoveryDocument.java    | 128 ++++++
 .../core/logic/model/TokenEndpointResponse.java |  86 ++++
 .../core/logic/oidc/OIDCUserManager.java        | 306 ++++++++++++++
 ext/oidcclient/persistence-api/pom.xml          |  61 +++
 .../persistence/api/dao/OIDCProviderDAO.java    |  35 ++
 .../api/entity/OIDCEntityFactory.java           |  25 ++
 .../persistence/api/entity/OIDCProvider.java    |  84 ++++
 .../api/entity/OIDCProviderItem.java            |  29 ++
 .../api/entity/OIDCUserTemplate.java            |  27 ++
 ext/oidcclient/persistence-jpa/pom.xml          | 134 ++++++
 .../persistence/jpa/dao/JPAOIDCProviderDAO.java |  78 ++++
 .../jpa/entity/JPAOIDCEntityFactory.java        |  49 +++
 .../persistence/jpa/entity/JPAOIDCProvider.java | 257 ++++++++++++
 .../jpa/entity/JPAOIDCProviderItem.java         |  74 ++++
 .../jpa/entity/JPAOIDCUserTemplate.java         |  52 +++
 ext/oidcclient/pom.xml                          |  55 +++
 ext/oidcclient/provisioning-api/pom.xml         |  67 +++
 .../provisioning/api/OIDCProviderActions.java   |  35 ++
 .../api/data/OIDCProviderDataBinder.java        |  32 ++
 ext/oidcclient/provisioning-java/pom.xml        |  61 +++
 .../java/DefaultOIDCProviderActions.java        |  48 +++
 .../java/data/OIDCProviderDataBinderImpl.java   | 266 ++++++++++++
 ext/oidcclient/rest-api/pom.xml                 |  77 ++++
 .../rest/api/service/OIDCClientService.java     |  74 ++++
 .../rest/api/service/OIDCProviderService.java   | 151 +++++++
 ext/oidcclient/rest-cxf/pom.xml                 |  72 ++++
 .../rest/cxf/service/OIDCClientServiceImpl.java |  44 ++
 .../cxf/service/OIDCProviderServiceImpl.java    |  83 ++++
 ext/pom.xml                                     |   1 +
 .../syncope/ext/saml2lsp/agent/Login.java       |   2 +-
 fit/console-reference/pom.xml                   |   6 +
 .../main/resources/oidcclient-agent.properties  |  26 ++
 .../src/main/webapp/WEB-INF/web.xml             |  10 +
 .../src/test/resources/rebel.xml                |   8 +
 fit/core-reference/pom.xml                      |  23 +
 .../org/apache/syncope/fit/AbstractITCase.java  |   8 +
 .../apache/syncope/fit/OIDCClientDetector.java  |  52 +++
 .../syncope/fit/core/OIDCClientITCase.java      | 152 +++++++
 fit/core-reference/src/test/resources/rebel.xml |  17 +
 fit/enduser-reference/pom.xml                   |   6 +
 .../main/resources/oidcclient-agent.properties  |  26 ++
 .../src/main/webapp/WEB-INF/web.xml             |  10 +
 .../src/test/resources/rebel.xml                |   8 +
 pom.xml                                         |  10 +
 .../reference-guide/concepts/extensions.adoc    |  33 ++
 .../workingwithapachesyncope/customization.adoc |  52 +++
 113 files changed, 6920 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/archetype/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/pom.xml b/archetype/pom.xml
index baf1272..ee4d3fa 100644
--- a/archetype/pom.xml
+++ b/archetype/pom.xml
@@ -233,6 +233,13 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../ext/oidcclient/agent/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/resources/all</targetPath>
+        <includes>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../fit/console-reference/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/resources</targetPath>
         <includes>
@@ -250,6 +257,14 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../fit/console-reference/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/test/resources</targetPath>
+        <includes>
+          <include>console.properties</include>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../fit/console-reference/src/main/webapp/WEB-INF</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/webapp/WEB-INF</targetPath>
       </resource>
@@ -270,6 +285,13 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../ext/oidcclient/agent/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/resources/all</targetPath>
+        <includes>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../client/enduser/src/main/resources/META-INF/resources/app</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/webapp/app</targetPath>
       </resource>
@@ -290,6 +312,15 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../fit/enduser-reference/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/test/resources</targetPath>
+        <includes>
+          <include>enduser.properties</include>
+          <include>oidcclient-agent.properties</include>
+          <include>customForm.json</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../fit/enduser-reference/src/main/webapp/WEB-INF</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/webapp/WEB-INF</targetPath>
       </resource>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/archetype/src/main/resources/archetype-resources/console/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/console/pom.xml b/archetype/src/main/resources/archetype-resources/console/pom.xml
index 8506fa9..287562a 100644
--- a/archetype/src/main/resources/archetype-resources/console/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/console/pom.xml
@@ -141,6 +141,12 @@ under the License.
         </dependency>
         
         <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+          <version>${syncope.version}</version>
+        </dependency>
+        
+        <dependency>
           <groupId>org.apache.syncope.ext.scimv2</groupId>
           <artifactId>syncope-ext-scimv2-client-console</artifactId>
           <version>${syncope.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/archetype/src/main/resources/archetype-resources/core/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/core/pom.xml b/archetype/src/main/resources/archetype-resources/core/pom.xml
index 3f3ef73..a24b17d 100644
--- a/archetype/src/main/resources/archetype-resources/core/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/core/pom.xml
@@ -193,6 +193,17 @@ under the License.
         </dependency>
 
         <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+          <version>${syncope.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
+          <version>${syncope.version}</version>
+        </dependency>
+
+        <dependency>
           <groupId>org.apache.syncope.ext.scimv2</groupId>
           <artifactId>syncope-ext-scimv2-rest-cxf</artifactId>
           <version>${syncope.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/archetype/src/main/resources/archetype-resources/enduser/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/enduser/pom.xml b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
index 2f9943c..4e8826c 100644
--- a/archetype/src/main/resources/archetype-resources/enduser/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
@@ -332,6 +332,12 @@ under the License.
           <artifactId>syncope-ext-saml2sp-client-enduser</artifactId>
           <version>${syncope.version}</version>
         </dependency>
+
+        <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+          <version>${syncope.version}</version>
+        </dependency>
       </dependencies>
       
       <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/client/enduser/src/main/resources/META-INF/resources/app/index.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/index.html b/client/enduser/src/main/resources/META-INF/resources/app/index.html
index cd4542a..2613a6e 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/index.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/index.html
@@ -100,6 +100,7 @@ under the License.
   <script src="js/services/groupService.js"></script>
   <script src="js/services/anyService.js"></script>
   <script src="js/services/saml2IdPService.js"></script>
+  <script src="js/services/oidcProviderService.js"></script>
   <script src="js/services/saml2SPService.js"></script>
   <!--controllers-->
   <script src="js/controllers/HomeController.js"></script>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
index 3ae4f9d..1019cad 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
@@ -341,7 +341,8 @@ app.run(['$rootScope', '$location', '$state', 'AuthService',
     };
   }]);
 app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'InfoService', 'SAML2IdPService',
-  function ($scope, $rootScope, $location, InfoService, SAML2IdPService) {
+  'OIDCProviderService',
+  function ($scope, $rootScope, $location, InfoService, SAML2IdPService, OIDCProviderService) {
     $scope.initApplication = function () {
       /* 
        * disable by default wizard buttons in self-registration
@@ -377,6 +378,10 @@ app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'I
         available: [],
         selected: {}
       };
+      $rootScope.oidcops = {
+        available: [],
+        selected: {}
+      };
 
       InfoService.getInfo().then(
               function (response) {
@@ -406,6 +411,19 @@ app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'I
       /* 
        * configuration getters
        */
+
+      /* <Extensions> */
+      OIDCProviderService.getAvailableOIDCProviders().then(
+              function (response) {
+                $rootScope.oidcops.available = response;
+              },
+              function (response) {
+                console.debug("No OIDC Client extension available", response);
+              });
+      /* </Extensions> */
+      /* 
+       * configuration getters
+       */
       $rootScope.isSelfRegAllowed = function () {
         return $rootScope.selfRegAllowed === true;
       };
@@ -418,6 +436,13 @@ app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'I
       $rootScope.saml2login = function () {
         window.location.href = '../saml2sp/login?idp=' + $rootScope.saml2idps.selected.entityID;
       };
+
+      $rootScope.oidcclientExtAvailable = function () {
+        return $rootScope.oidcops.available.length > 0;
+      };
+      $rootScope.oidclogin = function () {
+        window.location.href = '../oidcclient/login?op=' + $rootScope.oidcops.selected.name;
+      };
       $rootScope.getVersion = function () {
         return $rootScope.version;
       };

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
index b58aced..d2b755c 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
@@ -34,6 +34,8 @@ angular.module("login").controller("LoginController", ['$scope', '$rootScope', '
         $scope.credentials.errorMessage = '';
         // reset SAML 2.0 entityID
         $rootScope.saml2idps.selected.entityID = null;
+        // reset OIDC name
+        $rootScope.oidcops.selected.name = null;
         // got to update page
         $location.path("/self/update");
       }, function (response) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/client/enduser/src/main/resources/META-INF/resources/app/js/services/oidcProviderService.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/services/oidcProviderService.js b/client/enduser/src/main/resources/META-INF/resources/app/js/services/oidcProviderService.js
new file mode 100644
index 0000000..d2ce926
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/services/oidcProviderService.js
@@ -0,0 +1,42 @@
+/* 
+ * 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.
+ */
+
+'use strict';
+
+angular.module('self')
+        .factory('OIDCProviderService', ['$resource', '$q', '$http',
+          function ($resource, $q, $http) {
+
+            var oidcProviderService = {};
+
+            oidcProviderService.getAvailableOIDCProviders = function () {
+              return $http.get("../api/oidcProviders")
+                      .then(function (response) {
+                        return response.data;
+                      }, function (response) {
+                        console.error("Something went wrong during oidcProviders extension retrieval, exit with status: ",
+                                response);
+                        return $q.reject(response.data || response.statusText);
+                      });
+            };
+
+            return oidcProviderService;
+          }]);
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/self.html b/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
index 1cc279e..cedb21c 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
@@ -62,6 +62,14 @@ under the License.
                           ng-model="saml2idps.selected" ng-change="saml2login()">
                   </select>
                 </div>
+                <div class="form-group">
+                  <span ng-if="$root.oidcclientExtAvailable()">OpenID Connect</span>
+                  <select id="oiscclient" style="width: 100%; text-align: left;" class="btn dropdown-toggle btn-default"
+                          ng-if="$root.oidcclientExtAvailable()"
+                          ng-options="op.name for op in oidcops.available track by op.name"
+                          ng-model="oidcops.selected" ng-change="oidclogin()">
+                  </select>
+                </div>
               </fieldset>
             </form>
           </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/deb/console/pom.xml
----------------------------------------------------------------------
diff --git a/deb/console/pom.xml b/deb/console/pom.xml
index 9688af6..8898ce3 100644
--- a/deb/console/pom.xml
+++ b/deb/console/pom.xml
@@ -55,6 +55,11 @@ under the License.
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.apache.syncope.ext.scimv2</groupId>
       <artifactId>syncope-ext-scimv2-client-console</artifactId>
       <version>${project.version}</version>
@@ -116,6 +121,14 @@ under the License.
         <filtering>true</filtering>
       </resource>
       <resource>
+        <directory>${project.basedir}/../../ext/oidcclient/agent/src/main/resources</directory>
+        <includes>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+        <targetPath>${project.build.directory}/etc</targetPath>
+        <filtering>true</filtering>
+      </resource>
+      <resource>
         <directory>${basedir}/../../fit/console-reference/src/main/resources</directory>
         <includes>
           <include>log4j2.xml</include>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/deb/console/src/deb/control/conffiles
----------------------------------------------------------------------
diff --git a/deb/console/src/deb/control/conffiles b/deb/console/src/deb/control/conffiles
index 47e5201..a9bfb08 100644
--- a/deb/console/src/deb/control/conffiles
+++ b/deb/console/src/deb/control/conffiles
@@ -1,3 +1,5 @@
 /etc/tomcat8/Catalina/localhost/syncope-console.xml
 /etc/apache-syncope/console.properties
 /etc/apache-syncope/saml2sp-agent.properties
+/etc/apache-syncope/oidcclient-agent.properties
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/deb/enduser/pom.xml
----------------------------------------------------------------------
diff --git a/deb/enduser/pom.xml b/deb/enduser/pom.xml
index fc2ce00..64505df 100644
--- a/deb/enduser/pom.xml
+++ b/deb/enduser/pom.xml
@@ -44,6 +44,11 @@ under the License.
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.apache.syncope.client</groupId>
       <artifactId>syncope-client-enduser</artifactId>
       <version>${project.version}</version>
@@ -106,6 +111,14 @@ under the License.
         <filtering>true</filtering>
       </resource>
       <resource>
+        <directory>${project.basedir}/../../ext/oidcclient/agent/src/main/resources</directory>
+        <includes>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+        <targetPath>${project.build.directory}/etc</targetPath>
+        <filtering>true</filtering>
+      </resource>
+      <resource>
         <directory>${basedir}/../../fit/enduser-reference/src/main/resources</directory>
         <includes>
           <include>log4j2.xml</include>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/deb/enduser/src/deb/control/conffiles
----------------------------------------------------------------------
diff --git a/deb/enduser/src/deb/control/conffiles b/deb/enduser/src/deb/control/conffiles
index 934c7fb..8fb2453 100644
--- a/deb/enduser/src/deb/control/conffiles
+++ b/deb/enduser/src/deb/control/conffiles
@@ -2,3 +2,5 @@
 /etc/apache-syncope/enduser.properties
 /etc/apache-syncope/customForm.json
 /etc/apache-syncope/saml2sp-agent.properties
+/etc/apache-syncope/oidcclient-agent.properties
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/deb/enduser/src/deb/control/preinst
----------------------------------------------------------------------
diff --git a/deb/enduser/src/deb/control/preinst b/deb/enduser/src/deb/control/preinst
index afdca38..0e88785 100644
--- a/deb/enduser/src/deb/control/preinst
+++ b/deb/enduser/src/deb/control/preinst
@@ -16,3 +16,4 @@
 # specific language governing permissions and limitations
 # under the License.
 dpkg-divert --add --package apache-syncope-enduser --rename --divert /etc/apache-syncope/saml2sp-agent.properties.disabled /etc/apache-syncope/saml2sp-agent.properties
+dpkg-divert --add --package apache-syncope-enduser --rename --divert /etc/apache-syncope/oidcclient-agent.properties.disabled /etc/apache-syncope/oidcclient-agent.properties

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/pom.xml b/ext/oidcclient/agent/pom.xml
new file mode 100644
index 0000000..adb83d1
--- /dev/null
+++ b/ext/oidcclient/agent/pom.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Agent</name>
+  <description>Apache Syncope Ext: OIDC Client Agent</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-agent</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency> 
+      <groupId>javax.servlet</groupId> 
+      <artifactId>javax.servlet-api</artifactId> 
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet.jsp</groupId>
+      <artifactId>javax.servlet.jsp-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>jstl</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+      
+      <resource>
+        <directory>${basedir}</directory>
+        <targetPath>META-INF</targetPath>
+        <includes>
+          <include>LICENSE</include>
+          <include>NOTICE</include>
+        </includes>
+      </resource>
+    </resources>
+  </build>
+  
+  <profiles>
+    <profile>
+      <id>apache-release</id>
+
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-source-plugin</artifactId>
+            <inherited>false</inherited>
+            <executions>
+              <execution>
+                <id>attach-sources</id>
+                <goals>
+                  <goal>jar-no-fork</goal>
+                </goals>
+                <configuration>
+                  <includes>
+                    <include>${basedir}/LICENSE</include>
+                    <include>${basedir}/NOTICE</include>
+                  </includes>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/CodeConsumer.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/CodeConsumer.java b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/CodeConsumer.java
new file mode 100644
index 0000000..08e9d23
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/CodeConsumer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.ext.oidcclient.agent;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.OIDCConstants;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@WebServlet(name = "codeConsumer", urlPatterns = { "/oidcclient/code-consumer" })
+public class CodeConsumer extends HttpServlet {
+
+    private static final long serialVersionUID = 968480296813639041L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(CodeConsumer.class);
+
+    @Override
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
+            throws ServletException, IOException {
+
+        try {
+            String authorizationCode = request.getParameter(OIDCConstants.CODE);
+            String state = request.getParameter(OIDCConstants.STATE);
+            if (StringUtils.isBlank(authorizationCode) || StringUtils.isBlank(state)) {
+                throw new IllegalArgumentException("Empty " + OIDCConstants.CODE + " or " + OIDCConstants.STATE);
+            }
+            if (state.equals(request.getSession().getAttribute(OIDCConstants.STATE).toString())) {
+                SyncopeClient anonymous = (SyncopeClient) request.getServletContext().
+                        getAttribute(Constants.SYNCOPE_ANONYMOUS_CLIENT);
+
+                OIDCLoginResponseTO responseTO = anonymous.getService(OIDCClientService.class).login(
+                        request.getRequestURL().toString(),
+                        authorizationCode,
+                        request.getSession().getAttribute(OIDCConstants.OP).toString());
+                request.getSession().setAttribute(
+                        Constants.OIDCCLIENTJWT, responseTO.getAccessToken());
+                request.getSession().setAttribute(
+                        Constants.OIDCCLIENTJWT_EXPIRE, responseTO.getAccessTokenExpiryTime());
+
+                String successURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_SUCCESS_URL);
+                if (successURL == null) {
+                    request.setAttribute("responseTO", responseTO);
+                    request.getRequestDispatcher("loginSuccess.jsp").forward(request, response);
+                } else {
+                    response.sendRedirect(successURL);
+                }
+            } else {
+                throw new IllegalArgumentException("Invalid " + OIDCConstants.STATE + " provided");
+            }
+        } catch (Exception e) {
+            LOG.error("While processing authentication response from OP", e);
+            String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_ERROR_URL);
+
+            if (errorURL == null) {
+                request.setAttribute("exception", e);
+                request.getRequestDispatcher("loginError.jsp").forward(request, response);
+
+                e.printStackTrace(response.getWriter());
+            } else {
+                response.sendRedirect(errorURL + "?errorMessage="
+                        + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Constants.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Constants.java b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Constants.java
new file mode 100644
index 0000000..1ff3327
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Constants.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.ext.oidcclient.agent;
+
+public final class Constants {
+
+    public static final String SYNCOPE_ANONYMOUS_CLIENT = "SyncopeAnonymousClient";
+
+    public static final String SYNCOPE_CLIENT_FACTORY = "SyncopeClientFactory";
+
+    public static final String PARAM_OP = "op";
+
+    public static final String CONTEXT_PARAM_LOGIN_SUCCESS_URL = "oidcclient.login.success.url";
+
+    public static final String CONTEXT_PARAM_LOGIN_ERROR_URL = "oidcclient.login.error.url";
+
+    public static final String OIDCCLIENTJWT = "oidcclient.jwt";
+
+    public static final String OIDCCLIENTJWT_EXPIRE = "oidcclient.jwt.expire";
+
+    private Constants() {
+        // private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Login.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Login.java b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Login.java
new file mode 100644
index 0000000..36850e4
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Login.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.ext.oidcclient.agent;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriBuilder;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.OIDCConstants;
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@WebServlet(name = "oidcclientLogin", urlPatterns = { "/oidcclient/login" })
+public class Login extends HttpServlet {
+
+    private static final long serialVersionUID = 968480296813639041L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(Login.class);
+
+    @Override
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
+            throws ServletException, IOException {
+
+        String op = request.getParameter(Constants.PARAM_OP);
+
+        SyncopeClient anonymous = (SyncopeClient) request.getServletContext().
+                getAttribute(Constants.SYNCOPE_ANONYMOUS_CLIENT);
+        try {
+            String redirectURI = StringUtils.substringBefore(request.getRequestURL().toString(), "/login")
+                    + "/code-consumer";
+            OIDCLoginRequestTO requestTO = anonymous.getService(OIDCClientService.class).
+                    createLoginRequest(redirectURI, op);
+
+            request.getSession().setAttribute(OIDCConstants.STATE, requestTO.getState());
+            request.getSession().setAttribute(OIDCConstants.OP, op);
+
+            response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache, no-store");
+            response.setHeader("Pragma", "no-cache");
+            response.setStatus(HttpServletResponse.SC_SEE_OTHER);
+
+            UriBuilder ub = UriBuilder.fromUri(requestTO.getProviderAddress());
+            ub.queryParam(OIDCConstants.CLIENT_ID, requestTO.getClientId());
+            ub.queryParam(OIDCConstants.REDIRECT_URI, requestTO.getRedirectURI());
+            ub.queryParam(OIDCConstants.RESPOSNSE_TYPE, requestTO.getResponseType());
+            ub.queryParam(OIDCConstants.SCOPE, requestTO.getScope());
+            ub.queryParam(OIDCConstants.STATE, requestTO.getState());
+            response.setHeader("Location", ub.build().toASCIIString());
+        } catch (Exception e) {
+            LOG.error("While preparing the Authentication Request", e);
+
+            String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_ERROR_URL);
+            if (errorURL == null) {
+                request.setAttribute("exception", e);
+                request.getRequestDispatcher("loginError.jsp").forward(request, response);
+
+                e.printStackTrace(response.getWriter());
+            } else {
+                response.sendRedirect(errorURL + "?errorMessage="
+                        + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/OIDCClientAgentSetup.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/OIDCClientAgentSetup.java b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/OIDCClientAgentSetup.java
new file mode 100644
index 0000000..887d0d6
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/OIDCClientAgentSetup.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.ext.oidcclient.agent;
+
+import java.util.Properties;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.lib.PropertyUtils;
+
+@WebListener
+public class OIDCClientAgentSetup implements ServletContextListener {
+
+    private static final String OIDCCLIENT_AGENT_PROPERTIES = "oidcclient-agent.properties";
+
+    private static <T> T assertNotNull(final T argument, final String name) {
+        if (argument == null) {
+            throw new IllegalArgumentException("Argument '" + name + "' may not be null.");
+        }
+        return argument;
+    }
+
+    @Override
+    public void contextInitialized(final ServletContextEvent sce) {
+        // read oidcclientagent.properties
+        Properties props = PropertyUtils.read(getClass(), OIDCCLIENT_AGENT_PROPERTIES, "conf.directory").getLeft();
+
+        String anonymousUser = props.getProperty("anonymousUser");
+        assertNotNull(anonymousUser, "<anonymousUser>");
+        String anonymousKey = props.getProperty("anonymousKey");
+        assertNotNull(anonymousKey, "<anonymousKey>");
+
+        String scheme = props.getProperty("scheme");
+        assertNotNull(scheme, "<scheme>");
+        String host = props.getProperty("host");
+        assertNotNull(host, "<host>");
+        String port = props.getProperty("port");
+        assertNotNull(port, "<port>");
+        String rootPath = props.getProperty("rootPath");
+        assertNotNull(rootPath, "<rootPath>");
+        String useGZIPCompression = props.getProperty("useGZIPCompression");
+        assertNotNull(useGZIPCompression, "<useGZIPCompression>");
+
+        SyncopeClientFactoryBean clientFactory = new SyncopeClientFactoryBean().
+                setAddress(scheme + "://" + host + ":" + port + "/" + rootPath).
+                setUseCompression(BooleanUtils.toBoolean(useGZIPCompression));
+
+        sce.getServletContext().setAttribute(Constants.SYNCOPE_CLIENT_FACTORY, clientFactory);
+        sce.getServletContext().setAttribute(
+                Constants.SYNCOPE_ANONYMOUS_CLIENT,
+                clientFactory.create(new AnonymousAuthenticationHandler(anonymousUser, anonymousKey)));
+    }
+
+    @Override
+    public void contextDestroyed(final ServletContextEvent sce) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginError.jsp
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginError.jsp b/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginError.jsp
new file mode 100644
index 0000000..b3c80d9
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginError.jsp
@@ -0,0 +1,35 @@
+<%--
+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.
+--%>
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%
+    Exception exception = (Exception) request.getAttribute("exception");
+%>
+<html>
+  <head>
+    <title>Apache Syncope ${syncope.version} - OIDC CLIENT - Login Error</title>
+  </head>
+  <body>
+    <h1>An error was found</h1>
+
+    <h2><%=exception.getMessage()%></h2>
+    <pre>
+      <%exception.printStackTrace(new java.io.PrintWriter(out));%>
+    </pre>
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginSuccess.jsp
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginSuccess.jsp b/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginSuccess.jsp
new file mode 100644
index 0000000..7361842
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginSuccess.jsp
@@ -0,0 +1,33 @@
+<%--
+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.
+--%>
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@page import="org.apache.syncope.common.lib.to.OIDCLoginResponseTO"%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<c:set var="responseTO" value="${requestScope['responseTO']}"/>
+<html>
+  <head>
+    <title>Apache Syncope ${syncope.version} - OIDC CLIENT - Successful Login</title>
+  </head>
+  <body>
+    <h1>Welcome ${responseTO.userName}</h1>
+
+    <p>You have been successfully authenticated by the requested OIDC Provider</p>
+
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/src/main/resources/META-INF/web-fragment.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/resources/META-INF/web-fragment.xml b/ext/oidcclient/agent/src/main/resources/META-INF/web-fragment.xml
new file mode 100644
index 0000000..fab858a
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/resources/META-INF/web-fragment.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<web-fragment xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+                                  http://xmlns.jcp.org/xml/ns/javaee/web-fragment_3_1.xsd"
+              id="${pom.artifactId}" version="3.1">
+
+</web-fragment>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/agent/src/main/resources/oidcclient-agent.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/resources/oidcclient-agent.properties b/ext/oidcclient/agent/src/main/resources/oidcclient-agent.properties
new file mode 100644
index 0000000..d3fee30
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/resources/oidcclient-agent.properties
@@ -0,0 +1,26 @@
+# 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.
+conf.directory=${conf.directory}
+
+anonymousUser=${anonymousUser}
+anonymousKey=${anonymousKey}
+
+scheme=http
+host=localhost
+port=8080
+rootPath=/syncope/rest/
+useGZIPCompression=true

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/pom.xml b/ext/oidcclient/client-console/pom.xml
new file mode 100644
index 0000000..51b190b
--- /dev/null
+++ b/ext/oidcclient/client-console/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Console</name>
+  <description>Apache Syncope Ext: OIDC Client Console</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-console</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClient.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClient.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClient.java
new file mode 100644
index 0000000..f588733
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClient.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.pages;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.annotations.ExtPage;
+import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel;
+import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+@ExtPage(label = "OIDC Client", icon = "fa-openid ", listEntitlement = OIDCClientEntitlement.OP_READ, priority = 100)
+public class OIDCClient extends BaseExtPage {
+
+    private static final long serialVersionUID = -599601954212606001L;
+
+    public OIDCClient(final PageParameters parameters) {
+        super(parameters);
+
+        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList()));
+        body.add(content);
+    }
+
+    private List<ITab> buildTabList() {
+
+        final List<ITab> tabs = new ArrayList<>(1);
+
+        tabs.add(new AbstractTab(new ResourceModel("op")) {
+            private static final long serialVersionUID = -6815067322125799251L;
+
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new OIDCProvidersDirectoryPanel(panelId, getPageReference());
+            }
+        });
+
+        return tabs;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClientLogin.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClientLogin.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClientLogin.java
new file mode 100644
index 0000000..41b6cdc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClientLogin.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+
+import org.apache.wicket.authentication.IAuthenticationStrategy;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OIDCClientLogin extends WebPage {
+
+    private static final long serialVersionUID = 8581614051773949262L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCClientLogin.class);
+
+    private static final String OIDC_ACCESS_ERROR = "OIDC access error";
+
+    public OIDCClientLogin(final PageParameters parameters) {
+        super(parameters);
+
+        String token = (String) ((ServletWebRequest) getRequest()).getContainerRequest().
+                getSession().getAttribute(org.apache.syncope.ext.oidcclient.agent.Constants.OIDCCLIENTJWT);
+        if (StringUtils.isBlank(token)) {
+            LOG.error("No JWT found, redirecting to default greeter");
+
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", OIDC_ACCESS_ERROR);
+            setResponsePage(Login.class, params);
+        }
+
+        IAuthenticationStrategy strategy = getApplication().getSecuritySettings().getAuthenticationStrategy();
+
+        if (SyncopeConsoleSession.get().authenticate(token)) {
+
+            // If login has been called because the user was not yet logged in, than continue to the
+            // original destination, otherwise to the Home page
+            continueToOriginalDestination();
+            setResponsePage(getApplication().getHomePage());
+        } else {
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", OIDC_ACCESS_ERROR);
+            setResponsePage(Login.class, params);
+        }
+        strategy.remove();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
new file mode 100644
index 0000000..dd66cc6
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel.OIDCProvidersProvider;
+import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.rest.OIDCProviderRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.client.console.wizards.OIDCProviderWizardBuilder;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.console.wizards.any.AnyWrapper;
+import org.apache.syncope.client.console.wizards.any.UserTemplateWizardBuilder;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+
+public class OIDCProvidersDirectoryPanel extends DirectoryPanel<
+        OIDCProviderTO, OIDCProviderTO, OIDCProvidersProvider, OIDCProviderRestClient> {
+
+    private static final long serialVersionUID = -1356497878858616714L;
+
+    private static final String PREF_OIDC_PROVIDERS_PAGINATOR_ROWS = "oidc.providers.paginator.rows";
+
+    private final BaseModal<Serializable> templateModal;
+
+    public OIDCProvidersDirectoryPanel(final String id, final PageReference pageRef) {
+        super(id, new Builder<OIDCProviderTO, OIDCProviderTO, OIDCProviderRestClient>(new OIDCProviderRestClient(),
+                pageRef) {
+
+            private static final long serialVersionUID = -5542535388772406165L;
+
+            @Override
+            protected WizardMgtPanel<OIDCProviderTO> newInstance(final String id, final boolean wizardInModal) {
+                throw new UnsupportedOperationException();
+            }
+        }.disableCheckBoxes());
+
+        this.addNewItemPanelBuilder(new OIDCProviderWizardBuilder(this, new OIDCProviderTO(), pageRef), true);
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, OIDCClientEntitlement.OP_CREATE);
+
+        modal.size(Modal.Size.Large);
+
+        actionTogglePanel = new ActionLinksTogglePanel<OIDCProviderTO>("outer", pageRef) {
+
+            private static final long serialVersionUID = -7688359318035249200L;
+
+            @Override
+            public void toggleWithContent(
+                    final AjaxRequestTarget target,
+                    final ActionsPanel<OIDCProviderTO> actionsPanel,
+                    final OIDCProviderTO modelObject) {
+
+                super.toggleWithContent(target, actionsPanel, modelObject);
+                setHeader(target, StringUtils.abbreviate(modelObject.getName(), 25));
+                this.toggle(target, true);
+            }
+
+        };
+        addOuterObject(actionTogglePanel);
+
+        templateModal = new BaseModal<Serializable>("outer") {
+
+            private static final long serialVersionUID = 5787433530654262016L;
+
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+                setFooterVisible(false);
+            }
+        };
+        templateModal.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
+
+            private static final long serialVersionUID = 8804221891699487139L;
+
+            @Override
+            public void onClose(final AjaxRequestTarget target) {
+//                target.add(content);
+                templateModal.show(false);
+            }
+        });
+        templateModal.size(Modal.Size.Large);
+        addOuterObject(templateModal);
+
+        initResultTable();
+
+    }
+
+    @Override
+    protected OIDCProvidersProvider dataProvider() {
+        return new OIDCProvidersProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return PREF_OIDC_PROVIDERS_PAGINATOR_ROWS;
+
+    }
+
+    @Override
+    protected List<IColumn<OIDCProviderTO, String>> getColumns() {
+        List<IColumn<OIDCProviderTO, String>> columns = new ArrayList<>();
+        columns.add(new KeyPropertyColumn<OIDCProviderTO>(new ResourceModel("key"), "key", "key"));
+        columns.add(new PropertyColumn<OIDCProviderTO, String>(new ResourceModel("name"), "name", "name"));
+        columns.add(new PropertyColumn<OIDCProviderTO, String>(new ResourceModel("issuer"), "issuer", "issuer"));
+        columns.add(new PropertyColumn<OIDCProviderTO, String>(new ResourceModel("clientID"), "clientID", "clientID"));
+        return columns;
+
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+
+    }
+
+    @Override
+    public ActionsPanel<OIDCProviderTO> getActions(final IModel<OIDCProviderTO> model) {
+        final ActionsPanel<OIDCProviderTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<OIDCProviderTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final OIDCProviderTO ignore) {
+                OIDCProviderTO object = restClient.read(model.getObject().getKey());
+                send(OIDCProvidersDirectoryPanel.this, Broadcast.EXACT,
+                        new AjaxWizard.EditItemActionEvent<>(object, target));
+                modal.header(Model.of(StringUtils.capitalize(("Edit " + object.getName()))));
+            }
+        }, ActionLink.ActionType.EDIT, OIDCClientEntitlement.OP_UPDATE);
+
+        panel.add(new ActionLink<OIDCProviderTO>() {
+
+            private static final long serialVersionUID = 8557679125857348178L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final OIDCProviderTO ignore) {
+                final OIDCProviderTO object = restClient.read(model.getObject().getKey());
+
+                UserTemplateWizardBuilder builder = new UserTemplateWizardBuilder(
+                        object.getUserTemplate(),
+                        new AnyTypeRestClient().read(AnyTypeKind.USER.name()).getClasses(),
+                        new UserFormLayoutInfo(),
+                        pageRef) {
+
+                    private static final long serialVersionUID = -7978723352517770634L;
+
+                    @Override
+                    protected Serializable onApplyInternal(final AnyWrapper<UserTO> modelObject) {
+                        object.setUserTemplate(modelObject.getInnerObject());
+                        restClient.update(object);
+
+                        return modelObject;
+                    }
+                };
+                templateModal.header(Model.of(StringUtils.capitalize(
+                        new StringResourceModel("template.title", OIDCProvidersDirectoryPanel.this).getString())));
+                templateModal.setContent(builder.build(BaseModal.CONTENT_ID));
+                templateModal.show(true);
+                target.add(templateModal);
+
+            }
+        }, ActionLink.ActionType.TEMPLATE, OIDCClientEntitlement.OP_UPDATE);
+
+        panel.add(new ActionLink<OIDCProviderTO>() {
+
+            private static final long serialVersionUID = -5467832321897812767L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final OIDCProviderTO ignore) {
+                try {
+                    restClient.delete(model.getObject().getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting object {}", model.getObject().getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, OIDCClientEntitlement.OP_DELETE, true);
+        return panel;
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        super.onEvent(event);
+
+        if (event.getPayload() instanceof AjaxWizard.NewItemEvent) {
+            AjaxWizard.NewItemEvent<?> newItemEvent = AjaxWizard.NewItemEvent.class.cast(event.getPayload());
+            WizardModalPanel<?> modalPanel = newItemEvent.getModalPanel();
+
+            if (event.getPayload() instanceof AjaxWizard.NewItemActionEvent && modalPanel != null) {
+                final IModel<Serializable> model = new CompoundPropertyModel<>(modalPanel.getItem());
+                templateModal.setFormModel(model);
+                templateModal.header(newItemEvent.getResourceModel());
+                newItemEvent.getTarget().add(templateModal.setContent(modalPanel));
+                templateModal.show(true);
+            } else if (event.getPayload() instanceof AjaxWizard.NewItemCancelEvent) {
+                templateModal.close(newItemEvent.getTarget());
+            } else if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
+                templateModal.close(newItemEvent.getTarget());
+            }
+        }
+    }
+
+    protected final class OIDCProvidersProvider extends DirectoryDataProvider<OIDCProviderTO> {
+
+        private static final long serialVersionUID = -2865055116864423761L;
+
+        private final SortableDataProviderComparator<OIDCProviderTO> comparator;
+
+        public OIDCProvidersProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("name", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<OIDCProviderTO> iterator(final long first, final long count) {
+            List<OIDCProviderTO> list = restClient.list();
+            Collections.sort(list, comparator);
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.list().size();
+        }
+
+        @Override
+        public IModel<OIDCProviderTO> model(final OIDCProviderTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.java
new file mode 100644
index 0000000..5b108e5
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.UrlUtils;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.handler.RedirectRequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OIDCSSOLoginFormPanel extends SSOLoginFormPanel {
+
+    private static final long serialVersionUID = 1154933731474854671L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCSSOLoginFormPanel.class);
+
+    public OIDCSSOLoginFormPanel(final String id) {
+        super(id);
+
+        List<OIDCProviderTO> available =
+                SyncopeConsoleSession.get().getAnonymousClient().getService(OIDCProviderService.class).list();
+
+        final Model<OIDCProviderTO> model = new Model<>();
+        AjaxDropDownChoicePanel<OIDCProviderTO> ops =
+                new AjaxDropDownChoicePanel<>("ops", "OpenID Connect", model, false);
+        ops.setChoices(available);
+        ops.setChoiceRenderer(new IChoiceRenderer<OIDCProviderTO>() {
+
+            private static final long serialVersionUID = 1814750973898916102L;
+
+            @Override
+            public Object getDisplayValue(final OIDCProviderTO object) {
+                return object.getName();
+            }
+
+            @Override
+            public String getIdValue(final OIDCProviderTO object, final int index) {
+                return object.getName();
+            }
+
+            @Override
+            public OIDCProviderTO getObject(final String id,
+                    final IModel<? extends List<? extends OIDCProviderTO>> choices) {
+                return IterableUtils.find(choices.getObject(), new Predicate<OIDCProviderTO>() {
+
+                    @Override
+                    public boolean evaluate(final OIDCProviderTO object) {
+                        return object.getName().equals(id);
+                    }
+                });
+            }
+        });
+
+        ops.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                if (model.getObject() != null) {
+                    try {
+                        RequestCycle.get().scheduleRequestHandlerAfterCurrent(new RedirectRequestHandler(
+                                UrlUtils.rewriteToContextRelative("oidcclient/login?op="
+                                        + URLEncoder.encode(
+                                                model.getObject().getName(), StandardCharsets.UTF_8.name()),
+                                        RequestCycle.get())));
+                    } catch (Exception e) {
+                        LOG.error("Could not redirect to the selected OP {}", model.getObject().getName(), e);
+                    }
+                }
+            }
+        });
+        ops.setOutputMarkupPlaceholderTag(true);
+        ops.setVisible(!available.isEmpty());
+        add(ops);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
new file mode 100644
index 0000000..a666d74
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.rest;
+
+import java.util.List;
+import java.util.Set;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+
+public class OIDCProviderRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -4006712447589576324L;
+
+    public List<OIDCProviderTO> list() {
+        return getService(OIDCProviderService.class).list();
+    }
+
+    public Set<String> getActionsClasses() {
+        return getService(OIDCProviderService.class).getActionsClasses();
+    }
+
+    public void create(final OIDCProviderTO op) {
+        SyncopeConsoleSession.get().getService(OIDCProviderService.class).create(op);
+    }
+
+    public void createFromDiscovery(final OIDCProviderTO op) {
+        SyncopeConsoleSession.get().getService(OIDCProviderService.class).createFromDiscovery(op);
+    }
+
+    public OIDCProviderTO read(final String key) {
+        return getService(OIDCProviderService.class).read(key);
+    }
+
+    public void update(final OIDCProviderTO op) {
+        getService(OIDCProviderService.class).update(op);
+    }
+
+    public void delete(final String key) {
+        getService(OIDCProviderService.class).delete(key);
+    }
+
+}


[3/8] syncope git commit: [SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderMappingPanel.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderMappingPanel.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderMappingPanel.java
new file mode 100644
index 0000000..d12965e
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderMappingPanel.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.wizards;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wizards.resources.ItemTransformersTogglePanel;
+import org.apache.syncope.client.console.wizards.resources.JEXLTransformersTogglePanel;
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.util.ListModel;
+
+public class OIDCProviderMappingPanel extends AbstractMappingPanel {
+
+    private static final long serialVersionUID = -4123879435574382968L;
+
+    public OIDCProviderMappingPanel(
+            final String id,
+            final OIDCProviderTO opTO,
+            final ItemTransformersTogglePanel mapItemTransformers,
+            final JEXLTransformersTogglePanel jexlTransformers) {
+
+        super(id,
+                mapItemTransformers,
+                jexlTransformers,
+                new ListModel<ItemTO>(opTO.getItems()),
+                true,
+                true,
+                MappingPurpose.NONE);
+
+        setOutputMarkupId(true);
+    }
+
+    @Override
+    protected void onBeforeRender() {
+        super.onBeforeRender();
+        purposeLabel.setVisible(false);
+    }
+
+    @Override
+    protected IModel<List<String>> getExtAttrNames() {
+        List<String> extAttrNames = new ArrayList<>();
+        extAttrNames.add("email");
+        extAttrNames.add("family_name");
+        extAttrNames.add("name");
+        extAttrNames.add("middle_name");
+        extAttrNames.add("given_name");
+        extAttrNames.add("preferred_username");
+        extAttrNames.add("nickname");
+        extAttrNames.add("profile");
+        extAttrNames.add("gender");
+        extAttrNames.add("locale");
+        extAttrNames.add("zoneinfo");
+        extAttrNames.add("birthdate");
+        extAttrNames.add("phone_number");
+        extAttrNames.add("address");
+        extAttrNames.add("updated_at");
+
+        return Model.ofList(extAttrNames);
+    }
+
+    @Override
+    protected void setAttrNames(final AjaxTextFieldPanel toBeUpdated) {
+        toBeUpdated.setRequired(true);
+        toBeUpdated.setEnabled(true);
+
+        List<String> choices = new ArrayList<>(USER_FIELD_NAMES);
+
+        for (AnyTypeClassTO anyTypeClassTO : anyTypeClassRestClient.list(
+                anyTypeRestClient.read(AnyTypeKind.USER.name()).getClasses())) {
+
+            choices.addAll(anyTypeClassTO.getPlainSchemas());
+            choices.addAll(anyTypeClassTO.getDerSchemas());
+            choices.addAll(anyTypeClassTO.getVirSchemas());
+        }
+
+        Collections.sort(choices);
+        toBeUpdated.setChoices(choices);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
new file mode 100644
index 0000000..2fa465a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
@@ -0,0 +1,290 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.wizards;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel;
+import org.apache.syncope.client.console.rest.OIDCProviderRestClient;
+import org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wizards.resources.ItemTransformersTogglePanel;
+import org.apache.syncope.client.console.wizards.resources.JEXLTransformersTogglePanel;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.model.util.ListModel;
+import org.apache.wicket.validation.validator.UrlValidator;
+
+public class OIDCProviderWizardBuilder extends AjaxWizardBuilder<OIDCProviderTO> {
+
+    private static final long serialVersionUID = -3310772400714122768L;
+
+    private final OIDCProviderRestClient restClient = new OIDCProviderRestClient();
+
+    private final OIDCProvidersDirectoryPanel directoryPanel;
+
+    private final IModel<List<String>> actionsClasses = new LoadableDetachableModel<List<String>>() {
+
+        private static final long serialVersionUID = 5275935387613157437L;
+
+        @Override
+        protected List<String> load() {
+            return new ArrayList<>(restClient.getActionsClasses());
+        }
+    };
+
+    public OIDCProviderWizardBuilder(final OIDCProvidersDirectoryPanel directoryPanel, final OIDCProviderTO defaultItem,
+            final PageReference pageRef) {
+        super(defaultItem, pageRef);
+        this.directoryPanel = directoryPanel;
+    }
+
+    @Override
+    protected Serializable onApplyInternal(final OIDCProviderTO modelObject) {
+        if (modelObject.getKey() == null) {
+            if (modelObject.getHasDiscovery()) {
+                restClient.createFromDiscovery(modelObject);
+            } else {
+                restClient.create(modelObject);
+            }
+
+        } else {
+            restClient.update(modelObject);
+        }
+        return modelObject;
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final OIDCProviderTO modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new OP(modelObject));
+        if (modelObject.getKey() == null) {
+            wizardModel.add(new OPContinue(modelObject));
+        } else {
+            wizardModel.add(new OPContinue(modelObject, true));
+        }
+
+        Mapping mapping = new Mapping(modelObject);
+        mapping.setOutputMarkupId(true);
+
+        ItemTransformersTogglePanel mapItemTransformers = new ItemTransformersTogglePanel(mapping, pageRef);
+        addOuterObject(mapItemTransformers);
+        JEXLTransformersTogglePanel jexlTransformers = new JEXLTransformersTogglePanel(mapping, pageRef);
+        addOuterObject(jexlTransformers);
+        mapping.add(new OIDCProviderMappingPanel("mapping", modelObject, mapItemTransformers, jexlTransformers));
+
+        wizardModel.add(mapping);
+
+        return wizardModel;
+    }
+
+    public class OP extends WizardStep {
+
+        private static final long serialVersionUID = 7127421283216134900L;
+
+        public OP(final OIDCProviderTO opTO) {
+            AjaxTextFieldPanel name = new AjaxTextFieldPanel(
+                    "name", "name", new PropertyModel<String>(opTO, "name"), false);
+            name.addRequiredLabel();
+            name.setEnabled(true);
+            add(name);
+
+            AjaxTextFieldPanel clientID = new AjaxTextFieldPanel(
+                    "clientID", "clientID", new PropertyModel<String>(opTO, "clientID"), false);
+            clientID.addRequiredLabel();
+            clientID.setEnabled(true);
+            add(clientID);
+
+            AjaxTextFieldPanel clientSecret = new AjaxTextFieldPanel(
+                    "clientSecret", "clientSecret", new PropertyModel<String>(opTO, "clientSecret"), false);
+            clientSecret.addRequiredLabel();
+            clientSecret.setEnabled(true);
+            add(clientSecret);
+
+            AjaxCheckBoxPanel createUnmatching = new AjaxCheckBoxPanel(
+                    "createUnmatching", "createUnmatching", new PropertyModel<Boolean>(opTO, "createUnmatching"),
+                    false);
+            add(createUnmatching);
+
+            AjaxCheckBoxPanel updateMatching = new AjaxCheckBoxPanel(
+                    "updateMatching", "updateMatching", new PropertyModel<Boolean>(opTO, "updateMatching"), false);
+            add(updateMatching);
+
+            AjaxPalettePanel<String> actionsClassNames = new AjaxPalettePanel.Builder<String>().
+                    setAllowMoveAll(true).setAllowOrder(true).
+                    setName(new StringResourceModel("actionsClassNames", directoryPanel).getString()).
+                    build("actionsClassNames",
+                            new PropertyModel<List<String>>(opTO, "actionsClassNames"),
+                            new ListModel<>(actionsClasses.getObject()));
+            actionsClassNames.setOutputMarkupId(true);
+            add(actionsClassNames);
+        }
+    }
+
+    public class OPContinue extends WizardStep {
+
+        private static final long serialVersionUID = -7087008312629522790L;
+
+        public OPContinue(final OIDCProviderTO opTO) {
+
+            final WebMarkupContainer content = new WebMarkupContainer("content");
+            this.setOutputMarkupId(true);
+            content.setOutputMarkupId(true);
+            add(content);
+
+            UrlValidator urlValidator = new UrlValidator();
+            final AjaxTextFieldPanel issuer = new AjaxTextFieldPanel(
+                    "issuer", "issuer", new PropertyModel<String>(opTO, "issuer"));
+            issuer.addValidator(urlValidator);
+            issuer.addRequiredLabel();
+            content.add(issuer);
+
+            final AjaxCheckBoxPanel hasDiscovery = new AjaxCheckBoxPanel(
+                    "hasDiscovery", "hasDiscovery", new PropertyModel<Boolean>(opTO, "hasDiscovery"));
+            content.add(hasDiscovery);
+
+            final AjaxTextFieldPanel authorizationEndpoint = new AjaxTextFieldPanel("authorizationEndpoint",
+                    "authorizationEndpoint", new PropertyModel<String>(opTO, "authorizationEndpoint"));
+            authorizationEndpoint.addRequiredLabel();
+            authorizationEndpoint.addValidator(urlValidator);
+            content.add(authorizationEndpoint);
+
+            final AjaxTextFieldPanel userinfoEndpoint = new AjaxTextFieldPanel("userinfoEndpoint",
+                    "userinfoEndpoint", new PropertyModel<String>(opTO, "userinfoEndpoint"));
+            userinfoEndpoint.addRequiredLabel();
+            userinfoEndpoint.addValidator(urlValidator);
+            content.add(userinfoEndpoint);
+
+            final AjaxTextFieldPanel tokenEndpoint = new AjaxTextFieldPanel("tokenEndpoint",
+                    "tokenEndpoint", new PropertyModel<String>(opTO, "tokenEndpoint"));
+            tokenEndpoint.addRequiredLabel();
+            tokenEndpoint.addValidator(urlValidator);
+            content.add(tokenEndpoint);
+
+            final AjaxTextFieldPanel jwksUri = new AjaxTextFieldPanel("jwksUri",
+                    "jwksUri", new PropertyModel<String>(opTO, "jwksUri"));
+            jwksUri.addRequiredLabel();
+            jwksUri.addValidator(urlValidator);
+            content.add(jwksUri);
+
+            final WebMarkupContainer visibleParam = new WebMarkupContainer("visibleParams");
+            visibleParam.setOutputMarkupPlaceholderTag(true);
+            visibleParam.add(authorizationEndpoint);
+            visibleParam.add(userinfoEndpoint);
+            visibleParam.add(tokenEndpoint);
+            visibleParam.add(jwksUri);
+            content.add(visibleParam);
+
+            showHide(hasDiscovery, visibleParam);
+
+            hasDiscovery.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    showHide(hasDiscovery, visibleParam);
+                    target.add(visibleParam);
+                }
+            });
+
+        }
+
+        public OPContinue(final OIDCProviderTO opTO, final boolean readOnly) {
+
+            final WebMarkupContainer content = new WebMarkupContainer("content");
+            this.setOutputMarkupId(true);
+            content.setOutputMarkupId(true);
+            add(content);
+
+            final AjaxTextFieldPanel issuer = new AjaxTextFieldPanel(
+                    "issuer", "issuer", new PropertyModel<String>(opTO, "issuer"));
+            issuer.setReadOnly(readOnly);
+            content.add(issuer);
+
+            final AjaxCheckBoxPanel hasDiscovery = new AjaxCheckBoxPanel(
+                    "hasDiscovery", "hasDiscovery", new PropertyModel<Boolean>(opTO, "hasDiscovery"));
+            hasDiscovery.setReadOnly(readOnly);
+            content.add(hasDiscovery);
+
+            final AjaxTextFieldPanel authorizationEndpoint = new AjaxTextFieldPanel("authorizationEndpoint",
+                    "authorizationEndpoint", new PropertyModel<String>(opTO, "authorizationEndpoint"));
+            authorizationEndpoint.setReadOnly(readOnly);
+            content.add(authorizationEndpoint);
+
+            final AjaxTextFieldPanel userinfoEndpoint = new AjaxTextFieldPanel("userinfoEndpoint",
+                    "userinfoEndpoint", new PropertyModel<String>(opTO, "userinfoEndpoint"));
+            userinfoEndpoint.setReadOnly(readOnly);
+            content.add(userinfoEndpoint);
+
+            final AjaxTextFieldPanel tokenEndpoint = new AjaxTextFieldPanel("tokenEndpoint",
+                    "tokenEndpoint", new PropertyModel<String>(opTO, "tokenEndpoint"));
+            tokenEndpoint.setReadOnly(readOnly);
+            content.add(tokenEndpoint);
+
+            final AjaxTextFieldPanel jwksUri = new AjaxTextFieldPanel("jwksUri",
+                    "jwksUri", new PropertyModel<String>(opTO, "jwksUri"));
+            jwksUri.setReadOnly(readOnly);
+            content.add(jwksUri);
+
+            final WebMarkupContainer visibleParam = new WebMarkupContainer("visibleParams");
+            visibleParam.setOutputMarkupPlaceholderTag(true);
+            visibleParam.add(authorizationEndpoint);
+            visibleParam.add(userinfoEndpoint);
+            visibleParam.add(tokenEndpoint);
+            visibleParam.add(jwksUri);
+            content.add(visibleParam);
+        }
+    }
+
+    private void showHide(final AjaxCheckBoxPanel hasDiscovery, final WebMarkupContainer visibleParams) {
+        if (hasDiscovery.getField().getValue().equals("false")) {
+            visibleParams.setVisible(true);
+        } else {
+            visibleParams.setVisible(false);
+        }
+    }
+
+    /**
+     * Mapping definition step.
+     */
+    private static final class Mapping extends WizardStep {
+
+        private static final long serialVersionUID = 3454904947720856253L;
+
+        Mapping(final OIDCProviderTO item) {
+            setTitleModel(Model.of("Mapping"));
+            setSummaryModel(Model.of(StringUtils.EMPTY));
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.html
new file mode 100644
index 0000000..a1a0aed
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:extend>
+    <section class="content-header">
+      <h1>&nbsp;</h1>
+      <ol class="breadcrumb">
+        <li><a wicket:id="dashboardBr"><i class="fa fa-dashboard"></i> <wicket:message key="dashboard"/></a></li>
+        <li class="active"><wicket:message key="extensions"/></li>
+        <li class="active">OIDC Client</li>
+      </ol>
+    </section>
+
+    <section class="content" wicket:id="content">
+      <div class="box">
+        <div class="box-body" wicket:id="tabbedPanel"/>
+      </div>
+    </section>
+  </wicket:extend>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.properties
new file mode 100644
index 0000000..b34f11c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.properties
@@ -0,0 +1,17 @@
+# 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.
+op=OIDC Providers

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_it.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_it.properties
new file mode 100644
index 0000000..b34f11c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_it.properties
@@ -0,0 +1,17 @@
+# 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.
+op=OIDC Providers

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_pt_BR.properties
new file mode 100644
index 0000000..b34f11c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_pt_BR.properties
@@ -0,0 +1,17 @@
+# 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.
+op=OIDC Providers

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_ru.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_ru.properties
new file mode 100644
index 0000000..b34f11c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_ru.properties
@@ -0,0 +1,17 @@
+# 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.
+op=OIDC Providers

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.html
new file mode 100644
index 0000000..ce0dbe8
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.html
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:extend>
+
+  </wicket:extend>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
new file mode 100644
index 0000000..569d0dc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
@@ -0,0 +1,30 @@
+# 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.
+clientID=Client ID
+clientSecret=Client Secret
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+any.new=New Provider
+template.title=user template
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
new file mode 100644
index 0000000..569d0dc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
@@ -0,0 +1,30 @@
+# 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.
+clientID=Client ID
+clientSecret=Client Secret
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+any.new=New Provider
+template.title=user template
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..569d0dc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
@@ -0,0 +1,30 @@
+# 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.
+clientID=Client ID
+clientSecret=Client Secret
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+any.new=New Provider
+template.title=user template
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
new file mode 100644
index 0000000..569d0dc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
@@ -0,0 +1,30 @@
+# 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.
+clientID=Client ID
+clientSecret=Client Secret
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+any.new=New Provider
+template.title=user template
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.html
new file mode 100644
index 0000000..3b30b4c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.html
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div class="form-group">
+      <span wicket:id="ops"></span>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$Mapping.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$Mapping.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$Mapping.html
new file mode 100644
index 0000000..11b1a52
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$Mapping.html
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <span wicket:id="mapping" />
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
new file mode 100644
index 0000000..4ad49d8
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
@@ -0,0 +1,28 @@
+<!--
+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.  actionsClassNames
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div class="form-group"><span wicket:id="name">[name]</span></div>
+    <div class="form-group"><span wicket:id="clientID">[clientID]</span></div>
+    <div class="form-group"><span wicket:id="clientSecret">[clientSecret]</span></div>
+    <div class="form-group"><span wicket:id="createUnmatching">[createUnmatching]</span></div>
+    <div class="form-group"><span wicket:id="updateMatching">[updateMatching]</span></div>
+    <div class="form-group"><span wicket:id="actionsClassNames">[actionsClassNames]</span></div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
new file mode 100644
index 0000000..5ed531a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
@@ -0,0 +1,24 @@
+# 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.
+name=Name
+clientID=Client ID
+clientSecret=Client Secret
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.html
new file mode 100644
index 0000000..01da6ec
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.html
@@ -0,0 +1,33 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div wicket:id="content">
+      <span wicket:id="issuer">[issuer]</span>
+      <span wicket:id="hasDiscovery">[hasDiscovery]</span>
+
+      <div wicket:id="visibleParams">
+        <span wicket:id="authorizationEndpoint">[authorizationEndpoint]</span>
+        <span wicket:id="tokenEndpoint">[tokenEndpoint]</span>
+        <span wicket:id="jwksUri">[jwksUri]</span>
+        <span wicket:id="userinfoEndpoint">[userinfoEndpoint]</span>
+      </div>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.properties
new file mode 100644
index 0000000..3f1d085
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.properties
@@ -0,0 +1,27 @@
+# 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.
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+tokenEndpoint= Token Endpoint
+jwksUri= JWKS URI
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_it.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_it.properties
new file mode 100644
index 0000000..3f1d085
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_it.properties
@@ -0,0 +1,27 @@
+# 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.
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+tokenEndpoint= Token Endpoint
+jwksUri= JWKS URI
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_pt_BR.properties
new file mode 100644
index 0000000..3f1d085
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_pt_BR.properties
@@ -0,0 +1,27 @@
+# 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.
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+tokenEndpoint= Token Endpoint
+jwksUri= JWKS URI
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_ru.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_ru.properties
new file mode 100644
index 0000000..3f1d085
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_ru.properties
@@ -0,0 +1,27 @@
+# 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.
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+tokenEndpoint= Token Endpoint
+jwksUri= JWKS URI
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
new file mode 100644
index 0000000..5ed531a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
@@ -0,0 +1,24 @@
+# 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.
+name=Name
+clientID=Client ID
+clientSecret=Client Secret
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
new file mode 100644
index 0000000..5ed531a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
@@ -0,0 +1,24 @@
+# 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.
+name=Name
+clientID=Client ID
+clientSecret=Client Secret
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
new file mode 100644
index 0000000..5ed531a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
@@ -0,0 +1,24 @@
+# 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.
+name=Name
+clientID=Client ID
+clientSecret=Client Secret
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-enduser/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-enduser/pom.xml b/ext/oidcclient/client-enduser/pom.xml
new file mode 100644
index 0000000..3a86a04
--- /dev/null
+++ b/ext/oidcclient/client-enduser/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Enduser</name>
+  <description>Apache Syncope Ext: OIDC Client Enduser</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-enduser</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/OIDCClientLogin.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/OIDCClientLogin.java b/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/OIDCClientLogin.java
new file mode 100644
index 0000000..5aa8578
--- /dev/null
+++ b/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/OIDCClientLogin.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.wicket.authentication.IAuthenticationStrategy;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OIDCClientLogin extends WebPage {
+
+    private static final long serialVersionUID = 8581614051773949262L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCClientLogin.class);
+
+    private static final String OIDC_ACCESS_ERROR = "OIDC access error";
+
+    public OIDCClientLogin(final PageParameters parameters) {
+        super(parameters);
+
+        String token = (String) ((ServletWebRequest) getRequest()).getContainerRequest().
+                getSession().getAttribute(org.apache.syncope.ext.oidcclient.agent.Constants.OIDCCLIENTJWT);
+        if (StringUtils.isBlank(token)) {
+            LOG.error("No JWT found, redirecting to default greeter");
+
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", OIDC_ACCESS_ERROR);
+            setResponsePage(getApplication().getHomePage(), params);
+        }
+
+        IAuthenticationStrategy strategy = getApplication().getSecuritySettings().getAuthenticationStrategy();
+
+        if (SyncopeEnduserSession.get().authenticate(token)) {
+            setResponsePage(getApplication().getHomePage());
+        } else {
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", OIDC_ACCESS_ERROR);
+            setResponsePage(getApplication().getHomePage(), params);
+        }
+        strategy.remove();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/OIDCProvidersResource.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/OIDCProvidersResource.java b/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/OIDCProvidersResource.java
new file mode 100644
index 0000000..3bb2d63
--- /dev/null
+++ b/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/OIDCProvidersResource.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.enduser.resources;
+
+import static org.apache.syncope.client.enduser.resources.BaseResource.MAPPER;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.annotations.Resource;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.apache.wicket.request.resource.AbstractResource;
+
+@Resource(key = "oidcProviders", path = "/api/oidcProviders")
+public class OIDCProvidersResource extends BaseResource {
+
+    private static final long serialVersionUID = -6595407257133697170L;
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+        ResourceResponse response = new ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
+        response.setTextEncoding(StandardCharsets.UTF_8.name());
+        try {
+            final ArrayNode result = MAPPER.createArrayNode();
+
+            for (OIDCProviderTO op : SyncopeEnduserSession.get().getService(OIDCProviderService.class).list()) {
+                ObjectNode opNode = MAPPER.createObjectNode();
+                opNode.put("name", op.getName());
+                result.add(opNode);
+            }
+
+            response.setWriteCallback(new AbstractResource.WriteCallback() {
+
+                @Override
+                public void writeData(final Attributes attributes) throws IOException {
+                    attributes.getResponse().write(MAPPER.writeValueAsString(result));
+                }
+            });
+            response.setStatusCode(Response.Status.OK.getStatusCode());
+        } catch (Exception e) {
+            LOG.error("Error retrieving available OIDC Providers", e);
+            response.setError(
+                    Response.Status.BAD_REQUEST.getStatusCode(),
+                    "ErrorMessage{{ " + e.getMessage() + "}}");
+        }
+
+        return response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/common-lib/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/pom.xml b/ext/oidcclient/common-lib/pom.xml
new file mode 100644
index 0000000..f60b0f0
--- /dev/null
+++ b/ext/oidcclient/common-lib/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Common Lib</name>
+  <description>Apache Syncope Ext: OIDC Client Common Lib</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.common</groupId>
+      <artifactId>syncope-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/OIDCConstants.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/OIDCConstants.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/OIDCConstants.java
new file mode 100644
index 0000000..12daa0a
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/OIDCConstants.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib;
+
+public final class OIDCConstants {
+
+    public static final String CLIENT_ID = "client_id";
+
+    public static final String CLIENT_SECRET = "client_secret";
+
+    public static final String SCOPE = "scope";
+
+    public static final String RESPOSNSE_TYPE = "response_type";
+
+    public static final String REDIRECT_URI = "redirect_uri";
+
+    public static final String STATE = "state";
+
+    public static final String CODE = "code";
+
+    public static final String GRANT_TYPE = "grant_type";
+
+    public static final String OP = "op";
+
+    private OIDCConstants() {
+        // private constructor for static utility class
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginRequestTO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginRequestTO.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginRequestTO.java
new file mode 100644
index 0000000..5d4dc31
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginRequestTO.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "oidcLoginRequest")
+@XmlType
+public class OIDCLoginRequestTO extends AbstractBaseBean {
+
+    private static final long serialVersionUID = -3509031322459942441L;
+
+    private String providerAddress;
+
+    private String clientId;
+
+    private String scope;
+
+    private String responseType;
+
+    private String redirectURI;
+
+    private String state;
+
+    public String getProviderAddress() {
+        return providerAddress;
+    }
+
+    public void setProviderAddress(final String providerAddress) {
+        this.providerAddress = providerAddress;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(final String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(final String scope) {
+        this.scope = scope;
+    }
+
+    public String getResponseType() {
+        return responseType;
+    }
+
+    public void setResponseType(final String responseType) {
+        this.responseType = responseType;
+    }
+
+    public String getRedirectURI() {
+        return redirectURI;
+    }
+
+    public void setRedirectURI(final String redirectURI) {
+        this.redirectURI = redirectURI;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public void setState(final String state) {
+        this.state = state;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginResponseTO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginResponseTO.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginResponseTO.java
new file mode 100644
index 0000000..fd356a9
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginResponseTO.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib.to;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "oidcLoginResponse")
+@XmlType
+public class OIDCLoginResponseTO extends AbstractBaseBean {
+
+    private static final long serialVersionUID = -5971442076182154492L;
+
+    private String username;
+
+    private String email;
+
+    private String name;
+
+    private String subject;
+
+    private String givenName;
+
+    private String familyName;
+
+    private String accessToken;
+
+    private Date accessTokenExpiryTime;
+
+    private final Set<AttrTO> attrs = new HashSet<>();
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(final String username) {
+        this.username = username;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(final String subject) {
+        this.subject = subject;
+    }
+
+    public String getGivenName() {
+        return givenName;
+    }
+
+    public void setGivenName(final String givenName) {
+        this.givenName = givenName;
+    }
+
+    public String getFamilyName() {
+        return familyName;
+    }
+
+    public void setFamilyName(final String familyName) {
+        this.familyName = familyName;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(final String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    public Date getAccessTokenExpiryTime() {
+        return accessTokenExpiryTime;
+    }
+
+    public void setAccessTokenExpiryTime(final Date accessTokenExpiryTime) {
+        this.accessTokenExpiryTime = accessTokenExpiryTime;
+    }
+
+    @JsonIgnore
+    public AttrTO getAttr(final String schema) {
+        return IterableUtils.find(attrs, new Predicate<AttrTO>() {
+
+            @Override
+            public boolean evaluate(final AttrTO object) {
+                return object.getSchema().equals(schema);
+            }
+        });
+    }
+
+    @XmlElementWrapper(name = "attrs")
+    @XmlElement(name = "attr")
+    @JsonProperty("attrs")
+    public Set<AttrTO> getAttrs() {
+        return attrs;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
new file mode 100644
index 0000000..2bb88aa
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib.to;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "oidcprovider")
+@XmlType
+public class OIDCProviderTO extends AbstractBaseBean implements EntityTO, ItemContainerTO {
+
+    private static final long serialVersionUID = -1229802774546135794L;
+
+    private String key;
+
+    private String name;
+
+    private String clientID;
+
+    private String clientSecret;
+
+    private String authorizationEndpoint;
+
+    private String tokenEndpoint;
+
+    private String jwksUri;
+
+    private String issuer;
+
+    private String userinfoEndpoint;
+
+    private boolean hasDiscovery;
+
+    private UserTO userTemplate;
+
+    private boolean createUnmatching;
+
+    private boolean updateMatching;
+
+    private final List<ItemTO> items = new ArrayList<>();
+
+    private final Set<String> actionsClassNames = new HashSet<>();
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getClientID() {
+        return clientID;
+    }
+
+    public void setClientID(final String clientID) {
+        this.clientID = clientID;
+    }
+
+    public String getClientSecret() {
+        return clientSecret;
+    }
+
+    public void setClientSecret(final String clientSecret) {
+        this.clientSecret = clientSecret;
+    }
+
+    public String getAuthorizationEndpoint() {
+        return authorizationEndpoint;
+    }
+
+    public void setAuthorizationEndpoint(final String authorizationEndpoint) {
+        this.authorizationEndpoint = authorizationEndpoint;
+    }
+
+    public String getTokenEndpoint() {
+        return tokenEndpoint;
+    }
+
+    public void setTokenEndpoint(final String tokenEndpoint) {
+        this.tokenEndpoint = tokenEndpoint;
+    }
+
+    public String getJwksUri() {
+        return jwksUri;
+    }
+
+    public void setJwksUri(final String jwksUri) {
+        this.jwksUri = jwksUri;
+    }
+
+    public String getIssuer() {
+        return issuer;
+    }
+
+    public void setIssuer(final String issuer) {
+        this.issuer = issuer;
+    }
+
+    public String getUserinfoEndpoint() {
+        return userinfoEndpoint;
+    }
+
+    public void setUserinfoEndpoint(final String userinfoEndpoint) {
+        this.userinfoEndpoint = userinfoEndpoint;
+    }
+
+    public UserTO getUserTemplate() {
+        return userTemplate;
+    }
+
+    public boolean getHasDiscovery() {
+        return hasDiscovery;
+    }
+
+    public void setHasDiscovery(final boolean hasDiscovery) {
+        this.hasDiscovery = hasDiscovery;
+    }
+
+    public void setUserTemplate(final UserTO userTemplate) {
+        this.userTemplate = userTemplate;
+    }
+
+    public boolean isCreateUnmatching() {
+        return createUnmatching;
+    }
+
+    public void setCreateUnmatching(final boolean createUnmatching) {
+        this.createUnmatching = createUnmatching;
+    }
+
+    public boolean isUpdateMatching() {
+        return updateMatching;
+    }
+
+    public void setUpdateMatching(final boolean updateMatching) {
+        this.updateMatching = updateMatching;
+    }
+    
+    
+
+    @Override
+    public ItemTO getConnObjectKeyItem() {
+        return IterableUtils.find(getItems(), new Predicate<ItemTO>() {
+
+            @Override
+            public boolean evaluate(final ItemTO item) {
+                return item.isConnObjectKey();
+            }
+        });
+    }
+
+    protected boolean addConnObjectKeyItem(final ItemTO connObjectItem) {
+        connObjectItem.setMandatoryCondition("true");
+        connObjectItem.setConnObjectKey(true);
+
+        return this.add(connObjectItem);
+    }
+
+    @Override
+    public boolean setConnObjectKeyItem(final ItemTO connObjectKeyItem) {
+        return connObjectKeyItem == null
+                ? remove(getConnObjectKeyItem())
+                : addConnObjectKeyItem(connObjectKeyItem);
+    }
+
+    @XmlElementWrapper(name = "items")
+    @XmlElement(name = "item")
+    @JsonProperty("items")
+    @Override
+    public List<ItemTO> getItems() {
+        return items;
+    }
+
+    @Override
+    public boolean add(final ItemTO item) {
+        return item == null ? false : this.items.contains(item) || this.items.add(item);
+    }
+
+    public boolean remove(final ItemTO item) {
+        return this.items.remove(item);
+    }
+
+    @XmlElementWrapper(name = "actionsClassNames")
+    @XmlElement(name = "actionsClassName")
+    @JsonProperty("actionsClassNames")
+    public Set<String> getActionsClassNames() {
+        return actionsClassNames;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientEntitlement.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientEntitlement.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientEntitlement.java
new file mode 100644
index 0000000..98cf4d6
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientEntitlement.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib.types;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Set;
+import java.util.TreeSet;
+
+public final class OIDCClientEntitlement {
+
+    public static final String OP_READ = "OP_READ";
+
+    public static final String OP_CREATE = "OP_CREATE";
+
+    public static final String OP_UPDATE = "OP_UPDATE";
+
+    public static final String OP_DELETE = "OP_DELETE";
+
+    private static final Set<String> VALUES;
+
+    static {
+        Set<String> values = new TreeSet<>();
+        for (Field field : OIDCClientEntitlement.class.getDeclaredFields()) {
+            if (Modifier.isStatic(field.getModifiers()) && String.class.equals(field.getType())) {
+                values.add(field.getName());
+            }
+        }
+        VALUES = Collections.unmodifiableSet(values);
+    }
+
+    public static Set<String> values() {
+        return VALUES;
+    }
+
+    private OIDCClientEntitlement() {
+        // private constructor for static utility class
+    }
+
+}


[8/8] syncope git commit: [SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser - This closes #74

Posted by il...@apache.org.
[SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser - This closes #74


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/797fd1cb
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/797fd1cb
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/797fd1cb

Branch: refs/heads/master
Commit: 797fd1cbd801aedabb54b5d1dcb65d34636adec5
Parents: 064b7ef
Author: dayash <di...@tirasa.net>
Authored: Wed May 2 09:50:13 2018 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu May 3 16:57:56 2018 +0200

----------------------------------------------------------------------
 archetype/pom.xml                               |  31 ++
 .../archetype-resources/console/pom.xml         |   6 +
 .../resources/archetype-resources/core/pom.xml  |  11 +
 .../archetype-resources/enduser/pom.xml         |   6 +
 .../resources/META-INF/resources/app/index.html |   1 +
 .../resources/META-INF/resources/app/js/app.js  |  27 +-
 .../app/js/controllers/LoginController.js       |   2 +
 .../app/js/services/oidcProviderService.js      |  42 ++
 .../META-INF/resources/app/views/self.html      |   8 +
 deb/console/pom.xml                             |  13 +
 deb/console/src/deb/control/conffiles           |   2 +
 deb/enduser/pom.xml                             |  13 +
 deb/enduser/src/deb/control/conffiles           |   2 +
 deb/enduser/src/deb/control/preinst             |   1 +
 ext/oidcclient/agent/pom.xml                    | 126 ++++++
 .../ext/oidcclient/agent/CodeConsumer.java      |  92 +++++
 .../syncope/ext/oidcclient/agent/Constants.java |  40 ++
 .../syncope/ext/oidcclient/agent/Login.java     |  89 ++++
 .../oidcclient/agent/OIDCClientAgentSetup.java  |  77 ++++
 .../resources/oidcclient/loginError.jsp         |  35 ++
 .../resources/oidcclient/loginSuccess.jsp       |  33 ++
 .../main/resources/META-INF/web-fragment.xml    |  26 ++
 .../main/resources/oidcclient-agent.properties  |  26 ++
 ext/oidcclient/client-console/pom.xml           |  83 ++++
 .../client/console/pages/OIDCClient.java        |  68 ++++
 .../client/console/pages/OIDCClientLogin.java   |  68 ++++
 .../panels/OIDCProvidersDirectoryPanel.java     | 291 +++++++++++++
 .../console/panels/OIDCSSOLoginFormPanel.java   | 109 +++++
 .../console/rest/OIDCProviderRestClient.java    |  59 +++
 .../wizards/OIDCProviderMappingPanel.java       | 104 +++++
 .../wizards/OIDCProviderWizardBuilder.java      | 290 +++++++++++++
 .../client/console/pages/OIDCClient.html        |  37 ++
 .../client/console/pages/OIDCClient.properties  |  17 +
 .../console/pages/OIDCClient_it.properties      |  17 +
 .../console/pages/OIDCClient_pt_BR.properties   |  17 +
 .../console/pages/OIDCClient_ru.properties      |  17 +
 .../panels/OIDCProvidersDirectoryPanel.html     |  23 ++
 .../OIDCProvidersDirectoryPanel.properties      |  30 ++
 .../OIDCProvidersDirectoryPanel_it.properties   |  30 ++
 ...OIDCProvidersDirectoryPanel_pt_BR.properties |  30 ++
 .../OIDCProvidersDirectoryPanel_ru.properties   |  30 ++
 .../console/panels/OIDCSSOLoginFormPanel.html   |  25 ++
 .../OIDCProviderWizardBuilder$Mapping.html      |  23 ++
 .../wizards/OIDCProviderWizardBuilder$OP.html   |  28 ++
 .../OIDCProviderWizardBuilder$OP.properties     |  24 ++
 .../OIDCProviderWizardBuilder$OPContinue.html   |  33 ++
 ...CProviderWizardBuilder$OPContinue.properties |  27 ++
 ...oviderWizardBuilder$OPContinue_it.properties |  27 ++
 ...derWizardBuilder$OPContinue_pt_BR.properties |  27 ++
 ...oviderWizardBuilder$OPContinue_ru.properties |  27 ++
 .../OIDCProviderWizardBuilder$OP_it.properties  |  24 ++
 ...IDCProviderWizardBuilder$OP_pt_BR.properties |  24 ++
 .../OIDCProviderWizardBuilder$OP_ru.properties  |  24 ++
 ext/oidcclient/client-enduser/pom.xml           |  83 ++++
 .../client/enduser/pages/OIDCClientLogin.java   |  63 +++
 .../resources/OIDCProvidersResource.java        |  69 ++++
 ext/oidcclient/common-lib/pom.xml               |  56 +++
 .../syncope/common/lib/OIDCConstants.java       |  45 ++
 .../common/lib/to/OIDCLoginRequestTO.java       |  91 +++++
 .../common/lib/to/OIDCLoginResponseTO.java      | 133 ++++++
 .../syncope/common/lib/to/OIDCProviderTO.java   | 219 ++++++++++
 .../common/lib/types/OIDCClientEntitlement.java |  57 +++
 ext/oidcclient/logic/pom.xml                    |  76 ++++
 .../syncope/core/logic/OIDCClientLogic.java     | 406 +++++++++++++++++++
 .../syncope/core/logic/OIDCProviderLogic.java   | 167 ++++++++
 ...ClientClassPathScanImplementationLookup.java |  78 ++++
 .../core/logic/init/OIDCClientLoader.java       |  55 +++
 .../model/OIDCProviderDiscoveryDocument.java    | 128 ++++++
 .../core/logic/model/TokenEndpointResponse.java |  86 ++++
 .../core/logic/oidc/OIDCUserManager.java        | 292 +++++++++++++
 ext/oidcclient/persistence-api/pom.xml          |  61 +++
 .../persistence/api/dao/OIDCProviderDAO.java    |  35 ++
 .../api/entity/OIDCEntityFactory.java           |  25 ++
 .../persistence/api/entity/OIDCProvider.java    |  85 ++++
 .../api/entity/OIDCProviderItem.java            |  29 ++
 .../api/entity/OIDCUserTemplate.java            |  27 ++
 ext/oidcclient/persistence-jpa/pom.xml          | 134 ++++++
 .../persistence/jpa/dao/JPAOIDCProviderDAO.java |  78 ++++
 .../jpa/entity/JPAOIDCEntityFactory.java        |  49 +++
 .../persistence/jpa/entity/JPAOIDCProvider.java | 250 ++++++++++++
 .../jpa/entity/JPAOIDCProviderItem.java         |  79 ++++
 .../jpa/entity/JPAOIDCUserTemplate.java         |  52 +++
 ext/oidcclient/pom.xml                          |  55 +++
 ext/oidcclient/provisioning-api/pom.xml         |  67 +++
 .../provisioning/api/OIDCProviderActions.java   |  35 ++
 .../api/data/OIDCProviderDataBinder.java        |  32 ++
 ext/oidcclient/provisioning-java/pom.xml        |  61 +++
 .../java/DefaultOIDCProviderActions.java        |  48 +++
 .../java/data/OIDCProviderDataBinderImpl.java   | 258 ++++++++++++
 ext/oidcclient/rest-api/pom.xml                 |  77 ++++
 .../rest/api/service/OIDCClientService.java     |  75 ++++
 .../rest/api/service/OIDCProviderService.java   | 156 +++++++
 ext/oidcclient/rest-cxf/pom.xml                 |  72 ++++
 .../rest/cxf/service/OIDCClientServiceImpl.java |  44 ++
 .../cxf/service/OIDCProviderServiceImpl.java    |  83 ++++
 ext/pom.xml                                     |   1 +
 .../syncope/ext/saml2lsp/agent/Login.java       |   2 +-
 fit/console-reference/pom.xml                   |   6 +
 .../main/resources/oidcclient-agent.properties  |  26 ++
 .../src/main/webapp/WEB-INF/web.xml             |  10 +
 .../src/test/resources/rebel.xml                |   8 +
 fit/core-reference/pom.xml                      |  23 ++
 .../org/apache/syncope/fit/AbstractITCase.java  |   8 +
 .../apache/syncope/fit/OIDCClientDetector.java  |  52 +++
 .../syncope/fit/core/OIDCClientITCase.java      | 152 +++++++
 fit/core-reference/src/test/resources/rebel.xml |  17 +
 fit/enduser-reference/pom.xml                   |   6 +
 .../main/resources/oidcclient-agent.properties  |  26 ++
 .../src/main/webapp/WEB-INF/web.xml             |  10 +
 .../src/test/resources/rebel.xml                |   8 +
 pom.xml                                         |  10 +
 .../reference-guide/concepts/extensions.adoc    |  33 ++
 .../workingwithapachesyncope/customization.adoc |  52 +++
 113 files changed, 6861 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/archetype/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/pom.xml b/archetype/pom.xml
index c4639be..27eccc4 100644
--- a/archetype/pom.xml
+++ b/archetype/pom.xml
@@ -233,6 +233,13 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../ext/oidcclient/agent/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/resources/all</targetPath>
+        <includes>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../fit/console-reference/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/resources</targetPath>
         <includes>
@@ -252,6 +259,14 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../fit/console-reference/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/test/resources</targetPath>
+        <includes>
+          <include>console.properties</include>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../fit/console-reference/src/main/webapp/WEB-INF</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/webapp/WEB-INF</targetPath>
       </resource>
@@ -272,6 +287,13 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../ext/oidcclient/agent/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/resources/all</targetPath>
+        <includes>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../client/enduser/src/main/resources/META-INF/resources/app</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/webapp/app</targetPath>
       </resource>
@@ -292,6 +314,15 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../fit/enduser-reference/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/test/resources</targetPath>
+        <includes>
+          <include>enduser.properties</include>
+          <include>oidcclient-agent.properties</include>
+          <include>customForm.json</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../fit/enduser-reference/src/main/webapp/WEB-INF</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/webapp/WEB-INF</targetPath>
       </resource>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/archetype/src/main/resources/archetype-resources/console/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/console/pom.xml b/archetype/src/main/resources/archetype-resources/console/pom.xml
index 401387e..479126c 100644
--- a/archetype/src/main/resources/archetype-resources/console/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/console/pom.xml
@@ -141,6 +141,12 @@ under the License.
         </dependency>
         
         <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+          <version>${syncope.version}</version>
+        </dependency>
+        
+        <dependency>
           <groupId>org.apache.syncope.ext.scimv2</groupId>
           <artifactId>syncope-ext-scimv2-client-console</artifactId>
           <version>${syncope.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/archetype/src/main/resources/archetype-resources/core/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/core/pom.xml b/archetype/src/main/resources/archetype-resources/core/pom.xml
index 68d62b3..f248d9a 100644
--- a/archetype/src/main/resources/archetype-resources/core/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/core/pom.xml
@@ -193,6 +193,17 @@ under the License.
         </dependency>
 
         <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+          <version>${syncope.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
+          <version>${syncope.version}</version>
+        </dependency>
+
+        <dependency>
           <groupId>org.apache.syncope.ext.scimv2</groupId>
           <artifactId>syncope-ext-scimv2-rest-cxf</artifactId>
           <version>${syncope.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/archetype/src/main/resources/archetype-resources/enduser/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/enduser/pom.xml b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
index 0b097f3..5f98d31 100644
--- a/archetype/src/main/resources/archetype-resources/enduser/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
@@ -332,6 +332,12 @@ under the License.
           <artifactId>syncope-ext-saml2sp-client-enduser</artifactId>
           <version>${syncope.version}</version>
         </dependency>
+
+        <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+          <version>${syncope.version}</version>
+        </dependency>
       </dependencies>
       
       <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/client/enduser/src/main/resources/META-INF/resources/app/index.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/index.html b/client/enduser/src/main/resources/META-INF/resources/app/index.html
index cd4542a..2613a6e 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/index.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/index.html
@@ -100,6 +100,7 @@ under the License.
   <script src="js/services/groupService.js"></script>
   <script src="js/services/anyService.js"></script>
   <script src="js/services/saml2IdPService.js"></script>
+  <script src="js/services/oidcProviderService.js"></script>
   <script src="js/services/saml2SPService.js"></script>
   <!--controllers-->
   <script src="js/controllers/HomeController.js"></script>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
index 3ae4f9d..1019cad 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
@@ -341,7 +341,8 @@ app.run(['$rootScope', '$location', '$state', 'AuthService',
     };
   }]);
 app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'InfoService', 'SAML2IdPService',
-  function ($scope, $rootScope, $location, InfoService, SAML2IdPService) {
+  'OIDCProviderService',
+  function ($scope, $rootScope, $location, InfoService, SAML2IdPService, OIDCProviderService) {
     $scope.initApplication = function () {
       /* 
        * disable by default wizard buttons in self-registration
@@ -377,6 +378,10 @@ app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'I
         available: [],
         selected: {}
       };
+      $rootScope.oidcops = {
+        available: [],
+        selected: {}
+      };
 
       InfoService.getInfo().then(
               function (response) {
@@ -406,6 +411,19 @@ app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'I
       /* 
        * configuration getters
        */
+
+      /* <Extensions> */
+      OIDCProviderService.getAvailableOIDCProviders().then(
+              function (response) {
+                $rootScope.oidcops.available = response;
+              },
+              function (response) {
+                console.debug("No OIDC Client extension available", response);
+              });
+      /* </Extensions> */
+      /* 
+       * configuration getters
+       */
       $rootScope.isSelfRegAllowed = function () {
         return $rootScope.selfRegAllowed === true;
       };
@@ -418,6 +436,13 @@ app.controller('ApplicationController', ['$scope', '$rootScope', '$location', 'I
       $rootScope.saml2login = function () {
         window.location.href = '../saml2sp/login?idp=' + $rootScope.saml2idps.selected.entityID;
       };
+
+      $rootScope.oidcclientExtAvailable = function () {
+        return $rootScope.oidcops.available.length > 0;
+      };
+      $rootScope.oidclogin = function () {
+        window.location.href = '../oidcclient/login?op=' + $rootScope.oidcops.selected.name;
+      };
       $rootScope.getVersion = function () {
         return $rootScope.version;
       };

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
index b58aced..d2b755c 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/LoginController.js
@@ -34,6 +34,8 @@ angular.module("login").controller("LoginController", ['$scope', '$rootScope', '
         $scope.credentials.errorMessage = '';
         // reset SAML 2.0 entityID
         $rootScope.saml2idps.selected.entityID = null;
+        // reset OIDC name
+        $rootScope.oidcops.selected.name = null;
         // got to update page
         $location.path("/self/update");
       }, function (response) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/client/enduser/src/main/resources/META-INF/resources/app/js/services/oidcProviderService.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/services/oidcProviderService.js b/client/enduser/src/main/resources/META-INF/resources/app/js/services/oidcProviderService.js
new file mode 100644
index 0000000..d2ce926
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/services/oidcProviderService.js
@@ -0,0 +1,42 @@
+/* 
+ * 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.
+ */
+
+'use strict';
+
+angular.module('self')
+        .factory('OIDCProviderService', ['$resource', '$q', '$http',
+          function ($resource, $q, $http) {
+
+            var oidcProviderService = {};
+
+            oidcProviderService.getAvailableOIDCProviders = function () {
+              return $http.get("../api/oidcProviders")
+                      .then(function (response) {
+                        return response.data;
+                      }, function (response) {
+                        console.error("Something went wrong during oidcProviders extension retrieval, exit with status: ",
+                                response);
+                        return $q.reject(response.data || response.statusText);
+                      });
+            };
+
+            return oidcProviderService;
+          }]);
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/self.html b/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
index 1cc279e..cedb21c 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/self.html
@@ -62,6 +62,14 @@ under the License.
                           ng-model="saml2idps.selected" ng-change="saml2login()">
                   </select>
                 </div>
+                <div class="form-group">
+                  <span ng-if="$root.oidcclientExtAvailable()">OpenID Connect</span>
+                  <select id="oiscclient" style="width: 100%; text-align: left;" class="btn dropdown-toggle btn-default"
+                          ng-if="$root.oidcclientExtAvailable()"
+                          ng-options="op.name for op in oidcops.available track by op.name"
+                          ng-model="oidcops.selected" ng-change="oidclogin()">
+                  </select>
+                </div>
               </fieldset>
             </form>
           </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/deb/console/pom.xml
----------------------------------------------------------------------
diff --git a/deb/console/pom.xml b/deb/console/pom.xml
index 3a4df52..85c6495 100644
--- a/deb/console/pom.xml
+++ b/deb/console/pom.xml
@@ -55,6 +55,11 @@ under the License.
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.apache.syncope.ext.scimv2</groupId>
       <artifactId>syncope-ext-scimv2-client-console</artifactId>
       <version>${project.version}</version>
@@ -116,6 +121,14 @@ under the License.
         <filtering>true</filtering>
       </resource>
       <resource>
+        <directory>${project.basedir}/../../ext/oidcclient/agent/src/main/resources</directory>
+        <includes>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+        <targetPath>${project.build.directory}/etc</targetPath>
+        <filtering>true</filtering>
+      </resource>
+      <resource>
         <directory>${basedir}/../../fit/console-reference/src/main/resources</directory>
         <includes>
           <include>log4j2.xml</include>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/deb/console/src/deb/control/conffiles
----------------------------------------------------------------------
diff --git a/deb/console/src/deb/control/conffiles b/deb/console/src/deb/control/conffiles
index 47e5201..a9bfb08 100644
--- a/deb/console/src/deb/control/conffiles
+++ b/deb/console/src/deb/control/conffiles
@@ -1,3 +1,5 @@
 /etc/tomcat8/Catalina/localhost/syncope-console.xml
 /etc/apache-syncope/console.properties
 /etc/apache-syncope/saml2sp-agent.properties
+/etc/apache-syncope/oidcclient-agent.properties
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/deb/enduser/pom.xml
----------------------------------------------------------------------
diff --git a/deb/enduser/pom.xml b/deb/enduser/pom.xml
index 322b478..c27bf75 100644
--- a/deb/enduser/pom.xml
+++ b/deb/enduser/pom.xml
@@ -44,6 +44,11 @@ under the License.
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.apache.syncope.client</groupId>
       <artifactId>syncope-client-enduser</artifactId>
       <version>${project.version}</version>
@@ -106,6 +111,14 @@ under the License.
         <filtering>true</filtering>
       </resource>
       <resource>
+        <directory>${project.basedir}/../../ext/oidcclient/agent/src/main/resources</directory>
+        <includes>
+          <include>oidcclient-agent.properties</include>
+        </includes>
+        <targetPath>${project.build.directory}/etc</targetPath>
+        <filtering>true</filtering>
+      </resource>
+      <resource>
         <directory>${basedir}/../../fit/enduser-reference/src/main/resources</directory>
         <includes>
           <include>log4j2.xml</include>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/deb/enduser/src/deb/control/conffiles
----------------------------------------------------------------------
diff --git a/deb/enduser/src/deb/control/conffiles b/deb/enduser/src/deb/control/conffiles
index 934c7fb..8fb2453 100644
--- a/deb/enduser/src/deb/control/conffiles
+++ b/deb/enduser/src/deb/control/conffiles
@@ -2,3 +2,5 @@
 /etc/apache-syncope/enduser.properties
 /etc/apache-syncope/customForm.json
 /etc/apache-syncope/saml2sp-agent.properties
+/etc/apache-syncope/oidcclient-agent.properties
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/deb/enduser/src/deb/control/preinst
----------------------------------------------------------------------
diff --git a/deb/enduser/src/deb/control/preinst b/deb/enduser/src/deb/control/preinst
index afdca38..0e88785 100644
--- a/deb/enduser/src/deb/control/preinst
+++ b/deb/enduser/src/deb/control/preinst
@@ -16,3 +16,4 @@
 # specific language governing permissions and limitations
 # under the License.
 dpkg-divert --add --package apache-syncope-enduser --rename --divert /etc/apache-syncope/saml2sp-agent.properties.disabled /etc/apache-syncope/saml2sp-agent.properties
+dpkg-divert --add --package apache-syncope-enduser --rename --divert /etc/apache-syncope/oidcclient-agent.properties.disabled /etc/apache-syncope/oidcclient-agent.properties

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/pom.xml b/ext/oidcclient/agent/pom.xml
new file mode 100644
index 0000000..b4c218e
--- /dev/null
+++ b/ext/oidcclient/agent/pom.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Agent</name>
+  <description>Apache Syncope Ext: OIDC Client Agent</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-agent</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency> 
+      <groupId>javax.servlet</groupId> 
+      <artifactId>javax.servlet-api</artifactId> 
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet.jsp</groupId>
+      <artifactId>javax.servlet.jsp-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>jstl</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+      
+      <resource>
+        <directory>${basedir}</directory>
+        <targetPath>META-INF</targetPath>
+        <includes>
+          <include>LICENSE</include>
+          <include>NOTICE</include>
+        </includes>
+      </resource>
+    </resources>
+  </build>
+  
+  <profiles>
+    <profile>
+      <id>apache-release</id>
+
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-source-plugin</artifactId>
+            <inherited>false</inherited>
+            <executions>
+              <execution>
+                <id>attach-sources</id>
+                <goals>
+                  <goal>jar-no-fork</goal>
+                </goals>
+                <configuration>
+                  <includes>
+                    <include>${basedir}/LICENSE</include>
+                    <include>${basedir}/NOTICE</include>
+                  </includes>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/CodeConsumer.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/CodeConsumer.java b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/CodeConsumer.java
new file mode 100644
index 0000000..08e9d23
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/CodeConsumer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.ext.oidcclient.agent;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.OIDCConstants;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@WebServlet(name = "codeConsumer", urlPatterns = { "/oidcclient/code-consumer" })
+public class CodeConsumer extends HttpServlet {
+
+    private static final long serialVersionUID = 968480296813639041L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(CodeConsumer.class);
+
+    @Override
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
+            throws ServletException, IOException {
+
+        try {
+            String authorizationCode = request.getParameter(OIDCConstants.CODE);
+            String state = request.getParameter(OIDCConstants.STATE);
+            if (StringUtils.isBlank(authorizationCode) || StringUtils.isBlank(state)) {
+                throw new IllegalArgumentException("Empty " + OIDCConstants.CODE + " or " + OIDCConstants.STATE);
+            }
+            if (state.equals(request.getSession().getAttribute(OIDCConstants.STATE).toString())) {
+                SyncopeClient anonymous = (SyncopeClient) request.getServletContext().
+                        getAttribute(Constants.SYNCOPE_ANONYMOUS_CLIENT);
+
+                OIDCLoginResponseTO responseTO = anonymous.getService(OIDCClientService.class).login(
+                        request.getRequestURL().toString(),
+                        authorizationCode,
+                        request.getSession().getAttribute(OIDCConstants.OP).toString());
+                request.getSession().setAttribute(
+                        Constants.OIDCCLIENTJWT, responseTO.getAccessToken());
+                request.getSession().setAttribute(
+                        Constants.OIDCCLIENTJWT_EXPIRE, responseTO.getAccessTokenExpiryTime());
+
+                String successURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_SUCCESS_URL);
+                if (successURL == null) {
+                    request.setAttribute("responseTO", responseTO);
+                    request.getRequestDispatcher("loginSuccess.jsp").forward(request, response);
+                } else {
+                    response.sendRedirect(successURL);
+                }
+            } else {
+                throw new IllegalArgumentException("Invalid " + OIDCConstants.STATE + " provided");
+            }
+        } catch (Exception e) {
+            LOG.error("While processing authentication response from OP", e);
+            String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_ERROR_URL);
+
+            if (errorURL == null) {
+                request.setAttribute("exception", e);
+                request.getRequestDispatcher("loginError.jsp").forward(request, response);
+
+                e.printStackTrace(response.getWriter());
+            } else {
+                response.sendRedirect(errorURL + "?errorMessage="
+                        + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Constants.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Constants.java b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Constants.java
new file mode 100644
index 0000000..1ff3327
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Constants.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.ext.oidcclient.agent;
+
+public final class Constants {
+
+    public static final String SYNCOPE_ANONYMOUS_CLIENT = "SyncopeAnonymousClient";
+
+    public static final String SYNCOPE_CLIENT_FACTORY = "SyncopeClientFactory";
+
+    public static final String PARAM_OP = "op";
+
+    public static final String CONTEXT_PARAM_LOGIN_SUCCESS_URL = "oidcclient.login.success.url";
+
+    public static final String CONTEXT_PARAM_LOGIN_ERROR_URL = "oidcclient.login.error.url";
+
+    public static final String OIDCCLIENTJWT = "oidcclient.jwt";
+
+    public static final String OIDCCLIENTJWT_EXPIRE = "oidcclient.jwt.expire";
+
+    private Constants() {
+        // private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Login.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Login.java b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Login.java
new file mode 100644
index 0000000..36850e4
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/Login.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.ext.oidcclient.agent;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriBuilder;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.OIDCConstants;
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@WebServlet(name = "oidcclientLogin", urlPatterns = { "/oidcclient/login" })
+public class Login extends HttpServlet {
+
+    private static final long serialVersionUID = 968480296813639041L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(Login.class);
+
+    @Override
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
+            throws ServletException, IOException {
+
+        String op = request.getParameter(Constants.PARAM_OP);
+
+        SyncopeClient anonymous = (SyncopeClient) request.getServletContext().
+                getAttribute(Constants.SYNCOPE_ANONYMOUS_CLIENT);
+        try {
+            String redirectURI = StringUtils.substringBefore(request.getRequestURL().toString(), "/login")
+                    + "/code-consumer";
+            OIDCLoginRequestTO requestTO = anonymous.getService(OIDCClientService.class).
+                    createLoginRequest(redirectURI, op);
+
+            request.getSession().setAttribute(OIDCConstants.STATE, requestTO.getState());
+            request.getSession().setAttribute(OIDCConstants.OP, op);
+
+            response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache, no-store");
+            response.setHeader("Pragma", "no-cache");
+            response.setStatus(HttpServletResponse.SC_SEE_OTHER);
+
+            UriBuilder ub = UriBuilder.fromUri(requestTO.getProviderAddress());
+            ub.queryParam(OIDCConstants.CLIENT_ID, requestTO.getClientId());
+            ub.queryParam(OIDCConstants.REDIRECT_URI, requestTO.getRedirectURI());
+            ub.queryParam(OIDCConstants.RESPOSNSE_TYPE, requestTO.getResponseType());
+            ub.queryParam(OIDCConstants.SCOPE, requestTO.getScope());
+            ub.queryParam(OIDCConstants.STATE, requestTO.getState());
+            response.setHeader("Location", ub.build().toASCIIString());
+        } catch (Exception e) {
+            LOG.error("While preparing the Authentication Request", e);
+
+            String errorURL = getServletContext().getInitParameter(Constants.CONTEXT_PARAM_LOGIN_ERROR_URL);
+            if (errorURL == null) {
+                request.setAttribute("exception", e);
+                request.getRequestDispatcher("loginError.jsp").forward(request, response);
+
+                e.printStackTrace(response.getWriter());
+            } else {
+                response.sendRedirect(errorURL + "?errorMessage="
+                        + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/OIDCClientAgentSetup.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/OIDCClientAgentSetup.java b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/OIDCClientAgentSetup.java
new file mode 100644
index 0000000..887d0d6
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/java/org/apache/syncope/ext/oidcclient/agent/OIDCClientAgentSetup.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.ext.oidcclient.agent;
+
+import java.util.Properties;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.lib.PropertyUtils;
+
+@WebListener
+public class OIDCClientAgentSetup implements ServletContextListener {
+
+    private static final String OIDCCLIENT_AGENT_PROPERTIES = "oidcclient-agent.properties";
+
+    private static <T> T assertNotNull(final T argument, final String name) {
+        if (argument == null) {
+            throw new IllegalArgumentException("Argument '" + name + "' may not be null.");
+        }
+        return argument;
+    }
+
+    @Override
+    public void contextInitialized(final ServletContextEvent sce) {
+        // read oidcclientagent.properties
+        Properties props = PropertyUtils.read(getClass(), OIDCCLIENT_AGENT_PROPERTIES, "conf.directory").getLeft();
+
+        String anonymousUser = props.getProperty("anonymousUser");
+        assertNotNull(anonymousUser, "<anonymousUser>");
+        String anonymousKey = props.getProperty("anonymousKey");
+        assertNotNull(anonymousKey, "<anonymousKey>");
+
+        String scheme = props.getProperty("scheme");
+        assertNotNull(scheme, "<scheme>");
+        String host = props.getProperty("host");
+        assertNotNull(host, "<host>");
+        String port = props.getProperty("port");
+        assertNotNull(port, "<port>");
+        String rootPath = props.getProperty("rootPath");
+        assertNotNull(rootPath, "<rootPath>");
+        String useGZIPCompression = props.getProperty("useGZIPCompression");
+        assertNotNull(useGZIPCompression, "<useGZIPCompression>");
+
+        SyncopeClientFactoryBean clientFactory = new SyncopeClientFactoryBean().
+                setAddress(scheme + "://" + host + ":" + port + "/" + rootPath).
+                setUseCompression(BooleanUtils.toBoolean(useGZIPCompression));
+
+        sce.getServletContext().setAttribute(Constants.SYNCOPE_CLIENT_FACTORY, clientFactory);
+        sce.getServletContext().setAttribute(
+                Constants.SYNCOPE_ANONYMOUS_CLIENT,
+                clientFactory.create(new AnonymousAuthenticationHandler(anonymousUser, anonymousKey)));
+    }
+
+    @Override
+    public void contextDestroyed(final ServletContextEvent sce) {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginError.jsp
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginError.jsp b/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginError.jsp
new file mode 100644
index 0000000..b3c80d9
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginError.jsp
@@ -0,0 +1,35 @@
+<%--
+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.
+--%>
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%
+    Exception exception = (Exception) request.getAttribute("exception");
+%>
+<html>
+  <head>
+    <title>Apache Syncope ${syncope.version} - OIDC CLIENT - Login Error</title>
+  </head>
+  <body>
+    <h1>An error was found</h1>
+
+    <h2><%=exception.getMessage()%></h2>
+    <pre>
+      <%exception.printStackTrace(new java.io.PrintWriter(out));%>
+    </pre>
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginSuccess.jsp
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginSuccess.jsp b/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginSuccess.jsp
new file mode 100644
index 0000000..7361842
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/resources/META-INF/resources/oidcclient/loginSuccess.jsp
@@ -0,0 +1,33 @@
+<%--
+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.
+--%>
+<%@page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@page import="org.apache.syncope.common.lib.to.OIDCLoginResponseTO"%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<c:set var="responseTO" value="${requestScope['responseTO']}"/>
+<html>
+  <head>
+    <title>Apache Syncope ${syncope.version} - OIDC CLIENT - Successful Login</title>
+  </head>
+  <body>
+    <h1>Welcome ${responseTO.userName}</h1>
+
+    <p>You have been successfully authenticated by the requested OIDC Provider</p>
+
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/src/main/resources/META-INF/web-fragment.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/resources/META-INF/web-fragment.xml b/ext/oidcclient/agent/src/main/resources/META-INF/web-fragment.xml
new file mode 100644
index 0000000..fab858a
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/resources/META-INF/web-fragment.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<web-fragment xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+                                  http://xmlns.jcp.org/xml/ns/javaee/web-fragment_3_1.xsd"
+              id="${pom.artifactId}" version="3.1">
+
+</web-fragment>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/agent/src/main/resources/oidcclient-agent.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/agent/src/main/resources/oidcclient-agent.properties b/ext/oidcclient/agent/src/main/resources/oidcclient-agent.properties
new file mode 100644
index 0000000..d3fee30
--- /dev/null
+++ b/ext/oidcclient/agent/src/main/resources/oidcclient-agent.properties
@@ -0,0 +1,26 @@
+# 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.
+conf.directory=${conf.directory}
+
+anonymousUser=${anonymousUser}
+anonymousKey=${anonymousKey}
+
+scheme=http
+host=localhost
+port=8080
+rootPath=/syncope/rest/
+useGZIPCompression=true

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/pom.xml b/ext/oidcclient/client-console/pom.xml
new file mode 100644
index 0000000..59ba01a
--- /dev/null
+++ b/ext/oidcclient/client-console/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Console</name>
+  <description>Apache Syncope Ext: OIDC Client Console</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-console</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClient.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClient.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClient.java
new file mode 100644
index 0000000..f588733
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClient.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.pages;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.annotations.ExtPage;
+import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel;
+import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+@ExtPage(label = "OIDC Client", icon = "fa-openid ", listEntitlement = OIDCClientEntitlement.OP_READ, priority = 100)
+public class OIDCClient extends BaseExtPage {
+
+    private static final long serialVersionUID = -599601954212606001L;
+
+    public OIDCClient(final PageParameters parameters) {
+        super(parameters);
+
+        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", Dashboard.class));
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList()));
+        body.add(content);
+    }
+
+    private List<ITab> buildTabList() {
+
+        final List<ITab> tabs = new ArrayList<>(1);
+
+        tabs.add(new AbstractTab(new ResourceModel("op")) {
+            private static final long serialVersionUID = -6815067322125799251L;
+
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new OIDCProvidersDirectoryPanel(panelId, getPageReference());
+            }
+        });
+
+        return tabs;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClientLogin.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClientLogin.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClientLogin.java
new file mode 100644
index 0000000..41b6cdc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/pages/OIDCClientLogin.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+
+import org.apache.wicket.authentication.IAuthenticationStrategy;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OIDCClientLogin extends WebPage {
+
+    private static final long serialVersionUID = 8581614051773949262L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCClientLogin.class);
+
+    private static final String OIDC_ACCESS_ERROR = "OIDC access error";
+
+    public OIDCClientLogin(final PageParameters parameters) {
+        super(parameters);
+
+        String token = (String) ((ServletWebRequest) getRequest()).getContainerRequest().
+                getSession().getAttribute(org.apache.syncope.ext.oidcclient.agent.Constants.OIDCCLIENTJWT);
+        if (StringUtils.isBlank(token)) {
+            LOG.error("No JWT found, redirecting to default greeter");
+
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", OIDC_ACCESS_ERROR);
+            setResponsePage(Login.class, params);
+        }
+
+        IAuthenticationStrategy strategy = getApplication().getSecuritySettings().getAuthenticationStrategy();
+
+        if (SyncopeConsoleSession.get().authenticate(token)) {
+
+            // If login has been called because the user was not yet logged in, than continue to the
+            // original destination, otherwise to the Home page
+            continueToOriginalDestination();
+            setResponsePage(getApplication().getHomePage());
+        } else {
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", OIDC_ACCESS_ERROR);
+            setResponsePage(Login.class, params);
+        }
+        strategy.remove();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
new file mode 100644
index 0000000..dd66cc6
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel.OIDCProvidersProvider;
+import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.rest.OIDCProviderRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.client.console.wizards.OIDCProviderWizardBuilder;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.console.wizards.any.AnyWrapper;
+import org.apache.syncope.client.console.wizards.any.UserTemplateWizardBuilder;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+
+public class OIDCProvidersDirectoryPanel extends DirectoryPanel<
+        OIDCProviderTO, OIDCProviderTO, OIDCProvidersProvider, OIDCProviderRestClient> {
+
+    private static final long serialVersionUID = -1356497878858616714L;
+
+    private static final String PREF_OIDC_PROVIDERS_PAGINATOR_ROWS = "oidc.providers.paginator.rows";
+
+    private final BaseModal<Serializable> templateModal;
+
+    public OIDCProvidersDirectoryPanel(final String id, final PageReference pageRef) {
+        super(id, new Builder<OIDCProviderTO, OIDCProviderTO, OIDCProviderRestClient>(new OIDCProviderRestClient(),
+                pageRef) {
+
+            private static final long serialVersionUID = -5542535388772406165L;
+
+            @Override
+            protected WizardMgtPanel<OIDCProviderTO> newInstance(final String id, final boolean wizardInModal) {
+                throw new UnsupportedOperationException();
+            }
+        }.disableCheckBoxes());
+
+        this.addNewItemPanelBuilder(new OIDCProviderWizardBuilder(this, new OIDCProviderTO(), pageRef), true);
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, OIDCClientEntitlement.OP_CREATE);
+
+        modal.size(Modal.Size.Large);
+
+        actionTogglePanel = new ActionLinksTogglePanel<OIDCProviderTO>("outer", pageRef) {
+
+            private static final long serialVersionUID = -7688359318035249200L;
+
+            @Override
+            public void toggleWithContent(
+                    final AjaxRequestTarget target,
+                    final ActionsPanel<OIDCProviderTO> actionsPanel,
+                    final OIDCProviderTO modelObject) {
+
+                super.toggleWithContent(target, actionsPanel, modelObject);
+                setHeader(target, StringUtils.abbreviate(modelObject.getName(), 25));
+                this.toggle(target, true);
+            }
+
+        };
+        addOuterObject(actionTogglePanel);
+
+        templateModal = new BaseModal<Serializable>("outer") {
+
+            private static final long serialVersionUID = 5787433530654262016L;
+
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+                setFooterVisible(false);
+            }
+        };
+        templateModal.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
+
+            private static final long serialVersionUID = 8804221891699487139L;
+
+            @Override
+            public void onClose(final AjaxRequestTarget target) {
+//                target.add(content);
+                templateModal.show(false);
+            }
+        });
+        templateModal.size(Modal.Size.Large);
+        addOuterObject(templateModal);
+
+        initResultTable();
+
+    }
+
+    @Override
+    protected OIDCProvidersProvider dataProvider() {
+        return new OIDCProvidersProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return PREF_OIDC_PROVIDERS_PAGINATOR_ROWS;
+
+    }
+
+    @Override
+    protected List<IColumn<OIDCProviderTO, String>> getColumns() {
+        List<IColumn<OIDCProviderTO, String>> columns = new ArrayList<>();
+        columns.add(new KeyPropertyColumn<OIDCProviderTO>(new ResourceModel("key"), "key", "key"));
+        columns.add(new PropertyColumn<OIDCProviderTO, String>(new ResourceModel("name"), "name", "name"));
+        columns.add(new PropertyColumn<OIDCProviderTO, String>(new ResourceModel("issuer"), "issuer", "issuer"));
+        columns.add(new PropertyColumn<OIDCProviderTO, String>(new ResourceModel("clientID"), "clientID", "clientID"));
+        return columns;
+
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+
+    }
+
+    @Override
+    public ActionsPanel<OIDCProviderTO> getActions(final IModel<OIDCProviderTO> model) {
+        final ActionsPanel<OIDCProviderTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<OIDCProviderTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final OIDCProviderTO ignore) {
+                OIDCProviderTO object = restClient.read(model.getObject().getKey());
+                send(OIDCProvidersDirectoryPanel.this, Broadcast.EXACT,
+                        new AjaxWizard.EditItemActionEvent<>(object, target));
+                modal.header(Model.of(StringUtils.capitalize(("Edit " + object.getName()))));
+            }
+        }, ActionLink.ActionType.EDIT, OIDCClientEntitlement.OP_UPDATE);
+
+        panel.add(new ActionLink<OIDCProviderTO>() {
+
+            private static final long serialVersionUID = 8557679125857348178L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final OIDCProviderTO ignore) {
+                final OIDCProviderTO object = restClient.read(model.getObject().getKey());
+
+                UserTemplateWizardBuilder builder = new UserTemplateWizardBuilder(
+                        object.getUserTemplate(),
+                        new AnyTypeRestClient().read(AnyTypeKind.USER.name()).getClasses(),
+                        new UserFormLayoutInfo(),
+                        pageRef) {
+
+                    private static final long serialVersionUID = -7978723352517770634L;
+
+                    @Override
+                    protected Serializable onApplyInternal(final AnyWrapper<UserTO> modelObject) {
+                        object.setUserTemplate(modelObject.getInnerObject());
+                        restClient.update(object);
+
+                        return modelObject;
+                    }
+                };
+                templateModal.header(Model.of(StringUtils.capitalize(
+                        new StringResourceModel("template.title", OIDCProvidersDirectoryPanel.this).getString())));
+                templateModal.setContent(builder.build(BaseModal.CONTENT_ID));
+                templateModal.show(true);
+                target.add(templateModal);
+
+            }
+        }, ActionLink.ActionType.TEMPLATE, OIDCClientEntitlement.OP_UPDATE);
+
+        panel.add(new ActionLink<OIDCProviderTO>() {
+
+            private static final long serialVersionUID = -5467832321897812767L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final OIDCProviderTO ignore) {
+                try {
+                    restClient.delete(model.getObject().getKey());
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting object {}", model.getObject().getKey(), e);
+                    SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, OIDCClientEntitlement.OP_DELETE, true);
+        return panel;
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        super.onEvent(event);
+
+        if (event.getPayload() instanceof AjaxWizard.NewItemEvent) {
+            AjaxWizard.NewItemEvent<?> newItemEvent = AjaxWizard.NewItemEvent.class.cast(event.getPayload());
+            WizardModalPanel<?> modalPanel = newItemEvent.getModalPanel();
+
+            if (event.getPayload() instanceof AjaxWizard.NewItemActionEvent && modalPanel != null) {
+                final IModel<Serializable> model = new CompoundPropertyModel<>(modalPanel.getItem());
+                templateModal.setFormModel(model);
+                templateModal.header(newItemEvent.getResourceModel());
+                newItemEvent.getTarget().add(templateModal.setContent(modalPanel));
+                templateModal.show(true);
+            } else if (event.getPayload() instanceof AjaxWizard.NewItemCancelEvent) {
+                templateModal.close(newItemEvent.getTarget());
+            } else if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
+                templateModal.close(newItemEvent.getTarget());
+            }
+        }
+    }
+
+    protected final class OIDCProvidersProvider extends DirectoryDataProvider<OIDCProviderTO> {
+
+        private static final long serialVersionUID = -2865055116864423761L;
+
+        private final SortableDataProviderComparator<OIDCProviderTO> comparator;
+
+        public OIDCProvidersProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort("name", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<OIDCProviderTO> iterator(final long first, final long count) {
+            List<OIDCProviderTO> list = restClient.list();
+            Collections.sort(list, comparator);
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.list().size();
+        }
+
+        @Override
+        public IModel<OIDCProviderTO> model(final OIDCProviderTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.java
new file mode 100644
index 0000000..5b108e5
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.UrlUtils;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.handler.RedirectRequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OIDCSSOLoginFormPanel extends SSOLoginFormPanel {
+
+    private static final long serialVersionUID = 1154933731474854671L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCSSOLoginFormPanel.class);
+
+    public OIDCSSOLoginFormPanel(final String id) {
+        super(id);
+
+        List<OIDCProviderTO> available =
+                SyncopeConsoleSession.get().getAnonymousClient().getService(OIDCProviderService.class).list();
+
+        final Model<OIDCProviderTO> model = new Model<>();
+        AjaxDropDownChoicePanel<OIDCProviderTO> ops =
+                new AjaxDropDownChoicePanel<>("ops", "OpenID Connect", model, false);
+        ops.setChoices(available);
+        ops.setChoiceRenderer(new IChoiceRenderer<OIDCProviderTO>() {
+
+            private static final long serialVersionUID = 1814750973898916102L;
+
+            @Override
+            public Object getDisplayValue(final OIDCProviderTO object) {
+                return object.getName();
+            }
+
+            @Override
+            public String getIdValue(final OIDCProviderTO object, final int index) {
+                return object.getName();
+            }
+
+            @Override
+            public OIDCProviderTO getObject(final String id,
+                    final IModel<? extends List<? extends OIDCProviderTO>> choices) {
+                return IterableUtils.find(choices.getObject(), new Predicate<OIDCProviderTO>() {
+
+                    @Override
+                    public boolean evaluate(final OIDCProviderTO object) {
+                        return object.getName().equals(id);
+                    }
+                });
+            }
+        });
+
+        ops.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                if (model.getObject() != null) {
+                    try {
+                        RequestCycle.get().scheduleRequestHandlerAfterCurrent(new RedirectRequestHandler(
+                                UrlUtils.rewriteToContextRelative("oidcclient/login?op="
+                                        + URLEncoder.encode(
+                                                model.getObject().getName(), StandardCharsets.UTF_8.name()),
+                                        RequestCycle.get())));
+                    } catch (Exception e) {
+                        LOG.error("Could not redirect to the selected OP {}", model.getObject().getName(), e);
+                    }
+                }
+            }
+        });
+        ops.setOutputMarkupPlaceholderTag(true);
+        ops.setVisible(!available.isEmpty());
+        add(ops);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
new file mode 100644
index 0000000..a666d74
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/rest/OIDCProviderRestClient.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.rest;
+
+import java.util.List;
+import java.util.Set;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+
+public class OIDCProviderRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -4006712447589576324L;
+
+    public List<OIDCProviderTO> list() {
+        return getService(OIDCProviderService.class).list();
+    }
+
+    public Set<String> getActionsClasses() {
+        return getService(OIDCProviderService.class).getActionsClasses();
+    }
+
+    public void create(final OIDCProviderTO op) {
+        SyncopeConsoleSession.get().getService(OIDCProviderService.class).create(op);
+    }
+
+    public void createFromDiscovery(final OIDCProviderTO op) {
+        SyncopeConsoleSession.get().getService(OIDCProviderService.class).createFromDiscovery(op);
+    }
+
+    public OIDCProviderTO read(final String key) {
+        return getService(OIDCProviderService.class).read(key);
+    }
+
+    public void update(final OIDCProviderTO op) {
+        getService(OIDCProviderService.class).update(op);
+    }
+
+    public void delete(final String key) {
+        getService(OIDCProviderService.class).delete(key);
+    }
+
+}


[7/8] syncope git commit: [SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser - This closes #74

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderMappingPanel.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderMappingPanel.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderMappingPanel.java
new file mode 100644
index 0000000..d12965e
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderMappingPanel.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.wizards;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wizards.resources.ItemTransformersTogglePanel;
+import org.apache.syncope.client.console.wizards.resources.JEXLTransformersTogglePanel;
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.util.ListModel;
+
+public class OIDCProviderMappingPanel extends AbstractMappingPanel {
+
+    private static final long serialVersionUID = -4123879435574382968L;
+
+    public OIDCProviderMappingPanel(
+            final String id,
+            final OIDCProviderTO opTO,
+            final ItemTransformersTogglePanel mapItemTransformers,
+            final JEXLTransformersTogglePanel jexlTransformers) {
+
+        super(id,
+                mapItemTransformers,
+                jexlTransformers,
+                new ListModel<ItemTO>(opTO.getItems()),
+                true,
+                true,
+                MappingPurpose.NONE);
+
+        setOutputMarkupId(true);
+    }
+
+    @Override
+    protected void onBeforeRender() {
+        super.onBeforeRender();
+        purposeLabel.setVisible(false);
+    }
+
+    @Override
+    protected IModel<List<String>> getExtAttrNames() {
+        List<String> extAttrNames = new ArrayList<>();
+        extAttrNames.add("email");
+        extAttrNames.add("family_name");
+        extAttrNames.add("name");
+        extAttrNames.add("middle_name");
+        extAttrNames.add("given_name");
+        extAttrNames.add("preferred_username");
+        extAttrNames.add("nickname");
+        extAttrNames.add("profile");
+        extAttrNames.add("gender");
+        extAttrNames.add("locale");
+        extAttrNames.add("zoneinfo");
+        extAttrNames.add("birthdate");
+        extAttrNames.add("phone_number");
+        extAttrNames.add("address");
+        extAttrNames.add("updated_at");
+
+        return Model.ofList(extAttrNames);
+    }
+
+    @Override
+    protected void setAttrNames(final AjaxTextFieldPanel toBeUpdated) {
+        toBeUpdated.setRequired(true);
+        toBeUpdated.setEnabled(true);
+
+        List<String> choices = new ArrayList<>(USER_FIELD_NAMES);
+
+        for (AnyTypeClassTO anyTypeClassTO : anyTypeClassRestClient.list(
+                anyTypeRestClient.read(AnyTypeKind.USER.name()).getClasses())) {
+
+            choices.addAll(anyTypeClassTO.getPlainSchemas());
+            choices.addAll(anyTypeClassTO.getDerSchemas());
+            choices.addAll(anyTypeClassTO.getVirSchemas());
+        }
+
+        Collections.sort(choices);
+        toBeUpdated.setChoices(choices);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
new file mode 100644
index 0000000..2fa465a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
@@ -0,0 +1,290 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.console.wizards;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel;
+import org.apache.syncope.client.console.rest.OIDCProviderRestClient;
+import org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wizards.resources.ItemTransformersTogglePanel;
+import org.apache.syncope.client.console.wizards.resources.JEXLTransformersTogglePanel;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.model.util.ListModel;
+import org.apache.wicket.validation.validator.UrlValidator;
+
+public class OIDCProviderWizardBuilder extends AjaxWizardBuilder<OIDCProviderTO> {
+
+    private static final long serialVersionUID = -3310772400714122768L;
+
+    private final OIDCProviderRestClient restClient = new OIDCProviderRestClient();
+
+    private final OIDCProvidersDirectoryPanel directoryPanel;
+
+    private final IModel<List<String>> actionsClasses = new LoadableDetachableModel<List<String>>() {
+
+        private static final long serialVersionUID = 5275935387613157437L;
+
+        @Override
+        protected List<String> load() {
+            return new ArrayList<>(restClient.getActionsClasses());
+        }
+    };
+
+    public OIDCProviderWizardBuilder(final OIDCProvidersDirectoryPanel directoryPanel, final OIDCProviderTO defaultItem,
+            final PageReference pageRef) {
+        super(defaultItem, pageRef);
+        this.directoryPanel = directoryPanel;
+    }
+
+    @Override
+    protected Serializable onApplyInternal(final OIDCProviderTO modelObject) {
+        if (modelObject.getKey() == null) {
+            if (modelObject.getHasDiscovery()) {
+                restClient.createFromDiscovery(modelObject);
+            } else {
+                restClient.create(modelObject);
+            }
+
+        } else {
+            restClient.update(modelObject);
+        }
+        return modelObject;
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final OIDCProviderTO modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new OP(modelObject));
+        if (modelObject.getKey() == null) {
+            wizardModel.add(new OPContinue(modelObject));
+        } else {
+            wizardModel.add(new OPContinue(modelObject, true));
+        }
+
+        Mapping mapping = new Mapping(modelObject);
+        mapping.setOutputMarkupId(true);
+
+        ItemTransformersTogglePanel mapItemTransformers = new ItemTransformersTogglePanel(mapping, pageRef);
+        addOuterObject(mapItemTransformers);
+        JEXLTransformersTogglePanel jexlTransformers = new JEXLTransformersTogglePanel(mapping, pageRef);
+        addOuterObject(jexlTransformers);
+        mapping.add(new OIDCProviderMappingPanel("mapping", modelObject, mapItemTransformers, jexlTransformers));
+
+        wizardModel.add(mapping);
+
+        return wizardModel;
+    }
+
+    public class OP extends WizardStep {
+
+        private static final long serialVersionUID = 7127421283216134900L;
+
+        public OP(final OIDCProviderTO opTO) {
+            AjaxTextFieldPanel name = new AjaxTextFieldPanel(
+                    "name", "name", new PropertyModel<String>(opTO, "name"), false);
+            name.addRequiredLabel();
+            name.setEnabled(true);
+            add(name);
+
+            AjaxTextFieldPanel clientID = new AjaxTextFieldPanel(
+                    "clientID", "clientID", new PropertyModel<String>(opTO, "clientID"), false);
+            clientID.addRequiredLabel();
+            clientID.setEnabled(true);
+            add(clientID);
+
+            AjaxTextFieldPanel clientSecret = new AjaxTextFieldPanel(
+                    "clientSecret", "clientSecret", new PropertyModel<String>(opTO, "clientSecret"), false);
+            clientSecret.addRequiredLabel();
+            clientSecret.setEnabled(true);
+            add(clientSecret);
+
+            AjaxCheckBoxPanel createUnmatching = new AjaxCheckBoxPanel(
+                    "createUnmatching", "createUnmatching", new PropertyModel<Boolean>(opTO, "createUnmatching"),
+                    false);
+            add(createUnmatching);
+
+            AjaxCheckBoxPanel updateMatching = new AjaxCheckBoxPanel(
+                    "updateMatching", "updateMatching", new PropertyModel<Boolean>(opTO, "updateMatching"), false);
+            add(updateMatching);
+
+            AjaxPalettePanel<String> actionsClassNames = new AjaxPalettePanel.Builder<String>().
+                    setAllowMoveAll(true).setAllowOrder(true).
+                    setName(new StringResourceModel("actionsClassNames", directoryPanel).getString()).
+                    build("actionsClassNames",
+                            new PropertyModel<List<String>>(opTO, "actionsClassNames"),
+                            new ListModel<>(actionsClasses.getObject()));
+            actionsClassNames.setOutputMarkupId(true);
+            add(actionsClassNames);
+        }
+    }
+
+    public class OPContinue extends WizardStep {
+
+        private static final long serialVersionUID = -7087008312629522790L;
+
+        public OPContinue(final OIDCProviderTO opTO) {
+
+            final WebMarkupContainer content = new WebMarkupContainer("content");
+            this.setOutputMarkupId(true);
+            content.setOutputMarkupId(true);
+            add(content);
+
+            UrlValidator urlValidator = new UrlValidator();
+            final AjaxTextFieldPanel issuer = new AjaxTextFieldPanel(
+                    "issuer", "issuer", new PropertyModel<String>(opTO, "issuer"));
+            issuer.addValidator(urlValidator);
+            issuer.addRequiredLabel();
+            content.add(issuer);
+
+            final AjaxCheckBoxPanel hasDiscovery = new AjaxCheckBoxPanel(
+                    "hasDiscovery", "hasDiscovery", new PropertyModel<Boolean>(opTO, "hasDiscovery"));
+            content.add(hasDiscovery);
+
+            final AjaxTextFieldPanel authorizationEndpoint = new AjaxTextFieldPanel("authorizationEndpoint",
+                    "authorizationEndpoint", new PropertyModel<String>(opTO, "authorizationEndpoint"));
+            authorizationEndpoint.addRequiredLabel();
+            authorizationEndpoint.addValidator(urlValidator);
+            content.add(authorizationEndpoint);
+
+            final AjaxTextFieldPanel userinfoEndpoint = new AjaxTextFieldPanel("userinfoEndpoint",
+                    "userinfoEndpoint", new PropertyModel<String>(opTO, "userinfoEndpoint"));
+            userinfoEndpoint.addRequiredLabel();
+            userinfoEndpoint.addValidator(urlValidator);
+            content.add(userinfoEndpoint);
+
+            final AjaxTextFieldPanel tokenEndpoint = new AjaxTextFieldPanel("tokenEndpoint",
+                    "tokenEndpoint", new PropertyModel<String>(opTO, "tokenEndpoint"));
+            tokenEndpoint.addRequiredLabel();
+            tokenEndpoint.addValidator(urlValidator);
+            content.add(tokenEndpoint);
+
+            final AjaxTextFieldPanel jwksUri = new AjaxTextFieldPanel("jwksUri",
+                    "jwksUri", new PropertyModel<String>(opTO, "jwksUri"));
+            jwksUri.addRequiredLabel();
+            jwksUri.addValidator(urlValidator);
+            content.add(jwksUri);
+
+            final WebMarkupContainer visibleParam = new WebMarkupContainer("visibleParams");
+            visibleParam.setOutputMarkupPlaceholderTag(true);
+            visibleParam.add(authorizationEndpoint);
+            visibleParam.add(userinfoEndpoint);
+            visibleParam.add(tokenEndpoint);
+            visibleParam.add(jwksUri);
+            content.add(visibleParam);
+
+            showHide(hasDiscovery, visibleParam);
+
+            hasDiscovery.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    showHide(hasDiscovery, visibleParam);
+                    target.add(visibleParam);
+                }
+            });
+
+        }
+
+        public OPContinue(final OIDCProviderTO opTO, final boolean readOnly) {
+
+            final WebMarkupContainer content = new WebMarkupContainer("content");
+            this.setOutputMarkupId(true);
+            content.setOutputMarkupId(true);
+            add(content);
+
+            final AjaxTextFieldPanel issuer = new AjaxTextFieldPanel(
+                    "issuer", "issuer", new PropertyModel<String>(opTO, "issuer"));
+            issuer.setReadOnly(readOnly);
+            content.add(issuer);
+
+            final AjaxCheckBoxPanel hasDiscovery = new AjaxCheckBoxPanel(
+                    "hasDiscovery", "hasDiscovery", new PropertyModel<Boolean>(opTO, "hasDiscovery"));
+            hasDiscovery.setReadOnly(readOnly);
+            content.add(hasDiscovery);
+
+            final AjaxTextFieldPanel authorizationEndpoint = new AjaxTextFieldPanel("authorizationEndpoint",
+                    "authorizationEndpoint", new PropertyModel<String>(opTO, "authorizationEndpoint"));
+            authorizationEndpoint.setReadOnly(readOnly);
+            content.add(authorizationEndpoint);
+
+            final AjaxTextFieldPanel userinfoEndpoint = new AjaxTextFieldPanel("userinfoEndpoint",
+                    "userinfoEndpoint", new PropertyModel<String>(opTO, "userinfoEndpoint"));
+            userinfoEndpoint.setReadOnly(readOnly);
+            content.add(userinfoEndpoint);
+
+            final AjaxTextFieldPanel tokenEndpoint = new AjaxTextFieldPanel("tokenEndpoint",
+                    "tokenEndpoint", new PropertyModel<String>(opTO, "tokenEndpoint"));
+            tokenEndpoint.setReadOnly(readOnly);
+            content.add(tokenEndpoint);
+
+            final AjaxTextFieldPanel jwksUri = new AjaxTextFieldPanel("jwksUri",
+                    "jwksUri", new PropertyModel<String>(opTO, "jwksUri"));
+            jwksUri.setReadOnly(readOnly);
+            content.add(jwksUri);
+
+            final WebMarkupContainer visibleParam = new WebMarkupContainer("visibleParams");
+            visibleParam.setOutputMarkupPlaceholderTag(true);
+            visibleParam.add(authorizationEndpoint);
+            visibleParam.add(userinfoEndpoint);
+            visibleParam.add(tokenEndpoint);
+            visibleParam.add(jwksUri);
+            content.add(visibleParam);
+        }
+    }
+
+    private void showHide(final AjaxCheckBoxPanel hasDiscovery, final WebMarkupContainer visibleParams) {
+        if (hasDiscovery.getField().getValue().equals("false")) {
+            visibleParams.setVisible(true);
+        } else {
+            visibleParams.setVisible(false);
+        }
+    }
+
+    /**
+     * Mapping definition step.
+     */
+    private static final class Mapping extends WizardStep {
+
+        private static final long serialVersionUID = 3454904947720856253L;
+
+        Mapping(final OIDCProviderTO item) {
+            setTitleModel(Model.of("Mapping"));
+            setSummaryModel(Model.of(StringUtils.EMPTY));
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.html
new file mode 100644
index 0000000..a1a0aed
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:extend>
+    <section class="content-header">
+      <h1>&nbsp;</h1>
+      <ol class="breadcrumb">
+        <li><a wicket:id="dashboardBr"><i class="fa fa-dashboard"></i> <wicket:message key="dashboard"/></a></li>
+        <li class="active"><wicket:message key="extensions"/></li>
+        <li class="active">OIDC Client</li>
+      </ol>
+    </section>
+
+    <section class="content" wicket:id="content">
+      <div class="box">
+        <div class="box-body" wicket:id="tabbedPanel"/>
+      </div>
+    </section>
+  </wicket:extend>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.properties
new file mode 100644
index 0000000..b34f11c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient.properties
@@ -0,0 +1,17 @@
+# 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.
+op=OIDC Providers

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_it.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_it.properties
new file mode 100644
index 0000000..b34f11c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_it.properties
@@ -0,0 +1,17 @@
+# 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.
+op=OIDC Providers

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_pt_BR.properties
new file mode 100644
index 0000000..b34f11c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_pt_BR.properties
@@ -0,0 +1,17 @@
+# 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.
+op=OIDC Providers

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_ru.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_ru.properties
new file mode 100644
index 0000000..b34f11c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/pages/OIDCClient_ru.properties
@@ -0,0 +1,17 @@
+# 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.
+op=OIDC Providers

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.html
new file mode 100644
index 0000000..ce0dbe8
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.html
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:extend>
+
+  </wicket:extend>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
new file mode 100644
index 0000000..569d0dc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.properties
@@ -0,0 +1,30 @@
+# 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.
+clientID=Client ID
+clientSecret=Client Secret
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+any.new=New Provider
+template.title=user template
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
new file mode 100644
index 0000000..569d0dc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_it.properties
@@ -0,0 +1,30 @@
+# 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.
+clientID=Client ID
+clientSecret=Client Secret
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+any.new=New Provider
+template.title=user template
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..569d0dc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_pt_BR.properties
@@ -0,0 +1,30 @@
+# 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.
+clientID=Client ID
+clientSecret=Client Secret
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+any.new=New Provider
+template.title=user template
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
new file mode 100644
index 0000000..569d0dc
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel_ru.properties
@@ -0,0 +1,30 @@
+# 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.
+clientID=Client ID
+clientSecret=Client Secret
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+any.new=New Provider
+template.title=user template
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.html
new file mode 100644
index 0000000..3b30b4c
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/panels/OIDCSSOLoginFormPanel.html
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div class="form-group">
+      <span wicket:id="ops"></span>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$Mapping.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$Mapping.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$Mapping.html
new file mode 100644
index 0000000..11b1a52
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$Mapping.html
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <span wicket:id="mapping" />
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
new file mode 100644
index 0000000..4ad49d8
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.html
@@ -0,0 +1,28 @@
+<!--
+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.  actionsClassNames
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div class="form-group"><span wicket:id="name">[name]</span></div>
+    <div class="form-group"><span wicket:id="clientID">[clientID]</span></div>
+    <div class="form-group"><span wicket:id="clientSecret">[clientSecret]</span></div>
+    <div class="form-group"><span wicket:id="createUnmatching">[createUnmatching]</span></div>
+    <div class="form-group"><span wicket:id="updateMatching">[updateMatching]</span></div>
+    <div class="form-group"><span wicket:id="actionsClassNames">[actionsClassNames]</span></div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
new file mode 100644
index 0000000..5ed531a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP.properties
@@ -0,0 +1,24 @@
+# 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.
+name=Name
+clientID=Client ID
+clientSecret=Client Secret
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.html
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.html b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.html
new file mode 100644
index 0000000..01da6ec
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.html
@@ -0,0 +1,33 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div wicket:id="content">
+      <span wicket:id="issuer">[issuer]</span>
+      <span wicket:id="hasDiscovery">[hasDiscovery]</span>
+
+      <div wicket:id="visibleParams">
+        <span wicket:id="authorizationEndpoint">[authorizationEndpoint]</span>
+        <span wicket:id="tokenEndpoint">[tokenEndpoint]</span>
+        <span wicket:id="jwksUri">[jwksUri]</span>
+        <span wicket:id="userinfoEndpoint">[userinfoEndpoint]</span>
+      </div>
+    </div>
+  </wicket:panel>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.properties
new file mode 100644
index 0000000..3f1d085
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue.properties
@@ -0,0 +1,27 @@
+# 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.
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+tokenEndpoint= Token Endpoint
+jwksUri= JWKS URI
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_it.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_it.properties
new file mode 100644
index 0000000..3f1d085
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_it.properties
@@ -0,0 +1,27 @@
+# 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.
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+tokenEndpoint= Token Endpoint
+jwksUri= JWKS URI
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_pt_BR.properties
new file mode 100644
index 0000000..3f1d085
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_pt_BR.properties
@@ -0,0 +1,27 @@
+# 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.
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+tokenEndpoint= Token Endpoint
+jwksUri= JWKS URI
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_ru.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_ru.properties
new file mode 100644
index 0000000..3f1d085
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OPContinue_ru.properties
@@ -0,0 +1,27 @@
+# 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.
+issuer=Issuer
+hasDiscovery= Has Discovery Document
+authorizationEndpoint= Authorization Endpoint
+userinfoEndpoint= Userinfo Endpoint
+tokenEndpoint= Token Endpoint
+jwksUri= JWKS URI
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
new file mode 100644
index 0000000..5ed531a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_it.properties
@@ -0,0 +1,24 @@
+# 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.
+name=Name
+clientID=Client ID
+clientSecret=Client Secret
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
new file mode 100644
index 0000000..5ed531a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_pt_BR.properties
@@ -0,0 +1,24 @@
+# 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.
+name=Name
+clientID=Client ID
+clientSecret=Client Secret
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
new file mode 100644
index 0000000..5ed531a
--- /dev/null
+++ b/ext/oidcclient/client-console/src/main/resources/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder$OP_ru.properties
@@ -0,0 +1,24 @@
+# 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.
+name=Name
+clientID=Client ID
+clientSecret=Client Secret
+createUnmatching=Create unmatching users
+updateMatching=Update matching users
+actionsClassNames=Actions
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-enduser/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-enduser/pom.xml b/ext/oidcclient/client-enduser/pom.xml
new file mode 100644
index 0000000..c7213c8
--- /dev/null
+++ b/ext/oidcclient/client-enduser/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Enduser</name>
+  <description>Apache Syncope Ext: OIDC Client Enduser</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.client</groupId>
+      <artifactId>syncope-client-enduser</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/OIDCClientLogin.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/OIDCClientLogin.java b/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/OIDCClientLogin.java
new file mode 100644
index 0000000..5aa8578
--- /dev/null
+++ b/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/OIDCClientLogin.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.enduser.pages;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.wicket.authentication.IAuthenticationStrategy;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OIDCClientLogin extends WebPage {
+
+    private static final long serialVersionUID = 8581614051773949262L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCClientLogin.class);
+
+    private static final String OIDC_ACCESS_ERROR = "OIDC access error";
+
+    public OIDCClientLogin(final PageParameters parameters) {
+        super(parameters);
+
+        String token = (String) ((ServletWebRequest) getRequest()).getContainerRequest().
+                getSession().getAttribute(org.apache.syncope.ext.oidcclient.agent.Constants.OIDCCLIENTJWT);
+        if (StringUtils.isBlank(token)) {
+            LOG.error("No JWT found, redirecting to default greeter");
+
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", OIDC_ACCESS_ERROR);
+            setResponsePage(getApplication().getHomePage(), params);
+        }
+
+        IAuthenticationStrategy strategy = getApplication().getSecuritySettings().getAuthenticationStrategy();
+
+        if (SyncopeEnduserSession.get().authenticate(token)) {
+            setResponsePage(getApplication().getHomePage());
+        } else {
+            PageParameters params = new PageParameters();
+            params.add("errorMessage", OIDC_ACCESS_ERROR);
+            setResponsePage(getApplication().getHomePage(), params);
+        }
+        strategy.remove();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/OIDCProvidersResource.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/OIDCProvidersResource.java b/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/OIDCProvidersResource.java
new file mode 100644
index 0000000..0faeba6
--- /dev/null
+++ b/ext/oidcclient/client-enduser/src/main/java/org/apache/syncope/client/enduser/resources/OIDCProvidersResource.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.enduser.resources;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.annotations.Resource;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.apache.wicket.request.resource.AbstractResource;
+
+@Resource(key = "oidcProviders", path = "/api/oidcProviders")
+public class OIDCProvidersResource extends BaseResource {
+
+    private static final long serialVersionUID = -6595407257133697170L;
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+        ResourceResponse response = new ResourceResponse();
+        response.setContentType(MediaType.APPLICATION_JSON);
+        response.setTextEncoding(StandardCharsets.UTF_8.name());
+        try {
+            final ArrayNode result = MAPPER.createArrayNode();
+
+            SyncopeEnduserSession.get().getService(OIDCProviderService.class).list().forEach(op -> {
+                ObjectNode opNode = MAPPER.createObjectNode();
+                opNode.put("name", op.getName());
+                result.add(opNode);
+            });
+
+            response.setWriteCallback(new AbstractResource.WriteCallback() {
+
+                @Override
+                public void writeData(final Attributes attributes) throws IOException {
+                    attributes.getResponse().write(MAPPER.writeValueAsString(result));
+                }
+            });
+            response.setStatusCode(Response.Status.OK.getStatusCode());
+        } catch (Exception e) {
+            LOG.error("Error retrieving available OIDC Providers", e);
+            response.setError(
+                    Response.Status.BAD_REQUEST.getStatusCode(),
+                    "ErrorMessage{{ " + e.getMessage() + "}}");
+        }
+
+        return response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/common-lib/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/pom.xml b/ext/oidcclient/common-lib/pom.xml
new file mode 100644
index 0000000..a335452
--- /dev/null
+++ b/ext/oidcclient/common-lib/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Common Lib</name>
+  <description>Apache Syncope Ext: OIDC Client Common Lib</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.common</groupId>
+      <artifactId>syncope-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/OIDCConstants.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/OIDCConstants.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/OIDCConstants.java
new file mode 100644
index 0000000..12daa0a
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/OIDCConstants.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib;
+
+public final class OIDCConstants {
+
+    public static final String CLIENT_ID = "client_id";
+
+    public static final String CLIENT_SECRET = "client_secret";
+
+    public static final String SCOPE = "scope";
+
+    public static final String RESPOSNSE_TYPE = "response_type";
+
+    public static final String REDIRECT_URI = "redirect_uri";
+
+    public static final String STATE = "state";
+
+    public static final String CODE = "code";
+
+    public static final String GRANT_TYPE = "grant_type";
+
+    public static final String OP = "op";
+
+    private OIDCConstants() {
+        // private constructor for static utility class
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginRequestTO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginRequestTO.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginRequestTO.java
new file mode 100644
index 0000000..5d4dc31
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginRequestTO.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "oidcLoginRequest")
+@XmlType
+public class OIDCLoginRequestTO extends AbstractBaseBean {
+
+    private static final long serialVersionUID = -3509031322459942441L;
+
+    private String providerAddress;
+
+    private String clientId;
+
+    private String scope;
+
+    private String responseType;
+
+    private String redirectURI;
+
+    private String state;
+
+    public String getProviderAddress() {
+        return providerAddress;
+    }
+
+    public void setProviderAddress(final String providerAddress) {
+        this.providerAddress = providerAddress;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(final String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(final String scope) {
+        this.scope = scope;
+    }
+
+    public String getResponseType() {
+        return responseType;
+    }
+
+    public void setResponseType(final String responseType) {
+        this.responseType = responseType;
+    }
+
+    public String getRedirectURI() {
+        return redirectURI;
+    }
+
+    public void setRedirectURI(final String redirectURI) {
+        this.redirectURI = redirectURI;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public void setState(final String state) {
+        this.state = state;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginResponseTO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginResponseTO.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginResponseTO.java
new file mode 100644
index 0000000..8e7798a
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCLoginResponseTO.java
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib.to;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "oidcLoginResponse")
+@XmlType
+public class OIDCLoginResponseTO extends AbstractBaseBean {
+
+    private static final long serialVersionUID = -5971442076182154492L;
+
+    private String username;
+
+    private String email;
+
+    private String name;
+
+    private String subject;
+
+    private String givenName;
+
+    private String familyName;
+
+    private String accessToken;
+
+    private Date accessTokenExpiryTime;
+
+    private final Set<AttrTO> attrs = new HashSet<>();
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(final String username) {
+        this.username = username;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(final String subject) {
+        this.subject = subject;
+    }
+
+    public String getGivenName() {
+        return givenName;
+    }
+
+    public void setGivenName(final String givenName) {
+        this.givenName = givenName;
+    }
+
+    public String getFamilyName() {
+        return familyName;
+    }
+
+    public void setFamilyName(final String familyName) {
+        this.familyName = familyName;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(final String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    public Date getAccessTokenExpiryTime() {
+        return accessTokenExpiryTime;
+    }
+
+    public void setAccessTokenExpiryTime(final Date accessTokenExpiryTime) {
+        this.accessTokenExpiryTime = accessTokenExpiryTime;
+    }
+
+    @JsonIgnore
+    public Optional<AttrTO> getAttr(final String schema) {
+        return attrs.stream().filter(attr -> attr.getSchema().equals(schema)).findFirst();
+    }
+
+    @XmlElementWrapper(name = "attrs")
+    @XmlElement(name = "attr")
+    @JsonProperty("attrs")
+    public Set<AttrTO> getAttrs() {
+        return attrs;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
new file mode 100644
index 0000000..eec4a83
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/to/OIDCProviderTO.java
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib.to;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "oidcprovider")
+@XmlType
+public class OIDCProviderTO extends AbstractBaseBean implements EntityTO, ItemContainerTO {
+
+    private static final long serialVersionUID = -1229802774546135794L;
+
+    private String key;
+
+    private String name;
+
+    private String clientID;
+
+    private String clientSecret;
+
+    private String authorizationEndpoint;
+
+    private String tokenEndpoint;
+
+    private String jwksUri;
+
+    private String issuer;
+
+    private String userinfoEndpoint;
+
+    private boolean hasDiscovery;
+
+    private UserTO userTemplate;
+
+    private boolean createUnmatching;
+
+    private boolean updateMatching;
+
+    private final List<ItemTO> items = new ArrayList<>();
+
+    private final Set<String> actionsClassNames = new HashSet<>();
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getClientID() {
+        return clientID;
+    }
+
+    public void setClientID(final String clientID) {
+        this.clientID = clientID;
+    }
+
+    public String getClientSecret() {
+        return clientSecret;
+    }
+
+    public void setClientSecret(final String clientSecret) {
+        this.clientSecret = clientSecret;
+    }
+
+    public String getAuthorizationEndpoint() {
+        return authorizationEndpoint;
+    }
+
+    public void setAuthorizationEndpoint(final String authorizationEndpoint) {
+        this.authorizationEndpoint = authorizationEndpoint;
+    }
+
+    public String getTokenEndpoint() {
+        return tokenEndpoint;
+    }
+
+    public void setTokenEndpoint(final String tokenEndpoint) {
+        this.tokenEndpoint = tokenEndpoint;
+    }
+
+    public String getJwksUri() {
+        return jwksUri;
+    }
+
+    public void setJwksUri(final String jwksUri) {
+        this.jwksUri = jwksUri;
+    }
+
+    public String getIssuer() {
+        return issuer;
+    }
+
+    public void setIssuer(final String issuer) {
+        this.issuer = issuer;
+    }
+
+    public String getUserinfoEndpoint() {
+        return userinfoEndpoint;
+    }
+
+    public void setUserinfoEndpoint(final String userinfoEndpoint) {
+        this.userinfoEndpoint = userinfoEndpoint;
+    }
+
+    public UserTO getUserTemplate() {
+        return userTemplate;
+    }
+
+    public boolean getHasDiscovery() {
+        return hasDiscovery;
+    }
+
+    public void setHasDiscovery(final boolean hasDiscovery) {
+        this.hasDiscovery = hasDiscovery;
+    }
+
+    public void setUserTemplate(final UserTO userTemplate) {
+        this.userTemplate = userTemplate;
+    }
+
+    public boolean isCreateUnmatching() {
+        return createUnmatching;
+    }
+
+    public void setCreateUnmatching(final boolean createUnmatching) {
+        this.createUnmatching = createUnmatching;
+    }
+
+    public boolean isUpdateMatching() {
+        return updateMatching;
+    }
+
+    public void setUpdateMatching(final boolean updateMatching) {
+        this.updateMatching = updateMatching;
+    }
+
+    @Override
+    public ItemTO getConnObjectKeyItem() {
+        return getItems().stream().filter(ItemTO::isConnObjectKey).findFirst().get();
+    }
+
+    protected boolean addConnObjectKeyItem(final ItemTO connObjectItem) {
+        connObjectItem.setMandatoryCondition("true");
+        connObjectItem.setConnObjectKey(true);
+
+        return this.add(connObjectItem);
+    }
+
+    @Override
+    public boolean setConnObjectKeyItem(final ItemTO connObjectKeyItem) {
+        return connObjectKeyItem == null
+                ? remove(getConnObjectKeyItem())
+                : addConnObjectKeyItem(connObjectKeyItem);
+    }
+
+    @XmlElementWrapper(name = "items")
+    @XmlElement(name = "item")
+    @JsonProperty("items")
+    @Override
+    public List<ItemTO> getItems() {
+        return items;
+    }
+
+    @Override
+    public boolean add(final ItemTO item) {
+        return item == null ? false : this.items.contains(item) || this.items.add(item);
+    }
+
+    public boolean remove(final ItemTO item) {
+        return this.items.remove(item);
+    }
+
+    @XmlElementWrapper(name = "actionsClassNames")
+    @XmlElement(name = "actionsClassName")
+    @JsonProperty("actionsClassNames")
+    public Set<String> getActionsClassNames() {
+        return actionsClassNames;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientEntitlement.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientEntitlement.java b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientEntitlement.java
new file mode 100644
index 0000000..98cf4d6
--- /dev/null
+++ b/ext/oidcclient/common-lib/src/main/java/org/apache/syncope/common/lib/types/OIDCClientEntitlement.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.lib.types;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Set;
+import java.util.TreeSet;
+
+public final class OIDCClientEntitlement {
+
+    public static final String OP_READ = "OP_READ";
+
+    public static final String OP_CREATE = "OP_CREATE";
+
+    public static final String OP_UPDATE = "OP_UPDATE";
+
+    public static final String OP_DELETE = "OP_DELETE";
+
+    private static final Set<String> VALUES;
+
+    static {
+        Set<String> values = new TreeSet<>();
+        for (Field field : OIDCClientEntitlement.class.getDeclaredFields()) {
+            if (Modifier.isStatic(field.getModifiers()) && String.class.equals(field.getType())) {
+                values.add(field.getName());
+            }
+        }
+        VALUES = Collections.unmodifiableSet(values);
+    }
+
+    public static Set<String> values() {
+        return VALUES;
+    }
+
+    private OIDCClientEntitlement() {
+        // private constructor for static utility class
+    }
+
+}


[5/8] syncope git commit: [SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser - This closes #74

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
new file mode 100644
index 0000000..69268cb
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProviderItem.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.Cacheable;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.jpa.entity.resource.AbstractItem;
+
+@Entity
+@Table(name = JPAOIDCProviderItem.TABLE)
+@Cacheable
+public class JPAOIDCProviderItem extends AbstractItem implements OIDCProviderItem {
+
+    public static final String TABLE = "OIDCProviderItem";
+
+    private static final long serialVersionUID = -6903418265811089724L;
+
+    @ManyToOne
+    private JPAOIDCProvider op;
+
+    @Override
+    public OIDCProvider getOP() {
+        return op;
+    }
+
+    @Override
+    public void setOP(final OIDCProvider op) {
+        checkType(op, JPAOIDCProvider.class);
+        this.op = (JPAOIDCProvider) op;
+    }
+
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = TABLE + "Transformer",
+            joinColumns =
+            @JoinColumn(name = "item_id"),
+            inverseJoinColumns =
+            @JoinColumn(name = "implementation_id"))
+    private List<JPAImplementation> transformers = new ArrayList<>();
+
+    @Override
+    public boolean add(final Implementation transformer) {
+        checkType(transformer, JPAImplementation.class);
+        checkImplementationType(transformer, ImplementationType.ITEM_TRANSFORMER);
+        return this.transformers.add((JPAImplementation) transformer);
+    }
+
+    @Override
+    public List<? extends Implementation> getTransformers() {
+        return transformers;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCUserTemplate.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCUserTemplate.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCUserTemplate.java
new file mode 100644
index 0000000..54eea72
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCUserTemplate.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
+import org.apache.syncope.core.persistence.jpa.entity.resource.AbstractAnyTemplate;
+
+@Entity
+@Table(name = JPAOIDCUserTemplate.TABLE, uniqueConstraints =
+        @UniqueConstraint(columnNames = { "op_id" }))
+public class JPAOIDCUserTemplate extends AbstractAnyTemplate implements OIDCUserTemplate {
+
+    public static final String TABLE = "OIDCUserTemplate";
+
+    private static final long serialVersionUID = 3964321047520954968L;
+
+    @ManyToOne
+    private JPAOIDCProvider op;
+
+    @Override
+    public OIDCProvider getOP() {
+        return op;
+    }
+
+    @Override
+    public void setOP(final OIDCProvider op) {
+        checkType(op, JPAOIDCProvider.class);
+        this.op = (JPAOIDCProvider) op;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/pom.xml b/ext/oidcclient/pom.xml
new file mode 100644
index 0000000..6bdb336
--- /dev/null
+++ b/ext/oidcclient/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-ext</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client</name>
+  <description>Apache Syncope Ext: OIDC Client</description>
+  <groupId>org.apache.syncope.ext</groupId>
+  <artifactId>syncope-ext-oidcclient</artifactId>
+  <packaging>pom</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+  </properties>
+ 
+
+  <modules>
+    <module>common-lib</module>
+    <module>rest-api</module>
+    <module>logic</module>
+    <module>rest-cxf</module>
+    <module>persistence-api</module>
+    <module>persistence-jpa</module>
+    <module>agent</module>
+    <module>client-console</module>
+    <module>client-enduser</module>
+    <module>provisioning-api</module>
+    <module>provisioning-java</module>
+  </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/provisioning-api/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-api/pom.xml b/ext/oidcclient/provisioning-api/pom.xml
new file mode 100644
index 0000000..3d58721
--- /dev/null
+++ b/ext/oidcclient/provisioning-api/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Provisioning API</name>
+  <description>Apache Syncope Ext: OIDC Client Provisioning API</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-provisioning-api</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-provisioning-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-persistence-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/OIDCProviderActions.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/OIDCProviderActions.java b/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/OIDCProviderActions.java
new file mode 100644
index 0000000..40ea6a8
--- /dev/null
+++ b/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/OIDCProviderActions.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.provisioning.api;
+
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.lib.to.UserTO;
+
+public interface OIDCProviderActions {
+
+    UserTO beforeCreate(UserTO input, OIDCLoginResponseTO loginResponse);
+
+    UserTO afterCreate(UserTO input, OIDCLoginResponseTO loginResponse);
+
+    UserPatch beforeUpdate(UserPatch input, OIDCLoginResponseTO loginResponse);
+
+    UserTO afterUpdate(UserTO input, OIDCLoginResponseTO loginResponse);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCProviderDataBinder.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCProviderDataBinder.java b/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCProviderDataBinder.java
new file mode 100644
index 0000000..acea05d
--- /dev/null
+++ b/ext/oidcclient/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCProviderDataBinder.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+
+public interface OIDCProviderDataBinder {
+
+    OIDCProvider create(OIDCProviderTO op);
+
+    OIDCProvider update(OIDCProvider op, OIDCProviderTO opTO);
+
+    OIDCProviderTO getOIDCProviderTO(OIDCProvider op);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-java/pom.xml b/ext/oidcclient/provisioning-java/pom.xml
new file mode 100644
index 0000000..962c2b7
--- /dev/null
+++ b/ext/oidcclient/provisioning-java/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Provisioning Java</name>
+  <description>Apache Syncope Ext: OIDC Client Provisioning Java</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-provisioning-java</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-provisioning-java</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-provisioning-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultOIDCProviderActions.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultOIDCProviderActions.java b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultOIDCProviderActions.java
new file mode 100644
index 0000000..e6b060c
--- /dev/null
+++ b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultOIDCProviderActions.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.provisioning.java;
+
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.core.provisioning.api.OIDCProviderActions;
+
+public class DefaultOIDCProviderActions implements OIDCProviderActions {
+
+    @Override
+    public UserTO beforeCreate(final UserTO input, final OIDCLoginResponseTO loginResponse) {
+        return input;
+    }
+
+    @Override
+    public UserTO afterCreate(final UserTO input, final OIDCLoginResponseTO loginResponse) {
+        return input;
+    }
+
+    @Override
+    public UserPatch beforeUpdate(final UserPatch input, final OIDCLoginResponseTO loginResponse) {
+        return input;
+    }
+
+    @Override
+    public UserTO afterUpdate(final UserTO input, final OIDCLoginResponseTO loginResponse) {
+        return input;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
new file mode 100644
index 0000000..75ead03
--- /dev/null
+++ b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
@@ -0,0 +1,258 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import java.text.ParseException;
+import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.SyncopeClientCompositeException;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
+import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.OIDCEntityFactory;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
+import org.apache.syncope.core.provisioning.api.IntAttrName;
+import org.apache.syncope.core.provisioning.api.data.OIDCProviderDataBinder;
+import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.spring.BeanUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class OIDCProviderDataBinderImpl implements OIDCProviderDataBinder {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCProviderDataBinder.class);
+
+    private static final String[] ITEM_IGNORE_PROPERTIES = { "key", "purpose" };
+
+    @Autowired
+    private AnyTypeDAO anyTypeDAO;
+
+    @Autowired
+    private OIDCProviderDAO oidcOPDAO;
+
+    @Autowired
+    private OIDCEntityFactory entityFactory;
+
+    @Autowired
+    private IntAttrNameParser intAttrNameParser;
+
+    @Override
+    public OIDCProvider create(final OIDCProviderTO opTO) {
+        return update(entityFactory.newEntity(OIDCProvider.class), opTO);
+
+    }
+
+    private void populateItems(
+            final OIDCProviderTO opTO,
+            final OIDCProvider op,
+            final AnyTypeClassTO allowedSchemas) {
+
+        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
+        SyncopeClientException invalidMapping =
+                SyncopeClientException.build(ClientExceptionType.InvalidMapping);
+        SyncopeClientException requiredValuesMissing =
+                SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
+
+        for (ItemTO itemTO : opTO.getItems()) {
+            if (itemTO == null) {
+                LOG.error("Null {}", ItemTO.class.getSimpleName());
+                invalidMapping.getElements().add("Null " + ItemTO.class.getSimpleName());
+            } else if (itemTO.getIntAttrName() == null) {
+                requiredValuesMissing.getElements().add("intAttrName");
+                scce.addException(requiredValuesMissing);
+            } else {
+                IntAttrName intAttrName = null;
+                try {
+                    intAttrName = intAttrNameParser.parse(itemTO.getIntAttrName(), AnyTypeKind.USER);
+                } catch (ParseException e) {
+                    LOG.error("Invalid intAttrName '{}' specified, ignoring", itemTO.getIntAttrName(), e);
+                }
+
+                if (intAttrName == null || intAttrName.getSchemaType() == null && intAttrName.getField() == null) {
+                    LOG.error("'{}' not existing", itemTO.getIntAttrName());
+                    invalidMapping.getElements().add("'" + itemTO.getIntAttrName() + "' not existing");
+                } else {
+                    boolean allowed = true;
+                    if (intAttrName.getSchemaType() != null
+                            && intAttrName.getEnclosingGroup() == null
+                            && intAttrName.getRelatedAnyObject() == null) {
+                        switch (intAttrName.getSchemaType()) {
+                            case PLAIN:
+                                allowed = allowedSchemas.getPlainSchemas().contains(intAttrName.getSchemaName());
+                                break;
+
+                            case DERIVED:
+                                allowed = allowedSchemas.getDerSchemas().contains(intAttrName.getSchemaName());
+                                break;
+
+                            case VIRTUAL:
+                                allowed = allowedSchemas.getVirSchemas().contains(intAttrName.getSchemaName());
+                                break;
+
+                            default:
+                        }
+                    }
+
+                    if (allowed) {
+                        // no mandatory condition implies mandatory condition false
+                        if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
+                                ? "false" : itemTO.getMandatoryCondition())) {
+
+                            SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
+                                    ClientExceptionType.InvalidValues);
+                            invalidMandatoryCondition.getElements().add(itemTO.getMandatoryCondition());
+                            scce.addException(invalidMandatoryCondition);
+                        }
+
+                        OIDCProviderItem item = entityFactory.newEntity(OIDCProviderItem.class);
+                        BeanUtils.copyProperties(itemTO, item, ITEM_IGNORE_PROPERTIES);
+                        item.setOP(op);
+                        item.setPurpose(MappingPurpose.NONE);
+                        if (item.isConnObjectKey()) {
+                            if (intAttrName.getSchemaType() == SchemaType.VIRTUAL) {
+                                invalidMapping.getElements().
+                                        add("Virtual attributes cannot be set as ConnObjectKey");
+                            }
+                            if ("password".equals(intAttrName.getField())) {
+                                invalidMapping.getElements().add(
+                                        "Password attributes cannot be set as ConnObjectKey");
+                            }
+
+                            op.setConnObjectKeyItem(item);
+                        } else {
+                            op.add(item);
+                        }
+                    } else {
+                        LOG.error("'{}' not allowed", itemTO.getIntAttrName());
+                        invalidMapping.getElements().add("'" + itemTO.getIntAttrName() + "' not allowed");
+                    }
+                }
+            }
+        }
+
+        if (!invalidMapping.getElements().isEmpty()) {
+            scce.addException(invalidMapping);
+        }
+        if (scce.hasExceptions()) {
+            throw scce;
+        }
+    }
+
+    @Override
+    public OIDCProvider update(final OIDCProvider op, final OIDCProviderTO opTO) {
+        op.setAuthorizationEndpoint(opTO.getAuthorizationEndpoint());
+        op.setClientID(opTO.getClientID());
+        op.setClientSecret(opTO.getClientSecret());
+        op.setName(opTO.getName());
+        op.setIssuer(opTO.getIssuer());
+        op.setJwksUri(opTO.getJwksUri());
+        op.setTokenEndpoint(opTO.getTokenEndpoint());
+        op.setUserinfoEndpoint(opTO.getUserinfoEndpoint());
+        op.setHasDiscovery(opTO.getHasDiscovery());
+        op.setCreateUnmatching(opTO.isCreateUnmatching());
+        op.setUpdateMatching(opTO.isUpdateMatching());
+
+        if (opTO.getUserTemplate() == null) {
+            op.setUserTemplate(null);
+        } else {
+            OIDCUserTemplate userTemplate = op.getUserTemplate();
+            if (userTemplate == null) {
+                userTemplate = entityFactory.newEntity(OIDCUserTemplate.class);
+                userTemplate.setAnyType(anyTypeDAO.findUser());
+                userTemplate.setOP(op);
+                op.setUserTemplate(userTemplate);
+            }
+            userTemplate.set(opTO.getUserTemplate());
+        }
+
+        op.getItems().clear();
+        AnyTypeClassTO allowedSchemas = new AnyTypeClassTO();
+        anyTypeDAO.findUser().getClasses().forEach(anyTypeClass -> {
+            allowedSchemas.getPlainSchemas().addAll(anyTypeClass.getPlainSchemas().stream().
+                    map(Entity::getKey).collect(Collectors.toList()));
+            allowedSchemas.getDerSchemas().addAll(anyTypeClass.getDerSchemas().stream().
+                    map(Entity::getKey).collect(Collectors.toList()));
+            allowedSchemas.getVirSchemas().addAll(anyTypeClass.getVirSchemas().stream().
+                    map(Entity::getKey).collect(Collectors.toList()));
+        });
+        populateItems(opTO, op, allowedSchemas);
+
+        op.getActionsClassNames().clear();
+        op.getActionsClassNames().addAll(opTO.getActionsClassNames());
+
+        return oidcOPDAO.save(op);
+    }
+
+    private void populateItems(final OIDCProvider op, final OIDCProviderTO opTO) {
+        op.getItems().forEach(item -> {
+            ItemTO itemTO = new ItemTO();
+            itemTO.setKey(item.getKey());
+            BeanUtils.copyProperties(item, itemTO, ITEM_IGNORE_PROPERTIES);
+            itemTO.setPurpose(MappingPurpose.NONE);
+
+            if (itemTO.isConnObjectKey()) {
+                opTO.setConnObjectKeyItem(itemTO);
+            } else {
+                opTO.add(itemTO);
+            }
+        });
+    }
+
+    @Override
+    public OIDCProviderTO getOIDCProviderTO(final OIDCProvider op) {
+        OIDCProviderTO opTO = new OIDCProviderTO();
+
+        opTO.setKey(op.getKey());
+        opTO.setAuthorizationEndpoint(op.getAuthorizationEndpoint());
+        opTO.setClientID(op.getClientID());
+        opTO.setClientSecret(op.getClientSecret());
+        opTO.setName(op.getName());
+        opTO.setIssuer(op.getIssuer());
+        opTO.setJwksUri(op.getJwksUri());
+        opTO.setTokenEndpoint(op.getTokenEndpoint());
+        opTO.setUserinfoEndpoint(op.getUserinfoEndpoint());
+        opTO.setHasDiscovery(op.getHasDiscovery());
+        opTO.setCreateUnmatching(op.isCreateUnmatching());
+        opTO.setUpdateMatching(op.isUpdateMatching());
+
+        if (op.getUserTemplate() != null) {
+            opTO.setUserTemplate((UserTO) op.getUserTemplate().get());
+        }
+
+        populateItems(op, opTO);
+
+        opTO.getActionsClassNames().addAll(op.getActionsClassNames());
+
+        return opTO;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/rest-api/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-api/pom.xml b/ext/oidcclient/rest-api/pom.xml
new file mode 100644
index 0000000..60c469f
--- /dev/null
+++ b/ext/oidcclient/rest-api/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client REST API</name>
+  <description>Apache Syncope Ext: OIDC Client REST API</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.common</groupId>
+      <artifactId>syncope-common-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>      
+      <version>${project.version}</version>
+    </dependency>    
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!-- Generating javadoc JAR artifact for usage with CXF's WADL generator (for core) -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>attach-javadocs</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCClientService.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCClientService.java b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCClientService.java
new file mode 100644
index 0000000..d599e75
--- /dev/null
+++ b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCClientService.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.rest.api.service;
+
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.OIDCConstants;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+
+/**
+ * REST operations for OpenID Connect Clients.
+ */
+@Tag(name = "OIDCClients")
+@SecurityRequirements({
+    @SecurityRequirement(name = "BasicAuthentication"),
+    @SecurityRequirement(name = "Bearer") })
+@Path("oidcclient/clients")
+public interface OIDCClientService extends JAXRSService {
+
+    /**
+     * Generates OpenID Connect authentication request for the Provider matching the provided op.
+     *
+     * @param redirectURI redirect URI
+     * @param op OpenID Connect Provider
+     * @return OpenID Connect authentication request
+     */
+    @POST
+    @Path("loginRequest")
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    OIDCLoginRequestTO createLoginRequest(
+            @QueryParam(OIDCConstants.REDIRECT_URI) String redirectURI,
+            @QueryParam(OIDCConstants.OP) String op);
+
+    /**
+     * Uses the provided authorization code to go through the OpenID Connect tokens process and finally creates JWT for
+     * the matching user, if found.
+     *
+     * @param redirectURI redirect URI
+     * @param authorizationCode authorization code generated by the remote OpenID Connect Provider
+     * @param op OpenID Connect Provider
+     * @return JWT for the matching user plus attributes returned in the response
+     */
+    @POST
+    @Path("login")
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    OIDCLoginResponseTO login(
+            @QueryParam(OIDCConstants.REDIRECT_URI) String redirectURI,
+            @QueryParam("authorizationCode") String authorizationCode,
+            @QueryParam(OIDCConstants.OP) String op);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
new file mode 100644
index 0000000..9705b31
--- /dev/null
+++ b/ext/oidcclient/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/OIDCProviderService.java
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.common.rest.api.service;
+
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.PUT;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.HttpHeaders;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+/**
+ * REST operations for OpenID Connect Providers.
+ */
+@Tag(name = "OIDCProviders")
+@SecurityRequirements({
+    @SecurityRequirement(name = "BasicAuthentication"),
+    @SecurityRequirement(name = "Bearer") })
+@Path("oidcclient/providers")
+public interface OIDCProviderService extends JAXRSService {
+
+    /**
+     * Returns the list of available OIDCProviderActions implementations.
+     *
+     * @return the list of available OIDCProviderActions implementations
+     */
+    @GET
+    @Path("actionsClasses")
+    @Produces({ MediaType.APPLICATION_JSON })
+    Set<String> getActionsClasses();
+
+    /**
+     * Returns a list of all defined OIDC Providers.
+     *
+     * @return list of all defined OIDC Providers
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    List<OIDCProviderTO> list();
+
+    /**
+     * Returns the OIDC Provider with matching key, if available.
+     *
+     * @param key OIDC Provider's key
+     * @return OIDC Providers with matching key, if available
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    OIDCProviderTO read(@PathParam("key") String key);
+
+    /**
+     * Creates a new OIDC Provider.
+     *
+     * @param oidcProviderTO OpenID Connect Provider configuration to be stored
+     * @return Response object featuring Location header of created OIDC Provider
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "201",
+                    description = "OIDC Provider successfully created", headers = {
+                @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+                        @Schema(type = "string"),
+                        description = "Key value for the entity created"),
+                @Header(name = HttpHeaders.LOCATION, schema =
+                        @Schema(type = "string"),
+                        description = "URL of the entity created") }))
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    Response create(OIDCProviderTO oidcProviderTO);
+
+    /**
+     * Creates a new OIDC Provider by using its Discovery Document.
+     *
+     * @param oidcProviderTO OpenID Connect Provider configuration to be stored  
+     * @return Response object featuring Location header of created OIDC Provider
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "201",
+                    description = "OIDC Provider successfully created", headers = {
+                @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+                        @Schema(type = "string"),
+                        description = "Key value for the entity created"),
+                @Header(name = HttpHeaders.LOCATION, schema =
+                        @Schema(type = "string"),
+                        description = "URL of the entity created") }))
+    @POST
+    @Path("fromDiscovery")
+    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    Response createFromDiscovery(OIDCProviderTO oidcProviderTO);
+
+    /**
+     * Updates the OIDC Provider with matching key.
+     *
+     * @param oidcProviderTO OpenID Connect Provider configuration to be stored
+     */
+    @Parameter(name = "key", description = "OIDC Provider's key", in = ParameterIn.PATH, schema =
+            @Schema(type = "string"))
+    @ApiResponses(
+            @ApiResponse(responseCode = "204", description = "Operation was successful"))
+    @PUT
+    @Path("{key}")
+    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void update(@NotNull OIDCProviderTO oidcProviderTO);
+
+    /**
+     * Deletes the OIDC Provider with matching key.
+     *
+     * @param key OIDC Provider key
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "204", description = "Operation was successful"))
+    @DELETE
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void delete(@PathParam("key") String key);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/rest-cxf/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-cxf/pom.xml b/ext/oidcclient/rest-cxf/pom.xml
new file mode 100644
index 0000000..38798be
--- /dev/null
+++ b/ext/oidcclient/rest-cxf/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client REST CXF</name>
+  <description>Apache Syncope Ext: OIDC Client REST CXF</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-rest-cxf</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-api</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-logic</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCClientServiceImpl.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCClientServiceImpl.java b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCClientServiceImpl.java
new file mode 100644
index 0000000..74c14b9
--- /dev/null
+++ b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCClientServiceImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.apache.syncope.core.logic.OIDCClientLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+
+@Service
+public class OIDCClientServiceImpl extends AbstractServiceImpl implements OIDCClientService {
+
+    @Autowired
+    private OIDCClientLogic logic;
+
+    @Override
+    public OIDCLoginRequestTO createLoginRequest(final String redirectURI, final String op) {
+        return logic.createLoginRequest(redirectURI, op);
+    }
+
+    @Override
+    public OIDCLoginResponseTO login(final String redirectURI, final String authorizationCode, final String op) {
+        return logic.login(redirectURI, authorizationCode, op);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
new file mode 100644
index 0000000..7af4366
--- /dev/null
+++ b/ext/oidcclient/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/OIDCProviderServiceImpl.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.apache.syncope.core.logic.OIDCProviderLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class OIDCProviderServiceImpl extends AbstractServiceImpl implements OIDCProviderService {
+
+    @Autowired
+    private OIDCProviderLogic logic;
+
+    @Override
+    public Set<String> getActionsClasses() {
+        return logic.getActionsClasses();
+    }
+
+    @Override
+    public Response create(final OIDCProviderTO oidcProviderTO) {
+        String created = logic.create(oidcProviderTO);
+
+        URI location = uriInfo.getAbsolutePathBuilder().path(created).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created).
+                build();
+    }
+
+    @Override
+    public Response createFromDiscovery(final OIDCProviderTO oidcProviderTO) {
+        String created = logic.createFromDiscovery(oidcProviderTO);
+
+        URI location = uriInfo.getAbsolutePathBuilder().path(created).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created).
+                build();
+    }
+
+    @Override
+    public List<OIDCProviderTO> list() {
+        return logic.list();
+
+    }
+
+    @Override
+    public OIDCProviderTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public void update(final OIDCProviderTO oidcProviderTO) {
+        logic.update(oidcProviderTO);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/pom.xml
----------------------------------------------------------------------
diff --git a/ext/pom.xml b/ext/pom.xml
index a2aa130..d1e5b1b 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -82,6 +82,7 @@ under the License.
     <module>saml2sp</module>
     <module>elasticsearch</module>
     <module>scimv2</module>
+    <module>oidcclient</module>
   </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
index e206ff7..98d4504 100644
--- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
+++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/Login.java
@@ -30,7 +30,7 @@ import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.to.SAML2RequestTO;
 import org.apache.syncope.common.rest.api.service.SAML2SPService;
 
-@WebServlet(name = "login", urlPatterns = { "/saml2sp/login" })
+@WebServlet(name = "saml2spLogin", urlPatterns = { "/saml2sp/login" })
 public class Login extends AbstractSAML2SPServlet {
 
     private static final long serialVersionUID = 968480296813639041L;

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/console-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/console-reference/pom.xml b/fit/console-reference/pom.xml
index c13b264..698bc94 100644
--- a/fit/console-reference/pom.xml
+++ b/fit/console-reference/pom.xml
@@ -75,6 +75,12 @@ under the License.
     </dependency>
 
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/console-reference/src/main/resources/oidcclient-agent.properties
----------------------------------------------------------------------
diff --git a/fit/console-reference/src/main/resources/oidcclient-agent.properties b/fit/console-reference/src/main/resources/oidcclient-agent.properties
new file mode 100644
index 0000000..1d53d49
--- /dev/null
+++ b/fit/console-reference/src/main/resources/oidcclient-agent.properties
@@ -0,0 +1,26 @@
+# 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.
+conf.directory=${conf.directory}
+
+anonymousUser=${anonymousUser}
+anonymousKey=${anonymousKey}
+
+scheme=http
+host=localhost
+port=9080
+rootPath=/syncope/rest/
+useGZIPCompression=true

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/console-reference/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/fit/console-reference/src/main/webapp/WEB-INF/web.xml b/fit/console-reference/src/main/webapp/WEB-INF/web.xml
index 890e5b0..1dc41d1 100644
--- a/fit/console-reference/src/main/webapp/WEB-INF/web.xml
+++ b/fit/console-reference/src/main/webapp/WEB-INF/web.xml
@@ -53,6 +53,16 @@ under the License.
     <param-value>../wicket/bookmarkable/org.apache.syncope.client.console.pages.SAML2SPSelfReg</param-value>
   </context-param>
   
+  <!-- OIDC Client Parameters -->
+  <context-param>
+    <param-name>oidcclient.login.success.url</param-name>
+    <param-value>../wicket/bookmarkable/org.apache.syncope.client.console.pages.OIDCClientLogin</param-value>
+  </context-param>
+  <context-param>
+    <param-name>oidcclient.login.error.url</param-name>
+    <param-value>../wicket/bookmarkable/org.apache.syncope.client.console.pages.Login</param-value>
+  </context-param>
+
   <!-- SESSION TIMEOUT (MINUTES)-->
   <session-config>
     <session-timeout>30</session-timeout>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/console-reference/src/test/resources/rebel.xml
----------------------------------------------------------------------
diff --git a/fit/console-reference/src/test/resources/rebel.xml b/fit/console-reference/src/test/resources/rebel.xml
index cfd69c9..876c58f 100644
--- a/fit/console-reference/src/test/resources/rebel.xml
+++ b/fit/console-reference/src/test/resources/rebel.xml
@@ -34,6 +34,10 @@ under the License.
     </dir>
     <dir name="${basedir}/../../ext/scimv2/client-console/target/classes">
     </dir>
+    <dir name="${basedir}/../../ext/oidcclient/agent/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/client-console/target/classes">
+    </dir>
   </classpath>
 
   <web>
@@ -57,6 +61,10 @@ under the License.
       <dir name="${basedir}/../../ext/scimv2/client-console/target/classes">
       </dir>
     </link>
+    <link target="/">
+      <dir name="${basedir}/../../ext/oidcclient/client-console/target/classes">
+      </dir>
+    </link>
   </web>
 
 </application>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 4e5e4a1..9b5d717 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -185,6 +185,18 @@ under the License.
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>          
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
       <scope>test</scope>
@@ -1099,6 +1111,17 @@ under the License.
         </dependency>
         
         <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.syncope.ext.oidcclient</groupId>
+          <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+        
+        <dependency>
           <groupId>org.apache.syncope.ext.scimv2</groupId>
           <artifactId>syncope-ext-scimv2-rest-cxf</artifactId>
           <version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 759f5e4..9e7d541 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -84,6 +84,8 @@ import org.apache.syncope.common.rest.api.service.ResourceService;
 import org.apache.syncope.common.rest.api.service.GroupService;
 import org.apache.syncope.common.rest.api.service.ImplementationService;
 import org.apache.syncope.common.rest.api.service.MailTemplateService;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
 import org.apache.syncope.common.rest.api.service.RealmService;
 import org.apache.syncope.common.rest.api.service.ReconciliationService;
 import org.apache.syncope.common.rest.api.service.RelationshipTypeService;
@@ -252,6 +254,10 @@ public abstract class AbstractITCase {
     protected static SAML2SPService saml2SpService;
 
     protected static SAML2IdPService saml2IdPService;
+    
+    protected static OIDCClientService oidcClientService;
+
+    protected static OIDCProviderService oidcProviderService;
 
     protected static SCIMConfService scimConfService;
 
@@ -322,6 +328,8 @@ public abstract class AbstractITCase {
         camelRouteService = adminClient.getService(CamelRouteService.class);
         saml2SpService = adminClient.getService(SAML2SPService.class);
         saml2IdPService = adminClient.getService(SAML2IdPService.class);
+        oidcClientService = adminClient.getService(OIDCClientService.class);
+        oidcProviderService = adminClient.getService(OIDCProviderService.class);
         scimConfService = adminClient.getService(SCIMConfService.class);
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/core-reference/src/test/java/org/apache/syncope/fit/OIDCClientDetector.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/OIDCClientDetector.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/OIDCClientDetector.java
new file mode 100644
index 0000000..b08342e
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/OIDCClientDetector.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.fit;
+
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OIDCClientDetector {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCClientDetector.class);
+
+    private static Boolean ENABLED;
+
+    public static boolean isOIDCClientAvailable() {
+        synchronized (LOG) {
+            if (ENABLED == null) {
+                try {
+                    new SyncopeClientFactoryBean().
+                            setAddress(AbstractITCase.ADDRESS).
+                            setContentType(SyncopeClientFactoryBean.ContentType.XML).
+                            create(new AnonymousAuthenticationHandler(
+                                    AbstractITCase.ANONYMOUS_UNAME, AbstractITCase.ANONYMOUS_KEY)).
+                            getService(OIDCProviderService.class).list();
+                    ENABLED = true;
+                } catch (Exception e) {
+                    // ignore
+                    ENABLED = false;
+                }
+            }
+        }
+        return ENABLED;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCClientITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCClientITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCClientITCase.java
new file mode 100644
index 0000000..33a9d44
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCClientITCase.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.fit.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.BasicAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.service.OIDCClientService;
+import org.apache.syncope.common.rest.api.service.OIDCProviderService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.apache.syncope.fit.OIDCClientDetector;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class OIDCClientITCase extends AbstractITCase {
+
+    private static SyncopeClient anonymous;
+
+    private static SyncopeClient admin;
+
+    @BeforeClass
+    public static void setup() {
+        anonymous = new SyncopeClientFactoryBean().
+                setAddress(ADDRESS).
+                create(new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
+
+        admin = new SyncopeClientFactoryBean().
+                setAddress(ADDRESS).
+                create(new BasicAuthenticationHandler(ADMIN_UNAME, ADMIN_PWD));
+    }
+
+    @BeforeClass
+    public static void createOIDCProviderWithoutDiscovery() throws Exception {
+        if (!OIDCClientDetector.isOIDCClientAvailable()) {
+            return;
+        }
+
+        assertTrue(oidcProviderService.list().isEmpty());
+
+        OIDCProviderTO oidcProviderTO = new OIDCProviderTO();
+        oidcProviderTO.setAuthorizationEndpoint("AuthorizationEndpoint");
+        oidcProviderTO.setClientID("ClientID");
+        oidcProviderTO.setClientSecret("ClientSecret");
+        oidcProviderTO.setIssuer("https://accounts.google.com");
+        oidcProviderTO.setJwksUri("JwksUri");
+        oidcProviderTO.setName("Google");
+        oidcProviderTO.setTokenEndpoint("TokenEndpoint");
+        oidcProviderTO.setUserinfoEndpoint("UserinfoEndpoint");
+
+        admin.getService(OIDCProviderService.class).create(oidcProviderTO);
+    }
+
+    @AfterClass
+    public static void clearProviders() throws Exception {
+        if (!OIDCClientDetector.isOIDCClientAvailable()) {
+            return;
+        }
+
+        for (OIDCProviderTO op : oidcProviderService.list()) {
+            oidcProviderService.delete(op.getKey());
+        }
+    }
+
+    @Test
+    public void createLoginRequest() {
+        Assume.assumeTrue(OIDCClientDetector.isOIDCClientAvailable());
+
+        OIDCLoginRequestTO loginRequest = anonymous.getService(OIDCClientService.class).
+                createLoginRequest("http://localhost:9080/syncope-console/oidcclient/code-consumer", "Google");
+
+        assertNotNull(loginRequest);
+        assertEquals("http://localhost:9080/syncope-console/oidcclient/code-consumer", loginRequest.getRedirectURI());
+        assertNotNull(loginRequest.getProviderAddress());
+        assertNotNull(loginRequest.getClientId());
+        assertNotNull(loginRequest.getResponseType());
+        assertNotNull(loginRequest.getScope());
+        assertNotNull(loginRequest.getState());
+    }
+
+    @Test
+    public void setProviderMapping() {
+        Assume.assumeTrue(OIDCClientDetector.isOIDCClientAvailable());
+
+        OIDCProviderTO ssoCircle = IterableUtils.find(oidcProviderService.list(), new Predicate<OIDCProviderTO>() {
+
+            @Override
+            public boolean evaluate(final OIDCProviderTO object) {
+                return "Google".equals(object.getName());
+            }
+        });
+        assertNotNull(ssoCircle);
+        assertFalse(ssoCircle.isCreateUnmatching());
+        assertNull(ssoCircle.getUserTemplate());
+        assertFalse(ssoCircle.getItems().isEmpty());
+        assertNotNull(ssoCircle.getConnObjectKeyItem());
+        assertNotEquals("fullname", ssoCircle.getConnObjectKeyItem().getIntAttrName());
+        assertNotEquals("given_name", ssoCircle.getConnObjectKeyItem().getExtAttrName());
+
+        ssoCircle.setCreateUnmatching(true);
+
+        UserTO userTemplate = new UserTO();
+        userTemplate.setRealm("'/'");
+        ssoCircle.setUserTemplate(userTemplate);
+
+        ssoCircle.getItems().clear();
+        ItemTO keyMapping = new ItemTO();
+        keyMapping.setIntAttrName("fullname");
+        keyMapping.setExtAttrName("given_name");
+        ssoCircle.setConnObjectKeyItem(keyMapping);
+
+        oidcProviderService.update(ssoCircle);
+
+        ssoCircle = oidcProviderService.read(ssoCircle.getKey());
+        assertTrue(ssoCircle.isCreateUnmatching());
+        assertEquals(userTemplate, ssoCircle.getUserTemplate());
+        assertEquals("fullname", ssoCircle.getConnObjectKeyItem().getIntAttrName());
+        assertEquals("given_name", ssoCircle.getConnObjectKeyItem().getExtAttrName());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/core-reference/src/test/resources/rebel.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/rebel.xml b/fit/core-reference/src/test/resources/rebel.xml
index da4b4b1..0a3965b 100644
--- a/fit/core-reference/src/test/resources/rebel.xml
+++ b/fit/core-reference/src/test/resources/rebel.xml
@@ -103,6 +103,23 @@ under the License.
     </dir>
     <dir name="${basedir}/../../ext/scimv2/logic/target/classes">
     </dir>
+    <dir name="${basedir}/../../ext/oidcclient/common-lib/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/logic/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/persistence-api/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/persistence-jpa/target/classes">
+      <exclude name="org/apache/syncope/core/persistence/jpa/entity/**"/>
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/provisioning-api/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/provisioning-java/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/rest-api/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/rest-cxf/target/classes">
+    </dir>
   </classpath>
 
   <web>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/enduser-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/enduser-reference/pom.xml b/fit/enduser-reference/pom.xml
index c2fe31a..127d62d 100644
--- a/fit/enduser-reference/pom.xml
+++ b/fit/enduser-reference/pom.xml
@@ -68,6 +68,12 @@ under the License.
     </dependency>
 
     <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/enduser-reference/src/main/resources/oidcclient-agent.properties
----------------------------------------------------------------------
diff --git a/fit/enduser-reference/src/main/resources/oidcclient-agent.properties b/fit/enduser-reference/src/main/resources/oidcclient-agent.properties
new file mode 100644
index 0000000..1d53d49
--- /dev/null
+++ b/fit/enduser-reference/src/main/resources/oidcclient-agent.properties
@@ -0,0 +1,26 @@
+# 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.
+conf.directory=${conf.directory}
+
+anonymousUser=${anonymousUser}
+anonymousKey=${anonymousKey}
+
+scheme=http
+host=localhost
+port=9080
+rootPath=/syncope/rest/
+useGZIPCompression=true

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml b/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml
index 7f61a36..81a7651 100644
--- a/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml
+++ b/fit/enduser-reference/src/main/webapp/WEB-INF/web.xml
@@ -53,6 +53,16 @@ under the License.
     <param-value>../wicket/bookmarkable/org.apache.syncope.client.enduser.pages.SAML2SPSelfReg</param-value>
   </context-param>
 
+  <context-param>
+    <param-name>oidcclient.login.success.url</param-name>
+    <param-value>../wicket/bookmarkable/org.apache.syncope.client.enduser.pages.OIDCClientLogin</param-value>
+  </context-param>
+  <context-param>
+    <param-name>oidcclient.login.error.url</param-name>
+    <param-value>../wicket/bookmarkable/org.apache.syncope.client.enduser.pages.HomePage</param-value>
+  </context-param>
+
+
   <!-- SESSION TIMEOUT (MINUTES)-->
   <session-config>
     <session-timeout>30</session-timeout>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/fit/enduser-reference/src/test/resources/rebel.xml
----------------------------------------------------------------------
diff --git a/fit/enduser-reference/src/test/resources/rebel.xml b/fit/enduser-reference/src/test/resources/rebel.xml
index da15397..d544b8d 100644
--- a/fit/enduser-reference/src/test/resources/rebel.xml
+++ b/fit/enduser-reference/src/test/resources/rebel.xml
@@ -30,6 +30,10 @@ under the License.
     </dir>
     <dir name="${basedir}/../../ext/saml2sp/client-enduser/target/classes">
     </dir>
+    <dir name="${basedir}/../../ext/oidcclient/agent/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/oidcclient/client-enduser/target/classes">
+    </dir>
   </classpath>
 
   <web>
@@ -45,6 +49,10 @@ under the License.
       <dir name="${basedir}/../../ext/saml2sp/client-enduser/target/classes">
       </dir>
     </link>
+    <link target="/">
+      <dir name="${basedir}/../../ext/oidcclient/client-enduser/target/classes">
+      </dir>
+    </link>
   </web>
 
 </application>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 49ef0d7..e20e477 100644
--- a/pom.xml
+++ b/pom.xml
@@ -604,6 +604,16 @@ under the License.
           </exclusion>
         </exclusions>
       </dependency>
+      <dependency>
+        <groupId>org.apache.cxf</groupId>
+        <artifactId>cxf-rt-rs-security-sso-oidc</artifactId>
+        <version>${cxf.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.cxf</groupId>
+        <artifactId>cxf-rt-rs-extension-providers</artifactId>
+        <version>${cxf.version}</version>
+      </dependency>
       <!-- /CXF -->
 
       <!-- Swagger -->      

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/src/main/asciidoc/reference-guide/concepts/extensions.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/concepts/extensions.adoc b/src/main/asciidoc/reference-guide/concepts/extensions.adoc
index d6493b6..52d245e 100644
--- a/src/main/asciidoc/reference-guide/concepts/extensions.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/extensions.adoc
@@ -116,6 +116,39 @@ This extension adds features to all components and layers that are available, an
 <<customization-extensions,new extensions>>.
 ====
 
+==== OpenID Connect Client 
+
+This extension can be leveraged to provide http://openid.net/connect/[OpenID Connect^]-based
+https://en.wikipedia.org/wiki/Single_sign-on[Single Sign-On^] access to the <<admin-console-component>>,
+the <<enduser-component>> or any other Java EE application dealing with the <<core>>.
+
+Once installed, one or more OpenID Providers can be created either from 
+the http://openid.net/specs/openid-connect-discovery-1_0.html[discovery document^] if it is supported or from inserting 
+manually the required attributes, in any case the client_id and the secret_id from the OAuth 2.0 credential and the issuer 
+are required.
+After configuration the OpenID provider, the http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[Authorization Code Flow^]
+is going to be implemented in order to reach the user information to be used by Syncope to match the internal users.
+
+
+[NOTE]
+.Extension Sources
+====
+The source code of this extension is available from the Apache Syncope
+ifeval::["{snapshotOrRelease}" == "release"]
+https://github.com/apache/syncope/tree/syncope-{docVersion}/ext/oidcclient[source tree^]
+endif::[]
+ifeval::["{snapshotOrRelease}" == "snapshot"]
+https://github.com/apache/syncope/tree/2_0_X/ext/oidcclient[source tree^]
+endif::[]
+.
+====
+
+[TIP]
+====
+This extension adds features to all components and layers that are available, and can be taken as reference when creating
+<<customization-extensions,new extensions>>.
+====
+
 ==== Elasticsearch
 
 This extension provides an alternate internal search engine for <<users-groups-and-any-objects>>, requiring an external 

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
index 80a3870..622f51f 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
@@ -350,6 +350,25 @@ Setup a <<keystore,keystore>> and place it under the <<properties-files-location
 the content of `core/src/main/resources/saml2sp-logic.properties` accordingly.
 
 [discrete]
+===== Enable the <<openid-connect-client>> extension
+
+Add the following dependencies to `core/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-rest-cxf</artifactId>
+  <version>${syncope.version}</version>
+</dependency>
+<dependency>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+[discrete]
 ===== Enable the <<elasticsearch>> extension
 
 Add the following dependencies to `core/pom.xml`:
@@ -474,6 +493,23 @@ Add the following dependencies to `console/pom.xml`:
 Copy `console/src/main/resources/all/saml2sp-agent.properties` to `console/src/main/resources/saml2sp-agent.properties`.
 
 [discrete]
+===== Enable the <<openid-connect-client>> extension
+
+Add the following dependencies to `console/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-client-console</artifactId>
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+Copy `console/src/main/resources/all/oidcclient-agent.properties` to `console/src/main/resources/oidcclient-agent.properties`.
+
+
+[discrete]
 ===== Enable the <<SCIM>> extension
 
 Add the following dependencies to `console/pom.xml`:
@@ -512,6 +548,22 @@ Add the following dependencies to `enduser/pom.xml`:
 
 Copy `enduser/src/main/resources/all/saml2sp-agent.properties` to `enduser/src/main/resources/saml2sp-agent.properties`.
 
+[discrete]
+===== Enable the <<openid-connect-client>> extension
+
+Add the following dependencies to `enduser/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-client-enduser</artifactId>
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+Copy `enduser/src/main/resources/all/oidcclient-agent.properties` to `enduser/src/main/resources/oidcclient-agent.properties`.
+
 [[customization-enduser-i18n]]
 ===== i18n 
 


[2/8] syncope git commit: [SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/logic/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/pom.xml b/ext/oidcclient/logic/pom.xml
new file mode 100644
index 0000000..6daef24
--- /dev/null
+++ b/ext/oidcclient/logic/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Logic</name>
+  <description>Apache Syncope Ext: OIDC Client Logic</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-logic</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-logic</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-provisioning-java</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-extension-providers</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-security-sso-oidc</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.fasterxml.jackson.jaxrs</groupId>
+      <artifactId>jackson-jaxrs-json-provider</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCClientLogic.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCClientLogic.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCClientLogic.java
new file mode 100644
index 0000000..20decf9
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCClientLogic.java
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.uuid.Generators;
+import com.fasterxml.uuid.impl.RandomBasedGenerator;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.MediaType;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.provider.json.JsonMapObjectProvider;
+import org.apache.cxf.rs.security.jose.jaxrs.JsonWebKeysProvider;
+import org.apache.cxf.rs.security.oauth2.client.Consumer;
+import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
+import org.apache.cxf.rs.security.oidc.common.IdToken;
+import org.apache.cxf.rs.security.oidc.common.UserInfo;
+import org.apache.cxf.rs.security.oidc.rp.IdTokenReader;
+import org.apache.cxf.rs.security.oidc.rp.UserInfoClient;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.OIDCConstants;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.logic.model.TokenEndpointResponse;
+import org.apache.syncope.core.logic.oidc.OIDCUserManager;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.spring.security.AuthDataAccessor;
+import org.apache.syncope.core.spring.security.Encryptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class OIDCClientLogic extends AbstractTransactionalLogic<AbstractBaseBean> {
+
+    private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private static final RandomBasedGenerator UUID_GENERATOR = Generators.randomBasedGenerator();
+
+    private static final String JWT_CLAIM_OP_ENTITYID = "OP_ENTITYID";
+
+    private static final String JWT_CLAIM_USERID = "USERID";
+
+    @Autowired
+    private AuthDataAccessor authDataAccessor;
+
+    @Autowired
+    private AccessTokenDataBinder accessTokenDataBinder;
+
+    @Autowired
+    private OIDCProviderDAO opDAO;
+
+    @Autowired
+    private OIDCUserManager userManager;
+
+    private OIDCProvider getOIDCProvider(final String opName) {
+        OIDCProvider op = null;
+        if (StringUtils.isBlank(opName)) {
+            List<OIDCProvider> ops = opDAO.findAll();
+            if (!ops.isEmpty()) {
+                op = ops.get(0);
+            }
+        } else {
+            op = opDAO.findByName(opName);
+        }
+        if (op == null) {
+            throw new NotFoundException(StringUtils.isBlank(opName)
+                    ? "Any OIDC Provider"
+                    : "OIDC Provider '" + opName + "'");
+        }
+        return op;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.ANONYMOUS + "')")
+    public OIDCLoginRequestTO createLoginRequest(final String redirectURI, final String opName) {
+        // 1. look for Provider
+        OIDCProvider op = getOIDCProvider(opName);
+
+        // 2. create AuthnRequest
+        OIDCLoginRequestTO requestTO = new OIDCLoginRequestTO();
+        requestTO.setProviderAddress(op.getAuthorizationEndpoint());
+        requestTO.setClientId(op.getClientID());
+        requestTO.setScope("openid email profile");
+        requestTO.setResponseType("code");
+        requestTO.setRedirectURI(redirectURI);
+        requestTO.setState(UUID_GENERATOR.generate().toString());
+        return requestTO;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.ANONYMOUS + "')")
+    public OIDCLoginResponseTO login(final String redirectURI, final String authorizationCode, final String opName) {
+        final OIDCProvider op = getOIDCProvider(opName);
+
+        // 1. get OpenID Connect tokens
+        String body = OIDCConstants.CODE + "=" + authorizationCode
+                + "&" + OIDCConstants.CLIENT_ID + "=" + op.getClientID()
+                + "&" + OIDCConstants.CLIENT_SECRET + "=" + op.getClientSecret()
+                + "&" + OIDCConstants.REDIRECT_URI + "=" + redirectURI
+                + "&" + OIDCConstants.GRANT_TYPE + "=authorization_code";
+        TokenEndpointResponse tokenEndpointResponse = getOIDCTokens(op.getTokenEndpoint(), body);
+
+        // 1. get OpenID Connect tokens
+        Consumer consumer = new Consumer(op.getClientID(), op.getClientSecret());
+
+        // 2. validate token
+        IdToken idToken = getValidatedIdToken(op, consumer, tokenEndpointResponse.getIdToken());
+
+        // 3. extract user information
+        UserInfo userInfo = getUserInfo(op, tokenEndpointResponse.getAccessToken(), idToken, consumer);
+
+        // 4. prepare the result: find matching user (if any) and return the received attributes
+        final OIDCLoginResponseTO responseTO = new OIDCLoginResponseTO();
+        responseTO.setEmail(userInfo.getEmail());
+        responseTO.setFamilyName(userInfo.getFamilyName());
+        responseTO.setGivenName(userInfo.getGivenName());
+        responseTO.setName(userInfo.getName());
+        responseTO.setSubject(userInfo.getSubject());
+
+        String keyValue = null;
+        for (OIDCProviderItem item : op.getItems()) {
+            AttrTO attrTO = new AttrTO();
+            attrTO.setSchema(item.getExtAttrName());
+            switch (item.getExtAttrName()) {
+                case UserInfo.PREFERRED_USERNAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getPreferredUserName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getPreferredUserName();
+                    }
+                    break;
+
+                case UserInfo.PROFILE_CLAIM:
+                    attrTO.getValues().add(userInfo.getProfile());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getProfile();
+                    }
+                    break;
+
+                case UserInfo.EMAIL_CLAIM:
+                    attrTO.getValues().add(userInfo.getEmail());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getEmail();
+                    }
+                    break;
+
+                case UserInfo.NAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getName();
+                    }
+                    break;
+
+                case UserInfo.FAMILY_NAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getFamilyName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getFamilyName();
+                    }
+                    break;
+
+                case UserInfo.MIDDLE_NAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getMiddleName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getMiddleName();
+                    }
+                    break;
+
+                case UserInfo.GIVEN_NAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getGivenName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getGivenName();
+                    }
+                    break;
+
+                case UserInfo.NICKNAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getNickName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getNickName();
+                    }
+                    break;
+
+                case UserInfo.GENDER_CLAIM:
+                    attrTO.getValues().add(userInfo.getGender());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getGender();
+                    }
+                    break;
+
+                case UserInfo.LOCALE_CLAIM:
+                    attrTO.getValues().add(userInfo.getLocale());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getLocale();
+                    }
+                    break;
+
+                case UserInfo.ZONEINFO_CLAIM:
+                    attrTO.getValues().add(userInfo.getZoneInfo());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getZoneInfo();
+                    }
+                    break;
+
+                case UserInfo.BIRTHDATE_CLAIM:
+                    attrTO.getValues().add(userInfo.getBirthDate());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getBirthDate();
+                    }
+                    break;
+
+                case UserInfo.PHONE_CLAIM:
+                    attrTO.getValues().add(userInfo.getPhoneNumber());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getPhoneNumber();
+                    }
+                    break;
+
+                case UserInfo.ADDRESS_CLAIM:
+                    attrTO.getValues().add(userInfo.getUserAddress().getFormatted());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getUserAddress().getFormatted();
+                    }
+                    break;
+
+                case UserInfo.UPDATED_AT_CLAIM:
+                    attrTO.getValues().add(Long.toString(userInfo.getUpdatedAt()));
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = Long.toString(userInfo.getUpdatedAt());
+                    }
+                    break;
+
+                default:
+                    LOG.warn("Unsupported: {} ", item.getExtAttrName());
+            }
+        }
+
+        final List<String> matchingUsers = keyValue == null
+                ? Collections.<String>emptyList()
+                : userManager.findMatchingUser(keyValue, op.getConnObjectKeyItem());
+        LOG.debug("Found {} matching users for {}", matchingUsers.size(), keyValue);
+
+        String username;
+        if (matchingUsers.isEmpty()) {
+            if (op.isCreateUnmatching()) {
+                LOG.debug("No user matching {}, about to create", keyValue);
+
+                final String emailValue = userInfo.getEmail();
+                username = AuthContextUtils.execWithAuthContext(
+                        AuthContextUtils.getDomain(), new AuthContextUtils.Executable<String>() {
+
+                    @Override
+                    public String exec() {
+                        return userManager.create(op, responseTO, emailValue);
+                    }
+                });
+            } else {
+                throw new NotFoundException("User matching the provided value " + keyValue);
+            }
+        } else if (matchingUsers.size() > 1) {
+            throw new IllegalArgumentException("Several users match the provided value " + keyValue);
+        } else {
+            if (op.isUpdateMatching()) {
+                LOG.debug("About to update {} for {}", matchingUsers.get(0), keyValue);
+
+                username = AuthContextUtils.execWithAuthContext(
+                        AuthContextUtils.getDomain(), new AuthContextUtils.Executable<String>() {
+
+                    @Override
+                    public String exec() {
+                        return userManager.update(matchingUsers.get(0), op, responseTO);
+                    }
+                });
+            } else {
+                username = matchingUsers.get(0);
+            }
+
+        }
+
+        responseTO.setUsername(username);
+
+        // 5. generate JWT for further access
+        Map<String, Object> claims = new HashMap<>();
+        claims.put(JWT_CLAIM_OP_ENTITYID, idToken.getIssuer());
+        claims.put(JWT_CLAIM_USERID, idToken.getSubject());
+
+        byte[] authorities = null;
+        try {
+            authorities = ENCRYPTOR.encode(POJOHelper.serialize(
+                    authDataAccessor.getAuthorities(responseTO.getUsername())), CipherAlgorithm.AES).
+                    getBytes();
+        } catch (Exception e) {
+            LOG.error("Could not fetch authorities", e);
+        }
+
+        Pair<String, Date> accessTokenInfo =
+                accessTokenDataBinder.create(responseTO.getUsername(), claims, authorities, true);
+        responseTO.setAccessToken(accessTokenInfo.getLeft());
+        responseTO.setAccessTokenExpiryTime(accessTokenInfo.getRight());
+
+        return responseTO;
+    }
+
+    private TokenEndpointResponse getOIDCTokens(final String url, final String body) {
+        String oidcTokens = WebClient.create(url).
+                type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.APPLICATION_JSON).
+                post(body).
+                readEntity(String.class);
+        TokenEndpointResponse endpointResponse = null;
+        try {
+            endpointResponse = MAPPER.readValue(oidcTokens, TokenEndpointResponse.class);
+        } catch (Exception e) {
+            LOG.error("While getting the Tokens from the OP", e);
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+        return endpointResponse;
+    }
+
+    private IdToken getValidatedIdToken(final OIDCProvider op, final Consumer consumer, final String jwtIdToken) {
+        IdTokenReader idTokenReader = new IdTokenReader();
+        idTokenReader.setClockOffset(10);
+        idTokenReader.setIssuerId(op.getIssuer());
+        WebClient jwkSetClient = WebClient.create(
+                op.getJwksUri(), Arrays.asList(new JsonWebKeysProvider())).
+                accept(MediaType.APPLICATION_JSON);
+        idTokenReader.setJwkSetClient(jwkSetClient);
+        IdToken idToken = null;
+        try {
+            idToken = idTokenReader.getIdToken(jwtIdToken, consumer);
+        } catch (Exception e) {
+            LOG.error("While validating the id_token", e);
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+        return idToken;
+    }
+
+    private UserInfo getUserInfo(
+            final OIDCProvider op,
+            final String accessToken,
+            final IdToken idToken,
+            final Consumer consumer) {
+
+        WebClient userInfoServiceClient = WebClient.create(
+                op.getUserinfoEndpoint(), Arrays.asList(new JsonMapObjectProvider())).
+                accept(MediaType.APPLICATION_JSON);
+        ClientAccessToken clientAccessToken = new ClientAccessToken("Bearer", accessToken);
+        UserInfoClient userInfoClient = new UserInfoClient();
+        userInfoClient.setUserInfoServiceClient(userInfoServiceClient);
+        UserInfo userInfo = null;
+        try {
+            userInfo = userInfoClient.getUserInfo(clientAccessToken, idToken, consumer);
+        } catch (Exception e) {
+            LOG.error("While getting the userInfo", e);
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+        return userInfo;
+    }
+
+    @Override
+    protected AbstractBaseBean resolveReference(
+            final Method method, final Object... args) throws UnresolvedReferenceException {
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
new file mode 100644
index 0000000..17873f3
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic;
+
+import static org.apache.syncope.core.logic.AbstractLogic.LOG;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.core.MediaType;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.syncope.core.logic.init.OIDCClientClassPathScanImplementationLookup;
+import org.apache.syncope.core.logic.model.OIDCProviderDiscoveryDocument;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.provisioning.api.data.OIDCProviderDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class OIDCProviderLogic extends AbstractTransactionalLogic<OIDCProviderTO> {
+
+    @Autowired
+    private OIDCProviderDAO opDAO;
+
+    @Autowired
+    private OIDCProviderDataBinder binder;
+
+    @Autowired
+    private OIDCClientClassPathScanImplementationLookup implLookup;
+
+    @PreAuthorize("isAuthenticated()")
+    public Set<String> getActionsClasses() {
+        return implLookup.getActionsClasses();
+    }
+
+    private OIDCProviderDiscoveryDocument getDiscoveryDocument(final String issuer) {
+        WebClient client = WebClient.create(
+                issuer + "/.well-known/openid-configuration", Arrays.asList(new JacksonJsonProvider())).
+                accept(MediaType.APPLICATION_JSON);
+        return client.get(OIDCProviderDiscoveryDocument.class);
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_CREATE + "')")
+    public String createFromDiscovery(final OIDCProviderTO opTO) {
+        OIDCProviderDiscoveryDocument discoveryDocument = getDiscoveryDocument(opTO.getIssuer());
+
+        opTO.setAuthorizationEndpoint(discoveryDocument.getAuthorizationEndpoint());
+        opTO.setIssuer(discoveryDocument.getIssuer());
+        opTO.setJwksUri(discoveryDocument.getJwksUri());
+        opTO.setTokenEndpoint(discoveryDocument.getTokenEndpoint());
+        opTO.setUserinfoEndpoint(discoveryDocument.getUserinfoEndpoint());
+
+        return create(opTO);
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_CREATE + "')")
+    public String create(final OIDCProviderTO opTO) {
+        if (opTO.getConnObjectKeyItem() == null) {
+            ItemTO connObjectKeyItem = new ItemTO();
+            connObjectKeyItem.setIntAttrName("username");
+            connObjectKeyItem.setExtAttrName("email");
+            opTO.setConnObjectKeyItem(connObjectKeyItem);
+        }
+
+        OIDCProvider provider = opDAO.save(binder.create(opTO));
+
+        return provider.getKey();
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    public List<OIDCProviderTO> list() {
+        return CollectionUtils.collect(opDAO.findAll(), new Transformer<OIDCProvider, OIDCProviderTO>() {
+
+            @Override
+            public OIDCProviderTO transform(final OIDCProvider input) {
+                return binder.getOIDCProviderTO(input);
+            }
+        }, new ArrayList<OIDCProviderTO>());
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_READ + "')")
+    @Transactional(readOnly = true)
+    public OIDCProviderTO read(final String key) {
+        OIDCProvider op = opDAO.find(key);
+        if (op == null) {
+            throw new NotFoundException("OIDC Provider '" + key + "'");
+        }
+        return binder.getOIDCProviderTO(op);
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_UPDATE + "')")
+    public void update(final OIDCProviderTO oidcProviderTO) {
+        OIDCProvider oidcProvider = opDAO.find(oidcProviderTO.getKey());
+        if (oidcProvider == null) {
+            throw new NotFoundException("OIDC Provider '" + oidcProviderTO.getKey() + "'");
+        }
+
+        if (!oidcProvider.getIssuer().equals(oidcProviderTO.getIssuer())) {
+            LOG.error("Issuers do not match: expected {}, found {}",
+                    oidcProvider.getIssuer(), oidcProviderTO.getIssuer());
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidEntity);
+            sce.getElements().add("Issuers do not match");
+            throw sce;
+        }
+
+        opDAO.save(binder.update(oidcProvider, oidcProviderTO));
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_DELETE + "')")
+    public void delete(final String key) {
+        OIDCProvider op = opDAO.find(key);
+        if (op == null) {
+            throw new NotFoundException("OIDC Provider '" + key + "'");
+        }
+        opDAO.delete(key);
+    }
+
+    @Override
+    protected OIDCProviderTO resolveReference(
+            final Method method, final Object... args) throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof OIDCProviderTO) {
+                    key = ((OIDCProviderTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                return binder.getOIDCProviderTO(opDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java
new file mode 100644
index 0000000..82d6d8a
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.init;
+
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.SyncopeLoader;
+import org.apache.syncope.core.provisioning.api.OIDCProviderActions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
+
+@Component
+public class OIDCClientClassPathScanImplementationLookup implements SyncopeLoader {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ImplementationLookup.class);
+
+    private static final String DEFAULT_BASE_PACKAGE = "org.apache.syncope.core";
+
+    private Set<String> actionsClasses;
+
+    @Override
+    public Integer getPriority() {
+        return Integer.MIN_VALUE;
+    }
+
+    @Override
+    public void load() {
+        actionsClasses = new HashSet<>();
+
+        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AssignableTypeFilter(OIDCProviderActions.class));
+
+        for (BeanDefinition bd : scanner.findCandidateComponents(DEFAULT_BASE_PACKAGE)) {
+            try {
+                Class<?> clazz = ClassUtils.resolveClassName(
+                        bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
+                boolean isAbstractClazz = Modifier.isAbstract(clazz.getModifiers());
+
+                if (OIDCProviderActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
+                    actionsClasses.add(clazz.getName());
+                }
+            } catch (Throwable t) {
+                LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
+            }
+        }
+
+        actionsClasses = Collections.unmodifiableSet(actionsClasses);
+    }
+
+    public Set<String> getActionsClasses() {
+        return actionsClasses;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
new file mode 100644
index 0000000..f24eb79
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.init;
+
+import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.persistence.api.SyncopeLoader;
+import org.apache.syncope.core.provisioning.api.EntitlementsHolder;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class OIDCClientLoader implements SyncopeLoader {
+
+    @Autowired
+    private DomainsHolder domainsHolder;
+
+    @Override
+    public Integer getPriority() {
+        return 1000;
+    }
+
+    @Override
+    public void load() {
+        EntitlementsHolder.getInstance().init(OIDCClientEntitlement.values());
+
+        for (String domain : domainsHolder.getDomains().keySet()) {
+            AuthContextUtils.execWithAuthContext(domain, new AuthContextUtils.Executable<Void>() {
+
+                @Override
+                public Void exec() {
+                    return null;
+                }
+            });
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/OIDCProviderDiscoveryDocument.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/OIDCProviderDiscoveryDocument.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/OIDCProviderDiscoveryDocument.java
new file mode 100644
index 0000000..aae3f0c
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/OIDCProviderDiscoveryDocument.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonPropertyOrder({
+    "issuer",
+    "authorization_endpoint",
+    "token_endpoint",
+    "userinfo_endpoint",
+    "end_session_endpoint",
+    "jwks_uri",
+    "registration_endpoint"
+})
+public class OIDCProviderDiscoveryDocument {
+
+    @JsonProperty("issuer")
+    private String issuer;
+
+    @JsonProperty("authorization_endpoint")
+    private String authorizationEndpoint;
+
+    @JsonProperty("token_endpoint")
+    private String tokenEndpoint;
+
+    @JsonProperty("userinfo_endpoint")
+    private String userinfoEndpoint;
+
+    @JsonProperty("end_session_endpoint")
+    private String endSessionEndpoint;
+
+    @JsonProperty("jwks_uri")
+    private String jwksUri;
+
+    @JsonProperty("registration_endpoint")
+    private String registrationEndpoint;
+
+    @JsonProperty("issuer")
+    public String getIssuer() {
+        return issuer;
+    }
+
+    @JsonProperty("issuer")
+    public void setIssuer(final String issuer) {
+        this.issuer = issuer;
+    }
+
+    @JsonProperty("authorization_endpoint")
+    public String getAuthorizationEndpoint() {
+        return authorizationEndpoint;
+    }
+
+    @JsonProperty("authorization_endpoint")
+    public void setAuthorizationEndpoint(final String authorizationEndpoint) {
+        this.authorizationEndpoint = authorizationEndpoint;
+    }
+
+    @JsonProperty("token_endpoint")
+    public String getTokenEndpoint() {
+        return tokenEndpoint;
+    }
+
+    @JsonProperty("token_endpoint")
+    public void setTokenEndpoint(final String tokenEndpoint) {
+        this.tokenEndpoint = tokenEndpoint;
+    }
+
+    @JsonProperty("userinfo_endpoint")
+    public String getUserinfoEndpoint() {
+        return userinfoEndpoint;
+    }
+
+    @JsonProperty("userinfo_endpoint")
+    public void setUserinfoEndpoint(final String userinfoEndpoint) {
+        this.userinfoEndpoint = userinfoEndpoint;
+    }
+
+    @JsonProperty("end_session_endpoint")
+    public String getEndSessionEndpoint() {
+        return endSessionEndpoint;
+    }
+
+    @JsonProperty("end_session_endpoint")
+    public void setEndSessionEndpoint(final String endSessionEndpoint) {
+        this.endSessionEndpoint = endSessionEndpoint;
+    }
+
+    @JsonProperty("jwks_uri")
+    public String getJwksUri() {
+        return jwksUri;
+    }
+
+    @JsonProperty("jwks_uri")
+    public void setJwksUri(final String jwksUri) {
+        this.jwksUri = jwksUri;
+    }
+
+    @JsonProperty("registration_endpoint")
+    public String getRegistrationEndpoint() {
+        return registrationEndpoint;
+    }
+
+    @JsonProperty("registration_endpoint")
+    public void setRegistrationEndpoint(final String registrationEndpoint) {
+        this.registrationEndpoint = registrationEndpoint;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/TokenEndpointResponse.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/TokenEndpointResponse.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/TokenEndpointResponse.java
new file mode 100644
index 0000000..7908b88
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/TokenEndpointResponse.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonPropertyOrder({
+    "access_token",
+    "id_token",
+    "expires_in",
+    "token_type"
+})
+public class TokenEndpointResponse {
+
+    @JsonProperty("access_token")
+    private String accessToken;
+
+    @JsonProperty("id_token")
+    private String idToken;
+
+    @JsonProperty("expires_in")
+    private int expiresIn;
+
+    @JsonProperty("token_type")
+    private String tokenType;
+
+    @JsonProperty("access_token")
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    @JsonProperty("access_token")
+    public void setAccessToken(final String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    @JsonProperty("id_token")
+    public String getIdToken() {
+        return idToken;
+    }
+
+    @JsonProperty("id_token")
+    public void setIdToken(final String idToken) {
+        this.idToken = idToken;
+    }
+
+    @JsonProperty("expires_in")
+    public int getExpiresIn() {
+        return expiresIn;
+    }
+
+    @JsonProperty("expires_in")
+    public void setExpiresIn(final int expiresIn) {
+        this.expiresIn = expiresIn;
+    }
+
+    @JsonProperty("token_type")
+    public String getTokenType() {
+        return tokenType;
+    }
+
+    @JsonProperty("token_type")
+    public void setTokenType(final String tokenType) {
+        this.tokenType = tokenType;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
new file mode 100644
index 0000000..60a2fd8
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.oidc;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.IntAttrName;
+import org.apache.syncope.core.provisioning.api.OIDCProviderActions;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class OIDCUserManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCUserManager.class);
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private PlainSchemaDAO plainSchemaDAO;
+
+    @Autowired
+    private IntAttrNameParser intAttrNameParser;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private TemplateUtils templateUtils;
+
+    @Autowired
+    private UserProvisioningManager provisioningManager;
+
+    @Autowired
+    private UserDataBinder binder;
+
+    @Transactional(readOnly = true)
+    public List<String> findMatchingUser(final String keyValue, final OIDCProviderItem connObjectKeyItem) {
+        List<String> result = new ArrayList<>();
+
+        String transformed = keyValue;
+        for (ItemTransformer transformer : MappingUtils.getItemTransformers(connObjectKeyItem)) {
+            List<Object> output = transformer.beforePull(
+                    null,
+                    null,
+                    Collections.<Object>singletonList(transformed));
+            if (output != null && !output.isEmpty()) {
+                transformed = output.get(0).toString();
+            }
+        }
+
+        IntAttrName intAttrName;
+        try {
+            intAttrName = intAttrNameParser.parse(connObjectKeyItem.getIntAttrName(), AnyTypeKind.USER);
+        } catch (ParseException e) {
+            LOG.error("Invalid intAttrName '{}' specified, ignoring", connObjectKeyItem.getIntAttrName(), e);
+            return result;
+        }
+
+        if (intAttrName.getField() != null) {
+            switch (intAttrName.getField()) {
+                case "key":
+                    User byKey = userDAO.find(transformed);
+                    if (byKey != null) {
+                        result.add(byKey.getUsername());
+                    }
+                    break;
+
+                case "username":
+                    User byUsername = userDAO.findByUsername(transformed);
+                    if (byUsername != null) {
+                        result.add(byUsername.getUsername());
+                    }
+                    break;
+
+                default:
+            }
+        } else if (intAttrName.getSchemaType() != null) {
+            switch (intAttrName.getSchemaType()) {
+                case PLAIN:
+                    PlainAttrValue value = entityFactory.newEntity(UPlainAttrValue.class);
+
+                    PlainSchema schema = plainSchemaDAO.find(intAttrName.getSchemaName());
+                    if (schema == null) {
+                        value.setStringValue(transformed);
+                    } else {
+                        try {
+                            value.parseValue(schema, transformed);
+                        } catch (ParsingValidationException e) {
+                            LOG.error("While parsing provided key value {}", transformed, e);
+                            value.setStringValue(transformed);
+                        }
+                    }
+
+                    CollectionUtils.collect(
+                            userDAO.findByPlainAttrValue(intAttrName.getSchemaName(), value),
+                            new Transformer<User, String>() {
+
+                        @Override
+                        public String transform(final User input) {
+                            return input.getUsername();
+                        }
+                    }, result);
+                    break;
+
+                case DERIVED:
+                    CollectionUtils.collect(
+                            userDAO.findByDerAttrValue(intAttrName.getSchemaName(), transformed),
+                            new Transformer<User, String>() {
+
+                        @Override
+                        public String transform(final User input) {
+                            return input.getUsername();
+                        }
+                    }, result);
+                    break;
+
+                default:
+            }
+        }
+
+        return result;
+    }
+
+    private List<OIDCProviderActions> getActions(final OIDCProvider op) {
+        List<OIDCProviderActions> actions = new ArrayList<>();
+
+        for (String className : op.getActionsClassNames()) {
+            try {
+                Class<?> actionsClass = Class.forName(className);
+                OIDCProviderActions opActions = (OIDCProviderActions) ApplicationContextProvider.getBeanFactory().
+                        createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+
+                actions.add(opActions);
+            } catch (Exception e) {
+                LOG.warn("Class '{}' not found", className, e);
+            }
+        }
+
+        return actions;
+    }
+
+    public void fill(final OIDCProvider op, final OIDCLoginResponseTO responseTO, final UserTO userTO) {
+        for (OIDCProviderItem item : op.getItems()) {
+            List<String> values = Collections.emptyList();
+            AttrTO oidcAttr = responseTO.getAttr(item.getExtAttrName());
+            if (oidcAttr != null && !oidcAttr.getValues().isEmpty()) {
+                values = oidcAttr.getValues();
+
+                List<Object> transformed = new ArrayList<Object>(values);
+                for (ItemTransformer transformer : MappingUtils.getItemTransformers(item)) {
+                    transformed = transformer.beforePull(null, userTO, transformed);
+                }
+                values.clear();
+                for (Object value : transformed) {
+                    values.add(value.toString());
+                }
+            }
+
+            IntAttrName intAttrName = null;
+            try {
+                intAttrName = intAttrNameParser.parse(item.getIntAttrName(), AnyTypeKind.USER);
+            } catch (ParseException e) {
+                LOG.error("Invalid intAttrName '{}' specified, ignoring", item.getIntAttrName(), e);
+            }
+
+            if (intAttrName != null && intAttrName.getField() != null) {
+                switch (intAttrName.getField()) {
+                    case "username":
+                        if (!values.isEmpty()) {
+                            userTO.setUsername(values.get(0));
+                        }
+                        break;
+
+                    default:
+                        LOG.warn("Unsupported: {}", intAttrName.getField());
+                }
+            } else if (intAttrName != null && intAttrName.getSchemaType() != null) {
+                switch (intAttrName.getSchemaType()) {
+                    case PLAIN:
+                        AttrTO attr = userTO.getPlainAttr(intAttrName.getSchemaName());
+                        if (attr == null) {
+                            attr = new AttrTO.Builder().schema(intAttrName.getSchemaName()).build();
+                            userTO.getPlainAttrs().add(attr);
+                        }
+                        attr.getValues().clear();
+                        attr.getValues().addAll(values);
+                        break;
+
+                    default:
+                        LOG.warn("Unsupported: {} {}", intAttrName.getSchemaType(), intAttrName.getSchemaName());
+                }
+            }
+        }
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public String create(final OIDCProvider op, final OIDCLoginResponseTO responseTO, final String email) {
+        UserTO userTO = new UserTO();
+
+        if (op.getUserTemplate() != null) {
+            templateUtils.apply(userTO, op.getUserTemplate());
+        }
+
+        List<OIDCProviderActions> actions = getActions(op);
+        for (OIDCProviderActions action : actions) {
+            userTO = action.beforeCreate(userTO, responseTO);
+        }
+
+        fill(op, responseTO, userTO);
+
+        if (userTO.getRealm() == null) {
+            userTO.setRealm(SyncopeConstants.ROOT_REALM);
+        }
+        if (userTO.getUsername() == null) {
+            userTO.setUsername(email);
+        }
+
+        Pair<String, List<PropagationStatus>> created = provisioningManager.create(userTO, false, false);
+        userTO = binder.getUserTO(created.getKey());
+
+        for (OIDCProviderActions action : actions) {
+            userTO = action.afterCreate(userTO, responseTO);
+        }
+
+        return userTO.getUsername();
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public String update(final String username, final OIDCProvider op, final OIDCLoginResponseTO responseTO) {
+        UserTO userTO = binder.getUserTO(userDAO.findKey(username));
+        UserTO original = SerializationUtils.clone(userTO);
+
+        fill(op, responseTO, userTO);
+
+        UserPatch userPatch = AnyOperations.diff(userTO, original, true);
+
+        List<OIDCProviderActions> actions = getActions(op);
+        for (OIDCProviderActions action : actions) {
+            userPatch = action.beforeUpdate(userPatch, responseTO);
+        }
+
+        Pair<UserPatch, List<PropagationStatus>> updated = provisioningManager.update(userPatch, false);
+        userTO = binder.getUserTO(updated.getLeft().getKey());
+
+        for (OIDCProviderActions action : actions) {
+            userTO = action.afterUpdate(userTO, responseTO);
+        }
+
+        return userTO.getUsername();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-api/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/pom.xml b/ext/oidcclient/persistence-api/pom.xml
new file mode 100644
index 0000000..c4b80e1
--- /dev/null
+++ b/ext/oidcclient/persistence-api/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Persistence API</name>
+  <description>Apache Syncope Ext: OIDC Client Persistence API</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-persistence-api</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-persistence-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCProviderDAO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCProviderDAO.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCProviderDAO.java
new file mode 100644
index 0000000..793e461
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCProviderDAO.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+
+public interface OIDCProviderDAO {
+
+    OIDCProvider find(String key);
+
+    OIDCProvider findByName(String name);
+
+    List<OIDCProvider> findAll();
+
+    OIDCProvider save(final OIDCProvider op);
+
+    void delete(String key);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCEntityFactory.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCEntityFactory.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCEntityFactory.java
new file mode 100644
index 0000000..489ce86
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCEntityFactory.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+public interface OIDCEntityFactory {
+
+    <E extends Entity> E newEntity(Class<E> reference);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
new file mode 100644
index 0000000..cf30781
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+import java.util.List;
+import java.util.Set;
+
+public interface OIDCProvider extends Entity {
+
+    String getName();
+
+    void setName(String entityID);
+
+    String getClientID();
+
+    void setClientID(String clientId);
+
+    String getClientSecret();
+
+    void setClientSecret(String clientSecret);
+
+    String getAuthorizationEndpoint();
+
+    void setAuthorizationEndpoint(String authorizationEndpoint);
+
+    String getTokenEndpoint();
+
+    void setTokenEndpoint(String tokenEndpoint);
+
+    String getJwksUri();
+
+    void setJwksUri(String jwsUri);
+
+    String getIssuer();
+
+    void setIssuer(String issuer);
+
+    String getUserinfoEndpoint();
+
+    void setUserinfoEndpoint(String userinfoEndpoint);
+
+    boolean getHasDiscovery();
+
+    void setHasDiscovery(boolean hasDiscovery);
+    
+    boolean isCreateUnmatching();
+
+    void setCreateUnmatching(boolean createUnmatching);
+
+    boolean isUpdateMatching();
+
+    void setUpdateMatching(boolean updateMatching);
+
+    OIDCUserTemplate getUserTemplate();
+
+    void setUserTemplate(OIDCUserTemplate userTemplate);
+
+    List<? extends OIDCProviderItem> getItems();
+
+    OIDCProviderItem getConnObjectKeyItem();
+
+    void setConnObjectKeyItem(OIDCProviderItem item);
+
+    boolean add(OIDCProviderItem item);
+
+    Set<String> getActionsClassNames();
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProviderItem.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProviderItem.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProviderItem.java
new file mode 100644
index 0000000..3047c88
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProviderItem.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+
+public interface OIDCProviderItem extends Item {
+
+    OIDCProvider getOP();
+
+    void setOP(OIDCProvider op);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCUserTemplate.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCUserTemplate.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCUserTemplate.java
new file mode 100644
index 0000000..339f6ec
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCUserTemplate.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+public interface OIDCUserTemplate extends AnyTemplate {
+
+    OIDCProvider getOP();
+
+    void setOP(OIDCProvider op);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/pom.xml b/ext/oidcclient/persistence-jpa/pom.xml
new file mode 100644
index 0000000..ac404cf
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/pom.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.0.9-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Persistence JPA</name>
+  <description>Apache Syncope Ext: OIDC Client Persistence JPA</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>    
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-persistence-jpa</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-persistence-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.openjpa</groupId>
+        <artifactId>openjpa-maven-plugin</artifactId>
+        <inherited>true</inherited>
+        <dependencies>
+          <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <version>${h2.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <persistenceXmlFile>${rootpom.basedir}/core/persistence-jpa/src/main/resources/persistence-enhance.xml</persistenceXmlFile> 
+          <includes>org/apache/syncope/core/persistence/jpa/entity/**/*.class</includes>
+          <connectionDriverName>org.springframework.jdbc.datasource.DriverManagerDataSource</connectionDriverName>
+          <connectionProperties>
+            driverClassName=org.h2.Driver,
+            url=jdbc:h2:mem:syncopedb
+            username=sa,
+            password=
+          </connectionProperties>
+        </configuration>
+        <executions>
+          <execution>
+            <id>enhancer</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>enhance</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+
+    <testResources>
+      <testResource>
+        <directory>${rootpom.basedir}/core/persistence-jpa/src/main/resources</directory>
+        <filtering>true</filtering>        
+      </testResource>
+    </testResources>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>sqlgen</id>
+      
+      <properties>
+        <skipTests>true</skipTests>
+      </properties>
+      
+      <build>
+        <defaultGoal>clean verify</defaultGoal>
+        
+        <plugins>
+          <plugin>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa-maven-plugin</artifactId>
+            <inherited>true</inherited>
+            <executions>
+              <execution>
+                <id>sqlgenr</id>
+                <phase>process-classes</phase>
+                <goals>
+                  <goal>sql</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>          
+        </plugins>
+      </build>
+        
+    </profile>
+  </profiles>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCProviderDAO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCProviderDAO.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCProviderDAO.java
new file mode 100644
index 0000000..91e3af0
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCProviderDAO.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.List;
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.jpa.entity.JPAOIDCProvider;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+@Repository
+public class JPAOIDCProviderDAO extends AbstractDAO<OIDCProvider> implements OIDCProviderDAO {
+
+    @Transactional(readOnly = true)
+    @Override
+    public OIDCProvider find(final String key) {
+        return entityManager().find(JPAOIDCProvider.class, key);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public OIDCProvider findByName(final String name) {
+        TypedQuery<OIDCProvider> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAOIDCProvider.class.getSimpleName()
+                + " e WHERE e.name = :name", OIDCProvider.class);
+        query.setParameter("name", name);
+
+        OIDCProvider result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (NoResultException e) {
+            LOG.debug("No OIDC Provider found with name {}", name, e);
+        }
+
+        return result;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<OIDCProvider> findAll() {
+        TypedQuery<OIDCProvider> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAOIDCProvider.class.getSimpleName() + " e", OIDCProvider.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public OIDCProvider save(final OIDCProvider op) {
+        return entityManager().merge(op);
+    }
+
+    @Override
+    public void delete(final String key) {
+        OIDCProvider op = find(key);
+        if (op != null) {
+            entityManager().remove(op);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCEntityFactory.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCEntityFactory.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCEntityFactory.java
new file mode 100644
index 0000000..e5870bf
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCEntityFactory.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.OIDCEntityFactory;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class JPAOIDCEntityFactory implements OIDCEntityFactory {
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <E extends Entity> E newEntity(final Class<E> reference) {
+        E result;
+
+        if (reference.equals(OIDCProvider.class)) {
+            result = (E) new JPAOIDCProvider();
+        } else if (reference.equals(OIDCProviderItem.class)) {
+            result = (E) new JPAOIDCProviderItem();
+        } else if (reference.equals(OIDCUserTemplate.class)) {
+            result = (E) new JPAOIDCUserTemplate();
+        } else {
+            throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName());
+        }
+
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f29e60f6/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
new file mode 100644
index 0000000..5ea6468
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.persistence.Cacheable;
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
+
+@Entity
+@Table(name = JPAOIDCProvider.TABLE)
+@Cacheable
+public class JPAOIDCProvider extends AbstractGeneratedKeyEntity implements OIDCProvider {
+
+    public static final String TABLE = "OIDCProvider";
+
+    private static final long serialVersionUID = 1423093003585826403L;
+
+    @Column(unique = true, nullable = false)
+    private String name;
+
+    @Column(unique = true, nullable = false)
+    private String clientID;
+
+    @Column(unique = true, nullable = false)
+    private String clientSecret;
+
+    @Column(nullable = false)
+    private String authorizationEndpoint;
+
+    @Column(nullable = false)
+    private String tokenEndpoint;
+
+    @Column(nullable = false)
+    private String jwksUri;
+
+    @Column(nullable = false)
+    private String issuer;
+
+    @Column(nullable = false)
+    private String userinfoEndpoint;
+
+    @Column(nullable = false)
+    private boolean hasDiscovery;
+
+    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "op")
+    private JPAOIDCUserTemplate userTemplate;
+
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "op")
+    private List<JPAOIDCProviderItem> items = new ArrayList<>();
+
+    @Min(0)
+    @Max(1)
+    @Column(nullable = false)
+    private Integer createUnmatching;
+
+    @Min(0)
+    @Max(1)
+    @Column(nullable = false)
+    private Integer updateMatching;
+
+    @ElementCollection(fetch = FetchType.EAGER)
+    @Column(name = "actionClassName")
+    @CollectionTable(name = TABLE + "_actionsClassNames",
+            joinColumns =
+            @JoinColumn(name = "oidcOP_id", referencedColumnName = "id"))
+    private Set<String> actionsClassNames = new HashSet<>();
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getClientID() {
+        return clientID;
+    }
+
+    @Override
+    public void setClientID(final String clientID) {
+        this.clientID = clientID;
+    }
+
+    @Override
+    public String getClientSecret() {
+        return clientSecret;
+    }
+
+    @Override
+    public void setClientSecret(final String clientSecret) {
+        this.clientSecret = clientSecret;
+    }
+
+    @Override
+    public String getAuthorizationEndpoint() {
+        return authorizationEndpoint;
+    }
+
+    @Override
+    public void setAuthorizationEndpoint(final String authorizationEndpoint) {
+        this.authorizationEndpoint = authorizationEndpoint;
+    }
+
+    @Override
+    public String getTokenEndpoint() {
+        return tokenEndpoint;
+    }
+
+    @Override
+    public void setTokenEndpoint(final String tokenEndpoint) {
+        this.tokenEndpoint = tokenEndpoint;
+    }
+
+    @Override
+    public String getJwksUri() {
+        return jwksUri;
+    }
+
+    @Override
+    public void setJwksUri(final String jwksUri) {
+        this.jwksUri = jwksUri;
+    }
+
+    @Override
+    public String getIssuer() {
+        return issuer;
+    }
+
+    @Override
+    public void setIssuer(final String issuer) {
+        this.issuer = issuer;
+    }
+
+    @Override
+    public String getUserinfoEndpoint() {
+        return userinfoEndpoint;
+    }
+
+    @Override
+    public void setUserinfoEndpoint(final String userinfoEndpoint) {
+        this.userinfoEndpoint = userinfoEndpoint;
+    }
+
+    @Override
+    public boolean getHasDiscovery() {
+        return hasDiscovery;
+    }
+
+    @Override
+    public void setHasDiscovery(final boolean hasDiscovery) {
+        this.hasDiscovery = hasDiscovery;
+    }
+
+    @Override
+    public boolean isCreateUnmatching() {
+        return isBooleanAsInteger(createUnmatching);
+    }
+
+    @Override
+    public void setCreateUnmatching(final boolean createUnmatching) {
+        this.createUnmatching = getBooleanAsInteger(createUnmatching);
+    }
+
+    @Override
+    public boolean isUpdateMatching() {
+        return isBooleanAsInteger(updateMatching);
+    }
+
+    @Override
+    public void setUpdateMatching(final boolean updateMatching) {
+        this.updateMatching = getBooleanAsInteger(updateMatching);
+    }
+
+    @Override
+    public OIDCUserTemplate getUserTemplate() {
+        return userTemplate;
+    }
+
+    @Override
+    public void setUserTemplate(final OIDCUserTemplate userTemplate) {
+        checkType(userTemplate, JPAOIDCUserTemplate.class);
+        this.userTemplate = (JPAOIDCUserTemplate) userTemplate;
+    }
+
+    @Override
+    public boolean add(final OIDCProviderItem item) {
+        checkType(item, JPAOIDCProviderItem.class);
+        return items.contains((JPAOIDCProviderItem) item) || items.add((JPAOIDCProviderItem) item);
+    }
+
+    @Override
+    public List<? extends OIDCProviderItem> getItems() {
+        return items;
+    }
+
+    @Override
+    public OIDCProviderItem getConnObjectKeyItem() {
+        return IterableUtils.find(getItems(), new Predicate<OIDCProviderItem>() {
+
+            @Override
+            public boolean evaluate(final OIDCProviderItem item) {
+                return item.isConnObjectKey();
+            }
+        });
+    }
+
+    @Override
+    public void setConnObjectKeyItem(final OIDCProviderItem item) {
+        item.setConnObjectKey(true);
+        this.add(item);
+    }
+
+    @Override
+    public Set<String> getActionsClassNames() {
+        return actionsClassNames;
+    }
+
+}


[6/8] syncope git commit: [SYNCOPE-1270] implementation for OpenID Connect for Admin Console and Enduser - This closes #74

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/logic/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/pom.xml b/ext/oidcclient/logic/pom.xml
new file mode 100644
index 0000000..6701bc3
--- /dev/null
+++ b/ext/oidcclient/logic/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Logic</name>
+  <description>Apache Syncope Ext: OIDC Client Logic</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-logic</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-logic</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-provisioning-java</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-extension-providers</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-security-sso-oidc</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.fasterxml.jackson.jaxrs</groupId>
+      <artifactId>jackson-jaxrs-json-provider</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCClientLogic.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCClientLogic.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCClientLogic.java
new file mode 100644
index 0000000..638ae5e
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCClientLogic.java
@@ -0,0 +1,406 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.uuid.Generators;
+import com.fasterxml.uuid.impl.RandomBasedGenerator;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.MediaType;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.provider.json.JsonMapObjectProvider;
+import org.apache.cxf.rs.security.jose.jaxrs.JsonWebKeysProvider;
+import org.apache.cxf.rs.security.oauth2.client.Consumer;
+import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
+import org.apache.cxf.rs.security.oidc.common.IdToken;
+import org.apache.cxf.rs.security.oidc.common.UserInfo;
+import org.apache.cxf.rs.security.oidc.rp.IdTokenReader;
+import org.apache.cxf.rs.security.oidc.rp.UserInfoClient;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.OIDCConstants;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.OIDCLoginRequestTO;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.logic.model.TokenEndpointResponse;
+import org.apache.syncope.core.logic.oidc.OIDCUserManager;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.spring.security.AuthDataAccessor;
+import org.apache.syncope.core.spring.security.Encryptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class OIDCClientLogic extends AbstractTransactionalLogic<AbstractBaseBean> {
+
+    private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private static final RandomBasedGenerator UUID_GENERATOR = Generators.randomBasedGenerator();
+
+    private static final String JWT_CLAIM_OP_ENTITYID = "OP_ENTITYID";
+
+    private static final String JWT_CLAIM_USERID = "USERID";
+
+    @Autowired
+    private AuthDataAccessor authDataAccessor;
+
+    @Autowired
+    private AccessTokenDataBinder accessTokenDataBinder;
+
+    @Autowired
+    private OIDCProviderDAO opDAO;
+
+    @Autowired
+    private OIDCUserManager userManager;
+
+    private OIDCProvider getOIDCProvider(final String opName) {
+        OIDCProvider op = null;
+        if (StringUtils.isBlank(opName)) {
+            List<OIDCProvider> ops = opDAO.findAll();
+            if (!ops.isEmpty()) {
+                op = ops.get(0);
+            }
+        } else {
+            op = opDAO.findByName(opName);
+        }
+        if (op == null) {
+            throw new NotFoundException(StringUtils.isBlank(opName)
+                    ? "Any OIDC Provider"
+                    : "OIDC Provider '" + opName + "'");
+        }
+        return op;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.ANONYMOUS + "')")
+    public OIDCLoginRequestTO createLoginRequest(final String redirectURI, final String opName) {
+        // 1. look for Provider
+        OIDCProvider op = getOIDCProvider(opName);
+
+        // 2. create AuthnRequest
+        OIDCLoginRequestTO requestTO = new OIDCLoginRequestTO();
+        requestTO.setProviderAddress(op.getAuthorizationEndpoint());
+        requestTO.setClientId(op.getClientID());
+        requestTO.setScope("openid email profile");
+        requestTO.setResponseType("code");
+        requestTO.setRedirectURI(redirectURI);
+        requestTO.setState(UUID_GENERATOR.generate().toString());
+        return requestTO;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.ANONYMOUS + "')")
+    public OIDCLoginResponseTO login(final String redirectURI, final String authorizationCode, final String opName) {
+        OIDCProvider op = getOIDCProvider(opName);
+
+        // 1. get OpenID Connect tokens
+        String body = OIDCConstants.CODE + "=" + authorizationCode
+                + "&" + OIDCConstants.CLIENT_ID + "=" + op.getClientID()
+                + "&" + OIDCConstants.CLIENT_SECRET + "=" + op.getClientSecret()
+                + "&" + OIDCConstants.REDIRECT_URI + "=" + redirectURI
+                + "&" + OIDCConstants.GRANT_TYPE + "=authorization_code";
+        TokenEndpointResponse tokenEndpointResponse = getOIDCTokens(op.getTokenEndpoint(), body);
+
+        // 1. get OpenID Connect tokens
+        Consumer consumer = new Consumer(op.getClientID(), op.getClientSecret());
+
+        // 2. validate token
+        IdToken idToken = getValidatedIdToken(op, consumer, tokenEndpointResponse.getIdToken());
+
+        // 3. extract user information
+        UserInfo userInfo = getUserInfo(op, tokenEndpointResponse.getAccessToken(), idToken, consumer);
+
+        // 4. prepare the result: find matching user (if any) and return the received attributes
+        final OIDCLoginResponseTO responseTO = new OIDCLoginResponseTO();
+        responseTO.setEmail(userInfo.getEmail());
+        responseTO.setFamilyName(userInfo.getFamilyName());
+        responseTO.setGivenName(userInfo.getGivenName());
+        responseTO.setName(userInfo.getName());
+        responseTO.setSubject(userInfo.getSubject());
+
+        String keyValue = null;
+        for (OIDCProviderItem item : op.getItems()) {
+            AttrTO attrTO = new AttrTO();
+            attrTO.setSchema(item.getExtAttrName());
+            switch (item.getExtAttrName()) {
+                case UserInfo.PREFERRED_USERNAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getPreferredUserName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getPreferredUserName();
+                    }
+                    break;
+
+                case UserInfo.PROFILE_CLAIM:
+                    attrTO.getValues().add(userInfo.getProfile());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getProfile();
+                    }
+                    break;
+
+                case UserInfo.EMAIL_CLAIM:
+                    attrTO.getValues().add(userInfo.getEmail());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getEmail();
+                    }
+                    break;
+
+                case UserInfo.NAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getName();
+                    }
+                    break;
+
+                case UserInfo.FAMILY_NAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getFamilyName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getFamilyName();
+                    }
+                    break;
+
+                case UserInfo.MIDDLE_NAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getMiddleName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getMiddleName();
+                    }
+                    break;
+
+                case UserInfo.GIVEN_NAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getGivenName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getGivenName();
+                    }
+                    break;
+
+                case UserInfo.NICKNAME_CLAIM:
+                    attrTO.getValues().add(userInfo.getNickName());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getNickName();
+                    }
+                    break;
+
+                case UserInfo.GENDER_CLAIM:
+                    attrTO.getValues().add(userInfo.getGender());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getGender();
+                    }
+                    break;
+
+                case UserInfo.LOCALE_CLAIM:
+                    attrTO.getValues().add(userInfo.getLocale());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getLocale();
+                    }
+                    break;
+
+                case UserInfo.ZONEINFO_CLAIM:
+                    attrTO.getValues().add(userInfo.getZoneInfo());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getZoneInfo();
+                    }
+                    break;
+
+                case UserInfo.BIRTHDATE_CLAIM:
+                    attrTO.getValues().add(userInfo.getBirthDate());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getBirthDate();
+                    }
+                    break;
+
+                case UserInfo.PHONE_CLAIM:
+                    attrTO.getValues().add(userInfo.getPhoneNumber());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getPhoneNumber();
+                    }
+                    break;
+
+                case UserInfo.ADDRESS_CLAIM:
+                    attrTO.getValues().add(userInfo.getUserAddress().getFormatted());
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = userInfo.getUserAddress().getFormatted();
+                    }
+                    break;
+
+                case UserInfo.UPDATED_AT_CLAIM:
+                    attrTO.getValues().add(Long.toString(userInfo.getUpdatedAt()));
+                    responseTO.getAttrs().add(attrTO);
+                    if (item.isConnObjectKey()) {
+                        keyValue = Long.toString(userInfo.getUpdatedAt());
+                    }
+                    break;
+
+                default:
+                    LOG.warn("Unsupported: {} ", item.getExtAttrName());
+            }
+        }
+
+        final List<String> matchingUsers = keyValue == null
+                ? Collections.<String>emptyList()
+                : userManager.findMatchingUser(keyValue, op.getConnObjectKeyItem().get());
+        LOG.debug("Found {} matching users for {}", matchingUsers.size(), keyValue);
+
+        String username;
+        if (matchingUsers.isEmpty()) {
+            if (op.isCreateUnmatching()) {
+                LOG.debug("No user matching {}, about to create", keyValue);
+
+                final String emailValue = userInfo.getEmail();
+                username = AuthContextUtils.execWithAuthContext(AuthContextUtils.getDomain(),
+                        () -> userManager.create(op, responseTO, emailValue));
+            } else {
+                throw new NotFoundException("User matching the provided value " + keyValue);
+            }
+        } else if (matchingUsers.size() > 1) {
+            throw new IllegalArgumentException("Several users match the provided value " + keyValue);
+        } else {
+            if (op.isUpdateMatching()) {
+                LOG.debug("About to update {} for {}", matchingUsers.get(0), keyValue);
+
+                username = AuthContextUtils.execWithAuthContext(AuthContextUtils.getDomain(),
+                        () -> userManager.update(matchingUsers.get(0), op, responseTO));
+            } else {
+                username = matchingUsers.get(0);
+            }
+
+        }
+
+        responseTO.setUsername(username);
+
+        // 5. generate JWT for further access
+        Map<String, Object> claims = new HashMap<>();
+        claims.put(JWT_CLAIM_OP_ENTITYID, idToken.getIssuer());
+        claims.put(JWT_CLAIM_USERID, idToken.getSubject());
+
+        byte[] authorities = null;
+        try {
+            authorities = ENCRYPTOR.encode(POJOHelper.serialize(
+                    authDataAccessor.getAuthorities(responseTO.getUsername())), CipherAlgorithm.AES).
+                    getBytes();
+        } catch (Exception e) {
+            LOG.error("Could not fetch authorities", e);
+        }
+
+        Pair<String, Date> accessTokenInfo =
+                accessTokenDataBinder.create(responseTO.getUsername(), claims, authorities, true);
+        responseTO.setAccessToken(accessTokenInfo.getLeft());
+        responseTO.setAccessTokenExpiryTime(accessTokenInfo.getRight());
+
+        return responseTO;
+    }
+
+    private TokenEndpointResponse getOIDCTokens(final String url, final String body) {
+        String oidcTokens = WebClient.create(url).
+                type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.APPLICATION_JSON).
+                post(body).
+                readEntity(String.class);
+        TokenEndpointResponse endpointResponse = null;
+        try {
+            endpointResponse = MAPPER.readValue(oidcTokens, TokenEndpointResponse.class);
+        } catch (Exception e) {
+            LOG.error("While getting the Tokens from the OP", e);
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+        return endpointResponse;
+    }
+
+    private IdToken getValidatedIdToken(final OIDCProvider op, final Consumer consumer, final String jwtIdToken) {
+        IdTokenReader idTokenReader = new IdTokenReader();
+        idTokenReader.setClockOffset(10);
+        idTokenReader.setIssuerId(op.getIssuer());
+        WebClient jwkSetClient = WebClient.create(
+                op.getJwksUri(), Arrays.asList(new JsonWebKeysProvider())).
+                accept(MediaType.APPLICATION_JSON);
+        idTokenReader.setJwkSetClient(jwkSetClient);
+        IdToken idToken = null;
+        try {
+            idToken = idTokenReader.getIdToken(jwtIdToken, consumer);
+        } catch (Exception e) {
+            LOG.error("While validating the id_token", e);
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+        return idToken;
+    }
+
+    private UserInfo getUserInfo(
+            final OIDCProvider op,
+            final String accessToken,
+            final IdToken idToken,
+            final Consumer consumer) {
+
+        WebClient userInfoServiceClient = WebClient.create(
+                op.getUserinfoEndpoint(), Arrays.asList(new JsonMapObjectProvider())).
+                accept(MediaType.APPLICATION_JSON);
+        ClientAccessToken clientAccessToken = new ClientAccessToken("Bearer", accessToken);
+        UserInfoClient userInfoClient = new UserInfoClient();
+        userInfoClient.setUserInfoServiceClient(userInfoServiceClient);
+        UserInfo userInfo = null;
+        try {
+            userInfo = userInfoClient.getUserInfo(clientAccessToken, idToken, consumer);
+        } catch (Exception e) {
+            LOG.error("While getting the userInfo", e);
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+        return userInfo;
+    }
+
+    @Override
+    protected AbstractBaseBean resolveReference(
+            final Method method, final Object... args) throws UnresolvedReferenceException {
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
new file mode 100644
index 0000000..af8e579
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/OIDCProviderLogic.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.ws.rs.core.MediaType;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.OIDCProviderTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.syncope.core.logic.init.OIDCClientClassPathScanImplementationLookup;
+import org.apache.syncope.core.logic.model.OIDCProviderDiscoveryDocument;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.provisioning.api.data.OIDCProviderDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class OIDCProviderLogic extends AbstractTransactionalLogic<OIDCProviderTO> {
+
+    @Autowired
+    private OIDCProviderDAO opDAO;
+
+    @Autowired
+    private OIDCProviderDataBinder binder;
+
+    @Autowired
+    private OIDCClientClassPathScanImplementationLookup implLookup;
+
+    @PreAuthorize("isAuthenticated()")
+    public Set<String> getActionsClasses() {
+        return implLookup.getActionsClasses();
+    }
+
+    private OIDCProviderDiscoveryDocument getDiscoveryDocument(final String issuer) {
+        WebClient client = WebClient.create(
+                issuer + "/.well-known/openid-configuration", Arrays.asList(new JacksonJsonProvider())).
+                accept(MediaType.APPLICATION_JSON);
+        return client.get(OIDCProviderDiscoveryDocument.class);
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_CREATE + "')")
+    public String createFromDiscovery(final OIDCProviderTO opTO) {
+        OIDCProviderDiscoveryDocument discoveryDocument = getDiscoveryDocument(opTO.getIssuer());
+
+        opTO.setAuthorizationEndpoint(discoveryDocument.getAuthorizationEndpoint());
+        opTO.setIssuer(discoveryDocument.getIssuer());
+        opTO.setJwksUri(discoveryDocument.getJwksUri());
+        opTO.setTokenEndpoint(discoveryDocument.getTokenEndpoint());
+        opTO.setUserinfoEndpoint(discoveryDocument.getUserinfoEndpoint());
+
+        return create(opTO);
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_CREATE + "')")
+    public String create(final OIDCProviderTO opTO) {
+        if (opTO.getConnObjectKeyItem() == null) {
+            ItemTO connObjectKeyItem = new ItemTO();
+            connObjectKeyItem.setIntAttrName("username");
+            connObjectKeyItem.setExtAttrName("email");
+            opTO.setConnObjectKeyItem(connObjectKeyItem);
+        }
+
+        OIDCProvider provider = opDAO.save(binder.create(opTO));
+
+        return provider.getKey();
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    public List<OIDCProviderTO> list() {
+        return opDAO.findAll().stream().map(binder::getOIDCProviderTO).collect(Collectors.toList());
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_READ + "')")
+    @Transactional(readOnly = true)
+    public OIDCProviderTO read(final String key) {
+        OIDCProvider op = opDAO.find(key);
+        if (op == null) {
+            throw new NotFoundException("OIDC Provider '" + key + "'");
+        }
+        return binder.getOIDCProviderTO(op);
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_UPDATE + "')")
+    public void update(final OIDCProviderTO oidcProviderTO) {
+        OIDCProvider oidcProvider = opDAO.find(oidcProviderTO.getKey());
+        if (oidcProvider == null) {
+            throw new NotFoundException("OIDC Provider '" + oidcProviderTO.getKey() + "'");
+        }
+
+        if (!oidcProvider.getIssuer().equals(oidcProviderTO.getIssuer())) {
+            LOG.error("Issuers do not match: expected {}, found {}",
+                    oidcProvider.getIssuer(), oidcProviderTO.getIssuer());
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidEntity);
+            sce.getElements().add("Issuers do not match");
+            throw sce;
+        }
+
+        opDAO.save(binder.update(oidcProvider, oidcProviderTO));
+    }
+
+    @PreAuthorize("hasRole('" + OIDCClientEntitlement.OP_DELETE + "')")
+    public void delete(final String key) {
+        OIDCProvider op = opDAO.find(key);
+        if (op == null) {
+            throw new NotFoundException("OIDC Provider '" + key + "'");
+        }
+        opDAO.delete(key);
+    }
+
+    @Override
+    protected OIDCProviderTO resolveReference(
+            final Method method, final Object... args) throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof OIDCProviderTO) {
+                    key = ((OIDCProviderTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                return binder.getOIDCProviderTO(opDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java
new file mode 100644
index 0000000..82d6d8a
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientClassPathScanImplementationLookup.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.init;
+
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.SyncopeLoader;
+import org.apache.syncope.core.provisioning.api.OIDCProviderActions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
+
+@Component
+public class OIDCClientClassPathScanImplementationLookup implements SyncopeLoader {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ImplementationLookup.class);
+
+    private static final String DEFAULT_BASE_PACKAGE = "org.apache.syncope.core";
+
+    private Set<String> actionsClasses;
+
+    @Override
+    public Integer getPriority() {
+        return Integer.MIN_VALUE;
+    }
+
+    @Override
+    public void load() {
+        actionsClasses = new HashSet<>();
+
+        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AssignableTypeFilter(OIDCProviderActions.class));
+
+        for (BeanDefinition bd : scanner.findCandidateComponents(DEFAULT_BASE_PACKAGE)) {
+            try {
+                Class<?> clazz = ClassUtils.resolveClassName(
+                        bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
+                boolean isAbstractClazz = Modifier.isAbstract(clazz.getModifiers());
+
+                if (OIDCProviderActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
+                    actionsClasses.add(clazz.getName());
+                }
+            } catch (Throwable t) {
+                LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
+            }
+        }
+
+        actionsClasses = Collections.unmodifiableSet(actionsClasses);
+    }
+
+    public Set<String> getActionsClasses() {
+        return actionsClasses;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
new file mode 100644
index 0000000..f24eb79
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/init/OIDCClientLoader.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.init;
+
+import org.apache.syncope.common.lib.types.OIDCClientEntitlement;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.persistence.api.SyncopeLoader;
+import org.apache.syncope.core.provisioning.api.EntitlementsHolder;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class OIDCClientLoader implements SyncopeLoader {
+
+    @Autowired
+    private DomainsHolder domainsHolder;
+
+    @Override
+    public Integer getPriority() {
+        return 1000;
+    }
+
+    @Override
+    public void load() {
+        EntitlementsHolder.getInstance().init(OIDCClientEntitlement.values());
+
+        for (String domain : domainsHolder.getDomains().keySet()) {
+            AuthContextUtils.execWithAuthContext(domain, new AuthContextUtils.Executable<Void>() {
+
+                @Override
+                public Void exec() {
+                    return null;
+                }
+            });
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/OIDCProviderDiscoveryDocument.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/OIDCProviderDiscoveryDocument.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/OIDCProviderDiscoveryDocument.java
new file mode 100644
index 0000000..aae3f0c
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/OIDCProviderDiscoveryDocument.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonPropertyOrder({
+    "issuer",
+    "authorization_endpoint",
+    "token_endpoint",
+    "userinfo_endpoint",
+    "end_session_endpoint",
+    "jwks_uri",
+    "registration_endpoint"
+})
+public class OIDCProviderDiscoveryDocument {
+
+    @JsonProperty("issuer")
+    private String issuer;
+
+    @JsonProperty("authorization_endpoint")
+    private String authorizationEndpoint;
+
+    @JsonProperty("token_endpoint")
+    private String tokenEndpoint;
+
+    @JsonProperty("userinfo_endpoint")
+    private String userinfoEndpoint;
+
+    @JsonProperty("end_session_endpoint")
+    private String endSessionEndpoint;
+
+    @JsonProperty("jwks_uri")
+    private String jwksUri;
+
+    @JsonProperty("registration_endpoint")
+    private String registrationEndpoint;
+
+    @JsonProperty("issuer")
+    public String getIssuer() {
+        return issuer;
+    }
+
+    @JsonProperty("issuer")
+    public void setIssuer(final String issuer) {
+        this.issuer = issuer;
+    }
+
+    @JsonProperty("authorization_endpoint")
+    public String getAuthorizationEndpoint() {
+        return authorizationEndpoint;
+    }
+
+    @JsonProperty("authorization_endpoint")
+    public void setAuthorizationEndpoint(final String authorizationEndpoint) {
+        this.authorizationEndpoint = authorizationEndpoint;
+    }
+
+    @JsonProperty("token_endpoint")
+    public String getTokenEndpoint() {
+        return tokenEndpoint;
+    }
+
+    @JsonProperty("token_endpoint")
+    public void setTokenEndpoint(final String tokenEndpoint) {
+        this.tokenEndpoint = tokenEndpoint;
+    }
+
+    @JsonProperty("userinfo_endpoint")
+    public String getUserinfoEndpoint() {
+        return userinfoEndpoint;
+    }
+
+    @JsonProperty("userinfo_endpoint")
+    public void setUserinfoEndpoint(final String userinfoEndpoint) {
+        this.userinfoEndpoint = userinfoEndpoint;
+    }
+
+    @JsonProperty("end_session_endpoint")
+    public String getEndSessionEndpoint() {
+        return endSessionEndpoint;
+    }
+
+    @JsonProperty("end_session_endpoint")
+    public void setEndSessionEndpoint(final String endSessionEndpoint) {
+        this.endSessionEndpoint = endSessionEndpoint;
+    }
+
+    @JsonProperty("jwks_uri")
+    public String getJwksUri() {
+        return jwksUri;
+    }
+
+    @JsonProperty("jwks_uri")
+    public void setJwksUri(final String jwksUri) {
+        this.jwksUri = jwksUri;
+    }
+
+    @JsonProperty("registration_endpoint")
+    public String getRegistrationEndpoint() {
+        return registrationEndpoint;
+    }
+
+    @JsonProperty("registration_endpoint")
+    public void setRegistrationEndpoint(final String registrationEndpoint) {
+        this.registrationEndpoint = registrationEndpoint;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/TokenEndpointResponse.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/TokenEndpointResponse.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/TokenEndpointResponse.java
new file mode 100644
index 0000000..7908b88
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/model/TokenEndpointResponse.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonPropertyOrder({
+    "access_token",
+    "id_token",
+    "expires_in",
+    "token_type"
+})
+public class TokenEndpointResponse {
+
+    @JsonProperty("access_token")
+    private String accessToken;
+
+    @JsonProperty("id_token")
+    private String idToken;
+
+    @JsonProperty("expires_in")
+    private int expiresIn;
+
+    @JsonProperty("token_type")
+    private String tokenType;
+
+    @JsonProperty("access_token")
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    @JsonProperty("access_token")
+    public void setAccessToken(final String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    @JsonProperty("id_token")
+    public String getIdToken() {
+        return idToken;
+    }
+
+    @JsonProperty("id_token")
+    public void setIdToken(final String idToken) {
+        this.idToken = idToken;
+    }
+
+    @JsonProperty("expires_in")
+    public int getExpiresIn() {
+        return expiresIn;
+    }
+
+    @JsonProperty("expires_in")
+    public void setExpiresIn(final int expiresIn) {
+        this.expiresIn = expiresIn;
+    }
+
+    @JsonProperty("token_type")
+    public String getTokenType() {
+        return tokenType;
+    }
+
+    @JsonProperty("token_type")
+    public void setTokenType(final String tokenType) {
+        this.tokenType = tokenType;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
new file mode 100644
index 0000000..25cb784
--- /dev/null
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.logic.oidc;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.OIDCLoginResponseTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.IntAttrName;
+import org.apache.syncope.core.provisioning.api.OIDCProviderActions;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
+import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class OIDCUserManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OIDCUserManager.class);
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private PlainSchemaDAO plainSchemaDAO;
+
+    @Autowired
+    private IntAttrNameParser intAttrNameParser;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private TemplateUtils templateUtils;
+
+    @Autowired
+    private UserProvisioningManager provisioningManager;
+
+    @Autowired
+    private UserDataBinder binder;
+
+    @Transactional(readOnly = true)
+    public List<String> findMatchingUser(final String keyValue, final OIDCProviderItem connObjectKeyItem) {
+        List<String> result = new ArrayList<>();
+
+        String transformed = keyValue;
+        for (ItemTransformer transformer : MappingUtils.getItemTransformers(connObjectKeyItem)) {
+            List<Object> output = transformer.beforePull(
+                    null,
+                    null,
+                    Collections.<Object>singletonList(transformed));
+            if (output != null && !output.isEmpty()) {
+                transformed = output.get(0).toString();
+            }
+        }
+
+        IntAttrName intAttrName;
+        try {
+            intAttrName = intAttrNameParser.parse(connObjectKeyItem.getIntAttrName(), AnyTypeKind.USER);
+        } catch (ParseException e) {
+            LOG.error("Invalid intAttrName '{}' specified, ignoring", connObjectKeyItem.getIntAttrName(), e);
+            return result;
+        }
+
+        if (intAttrName.getField() != null) {
+            switch (intAttrName.getField()) {
+                case "key":
+                    User byKey = userDAO.find(transformed);
+                    if (byKey != null) {
+                        result.add(byKey.getUsername());
+                    }
+                    break;
+
+                case "username":
+                    User byUsername = userDAO.findByUsername(transformed);
+                    if (byUsername != null) {
+                        result.add(byUsername.getUsername());
+                    }
+                    break;
+
+                default:
+            }
+        } else if (intAttrName.getSchemaType() != null) {
+            switch (intAttrName.getSchemaType()) {
+                case PLAIN:
+                    PlainAttrValue value = entityFactory.newEntity(UPlainAttrValue.class);
+
+                    PlainSchema schema = plainSchemaDAO.find(intAttrName.getSchemaName());
+                    if (schema == null) {
+                        value.setStringValue(transformed);
+                    } else {
+                        try {
+                            value.parseValue(schema, transformed);
+                        } catch (ParsingValidationException e) {
+                            LOG.error("While parsing provided key value {}", transformed, e);
+                            value.setStringValue(transformed);
+                        }
+                    }
+
+                    result.addAll(userDAO.findByPlainAttrValue(intAttrName.getSchemaName(), value, false).stream().
+                            map(User::getUsername).collect(Collectors.toList()));
+                    break;
+
+                case DERIVED:
+                    result.addAll(userDAO.findByDerAttrValue(intAttrName.getSchemaName(), transformed, false).stream().
+                            map(User::getUsername).collect(Collectors.toList()));
+                    break;
+
+                default:
+            }
+        }
+
+        return result;
+    }
+
+    private List<OIDCProviderActions> getActions(final OIDCProvider op) {
+        List<OIDCProviderActions> actions = new ArrayList<>();
+
+        op.getActionsClassNames().forEach(className -> {
+            try {
+                Class<?> actionsClass = Class.forName(className);
+                OIDCProviderActions opActions = (OIDCProviderActions) ApplicationContextProvider.getBeanFactory().
+                        createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+
+                actions.add(opActions);
+            } catch (Exception e) {
+                LOG.warn("Class '{}' not found", className, e);
+            }
+        });
+
+        return actions;
+    }
+
+    public void fill(final OIDCProvider op, final OIDCLoginResponseTO responseTO, final UserTO userTO) {
+        op.getItems().forEach(item -> {
+            List<String> values = Collections.emptyList();
+            Optional<AttrTO> oidcAttr = responseTO.getAttr(item.getExtAttrName());
+            if (oidcAttr.isPresent() && !oidcAttr.get().getValues().isEmpty()) {
+                values = oidcAttr.get().getValues();
+
+                List<Object> transformed = new ArrayList<>(values);
+                for (ItemTransformer transformer : MappingUtils.getItemTransformers(item)) {
+                    transformed = transformer.beforePull(null, userTO, transformed);
+                }
+                values.clear();
+                for (Object value : transformed) {
+                    values.add(value.toString());
+                }
+            }
+
+            IntAttrName intAttrName = null;
+            try {
+                intAttrName = intAttrNameParser.parse(item.getIntAttrName(), AnyTypeKind.USER);
+            } catch (ParseException e) {
+                LOG.error("Invalid intAttrName '{}' specified, ignoring", item.getIntAttrName(), e);
+            }
+
+            if (intAttrName != null && intAttrName.getField() != null) {
+                switch (intAttrName.getField()) {
+                    case "username":
+                        if (!values.isEmpty()) {
+                            userTO.setUsername(values.get(0));
+                        }
+                        break;
+
+                    default:
+                        LOG.warn("Unsupported: {}", intAttrName.getField());
+                }
+            } else if (intAttrName != null && intAttrName.getSchemaType() != null) {
+                switch (intAttrName.getSchemaType()) {
+                    case PLAIN:
+                        Optional<AttrTO> attr = userTO.getPlainAttr(intAttrName.getSchemaName());
+                        if (attr.isPresent()) {
+                            attr.get().getValues().clear();
+                        } else {
+                            attr = Optional.of(new AttrTO.Builder().schema(intAttrName.getSchemaName()).build());
+                            userTO.getPlainAttrs().add(attr.get());
+                        }
+                        attr.get().getValues().addAll(values);
+                        break;
+
+                    default:
+                        LOG.warn("Unsupported: {} {}", intAttrName.getSchemaType(), intAttrName.getSchemaName());
+                }
+            }
+        });
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public String create(final OIDCProvider op, final OIDCLoginResponseTO responseTO, final String email) {
+        UserTO userTO = new UserTO();
+
+        if (op.getUserTemplate() != null && op.getUserTemplate().get() != null) {
+            templateUtils.apply(userTO, op.getUserTemplate().get());
+        }
+
+        List<OIDCProviderActions> actions = getActions(op);
+        for (OIDCProviderActions action : actions) {
+            userTO = action.beforeCreate(userTO, responseTO);
+        }
+
+        fill(op, responseTO, userTO);
+
+        if (userTO.getRealm() == null) {
+            userTO.setRealm(SyncopeConstants.ROOT_REALM);
+        }
+        if (userTO.getUsername() == null) {
+            userTO.setUsername(email);
+        }
+
+        Pair<String, List<PropagationStatus>> created = provisioningManager.create(userTO, false, false);
+        userTO = binder.getUserTO(created.getKey());
+
+        for (OIDCProviderActions action : actions) {
+            userTO = action.afterCreate(userTO, responseTO);
+        }
+
+        return userTO.getUsername();
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW)
+    public String update(final String username, final OIDCProvider op, final OIDCLoginResponseTO responseTO) {
+        UserTO userTO = binder.getUserTO(userDAO.findKey(username));
+        UserTO original = SerializationUtils.clone(userTO);
+
+        fill(op, responseTO, userTO);
+
+        UserPatch userPatch = AnyOperations.diff(userTO, original, true);
+
+        List<OIDCProviderActions> actions = getActions(op);
+        for (OIDCProviderActions action : actions) {
+            userPatch = action.beforeUpdate(userPatch, responseTO);
+        }
+
+        Pair<UserPatch, List<PropagationStatus>> updated = provisioningManager.update(userPatch, false);
+        userTO = binder.getUserTO(updated.getLeft().getKey());
+
+        for (OIDCProviderActions action : actions) {
+            userTO = action.afterUpdate(userTO, responseTO);
+        }
+
+        return userTO.getUsername();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-api/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/pom.xml b/ext/oidcclient/persistence-api/pom.xml
new file mode 100644
index 0000000..321602b
--- /dev/null
+++ b/ext/oidcclient/persistence-api/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Persistence API</name>
+  <description>Apache Syncope Ext: OIDC Client Persistence API</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-persistence-api</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-persistence-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-common-lib</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCProviderDAO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCProviderDAO.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCProviderDAO.java
new file mode 100644
index 0000000..793e461
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCProviderDAO.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+
+public interface OIDCProviderDAO {
+
+    OIDCProvider find(String key);
+
+    OIDCProvider findByName(String name);
+
+    List<OIDCProvider> findAll();
+
+    OIDCProvider save(final OIDCProvider op);
+
+    void delete(String key);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCEntityFactory.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCEntityFactory.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCEntityFactory.java
new file mode 100644
index 0000000..489ce86
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCEntityFactory.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+public interface OIDCEntityFactory {
+
+    <E extends Entity> E newEntity(Class<E> reference);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
new file mode 100644
index 0000000..b0c7dee
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProvider.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public interface OIDCProvider extends Entity {
+
+    String getName();
+
+    void setName(String entityID);
+
+    String getClientID();
+
+    void setClientID(String clientId);
+
+    String getClientSecret();
+
+    void setClientSecret(String clientSecret);
+
+    String getAuthorizationEndpoint();
+
+    void setAuthorizationEndpoint(String authorizationEndpoint);
+
+    String getTokenEndpoint();
+
+    void setTokenEndpoint(String tokenEndpoint);
+
+    String getJwksUri();
+
+    void setJwksUri(String jwsUri);
+
+    String getIssuer();
+
+    void setIssuer(String issuer);
+
+    String getUserinfoEndpoint();
+
+    void setUserinfoEndpoint(String userinfoEndpoint);
+
+    boolean getHasDiscovery();
+
+    void setHasDiscovery(boolean hasDiscovery);
+
+    boolean isCreateUnmatching();
+
+    void setCreateUnmatching(boolean createUnmatching);
+
+    boolean isUpdateMatching();
+
+    void setUpdateMatching(boolean updateMatching);
+
+    OIDCUserTemplate getUserTemplate();
+
+    void setUserTemplate(OIDCUserTemplate userTemplate);
+
+    List<? extends OIDCProviderItem> getItems();
+
+    Optional<? extends OIDCProviderItem> getConnObjectKeyItem();
+
+    void setConnObjectKeyItem(OIDCProviderItem item);
+
+    boolean add(OIDCProviderItem item);
+
+    Set<String> getActionsClassNames();
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProviderItem.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProviderItem.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProviderItem.java
new file mode 100644
index 0000000..3047c88
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCProviderItem.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+
+public interface OIDCProviderItem extends Item {
+
+    OIDCProvider getOP();
+
+    void setOP(OIDCProvider op);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCUserTemplate.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCUserTemplate.java b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCUserTemplate.java
new file mode 100644
index 0000000..339f6ec
--- /dev/null
+++ b/ext/oidcclient/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/OIDCUserTemplate.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+public interface OIDCUserTemplate extends AnyTemplate {
+
+    OIDCProvider getOP();
+
+    void setOP(OIDCProvider op);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/pom.xml b/ext/oidcclient/persistence-jpa/pom.xml
new file mode 100644
index 0000000..f642ef6
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/pom.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-oidcclient</artifactId>
+    <version>2.1.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: OIDC Client Persistence JPA</name>
+  <description>Apache Syncope Ext: OIDC Client Persistence JPA</description>
+  <groupId>org.apache.syncope.ext.oidcclient</groupId>
+  <artifactId>syncope-ext-oidcclient-persistence-jpa</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>    
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-persistence-jpa</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.ext.oidcclient</groupId>
+      <artifactId>syncope-ext-oidcclient-persistence-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.openjpa</groupId>
+        <artifactId>openjpa-maven-plugin</artifactId>
+        <inherited>true</inherited>
+        <dependencies>
+          <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <version>${h2.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <persistenceXmlFile>${rootpom.basedir}/core/persistence-jpa/src/main/resources/persistence-enhance.xml</persistenceXmlFile> 
+          <includes>org/apache/syncope/core/persistence/jpa/entity/**/*.class</includes>
+          <connectionDriverName>org.springframework.jdbc.datasource.DriverManagerDataSource</connectionDriverName>
+          <connectionProperties>
+            driverClassName=org.h2.Driver,
+            url=jdbc:h2:mem:syncopedb
+            username=sa,
+            password=
+          </connectionProperties>
+        </configuration>
+        <executions>
+          <execution>
+            <id>enhancer</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>enhance</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+
+    <testResources>
+      <testResource>
+        <directory>${rootpom.basedir}/core/persistence-jpa/src/main/resources</directory>
+        <filtering>true</filtering>        
+      </testResource>
+    </testResources>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>sqlgen</id>
+      
+      <properties>
+        <skipTests>true</skipTests>
+      </properties>
+      
+      <build>
+        <defaultGoal>clean verify</defaultGoal>
+        
+        <plugins>
+          <plugin>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa-maven-plugin</artifactId>
+            <inherited>true</inherited>
+            <executions>
+              <execution>
+                <id>sqlgenr</id>
+                <phase>process-classes</phase>
+                <goals>
+                  <goal>sql</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>          
+        </plugins>
+      </build>
+        
+    </profile>
+  </profiles>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCProviderDAO.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCProviderDAO.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCProviderDAO.java
new file mode 100644
index 0000000..91e3af0
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCProviderDAO.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.List;
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.OIDCProviderDAO;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.jpa.entity.JPAOIDCProvider;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+@Repository
+public class JPAOIDCProviderDAO extends AbstractDAO<OIDCProvider> implements OIDCProviderDAO {
+
+    @Transactional(readOnly = true)
+    @Override
+    public OIDCProvider find(final String key) {
+        return entityManager().find(JPAOIDCProvider.class, key);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public OIDCProvider findByName(final String name) {
+        TypedQuery<OIDCProvider> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAOIDCProvider.class.getSimpleName()
+                + " e WHERE e.name = :name", OIDCProvider.class);
+        query.setParameter("name", name);
+
+        OIDCProvider result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (NoResultException e) {
+            LOG.debug("No OIDC Provider found with name {}", name, e);
+        }
+
+        return result;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<OIDCProvider> findAll() {
+        TypedQuery<OIDCProvider> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAOIDCProvider.class.getSimpleName() + " e", OIDCProvider.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public OIDCProvider save(final OIDCProvider op) {
+        return entityManager().merge(op);
+    }
+
+    @Override
+    public void delete(final String key) {
+        OIDCProvider op = find(key);
+        if (op != null) {
+            entityManager().remove(op);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCEntityFactory.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCEntityFactory.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCEntityFactory.java
new file mode 100644
index 0000000..e5870bf
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCEntityFactory.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.OIDCEntityFactory;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class JPAOIDCEntityFactory implements OIDCEntityFactory {
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <E extends Entity> E newEntity(final Class<E> reference) {
+        E result;
+
+        if (reference.equals(OIDCProvider.class)) {
+            result = (E) new JPAOIDCProvider();
+        } else if (reference.equals(OIDCProviderItem.class)) {
+            result = (E) new JPAOIDCProviderItem();
+        } else if (reference.equals(OIDCUserTemplate.class)) {
+            result = (E) new JPAOIDCUserTemplate();
+        } else {
+            throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName());
+        }
+
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/797fd1cb/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
----------------------------------------------------------------------
diff --git a/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
new file mode 100644
index 0000000..c32f91e
--- /dev/null
+++ b/ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCProvider.java
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.persistence.Cacheable;
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
+import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
+import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
+
+@Entity
+@Table(name = JPAOIDCProvider.TABLE)
+@Cacheable
+public class JPAOIDCProvider extends AbstractGeneratedKeyEntity implements OIDCProvider {
+
+    public static final String TABLE = "OIDCProvider";
+
+    private static final long serialVersionUID = 1423093003585826403L;
+
+    @Column(unique = true, nullable = false)
+    private String name;
+
+    @Column(unique = true, nullable = false)
+    private String clientID;
+
+    @Column(unique = true, nullable = false)
+    private String clientSecret;
+
+    @Column(nullable = false)
+    private String authorizationEndpoint;
+
+    @Column(nullable = false)
+    private String tokenEndpoint;
+
+    @Column(nullable = false)
+    private String jwksUri;
+
+    @Column(nullable = false)
+    private String issuer;
+
+    @Column(nullable = false)
+    private String userinfoEndpoint;
+
+    @Column(nullable = false)
+    private boolean hasDiscovery;
+
+    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "op")
+    private JPAOIDCUserTemplate userTemplate;
+
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "op")
+    private List<JPAOIDCProviderItem> items = new ArrayList<>();
+
+    @Min(0)
+    @Max(1)
+    @Column(nullable = false)
+    private Integer createUnmatching;
+
+    @Min(0)
+    @Max(1)
+    @Column(nullable = false)
+    private Integer updateMatching;
+
+    @ElementCollection(fetch = FetchType.EAGER)
+    @Column(name = "actionClassName")
+    @CollectionTable(name = TABLE + "_actionsClassNames",
+            joinColumns =
+            @JoinColumn(name = "oidcOP_id", referencedColumnName = "id"))
+    private Set<String> actionsClassNames = new HashSet<>();
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getClientID() {
+        return clientID;
+    }
+
+    @Override
+    public void setClientID(final String clientID) {
+        this.clientID = clientID;
+    }
+
+    @Override
+    public String getClientSecret() {
+        return clientSecret;
+    }
+
+    @Override
+    public void setClientSecret(final String clientSecret) {
+        this.clientSecret = clientSecret;
+    }
+
+    @Override
+    public String getAuthorizationEndpoint() {
+        return authorizationEndpoint;
+    }
+
+    @Override
+    public void setAuthorizationEndpoint(final String authorizationEndpoint) {
+        this.authorizationEndpoint = authorizationEndpoint;
+    }
+
+    @Override
+    public String getTokenEndpoint() {
+        return tokenEndpoint;
+    }
+
+    @Override
+    public void setTokenEndpoint(final String tokenEndpoint) {
+        this.tokenEndpoint = tokenEndpoint;
+    }
+
+    @Override
+    public String getJwksUri() {
+        return jwksUri;
+    }
+
+    @Override
+    public void setJwksUri(final String jwksUri) {
+        this.jwksUri = jwksUri;
+    }
+
+    @Override
+    public String getIssuer() {
+        return issuer;
+    }
+
+    @Override
+    public void setIssuer(final String issuer) {
+        this.issuer = issuer;
+    }
+
+    @Override
+    public String getUserinfoEndpoint() {
+        return userinfoEndpoint;
+    }
+
+    @Override
+    public void setUserinfoEndpoint(final String userinfoEndpoint) {
+        this.userinfoEndpoint = userinfoEndpoint;
+    }
+
+    @Override
+    public boolean getHasDiscovery() {
+        return hasDiscovery;
+    }
+
+    @Override
+    public void setHasDiscovery(final boolean hasDiscovery) {
+        this.hasDiscovery = hasDiscovery;
+    }
+
+    @Override
+    public boolean isCreateUnmatching() {
+        return isBooleanAsInteger(createUnmatching);
+    }
+
+    @Override
+    public void setCreateUnmatching(final boolean createUnmatching) {
+        this.createUnmatching = getBooleanAsInteger(createUnmatching);
+    }
+
+    @Override
+    public boolean isUpdateMatching() {
+        return isBooleanAsInteger(updateMatching);
+    }
+
+    @Override
+    public void setUpdateMatching(final boolean updateMatching) {
+        this.updateMatching = getBooleanAsInteger(updateMatching);
+    }
+
+    @Override
+    public OIDCUserTemplate getUserTemplate() {
+        return userTemplate;
+    }
+
+    @Override
+    public void setUserTemplate(final OIDCUserTemplate userTemplate) {
+        checkType(userTemplate, JPAOIDCUserTemplate.class);
+        this.userTemplate = (JPAOIDCUserTemplate) userTemplate;
+    }
+
+    @Override
+    public boolean add(final OIDCProviderItem item) {
+        checkType(item, JPAOIDCProviderItem.class);
+        return items.contains((JPAOIDCProviderItem) item) || items.add((JPAOIDCProviderItem) item);
+    }
+
+    @Override
+    public List<? extends OIDCProviderItem> getItems() {
+        return items;
+    }
+
+    @Override
+    public Optional<? extends OIDCProviderItem> getConnObjectKeyItem() {
+        return getItems().stream().filter(item -> item.isConnObjectKey()).findFirst();
+    }
+
+    @Override
+    public void setConnObjectKeyItem(final OIDCProviderItem item) {
+        item.setConnObjectKey(true);
+        this.add(item);
+    }
+
+    @Override
+    public Set<String> getActionsClassNames() {
+        return actionsClassNames;
+    }
+
+}