X-Git-Url: https://juplo.de/gitweb/?a=blobdiff_plain;ds=sidebyside;f=dist%2Ffacebook-utils-2.5.0%2Fxref%2Fde%2Fjuplo%2Ffacebook%2Ftoken%2FSignedRequestAwareAuthorizationCodeAccessTokenProvider.html;fp=dist%2Ffacebook-utils-2.5.0%2Fxref%2Fde%2Fjuplo%2Ffacebook%2Ftoken%2FSignedRequestAwareAuthorizationCodeAccessTokenProvider.html;h=5b65226f186e565f4f093b35282b3c765c438712;hb=a53595184bd6e57bdc45292cc92c393c4e2dfe6e;hp=0000000000000000000000000000000000000000;hpb=c48c9ee0e9faa89a4c0a5323b367b9f5a6abe602;p=website diff --git a/dist/facebook-utils-2.5.0/xref/de/juplo/facebook/token/SignedRequestAwareAuthorizationCodeAccessTokenProvider.html b/dist/facebook-utils-2.5.0/xref/de/juplo/facebook/token/SignedRequestAwareAuthorizationCodeAccessTokenProvider.html new file mode 100644 index 00000000..5b65226f --- /dev/null +++ b/dist/facebook-utils-2.5.0/xref/de/juplo/facebook/token/SignedRequestAwareAuthorizationCodeAccessTokenProvider.html @@ -0,0 +1,261 @@ + + +
++1 package de.juplo.facebook.token; +2 +3 +4 import java.io.IOException; +5 import java.io.UnsupportedEncodingException; +6 import java.security.InvalidKeyException; +7 import java.security.NoSuchAlgorithmException; +8 import java.util.Date; +9 import java.util.HashMap; +10 import java.util.Map; +11 import java.util.regex.Matcher; +12 import java.util.regex.Pattern; +13 import javax.crypto.Mac; +14 import javax.crypto.spec.SecretKeySpec; +15 import org.apache.commons.codec.binary.Base64; +16 import org.codehaus.jackson.JsonNode; +17 import org.codehaus.jackson.map.ObjectMapper; +18 import org.slf4j.Logger; +19 import org.slf4j.LoggerFactory; +20 import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +21 import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; +22 import org.springframework.security.oauth2.client.token.AccessTokenRequest; +23 import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; +24 import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +25 import org.springframework.security.oauth2.common.OAuth2AccessToken; +26 +27 +28 /** +29 * This class extends {@link AuthorizationCodeAccessTokenProvider} and adds +30 * support for signed requests, which are issued by Facebook, if the Canvas- +31 * or Tab-Page of a Facebook-App is accessed for the first time. +32 * +33 * @author Kai Moritz +34 */ +35 public class SignedRequestAwareAuthorizationCodeAccessTokenProvider +36 extends AuthorizationCodeAccessTokenProvider +37 { +38 private final Logger log = +39 LoggerFactory.getLogger(SignedRequestAwareAuthorizationCodeAccessTokenProvider.class); +40 private final static Pattern pattern = +41 Pattern.compile("([a-zA-Z0-9_-]+)\\.([a-zA-Z0-9_-]+)"); +42 +43 public final static String PARAM_SIGNED_REQUEST = "signed_request"; +44 +45 +46 private String secret; +47 private ObjectMapper objectMapper; +48 +49 +50 @Override +51 public OAuth2AccessToken obtainAccessToken( +52 OAuth2ProtectedResourceDetails details, +53 AccessTokenRequest parameters +54 ) +55 { +56 try +57 { +58 return super.obtainAccessToken(details, parameters); +59 } +60 catch (UserRedirectRequiredException redirect) +61 { +62 log.debug("no valid access-token available: checking for signed request"); +63 +64 if (!parameters.containsKey(PARAM_SIGNED_REQUEST)) +65 { +66 log.info( +67 "parameter " + PARAM_SIGNED_REQUEST + " is not present" +68 ); +69 throw redirect; +70 } +71 +72 String signed_request = parameters.get(PARAM_SIGNED_REQUEST).get(0); +73 +74 Matcher matcher = pattern.matcher(signed_request); +75 if (!matcher.matches()) +76 { +77 log.error("invalid signed_request: {}", signed_request); +78 throw redirect; +79 } +80 +81 String signature = matcher.group(1); +82 String rawdata = matcher.group(2); +83 +84 String data; +85 try +86 { +87 data = new String(Base64.decodeBase64(rawdata), "UTF-8"); +88 log.debug("JSON-data: {}", data); +89 } +90 catch (UnsupportedEncodingException e) +91 { +92 log.error("error while decoding data: {}", e.getMessage()); +93 throw redirect; +94 } +95 +96 JsonNode json; +97 try +98 { +99 json = objectMapper.readTree(data); +100 } +101 catch (IOException e) +102 { +103 log.error("error \"{}\" while parsing JSON-data: {}", e, data); +104 throw redirect; +105 } +106 +107 String algorithm = ""; +108 try +109 { +110 algorithm = json.get("algorithm").asText(); +111 } +112 catch (NullPointerException e) {} +113 if (algorithm.isEmpty()) +114 { +115 log.error("field \"algorithm\" is missing: {}", data); +116 throw redirect; +117 } +118 algorithm = algorithm.replaceAll("-", ""); +119 +120 String check; +121 try +122 { +123 SecretKeySpec key = new SecretKeySpec(secret.getBytes("UTF-8"), algorithm); +124 Mac mac = Mac.getInstance(algorithm); +125 mac.init(key); +126 byte[] hmacData = mac.doFinal(rawdata.getBytes("UTF-8")); +127 check = new String(Base64.encodeBase64URLSafe(hmacData), "UTF-8"); +128 } +129 catch ( +130 UnsupportedEncodingException | +131 NoSuchAlgorithmException | +132 InvalidKeyException | +133 IllegalStateException e +134 ) +135 { +136 log.error("signature check failed!", e); +137 throw redirect; +138 } +139 if (!check.equals(signature)) +140 { +141 log.error("signature does not match!"); +142 throw redirect; +143 } +144 +145 /** +146 * Extract additional information and store it in the token +147 * See: +148 * https://developers.facebook.com/docs/reference/login/signed-request/ +149 * TODO: +150 * - Attribute "code" +151 */ +152 Map<String,Object> additionalInformation = new HashMap<>(); +153 try +154 { +155 additionalInformation.put( +156 "issued_at", +157 new Date(json.get("issued_at").getLongValue()*1000L) +158 ); +159 Map<String,Object> user = new HashMap<>(); +160 user.put( +161 "country", +162 json.get("user").get("country").asText() +163 ); +164 user.put( +165 "locale", +166 json.get("user").get("locale").asText() +167 ); +168 user.put( +169 "age_min", +170 json.get("user").get("age").get("min").getNumberValue() +171 ); +172 if (json.get("user") != null && json.get("user").get("max") != null) +173 user.put( +174 "age_max", +175 json.get("user").get("age").get("max").getNumberValue() +176 ); +177 additionalInformation.put("user", user); +178 if (json.get("app_data") != null) +179 additionalInformation.put("app_data", json.get("app_data").asText()); +180 if (json.get("page") != null) +181 { +182 Map<String,Object> page = new HashMap<>(); +183 page.put("id", json.get("page").get("id").asText()); +184 page.put("liked", json.get("page").get("liked").asBoolean()); +185 page.put("admin", json.get("page").get("admin").asBoolean()); +186 additionalInformation.put("page", page); +187 } +188 } +189 catch (NullPointerException e) +190 { +191 log.warn("expected additional data is missing: {}", data); +192 } +193 +194 DefaultOAuth2AccessToken token = null; +195 try +196 { +197 String value = json.get("oauth_token").asText(); +198 if (value.isEmpty()) +199 { +200 log.error("field \"oauth_token\" is missing: {}", data); +201 throw redirect; +202 } +203 token = new DefaultOAuth2AccessToken(value); +204 token.setExpiration(new Date(json.get("expires").getLongValue()*1000L)); +205 +206 additionalInformation.put( +207 "user_id", +208 json.get("user_id").asText() +209 ); +210 +211 token.setAdditionalInformation(additionalInformation); +212 } +213 catch (NullPointerException e) +214 { +215 if (token == null) +216 { +217 log.error("field \"oauth_token\" is missing: {}", data); +218 throw redirect; +219 } +220 else +221 log.warn("expected additional data is missing: {}", data); +222 } +223 +224 return token; +225 } +226 } +227 +228 +229 public String getSecret() +230 { +231 return secret; +232 } +233 +234 public void setSecret(String secret) +235 { +236 this.secret = secret; +237 } +238 +239 public ObjectMapper getObjectMapper() +240 { +241 return objectMapper; +242 } +243 +244 public void setObjectMapper(ObjectMapper objectMapper) +245 { +246 this.objectMapper = objectMapper; +247 } +248 } ++