/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.examples.broker.twitter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.IdentityProvidersResource;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.conf.ConfigurationBuilder;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.io.PrintWriter;
/**
* <p>A simple servlet to proxy Twitter API using the Twitter4j library.</p>
*
* <p>It provides some additional code to properly handle token retrieval from the Twitter identity provider in Keycloak
* and use that token to invoke Twitter's API.</p>
*
* @author pedroigor
*/
@WebServlet(urlPatterns = "/twitter/showUser")
public class TwitterShowUserServlet extends HttpServlet {
private Keycloak keycloak;
private String authServer;
private String realmName;
private IdentityProviderRepresentation identityProvider;
@Override
public void init(ServletConfig config) throws ServletException {
initKeycloakClient(config);
}
@Override
public void destroy() {
this.keycloak.close();
}
@Override
protected void doGet(final HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
TwitterOAuthResponse twitterOAuthResponse = getTwitterOAuthResponse(request);
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey(this.identityProvider.getConfig().get("clientId"))
.setOAuthConsumerSecret(this.identityProvider.getConfig().get("clientSecret"))
.setOAuthAccessToken(twitterOAuthResponse.getToken())
.setOAuthAccessTokenSecret(twitterOAuthResponse.getTokenSecret());
TwitterFactory tf = new TwitterFactory(cb.build());
Twitter twitter = tf.getInstance();
try {
User user = twitter.users().showUser(twitterOAuthResponse.getScreenName());
response.setContentType(MediaType.APPLICATION_JSON);
PrintWriter writer = response.getWriter();
writer.println(new ObjectMapper().writeValueAsString(user));
writer.flush();
} catch (TwitterException e) {
throw new RuntimeException("Could not load social profile.", e);
}
}
private TwitterOAuthResponse getTwitterOAuthResponse(final HttpServletRequest req) {
ClientRequestFilter authFilter = new ClientRequestFilter() {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
KeycloakSecurityContext securityContext = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
String accessToken = securityContext.getTokenString();
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
}
};
Client client = ClientBuilder.newBuilder().register(authFilter).build();
WebTarget target = client.target(getIdentityProviderTokenUrl());
return target.request().get().readEntity(TwitterOAuthResponse.class);
}
private String getIdentityProviderTokenUrl() {
return this.authServer + "/realms/" + this.realmName + "/broker/" + this.identityProvider.getAlias() + "/token";
}
private void initKeycloakClient(ServletConfig config) {
ServletContext servletContext = config.getServletContext();
JsonNode keycloakConfig;
try {
keycloakConfig = new ObjectMapper().readTree(servletContext.getResourceAsStream("WEB-INF/keycloak.json"));
} catch (IOException e) {
throw new RuntimeException("Could not parse keycloak config.", e);
}
this.authServer = keycloakConfig.get("auth-server-url").asText();
this.realmName = keycloakConfig.get("realm").asText();
this.keycloak = Keycloak.getInstance(authServer, realmName, "admin", "password", "admin-client", "password");
IdentityProvidersResource providersResource = keycloak.realm(realmName).identityProviders();
this.identityProvider = providersResource.get("twitter").toRepresentation();
}
}