Upgrade twitter4j (#16828)
Closes https://github.com/keycloak/keycloak/issues/16731
This commit is contained in:
parent
0e374c7a45
commit
f8f112d8d2
5 changed files with 89 additions and 40 deletions
2
pom.xml
2
pom.xml
|
@ -141,7 +141,7 @@
|
||||||
<pax.web.version>7.1.0</pax.web.version>
|
<pax.web.version>7.1.0</pax.web.version>
|
||||||
<servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
|
<servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
|
||||||
<servlet.api.40.version>2.0.0.Final</servlet.api.40.version>
|
<servlet.api.40.version>2.0.0.Final</servlet.api.40.version>
|
||||||
<twitter4j.version>4.0.7</twitter4j.version>
|
<twitter4j.version>4.1.2</twitter4j.version>
|
||||||
<jna.version>4.1.0</jna.version>
|
<jna.version>4.1.0</jna.version>
|
||||||
|
|
||||||
<!-- Databases -->
|
<!-- Databases -->
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.keycloak.broker.provider.IdentityProvider;
|
||||||
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
||||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
|
import org.keycloak.common.util.Base64;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
@ -43,12 +44,14 @@ import org.keycloak.services.managers.ClientSessionCode;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
import org.keycloak.vault.VaultStringSecret;
|
import org.keycloak.vault.VaultStringSecret;
|
||||||
|
import twitter4j.AccessToken;
|
||||||
|
import twitter4j.OAuthAuthorization;
|
||||||
|
import twitter4j.RequestToken;
|
||||||
import twitter4j.Twitter;
|
import twitter4j.Twitter;
|
||||||
import twitter4j.TwitterFactory;
|
import twitter4j.v1.User;
|
||||||
import twitter4j.auth.AccessToken;
|
|
||||||
import twitter4j.auth.RequestToken;
|
|
||||||
import twitter4j.conf.ConfigurationBuilder;
|
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
@ -67,14 +70,19 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||||
|
|
||||||
String TWITTER_TOKEN_TYPE="twitter";
|
String TWITTER_TOKEN_TYPE="twitter";
|
||||||
|
|
||||||
|
|
||||||
protected static final Logger logger = Logger.getLogger(TwitterIdentityProvider.class);
|
protected static final Logger logger = Logger.getLogger(TwitterIdentityProvider.class);
|
||||||
|
|
||||||
private static final String TWITTER_TOKEN = "twitter_token";
|
private static final String TWITTER_TOKEN = "twitter_token";
|
||||||
private static final String TWITTER_TOKENSECRET = "twitter_tokenSecret";
|
|
||||||
|
private final OAuthAuthorization oAuthAuthorization;
|
||||||
|
|
||||||
public TwitterIdentityProvider(KeycloakSession session, OAuth2IdentityProviderConfig config) {
|
public TwitterIdentityProvider(KeycloakSession session, OAuth2IdentityProviderConfig config) {
|
||||||
super(session, config);
|
super(session, config);
|
||||||
|
try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(getConfig().getClientSecret())) {
|
||||||
|
oAuthAuthorization = OAuthAuthorization.newBuilder()
|
||||||
|
.oAuthConsumer(getConfig().getClientId(), vaultStringSecret.get().orElse(getConfig().getClientSecret()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -84,17 +92,12 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response performLogin(AuthenticationRequest request) {
|
public Response performLogin(AuthenticationRequest request) {
|
||||||
try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(getConfig().getClientSecret())) {
|
try {
|
||||||
Twitter twitter = new TwitterFactory().getInstance();
|
|
||||||
twitter.setOAuthConsumer(getConfig().getClientId(), vaultStringSecret.get().orElse(getConfig().getClientSecret()));
|
|
||||||
|
|
||||||
URI uri = new URI(request.getRedirectUri() + "?state=" + request.getState().getEncoded());
|
URI uri = new URI(request.getRedirectUri() + "?state=" + request.getState().getEncoded());
|
||||||
|
RequestToken requestToken = oAuthAuthorization.getOAuthRequestToken(uri.toString());
|
||||||
RequestToken requestToken = twitter.getOAuthRequestToken(uri.toString());
|
|
||||||
AuthenticationSessionModel authSession = request.getAuthenticationSession();
|
AuthenticationSessionModel authSession = request.getAuthenticationSession();
|
||||||
|
|
||||||
authSession.setAuthNote(TWITTER_TOKEN, requestToken.getToken());
|
authSession.setAuthNote(TWITTER_TOKEN, Base64.encodeObject(requestToken));
|
||||||
authSession.setAuthNote(TWITTER_TOKENSECRET, requestToken.getTokenSecret());
|
|
||||||
|
|
||||||
URI authenticationUrl = URI.create(requestToken.getAuthenticationURL());
|
URI authenticationUrl = URI.create(requestToken.getAuthenticationURL());
|
||||||
|
|
||||||
|
@ -205,16 +208,19 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||||
OAuth2IdentityProviderConfig providerConfig = provider.getConfig();
|
OAuth2IdentityProviderConfig providerConfig = provider.getConfig();
|
||||||
|
|
||||||
try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(providerConfig.getClientSecret())) {
|
try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(providerConfig.getClientSecret())) {
|
||||||
Twitter twitter = new TwitterFactory(new ConfigurationBuilder().setIncludeEmailEnabled(true).build()).getInstance();
|
|
||||||
twitter.setOAuthConsumer(providerConfig.getClientId(), vaultStringSecret.get().orElse(providerConfig.getClientSecret()));
|
|
||||||
|
|
||||||
String twitterToken = authSession.getAuthNote(TWITTER_TOKEN);
|
String twitterToken = authSession.getAuthNote(TWITTER_TOKEN);
|
||||||
String twitterSecret = authSession.getAuthNote(TWITTER_TOKENSECRET);
|
RequestToken requestToken;
|
||||||
|
try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(Base64.decode(twitterToken)))) {
|
||||||
|
requestToken = (RequestToken) in.readObject();
|
||||||
|
}
|
||||||
|
|
||||||
RequestToken requestToken = new RequestToken(twitterToken, twitterSecret);
|
AccessToken oAuthAccessToken = provider.oAuthAuthorization.getOAuthAccessToken(requestToken, verifier);
|
||||||
|
|
||||||
AccessToken oAuthAccessToken = twitter.getOAuthAccessToken(requestToken, verifier);
|
Twitter twitter = Twitter.newBuilder()
|
||||||
twitter4j.User twitterUser = twitter.verifyCredentials();
|
.oAuthConsumer(providerConfig.getClientId(), vaultStringSecret.get().orElse(providerConfig.getClientSecret()))
|
||||||
|
.oAuthAccessToken(oAuthAccessToken)
|
||||||
|
.build();
|
||||||
|
User twitterUser = twitter.v1().users().verifyCredentials();
|
||||||
|
|
||||||
BrokeredIdentityContext identity = new BrokeredIdentityContext(Long.toString(twitterUser.getId()));
|
BrokeredIdentityContext identity = new BrokeredIdentityContext(Long.toString(twitterUser.getId()));
|
||||||
identity.setIdp(provider);
|
identity.setIdp(provider);
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.testsuite.pages.social;
|
||||||
|
|
||||||
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
|
*/
|
||||||
|
public class TwitterConsentLoginPage extends AbstractSocialLoginPage {
|
||||||
|
|
||||||
|
@FindBy(xpath = "//input[@type='submit' and @id='allow']")
|
||||||
|
private WebElement signInButton;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
TwitterLoginPage loginPage;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void login(String user, String password) {
|
||||||
|
// twitter presents a consent page for the application
|
||||||
|
// the SignIn button should be clicked first to go to the real login
|
||||||
|
signInButton.click();
|
||||||
|
|
||||||
|
loginPage.login(user, password);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,8 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.pages.social;
|
package org.keycloak.testsuite.pages.social;
|
||||||
|
|
||||||
import org.openqa.selenium.NoSuchElementException;
|
import org.keycloak.testsuite.util.WaitUtils;
|
||||||
|
import org.openqa.selenium.Keys;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.support.FindBy;
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
@ -25,26 +26,25 @@ import org.openqa.selenium.support.FindBy;
|
||||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
*/
|
*/
|
||||||
public class TwitterLoginPage extends AbstractSocialLoginPage {
|
public class TwitterLoginPage extends AbstractSocialLoginPage {
|
||||||
@FindBy(id = "username_or_email")
|
|
||||||
|
@FindBy(xpath = "//input[@type='text' and @name='text']")
|
||||||
private WebElement usernameInput;
|
private WebElement usernameInput;
|
||||||
|
|
||||||
@FindBy(id = "password")
|
@FindBy(xpath = "//input[@type='password']")
|
||||||
private WebElement passwordInput;
|
private WebElement passwordInput;
|
||||||
|
|
||||||
@FindBy(id = "allow")
|
|
||||||
private WebElement loginButton;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void login(String user, String password) {
|
public void login(String user, String password) {
|
||||||
try {
|
// new login page is two phase login (username and then password) and it
|
||||||
usernameInput.clear();
|
// needs lots of JS, twitter does not work with default HtmlUnit driver
|
||||||
usernameInput.sendKeys(user);
|
usernameInput.clear();
|
||||||
passwordInput.sendKeys(password);
|
usernameInput.sendKeys(user);
|
||||||
}
|
usernameInput.sendKeys(Keys.RETURN);
|
||||||
catch (NoSuchElementException e) { // at some conditions we are already logged in and just need to confirm it
|
|
||||||
}
|
// wait for the password input to appear
|
||||||
finally {
|
WaitUtils.waitUntilElement(passwordInput).is().visible();
|
||||||
loginButton.click();
|
passwordInput.clear();
|
||||||
}
|
passwordInput.sendKeys(password);
|
||||||
|
passwordInput.sendKeys(Keys.RETURN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ import org.keycloak.testsuite.pages.social.MicrosoftLoginPage;
|
||||||
import org.keycloak.testsuite.pages.social.OpenShiftLoginPage;
|
import org.keycloak.testsuite.pages.social.OpenShiftLoginPage;
|
||||||
import org.keycloak.testsuite.pages.social.PayPalLoginPage;
|
import org.keycloak.testsuite.pages.social.PayPalLoginPage;
|
||||||
import org.keycloak.testsuite.pages.social.StackOverflowLoginPage;
|
import org.keycloak.testsuite.pages.social.StackOverflowLoginPage;
|
||||||
import org.keycloak.testsuite.pages.social.TwitterLoginPage;
|
import org.keycloak.testsuite.pages.social.TwitterConsentLoginPage;
|
||||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
import org.keycloak.testsuite.util.RealmBuilder;
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
|
@ -121,7 +121,7 @@ public class SocialLoginTest extends AbstractKeycloakTest {
|
||||||
FACEBOOK_INCLUDE_BIRTHDAY("facebook", FacebookLoginPage.class),
|
FACEBOOK_INCLUDE_BIRTHDAY("facebook", FacebookLoginPage.class),
|
||||||
GITHUB("github", GitHubLoginPage.class),
|
GITHUB("github", GitHubLoginPage.class),
|
||||||
GITHUB_PRIVATE_EMAIL("github", "github-private-email", GitHubLoginPage.class),
|
GITHUB_PRIVATE_EMAIL("github", "github-private-email", GitHubLoginPage.class),
|
||||||
TWITTER("twitter", TwitterLoginPage.class),
|
TWITTER("twitter", TwitterConsentLoginPage.class),
|
||||||
LINKEDIN("linkedin", LinkedInLoginPage.class),
|
LINKEDIN("linkedin", LinkedInLoginPage.class),
|
||||||
LINKEDIN_WITH_PROJECTION("linkedin", LinkedInLoginPage.class),
|
LINKEDIN_WITH_PROJECTION("linkedin", LinkedInLoginPage.class),
|
||||||
MICROSOFT("microsoft", MicrosoftLoginPage.class),
|
MICROSOFT("microsoft", MicrosoftLoginPage.class),
|
||||||
|
|
Loading…
Reference in a new issue