You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:18:14 UTC

[sling-org-apache-sling-auth-xing-oauth] 01/08: SLING-3731 SLING-3732 add Sling Authentication XING API, Sling Authentication XING OAuth and Sling Authentication XING Login

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.auth.xing.oauth-0.0.2
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-xing-oauth.git

commit 41d61afc2141ca497730c957e7cb92e907a69fac
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Fri Jul 4 15:43:38 2014 +0000

    SLING-3731 SLING-3732 add Sling Authentication XING API, Sling Authentication XING OAuth and Sling Authentication XING Login
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/auth/org.apache.sling.auth.xing.oauth@1607877 13f79535-47bb-0310-9956-ffa450edef68
---
 README.md                                          |   5 +
 pom.xml                                            | 197 +++++++++++++++++
 .../apache/sling/auth/xing/oauth/XingOauth.java    |  31 +++
 .../auth/xing/oauth/XingOauthUserManager.java      |  27 +++
 .../sling/auth/xing/oauth/XingOauthUtil.java       |  51 +++++
 .../oauth/impl/DefaultXingOauthUserManager.java    | 182 ++++++++++++++++
 .../oauth/impl/XingOauthAuthenticationHandler.java | 239 +++++++++++++++++++++
 .../oauth/impl/XingOauthAuthenticationPlugin.java  |  69 ++++++
 .../oauth/impl/XingOauthLoginModulePlugin.java     | 122 +++++++++++
 .../apache/sling/auth/xing/oauth/package-info.java |  20 ++
 .../OSGI-INF/metatype/metatype.properties          |  44 ++++
 11 files changed, 987 insertions(+)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d688ec7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+Apache Sling Authentication XING OAuth
+======================================
+
+* uses the [XING API](https://dev.xing.com/docs) with OAuth 1.0 for [authentication](https://dev.xing.com/docs/authentication)
+* allows creating and updating JCR users based on supplied user data from XING
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..267e5a0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,197 @@
+<?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.sling</groupId>
+    <artifactId>sling</artifactId>
+    <version>19</version>
+    <relativePath>../../../parent/pom.xml</relativePath>
+  </parent>
+
+  <artifactId>org.apache.sling.auth.xing.oauth</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <name>Apache Sling Authentication XING OAuth</name>
+  <description>Apache Sling Authentication XING OAuth</description>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <sling.java.version>6</sling.java.version>
+  </properties>
+
+  <dependencies>
+    <!-- javax -->
+    <dependency>
+      <groupId>javax.jcr</groupId>
+      <artifactId>jcr</artifactId>
+      <version>2.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <!-- OSGi -->
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <!-- Apache Commons -->
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>2.6</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- Apache Sling -->
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.api</artifactId>
+      <version>2.7.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.auth.core</artifactId>
+      <version>1.1.6</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.auth.xing.api</artifactId>
+      <version>0.0.1-SNAPSHOT</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.commons.osgi</artifactId>
+      <version>2.2.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.jcr.api</artifactId>
+      <version>2.2.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.jcr.jackrabbit.server</artifactId>
+      <version>2.1.2</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- Apache Jackrabbit -->
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>jackrabbit-api</artifactId>
+      <version>2.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- Apache Felix -->
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.scr.annotations</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <!-- bndlib -->
+    <dependency>
+      <groupId>biz.aQute.bnd</groupId>
+      <artifactId>bndlib</artifactId>
+      <version>2.3.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- Scribe OAuth -->
+    <dependency>
+      <groupId>org.scribe</groupId>
+      <artifactId>scribe</artifactId>
+      <version>1.3.5</version>
+      <scope>compile</scope>
+    </dependency>
+    <!-- Gson -->
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+      <version>2.2.4</version>
+      <scope>compile</scope>
+    </dependency>
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.7</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- testing -->
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <version>6.8.8</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Category>sling</Bundle-Category>
+            <Embed-Dependency>
+              *;scope=compile;inline=true,
+              org.apache.sling.commons.osgi;inline="org/apache/sling/commons/osgi/PropertiesUtil.*"
+            </Embed-Dependency>
+            <_removeheaders>
+              Embed-Dependency,
+              Private-Package,
+              Include-Resource
+            </_removeheaders>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-scr-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>scr</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/src/main/java/org/apache/sling/auth/xing/oauth/XingOauth.java b/src/main/java/org/apache/sling/auth/xing/oauth/XingOauth.java
new file mode 100644
index 0000000..944add2
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/xing/oauth/XingOauth.java
@@ -0,0 +1,31 @@
+/*
+ * 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.sling.auth.xing.oauth;
+
+public class XingOauth {
+
+    public static final String AUTH_TYPE = "xing-oauth";
+
+    public static final String AUTHENTICATION_CREDENTIALS_ACCESS_TOKEN_KEY = "xing-access-token";
+
+    public static final String AUTHENTICATION_CREDENTIALS_USER_KEY = "xing-user";
+
+    public static final String SERVICE_VENDOR = "The Apache Software Foundation";
+
+}
diff --git a/src/main/java/org/apache/sling/auth/xing/oauth/XingOauthUserManager.java b/src/main/java/org/apache/sling/auth/xing/oauth/XingOauthUserManager.java
new file mode 100644
index 0000000..6d158f4
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/xing/oauth/XingOauthUserManager.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.sling.auth.xing.oauth;
+
+import aQute.bnd.annotation.ProviderType;
+import org.apache.sling.auth.xing.api.XingUserManager;
+
+@ProviderType
+public interface XingOauthUserManager extends XingUserManager {
+
+}
diff --git a/src/main/java/org/apache/sling/auth/xing/oauth/XingOauthUtil.java b/src/main/java/org/apache/sling/auth/xing/oauth/XingOauthUtil.java
new file mode 100644
index 0000000..81c2f21
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/xing/oauth/XingOauthUtil.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sling.auth.xing.oauth;
+
+import javax.jcr.Credentials;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.sling.auth.xing.api.XingUser;
+import org.scribe.model.Token;
+
+public class XingOauthUtil {
+
+    public static Token getAccessToken(Credentials credentials) {
+        if (credentials instanceof SimpleCredentials) {
+            final SimpleCredentials simpleCredentials = (SimpleCredentials) credentials;
+            final Object attribute = simpleCredentials.getAttribute(XingOauth.AUTHENTICATION_CREDENTIALS_ACCESS_TOKEN_KEY);
+            if (attribute instanceof Token) {
+                return (Token) attribute;
+            }
+        }
+        return null;
+    }
+
+    public static XingUser getXingUser(Credentials credentials) {
+        if (credentials instanceof SimpleCredentials) {
+            final SimpleCredentials simpleCredentials = (SimpleCredentials) credentials;
+            final Object attribute = simpleCredentials.getAttribute(XingOauth.AUTHENTICATION_CREDENTIALS_USER_KEY);
+            if (attribute instanceof XingUser) {
+                return (XingUser) attribute;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/auth/xing/oauth/impl/DefaultXingOauthUserManager.java b/src/main/java/org/apache/sling/auth/xing/oauth/impl/DefaultXingOauthUserManager.java
new file mode 100644
index 0000000..a82d47e
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/xing/oauth/impl/DefaultXingOauthUserManager.java
@@ -0,0 +1,182 @@
+/*
+ * 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.sling.auth.xing.oauth.impl;
+
+import java.util.Dictionary;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.sling.auth.xing.api.AbstractXingUserManager;
+import org.apache.sling.auth.xing.api.XingUser;
+import org.apache.sling.auth.xing.oauth.XingOauth;
+import org.apache.sling.auth.xing.oauth.XingOauthUserManager;
+import org.apache.sling.auth.xing.oauth.XingOauthUtil;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(
+    label = "Apache Sling Authentication XING OAuth “Default User Manager”",
+    description = "Default User Manager for Sling Authentication XING OAuth",
+    immediate = true,
+    metatype = true
+)
+@Service
+@Properties({
+    @Property(name = Constants.SERVICE_VENDOR, value = XingOauth.SERVICE_VENDOR),
+    @Property(name = Constants.SERVICE_DESCRIPTION, value = "Default User Manager for Sling Authentication XING OAuth"),
+    @Property(name = Constants.SERVICE_RANKING, intValue = 0, propertyPrivate = false)
+})
+public class DefaultXingOauthUserManager extends AbstractXingUserManager implements XingOauthUserManager {
+
+    @Reference
+    private SlingRepository slingRepository;
+
+    private static final String FIRSTNAME_PROPERTY = "firstname";
+
+    private static final String LASTNAME_PROPERTY = "lastname";
+
+    @Property(boolValue = DEFAULT_AUTO_CREATE_USER)
+    private static final String AUTO_CREATE_USER_PARAMETER = "org.apache.sling.auth.xing.oauth.impl.DefaultXingOauthUserManager.user.create.auto";
+
+    @Property(boolValue = DEFAULT_AUTO_UPDATE_USER)
+    private static final String AUTO_UPDATE_USER_PARAMETER = "org.apache.sling.auth.xing.oauth.impl.DefaultXingOauthUserManager.user.update.auto";
+
+    private final Logger logger = LoggerFactory.getLogger(DefaultXingOauthUserManager.class);
+
+    public DefaultXingOauthUserManager() {
+    }
+
+    @Activate
+    protected void activate(final ComponentContext componentContext) {
+        logger.debug("activate");
+        configure(componentContext);
+    }
+
+    @Modified
+    protected void modified(final ComponentContext componentContext) {
+        logger.debug("modified");
+        configure(componentContext);
+    }
+
+    @Deactivate
+    protected void deactivate(final ComponentContext componentContext) {
+        logger.debug("deactivate");
+        if (session != null) {
+            session.logout();
+            session = null;
+        }
+    }
+
+    protected synchronized void configure(final ComponentContext componentContext) {
+        final Dictionary properties = componentContext.getProperties();
+        autoCreateUser = PropertiesUtil.toBoolean(properties.get(AUTO_CREATE_USER_PARAMETER), DEFAULT_AUTO_CREATE_USER);
+        autoUpdateUser = PropertiesUtil.toBoolean(properties.get(AUTO_UPDATE_USER_PARAMETER), DEFAULT_AUTO_UPDATE_USER);
+    }
+
+    @Override
+    protected SlingRepository getSlingRepository() {
+        return slingRepository;
+    }
+
+    @Override
+    public User createUser(final Credentials credentials) {
+        logger.debug("create user");
+        final XingUser xingUser = XingOauthUtil.getXingUser(credentials);
+        if (xingUser == null) {
+            return null;
+        }
+
+        try {
+            final String userId = xingUser.getId(); // TODO make configurable
+
+            final Session session = getSession();
+            final UserManager userManager = getUserManager(session);
+            final User user = userManager.createUser(userId, null);
+
+            // TODO disable user on create?
+            final ValueFactory valueFactory = session.getValueFactory();
+            final Value firstnameValue = valueFactory.createValue(xingUser.getFirstName());
+            final Value lastnameValue = valueFactory.createValue(xingUser.getLastName());
+            user.setProperty(FIRSTNAME_PROPERTY, firstnameValue);
+            user.setProperty(LASTNAME_PROPERTY, lastnameValue);
+            session.save();
+            return user;
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    @Override
+    public User updateUser(Credentials credentials) {
+        logger.debug("update user");
+        final XingUser xingUser = XingOauthUtil.getXingUser(credentials);
+        if (xingUser == null) {
+            return null;
+        }
+
+        try {
+            final Session session = getSession();
+            final User user = getUser(credentials);
+            final ValueFactory valueFactory = session.getValueFactory();
+
+            final boolean firstnameUpdated = updateUserProperty(user, valueFactory, FIRSTNAME_PROPERTY, xingUser.getFirstName());
+            final boolean lastnameUpdated = updateUserProperty(user, valueFactory, LASTNAME_PROPERTY, xingUser.getLastName());
+            if (firstnameUpdated || lastnameUpdated) {
+                session.save();
+            }
+
+            return user;
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    private boolean updateUserProperty(final User user, final ValueFactory valueFactory, final String property, final String string) throws RepositoryException {
+        final Value[] values = user.getProperty(property);
+        if (values != null && values.length > 0) {
+            if (string.equals(values[0].getString())) {
+                return false;
+            }
+        }
+        final Value value = valueFactory.createValue(string);
+        user.setProperty(property, value);
+        return true;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthAuthenticationHandler.java b/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthAuthenticationHandler.java
new file mode 100644
index 0000000..79f58d0
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthAuthenticationHandler.java
@@ -0,0 +1,239 @@
+/*
+ * 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.sling.auth.xing.oauth.impl;
+
+import java.io.IOException;
+import java.util.Dictionary;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyUnbounded;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.auth.core.spi.AuthenticationHandler;
+import org.apache.sling.auth.core.spi.AuthenticationInfo;
+import org.apache.sling.auth.core.spi.DefaultAuthenticationFeedbackHandler;
+import org.apache.sling.auth.xing.api.XingUser;
+import org.apache.sling.auth.xing.api.users.Users;
+import org.apache.sling.auth.xing.oauth.XingOauth;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+import org.scribe.builder.ServiceBuilder;
+import org.scribe.builder.api.XingApi;
+import org.scribe.model.OAuthConstants;
+import org.scribe.model.OAuthRequest;
+import org.scribe.model.Response;
+import org.scribe.model.Token;
+import org.scribe.model.Verb;
+import org.scribe.model.Verifier;
+import org.scribe.oauth.OAuthService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(
+    label = "Apache Sling Authentication XING OAuth “Authentication Handler”",
+    description = "Authentication Handler for Sling Authentication XING OAuth",
+    immediate = true,
+    metatype = true
+)
+@Service
+@Properties({
+    @Property(name = Constants.SERVICE_VENDOR, value = XingOauth.SERVICE_VENDOR),
+    @Property(name = Constants.SERVICE_DESCRIPTION, value = "Authentication Handler for Sling Authentication XING OAuth"),
+    @Property(name = Constants.SERVICE_RANKING, intValue = 0, propertyPrivate = false),
+    @Property(name = AuthenticationHandler.PATH_PROPERTY, value = "/", unbounded = PropertyUnbounded.ARRAY),
+    @Property(name = AuthenticationHandler.TYPE_PROPERTY, value = XingOauth.AUTH_TYPE, propertyPrivate = true)
+})
+public class XingOauthAuthenticationHandler extends DefaultAuthenticationFeedbackHandler implements AuthenticationHandler {
+
+    private OAuthService oAuthService;
+
+    private String consumerKey;
+
+    private String consumerSecret;
+
+    private String callbackUrl;
+
+    private String usersMeUrl;
+
+    private static final String DEFAULT_USERS_ME_URL = "https://api.xing.com/v1/users/me.json";
+
+    @Property(value = "")
+    private static final String CONSUMER_KEY_PARAMETER = "org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.consumerKey";
+
+    @Property(value = "")
+    private static final String CONSUMER_SECRET_PARAMETER = "org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.consumerSecret";
+
+    @Property(value = "")
+    private static final String CALLBACK_URL_PARAMETER = "org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.callbackUrl";
+
+    @Property(value = DEFAULT_USERS_ME_URL)
+    private static final String USERS_ME_URL_PARAMETER = "org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.usersMeUrl";
+
+    public static final String USER_SESSION_ATTRIBUTE_NAME = "xing-user";
+
+    private final Logger logger = LoggerFactory.getLogger(XingOauthAuthenticationHandler.class);
+
+    public XingOauthAuthenticationHandler() {
+    }
+
+    @Activate
+    protected void activate(final ComponentContext componentContext) {
+        logger.debug("activate");
+        configure(componentContext);
+    }
+
+    @Modified
+    protected void modified(final ComponentContext componentContext) {
+        logger.debug("modified");
+        configure(componentContext);
+    }
+
+    @Deactivate
+    protected void deactivate(final ComponentContext componentContext) {
+        logger.debug("deactivate");
+    }
+
+    protected synchronized void configure(final ComponentContext componentContext) {
+        final Dictionary properties = componentContext.getProperties();
+        consumerKey = PropertiesUtil.toString(properties.get(CONSUMER_KEY_PARAMETER), "").trim();
+        consumerSecret = PropertiesUtil.toString(properties.get(CONSUMER_SECRET_PARAMETER), "").trim();
+        callbackUrl = PropertiesUtil.toString(properties.get(CALLBACK_URL_PARAMETER), "").trim();
+        usersMeUrl = PropertiesUtil.toString(properties.get(USERS_ME_URL_PARAMETER), DEFAULT_USERS_ME_URL).trim();
+
+        if (StringUtils.isEmpty(consumerKey)) {
+            logger.warn("configured consumer key is empty");
+        }
+
+        if (StringUtils.isEmpty(consumerSecret)) {
+            logger.warn("configured consumer secret is empty");
+        }
+
+        if (StringUtils.isEmpty(callbackUrl)) {
+            logger.warn("configured callback URL is empty");
+        }
+
+        if (StringUtils.isEmpty(usersMeUrl)) {
+            logger.warn("configured users me URL is empty");
+        }
+
+        if (!StringUtils.isEmpty(consumerKey) && !StringUtils.isEmpty(consumerSecret) && !StringUtils.isEmpty(callbackUrl)) {
+            oAuthService = new ServiceBuilder().provider(XingApi.class).apiKey(consumerKey).apiSecret(consumerSecret).callback(callbackUrl).build();
+        }
+
+        logger.info("configured with consumer key '{}', callback url '{}' and users me url '{}'", consumerKey, callbackUrl, usersMeUrl);
+    }
+
+    // we need the OAuth access token and the user from XING (/v1/users/me)
+    @Override
+    public AuthenticationInfo extractCredentials(final HttpServletRequest request, final HttpServletResponse response) {
+        logger.debug("extract credentials");
+
+        try {
+            final HttpSession httpSession = request.getSession(true);
+
+            Token accessToken = (Token) httpSession.getAttribute(OAuthConstants.ACCESS_TOKEN);
+            XingUser xingUser = (XingUser) httpSession.getAttribute(USER_SESSION_ATTRIBUTE_NAME);
+
+            if (accessToken == null) {
+                // we need the request token and verifier to get an access token
+                final Token requestToken = (Token) httpSession.getAttribute(OAuthConstants.TOKEN);
+                final String verifier = request.getParameter(OAuthConstants.VERIFIER);
+                if (requestToken == null || verifier == null) {
+                    return null;
+                }
+                accessToken = oAuthService.getAccessToken(requestToken, new Verifier(verifier));
+                logger.debug("access token: {}", accessToken);
+                httpSession.setAttribute(OAuthConstants.ACCESS_TOKEN, accessToken);
+            }
+
+            if (xingUser == null) {
+                xingUser = fetchUser(accessToken);
+                logger.debug("xing user: {}", xingUser);
+                httpSession.setAttribute(USER_SESSION_ATTRIBUTE_NAME, xingUser);
+            }
+
+            final AuthenticationInfo authenticationInfo = new AuthenticationInfo(XingOauth.AUTH_TYPE, xingUser.getId());
+            authenticationInfo.put(XingOauth.AUTHENTICATION_CREDENTIALS_ACCESS_TOKEN_KEY, accessToken);
+            authenticationInfo.put(XingOauth.AUTHENTICATION_CREDENTIALS_USER_KEY, xingUser);
+            return authenticationInfo;
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+            removeAuthFromSession(request);
+            return null;
+        }
+    }
+
+    @Override
+    public boolean requestCredentials(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
+        logger.debug("request credentials");
+        try {
+            final Token requestToken = oAuthService.getRequestToken();
+            logger.debug("received request token: '{}'", requestToken);
+            final HttpSession httpSession = request.getSession(true);
+            httpSession.setAttribute(OAuthConstants.TOKEN, requestToken);
+            final String authUrl = oAuthService.getAuthorizationUrl(requestToken);
+            logger.debug("redirecting to auth url: '{}'", authUrl);
+            response.sendRedirect(authUrl);
+            return true;
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    @Override
+    public void dropCredentials(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
+        logger.debug("drop credentials");
+        removeAuthFromSession(request);
+    }
+
+    protected XingUser fetchUser(final Token accessToken) throws Exception {
+        final OAuthRequest request = new OAuthRequest(Verb.GET, usersMeUrl);
+        oAuthService.signRequest(accessToken, request);
+        final Response response = request.send();
+        final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+        final Users users = gson.fromJson(response.getBody(), Users.class);
+        return users.getUsers().get(0);
+    }
+
+    protected void removeAuthFromSession(final HttpServletRequest request) {
+        try {
+            final HttpSession httpSession = request.getSession();
+            httpSession.removeAttribute(OAuthConstants.TOKEN);
+            httpSession.removeAttribute(OAuthConstants.ACCESS_TOKEN);
+            httpSession.removeAttribute(USER_SESSION_ATTRIBUTE_NAME);
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthAuthenticationPlugin.java b/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthAuthenticationPlugin.java
new file mode 100644
index 0000000..6be5d90
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthAuthenticationPlugin.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.sling.auth.xing.oauth.impl;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.sling.auth.xing.api.XingUser;
+import org.apache.sling.auth.xing.oauth.XingOauthUserManager;
+import org.apache.sling.auth.xing.oauth.XingOauthUtil;
+import org.apache.sling.jcr.jackrabbit.server.security.AuthenticationPlugin;
+import org.scribe.model.Token;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class XingOauthAuthenticationPlugin implements AuthenticationPlugin {
+
+    final XingOauthUserManager xingOauthUserManager;
+
+    private final Logger logger = LoggerFactory.getLogger(XingOauthAuthenticationPlugin.class);
+
+    public XingOauthAuthenticationPlugin(final XingOauthUserManager xingOauthUserManager) {
+        this.xingOauthUserManager = xingOauthUserManager;
+    }
+
+    @Override
+    public boolean authenticate(final Credentials credentials) throws RepositoryException {
+        logger.debug("authenticate");
+
+        final Token accessToken = XingOauthUtil.getAccessToken(credentials);
+        final XingUser xingUser = XingOauthUtil.getXingUser(credentials);
+        if (accessToken == null || xingUser == null) {
+            return false;
+        }
+
+        User user = xingOauthUserManager.getUser(credentials);
+        if (user == null) { // check if given credentials pulled up an existing user
+            logger.debug("no user found for given credentials");
+            if (xingOauthUserManager.autoCreate()) {
+                logger.debug("creating a new user from given user data");
+                user = xingOauthUserManager.createUser(credentials);
+            }
+        } else {
+            if (xingOauthUserManager.autoUpdate()) {
+                xingOauthUserManager.updateUser(credentials);
+            }
+        }
+
+        return user != null;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthLoginModulePlugin.java b/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthLoginModulePlugin.java
new file mode 100644
index 0000000..a52b494
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/xing/oauth/impl/XingOauthLoginModulePlugin.java
@@ -0,0 +1,122 @@
+/*
+ * 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.sling.auth.xing.oauth.impl;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.sling.auth.xing.api.XingUser;
+import org.apache.sling.auth.xing.oauth.XingOauth;
+import org.apache.sling.auth.xing.oauth.XingOauthUserManager;
+import org.apache.sling.auth.xing.oauth.XingOauthUtil;
+import org.apache.sling.jcr.jackrabbit.server.security.AuthenticationPlugin;
+import org.apache.sling.jcr.jackrabbit.server.security.LoginModulePlugin;
+import org.osgi.framework.Constants;
+import org.scribe.model.Token;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(
+    label = "Apache Sling Authentication XING OAuth “Login Module Plugin”",
+    description = "Login Module Plugin for Sling Authentication XING OAuth",
+    immediate = true,
+    metatype = true
+)
+@Service
+@Properties({
+    @Property(name = Constants.SERVICE_VENDOR, value = XingOauth.SERVICE_VENDOR),
+    @Property(name = Constants.SERVICE_DESCRIPTION, value = "Login Module Plugin for Sling Authentication XING OAuth"),
+    @Property(name = Constants.SERVICE_RANKING, intValue = 0, propertyPrivate = false)
+})
+/**
+ * @see org.apache.jackrabbit.core.security.authentication.DefaultLoginModule
+ */
+public class XingOauthLoginModulePlugin implements LoginModulePlugin {
+
+    @Reference
+    private XingOauthUserManager xingOauthUserManager;
+
+    private final Logger logger = LoggerFactory.getLogger(XingOauthLoginModulePlugin.class);
+
+    public XingOauthLoginModulePlugin() {
+    }
+
+    @Override
+    public boolean canHandle(final Credentials credentials) {
+        logger.debug("canHandle({})", credentials);
+        final Token accessToken = XingOauthUtil.getAccessToken(credentials);
+        final XingUser xingUser = XingOauthUtil.getXingUser(credentials);
+        logger.debug("access token: {}, xing user: {}", accessToken, xingUser);
+        return accessToken != null && xingUser != null;
+    }
+
+    @Override
+    public void doInit(final CallbackHandler callbackHandler, final Session session, final Map map) throws LoginException {
+        logger.debug("doInit({}, {}, {})", callbackHandler, session, map);
+    }
+
+    @Override
+    public Principal getPrincipal(final Credentials credentials) {
+        logger.debug("getPrincipal({})", credentials);
+        try {
+            User user = xingOauthUserManager.getUser(credentials);
+            if (user == null && xingOauthUserManager.autoCreate()) {
+                user = xingOauthUserManager.createUser(credentials);
+            }
+            if (user != null) {
+                return user.getPrincipal();
+            }
+        } catch (RepositoryException e) {
+            logger.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    @Override
+    public void addPrincipals(final Set set) {
+        logger.debug("addPrincipals({})", set);
+    }
+
+    @Override
+    public AuthenticationPlugin getAuthentication(final Principal principal, final Credentials credentials) throws RepositoryException {
+        logger.debug("getAuthentication({}, {})", principal, credentials);
+        return new XingOauthAuthenticationPlugin(xingOauthUserManager);
+    }
+
+    @Override
+    public int impersonate(final Principal principal, final Credentials credentials) throws RepositoryException, FailedLoginException {
+        logger.debug("impersonate({}, {})", principal, credentials);
+        return LoginModulePlugin.IMPERSONATION_DEFAULT;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/auth/xing/oauth/package-info.java b/src/main/java/org/apache/sling/auth/xing/oauth/package-info.java
new file mode 100644
index 0000000..747ee1e
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/xing/oauth/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@aQute.bnd.annotation.Version("0.0.1")
+package org.apache.sling.auth.xing.oauth;
diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties b/src/main/resources/OSGI-INF/metatype/metatype.properties
new file mode 100644
index 0000000..da67f6e
--- /dev/null
+++ b/src/main/resources/OSGI-INF/metatype/metatype.properties
@@ -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.
+#
+
+path.name = path
+path.description = the path or paths this service is in charge for
+
+service.ranking.name = service ranking
+service.ranking.description = service property for identifying the service's ranking number
+
+# Authentication Handler
+org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.consumerKey.name = consumer key
+org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.consumerKey.description = consumer key from XING
+
+org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.consumerSecret.name = consumer secret
+org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.consumerSecret.description = consumer secret from XING
+
+org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.callbackUrl.name = callback URL
+org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.callbackUrl.description = URL to redirect the user-agent after authorization has been granted
+
+org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.usersMeUrl.name = user's profile URL
+org.apache.sling.auth.xing.oauth.impl.XingOauthAuthenticationHandler.usersMeUrl.description = URL of the web service to get the user's profile
+
+# User Manager
+org.apache.sling.auth.xing.oauth.impl.DefaultXingOauthUserManager.user.create.auto.name = create user automatically
+org.apache.sling.auth.xing.oauth.impl.DefaultXingOauthUserManager.user.create.auto.description = create a new JCR user automatically with given user data from XING
+
+org.apache.sling.auth.xing.oauth.impl.DefaultXingOauthUserManager.user.update.auto.name = update user automatically
+org.apache.sling.auth.xing.oauth.impl.DefaultXingOauthUserManager.user.update.auto.description = update an existing JCR user automatically with given user data from XING

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.