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>
|
||||
<servlet.api.30.version>1.0.2.Final</servlet.api.30.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>
|
||||
|
||||
<!-- Databases -->
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.keycloak.broker.provider.IdentityProvider;
|
|||
import org.keycloak.broker.provider.util.IdentityBrokerState;
|
||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.util.Base64;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
|
@ -43,12 +44,14 @@ import org.keycloak.services.managers.ClientSessionCode;
|
|||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.vault.VaultStringSecret;
|
||||
import twitter4j.AccessToken;
|
||||
import twitter4j.OAuthAuthorization;
|
||||
import twitter4j.RequestToken;
|
||||
import twitter4j.Twitter;
|
||||
import twitter4j.TwitterFactory;
|
||||
import twitter4j.auth.AccessToken;
|
||||
import twitter4j.auth.RequestToken;
|
||||
import twitter4j.conf.ConfigurationBuilder;
|
||||
import twitter4j.v1.User;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
|
@ -67,14 +70,19 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
|||
|
||||
String TWITTER_TOKEN_TYPE="twitter";
|
||||
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(TwitterIdentityProvider.class);
|
||||
|
||||
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) {
|
||||
super(session, config);
|
||||
try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(getConfig().getClientSecret())) {
|
||||
oAuthAuthorization = OAuthAuthorization.newBuilder()
|
||||
.oAuthConsumer(getConfig().getClientId(), vaultStringSecret.get().orElse(getConfig().getClientSecret()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -84,17 +92,12 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
|||
|
||||
@Override
|
||||
public Response performLogin(AuthenticationRequest request) {
|
||||
try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(getConfig().getClientSecret())) {
|
||||
Twitter twitter = new TwitterFactory().getInstance();
|
||||
twitter.setOAuthConsumer(getConfig().getClientId(), vaultStringSecret.get().orElse(getConfig().getClientSecret()));
|
||||
|
||||
try {
|
||||
URI uri = new URI(request.getRedirectUri() + "?state=" + request.getState().getEncoded());
|
||||
|
||||
RequestToken requestToken = twitter.getOAuthRequestToken(uri.toString());
|
||||
RequestToken requestToken = oAuthAuthorization.getOAuthRequestToken(uri.toString());
|
||||
AuthenticationSessionModel authSession = request.getAuthenticationSession();
|
||||
|
||||
authSession.setAuthNote(TWITTER_TOKEN, requestToken.getToken());
|
||||
authSession.setAuthNote(TWITTER_TOKENSECRET, requestToken.getTokenSecret());
|
||||
authSession.setAuthNote(TWITTER_TOKEN, Base64.encodeObject(requestToken));
|
||||
|
||||
URI authenticationUrl = URI.create(requestToken.getAuthenticationURL());
|
||||
|
||||
|
@ -205,16 +208,19 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
|||
OAuth2IdentityProviderConfig providerConfig = provider.getConfig();
|
||||
|
||||
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 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);
|
||||
twitter4j.User twitterUser = twitter.verifyCredentials();
|
||||
Twitter twitter = Twitter.newBuilder()
|
||||
.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()));
|
||||
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;
|
||||
|
||||
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.support.FindBy;
|
||||
|
||||
|
@ -25,26 +26,25 @@ import org.openqa.selenium.support.FindBy;
|
|||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||
*/
|
||||
public class TwitterLoginPage extends AbstractSocialLoginPage {
|
||||
@FindBy(id = "username_or_email")
|
||||
|
||||
@FindBy(xpath = "//input[@type='text' and @name='text']")
|
||||
private WebElement usernameInput;
|
||||
|
||||
@FindBy(id = "password")
|
||||
@FindBy(xpath = "//input[@type='password']")
|
||||
private WebElement passwordInput;
|
||||
|
||||
@FindBy(id = "allow")
|
||||
private WebElement loginButton;
|
||||
|
||||
@Override
|
||||
public void login(String user, String password) {
|
||||
try {
|
||||
// new login page is two phase login (username and then password) and it
|
||||
// needs lots of JS, twitter does not work with default HtmlUnit driver
|
||||
usernameInput.clear();
|
||||
usernameInput.sendKeys(user);
|
||||
usernameInput.sendKeys(Keys.RETURN);
|
||||
|
||||
// wait for the password input to appear
|
||||
WaitUtils.waitUntilElement(passwordInput).is().visible();
|
||||
passwordInput.clear();
|
||||
passwordInput.sendKeys(password);
|
||||
}
|
||||
catch (NoSuchElementException e) { // at some conditions we are already logged in and just need to confirm it
|
||||
}
|
||||
finally {
|
||||
loginButton.click();
|
||||
}
|
||||
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.PayPalLoginPage;
|
||||
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.OAuthClient;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
|
@ -121,7 +121,7 @@ public class SocialLoginTest extends AbstractKeycloakTest {
|
|||
FACEBOOK_INCLUDE_BIRTHDAY("facebook", FacebookLoginPage.class),
|
||||
GITHUB("github", GitHubLoginPage.class),
|
||||
GITHUB_PRIVATE_EMAIL("github", "github-private-email", GitHubLoginPage.class),
|
||||
TWITTER("twitter", TwitterLoginPage.class),
|
||||
TWITTER("twitter", TwitterConsentLoginPage.class),
|
||||
LINKEDIN("linkedin", LinkedInLoginPage.class),
|
||||
LINKEDIN_WITH_PROJECTION("linkedin", LinkedInLoginPage.class),
|
||||
MICROSOFT("microsoft", MicrosoftLoginPage.class),
|
||||
|
|
Loading…
Reference in a new issue