This documentation is for WSO2 Identity Server 5.0.0. View documentation for the latest release.
Writing a Custom Twitter Authenticator - Identity Server 5.0.0 - WSO2 Documentation
||
Skip to end of metadata
Go to start of metadata

The WSO2 Identity Server comes with several built in authenticators like Facebook, Google, OpenID, and SAML. This topic provides instructions on how to authenticate users via Twitter using the SAML configurations available in the Identity Server.

Related links
  • See here for information on browser sign in flow. This information is useful when working with websites and applications that are able to open or embed a web browser.
  • See here for information on implementing the sign in functionality using Twitter.
  • If you are doing this using Java, there is a library you can use called twitter4j. See the link for code samples on signing in with Twitter.

The following code block represents the structure of an authenticator pom.xml. Authenticators are basically OSGi (Open Service Gateway initiative) bundles, which are units of modularization that are comprised of Java classes and other resources that provide functions to end users. The pom.xml includes the dependencies for the project. Other than the twitter4j dependency, other dependencies are mandatory.

 Click here to expand and view the code block
<?xml version="1.0" encoding="utf-8"?><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/maven-v4_0_0.xsd">
 
    <groupId>org.emojotFoundation</groupId>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>authenticator-twitter</artifactId>
    <packaging>bundle</packaging>
    <version>1.0.0</version>
 
    <dependencies>
 
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.logging</artifactId>
            <version>4.2.0</version>
        </dependency>
 
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.identity.application.authentication.framework</artifactId>
            <version>4.2.2</version>
        </dependency>
 
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.ui</artifactId>
            <version>4.2.0</version>
        </dependency>
 
        <dependency>
            <groupId>org.apache.amber.wso2</groupId>
            <artifactId>amber</artifactId>
            <version>0.22.1358727.wso2v4</version>
        </dependency>
 
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.identity.application.common</artifactId>
            <version>4.2.0</version>
        </dependency>
 
        <dependency>
            <groupId>org.twitter4j</groupId>
            <artifactId>twitter4j-core</artifactId>
            <version>[4.0,)</version>
        </dependency>
    </dependencies>
 
    <repositories>
        <repository>
            <id>wso2-nexus</id>
            <name>WSO2 Internal Repository</name>
            <url>http://maven.wso2.org/nexus/content/groups/wso2-public/</url>
            <releases>
                <enabled>true</enabled>
                <updatePolicy>daily</updatePolicy>
                <checksumPolicy>ignore</checksumPolicy>
            </releases>
        </repository>
        <repository>
            <id>twitter4j.org</id>
            <name>twitter4j.org Repository</name>
            <url>http://twitter4j.org/maven2</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-scr-plugin</artifactId>
                <version>1.7.4</version>
                <executions>
                    <execution>
                        <id>generate-scr-scrdescriptor</id>
                        <goals>
                            <goal>scr</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                        <Bundle-Name>${project.artifactId}</Bundle-Name>
                        <Private-Package>org.emojotFoundation.authenticator.twitter.internal</Private-Package>
                        <Import-Package>org.twitter4j.*;
                            version="[4.0,)",
                            org.apache.axis2.*;
                            version="[1.6.1.wso2v1, 1.7.0)",
                            org.apache.axiom.*;
                            version="[1.2.11.wso2v2, 1.3.0)",
                            org.wso2.carbon.ui.*,
                            org.apache.commons.logging.*; version="1.0.4",
                            org.osgi.framework,
                            org.wso2.carbon.identity.application.authentication.framework.*,
                            javax.servlet;version="[2.6.0,3.0.0)",
                            javax.servlet.http;version="[2.6.0,3.0.0)",
                            *;resolution:=optional
                        </Import-Package>
                        <Export-Package>!org.emojotFoundation.authenticator.twitter.internal,
                            org.emojotFoundation.authenticator.twitter.*
                        </Export-Package>
                        <DynamicImport-Package>*</DynamicImport-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Since the project is an OSGi bundle, you must add this class to define the bundle activate method and deactivate method.

 Click here to expand and view the class used to define the bundle activate method and deactivate method
package org.emojotFoundation.authenticator.twitter.internal;
 
import java.util.Hashtable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator;
import org.emojotFoundation.authenticator.twitter.TwitterAuthenticator;
 
/**
 * @scr.component name="authenticator.twitter" immediate="true"
 */
 
public class TwitterAuthenticatorServiceComponent {
 
    private static final Log LOGGER = LogFactory.getLog(TwitterAuthenticatorServiceComponent.class);
 
    protected void activate(ComponentContext ctxt) {
        try {
            TwitterAuthenticator twitterAuthenticator = new TwitterAuthenticator();
            Hashtable<String, String> props = new Hashtable<String, String>()
            ctxt.getBundleContext().registerService(ApplicationAuthenticator.class.getName(),twitterAuthenticator, props); 
 
            LOGGER.info("----Twitter Authenticator bundle is activated----");
 
        } catch (Throwable e) {
            LOGGER.fatal("----Error while activating Twitter authenticator----", e);
        }
    }
 
    protected void deactivate(ComponentContext ctxt) {
        LOGGER.info("----Twitter Authenticator bundle is deactivated----");
    }
}

After adding this to your project, you are in a position to write your authenticator. Authenticators are defined by extending the AbstractApplicationAuthenticator class and implementing the FederatedApplicationAuthenticator interface. The important methods in the AbstractApplicationAuthenticator class and the FederatedApplicationAuthenticator interface are listed as follows.

  • public String getName()
  • public String getFriendlyName()
  • public String getContextIdentifier(HttpServletRequest request) - Returns a unique identifier that will map the authentication request and the response. The value returned by the invocation of authentication request and the response should be the same.
  • public boolean canHandle(HttpServletRequest request) - Specifies whether this authenticator can handle the authentication response.
  • protected void initiateAuthenticationRequest(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)
  • protected void processAuthenticationResponse(HttpServletRequest request,HttpServletResponse response, AuthenticationContext context)

Use the following steps to write the Twitter authenticator.

  1. Implement the canHandle() method using the above methods. When Twitter sends the OAuth response, it sends the parameters oauth_token and oauth_verifier in the request. This is a notification to identify that this response can be handled by the authenticator.

     Click here to expand and view a sample canHandle() method
    public boolean canHandle(HttpServletRequest request) {
            if (request.getParameter("oauth_token")!=null && request.getParameter("oauth_verifier")!=null) {
                return true;
            }
            return false;
    }
  2. For each authentication request that comes into the Identity Server, there is unique value that comes along as a parameter. That parameter is the sessionDataKey. Store this in the Twitter authentication redirection session to facilitate the requirement where getContextIdentifier gives the same value for authentication request and its response.

     Click here to expand and view getContextIdentifier()
    public String getContextIdentifier(HttpServletRequest request) {
            if(request.getSession().getAttribute("contextIdentifier")==null){ 
                request.getSession().setAttribute("contextIdentifier",request.getParameter("sessionDataKey"));
                return request.getParameter("sessionDataKey");
            }else{
                return (String) request.getSession().getAttribute("contextIdentifier");
            }
    }
  3. Next, implement the initiateAuthenticationRequest method and the processAuthenticationResponse method as follows.

     Click here to expand and view the initiateAuthenticationRequest method and processAuthenticationResponse method
    protected void initiateAuthenticationRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws AuthenticationFailedException {       
     
            String apiKey= resourceBundle.getString("API_Key");
            String apiSecret= resourceBundle.getString("API_Secret");
     
            Twitter twitter = new TwitterFactory().getInstance();
            twitter.setOAuthConsumer(apiKey, apiSecret);
            
            try {
                String callbackURL = resourceBundle.getString("Call_Back_URL");
                RequestToken requestToken = twitter.getOAuthRequestToken(callbackURL.toString());
                request.getSession().setAttribute("requestToken",requestToken);
                request.getSession().setAttribute("twitter",twitter);
                response.sendRedirect(requestToken.getAuthenticationURL());
     
            } catch (TwitterException e) {
                LOGGER.error("Exception while sending to the Twitter login page.", e);
                throw new AuthenticationFailedException(e.getMessage(), e);
            } catch (IOException e) {
                LOGGER.error("Exception while sending to the Twitter login page.", e);
                throw new AuthenticationFailedException(e.getMessage(), e);
            }
            return;
    }
     
    protected void processAuthenticationResponse(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws AuthenticationFailedException {
            Twitter twitter = (Twitter) request.getSession().getAttribute("twitter");
            RequestToken requestToken = (RequestToken) request.getSession().getAttribute("requestToken");
            String verifier = request.getParameter("oauth_verifier");
            try {
                AccessToken token=twitter.getOAuthAccessToken(requestToken, verifier);
                request.getSession().removeAttribute("requestToken");
                User user= twitter.verifyCredentials();
                buildClaims(user,context);
            } catch (TwitterException e) {
                LOGGER.error("Exception while obtaining OAuth token form Twitter", e);
                throw new AuthenticationFailedException("Exception while obtaining OAuth token form Twitter",e);
            }
    }
     
    public void buildClaims(User user, AuthenticationContext context) {
     
                context.setSubject(String.valueOf(user.getId()));
                Map<ClaimMapping, String> claims = new HashMap<ClaimMapping, String>();
                claims.put(ClaimMapping.build("name", "name", null,false), user.getName());
                claims.put(ClaimMapping.build("screen_name", "screen_name", null,false), user.getScreenName());
                claims.put(ClaimMapping.build("url", "url", null,false), user.getURL());
     
                context.setSubjectAttributes(claims);
    }
  4. The buildClaims method saves the retrieved user attributes to the authenticated context in the Identity Server. That is needed to map the claims to the built in claims of IS.
  5. After implementing these methods you can build your bundle. After building it you have to put that into the <IS_HOME>/repository/components/dropins directory.
  6. Restart the Identity Server to use the Twitter authenticator in IS.
  • No labels