From c576c003716851863c832b5e0dacb9186516f167 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Wed, 19 Jul 2023 23:50:14 +0300 Subject: Migrate to Twitter API 2.0 --- src/main/java/com/juick/www/controllers/Site.java | 1 + .../com/juick/www/controllers/SocialLogin.java | 89 ++++++++++------------ 2 files changed, 43 insertions(+), 47 deletions(-) (limited to 'src/main/java/com/juick/www') diff --git a/src/main/java/com/juick/www/controllers/Site.java b/src/main/java/com/juick/www/controllers/Site.java index 8b35593c..47438900 100644 --- a/src/main/java/com/juick/www/controllers/Site.java +++ b/src/main/java/com/juick/www/controllers/Site.java @@ -95,6 +95,7 @@ public class Site { tagService.getUserTagStats(user.getUid()).stream() .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).limit(20) .map(t -> t.getTag().getName()).toList()); + model.addAttribute("twitter1", userService.isTwitter1User(user)); } @GetMapping("/login") diff --git a/src/main/java/com/juick/www/controllers/SocialLogin.java b/src/main/java/com/juick/www/controllers/SocialLogin.java index 0cac26c5..bae9deb2 100644 --- a/src/main/java/com/juick/www/controllers/SocialLogin.java +++ b/src/main/java/com/juick/www/controllers/SocialLogin.java @@ -16,18 +16,19 @@ */ package com.juick.www.controllers; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.scribejava.apis.*; import com.github.scribejava.core.builder.ServiceBuilder; import com.github.scribejava.core.model.*; +import com.github.scribejava.core.oauth.AccessTokenRequestParams; +import com.github.scribejava.core.oauth.AuthorizationUrlBuilder; import com.github.scribejava.core.oauth.OAuth10aService; import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.pkce.PKCEService; import com.juick.model.ext.facebook.User; import com.juick.model.ext.vk.UsersResponse; -import com.juick.service.EmailService; -import com.juick.service.TelegramService; -import com.juick.service.UserService; -import com.juick.service.VKService; +import com.juick.service.*; import com.juick.service.security.entities.JuickUser; import com.juick.util.HttpBadRequestException; @@ -82,17 +83,16 @@ public class SocialLogin { private String FACEBOOK_SECRET; @Value("${ap_base_uri:http://localhost:8080/}") private String baseUri; - private static final String TWITTER_VERIFY_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; + private static final String TWITTER_VERIFY_URL = "https://api.twitter.com/2/users/me"; @Inject private ObjectMapper jsonMapper; private ServiceBuilder twitterBuilder; private OAuth20Service facebookAuthService, appleSignInService; @Inject private VKService vkService; - @Value("${twitter_consumer_key:appid}") - private String twitterConsumerKey; - @Value("${twitter_consumer_secret:secret}") - private String twitterConsumerSecret; + @Inject + private TwitterService twitterService; + @Value("${telegram_token:secret}") private String telegramToken; @Value("${apple_app_id:appid}") @@ -108,10 +108,11 @@ public class SocialLogin { @Inject private RememberMeServices rememberMeServices; + private AuthorizationUrlBuilder authorizationUrlBuilder; + @PostConstruct public void init() { ServiceBuilder facebookBuilder = new ServiceBuilder(FACEBOOK_APPID); - twitterBuilder = new ServiceBuilder(twitterConsumerKey); UriComponentsBuilder redirectBuilder = UriComponentsBuilder.fromUriString(baseUri); String facebookRedirectUri = redirectBuilder.replacePath("/_fblogin").build().toUriString(); @@ -189,47 +190,41 @@ public class SocialLogin { } @GetMapping("/_twitter") - protected void doTwitterLogin(com.juick.model.User user, HttpServletRequest request, - HttpServletResponse response) throws IOException, ExecutionException, InterruptedException { - String hash = StringUtils.EMPTY, request_token = StringUtils.EMPTY, request_token_secret = StringUtils.EMPTY; - String verifier = request.getParameter("oauth_verifier"); - Cookie[] cookies = request.getCookies(); - for (Cookie cookie : cookies) { - if (cookie.getName().equals("hash")) { - hash = cookie.getValue(); - } - if (cookie.getName().equals("request_token")) { - request_token = cookie.getValue(); - } - if (cookie.getName().equals("request_token_secret")) { - request_token_secret = cookie.getValue(); - } - } - OAuth10aService oAuthService = twitterBuilder.apiSecret(twitterConsumerSecret) - .callback("https://juick.com/_twitter").build(TwitterApi.instance()); + protected String doTwitterLogin(@RequestParam(required = false) String code, + @RequestParam(required = false) String state, + com.juick.model.User user, + HttpServletRequest request) + throws IOException, ExecutionException, InterruptedException { - if (request_token.isEmpty() && request_token_secret.isEmpty() && (verifier == null || verifier.isEmpty())) { - OAuth1RequestToken requestToken = oAuthService.getRequestToken(); - String authUrl = oAuthService.getAuthorizationUrl(requestToken); - response.addCookie(new Cookie("request_token", requestToken.getToken())); - response.addCookie(new Cookie("request_token_secret", requestToken.getTokenSecret())); - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", authUrl); + if (StringUtils.isBlank(code)) { + state = UUID.randomUUID().toString(); + authorizationUrlBuilder = twitterService.getTwitterAuthService().createAuthorizationUrlBuilder() + .state(state) + .initPKCE(); + return "redirect:" + authorizationUrlBuilder.build(); } else { - if (verifier != null && verifier.length() > 0) { - OAuth1RequestToken requestToken = new OAuth1RequestToken(request_token, request_token_secret); - OAuth1AccessToken accessToken = oAuthService.getAccessToken(requestToken, verifier); - OAuthRequest oAuthRequest = new OAuthRequest(Verb.GET, TWITTER_VERIFY_URL); - oAuthService.signRequest(accessToken, oAuthRequest); - com.juick.model.ext.twitter.User twitterUser = jsonMapper.readValue( - oAuthService.execute(oAuthRequest).getBody(), com.juick.model.ext.twitter.User.class); - if (userService.linkTwitterAccount(user, accessToken.getToken(), accessToken.getTokenSecret(), - twitterUser.screenName())) { - response.setStatus(HttpServletResponse.SC_FOUND); - response.setHeader("Location", "http://juick.com/settings"); + var token = twitterService.getTwitterAuthService().getAccessToken(AccessTokenRequestParams.create(code) + .pkceCodeVerifier(authorizationUrlBuilder.getPkce().getCodeVerifier())); + var me = new OAuthRequest(Verb.GET, TWITTER_VERIFY_URL); + twitterService.getTwitterAuthService().signRequest(token, me); + try (var response = twitterService.getTwitterAuthService().execute(me)) { + if (response.isSuccessful()) { + logger.info("Twitter response: {}", response.getBody()); + JsonNode json = jsonMapper.readTree(response.getBody()); + var screenName = json.get("data").get("username").asText(); + if (userService.linkTwitterAccount(user, token.getAccessToken(), token.getRefreshToken(), + screenName)) { + return "redirect:https://juick.com/settings"; + } else { + throw new HttpBadRequestException(); + } } else { - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + logger.warn("Twitter error {}: {}", response.getCode(), response.getBody()); + throw new HttpBadRequestException(); } + } catch (Exception e) { + logger.error("Twitter error", e); + throw new HttpBadRequestException(); } } } -- cgit v1.2.3