001 package org.tynamo.security.federatedaccounts.base;
002
003 import java.net.MalformedURLException;
004 import java.net.URI;
005 import java.util.ArrayList;
006 import java.util.List;
007
008 import org.apache.http.HttpEntity;
009 import org.apache.http.HttpResponse;
010 import org.apache.http.HttpStatus;
011 import org.apache.http.NameValuePair;
012 import org.apache.http.client.HttpClient;
013 import org.apache.http.client.methods.HttpGet;
014 import org.apache.http.client.utils.URIUtils;
015 import org.apache.http.client.utils.URLEncodedUtils;
016 import org.apache.http.impl.client.DefaultHttpClient;
017 import org.apache.http.message.BasicNameValuePair;
018 import org.apache.http.params.BasicHttpParams;
019 import org.apache.http.util.EntityUtils;
020 import org.apache.shiro.SecurityUtils;
021 import org.apache.shiro.authc.AuthenticationException;
022 import org.apache.tapestry5.annotations.Component;
023 import org.apache.tapestry5.annotations.Environmental;
024 import org.apache.tapestry5.annotations.Property;
025 import org.apache.tapestry5.ioc.annotations.Inject;
026 import org.apache.tapestry5.ioc.annotations.Symbol;
027 import org.apache.tapestry5.services.BaseURLSource;
028 import org.apache.tapestry5.services.PageRenderLinkSource;
029 import org.apache.tapestry5.services.Request;
030 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
031 import org.esxx.js.protocol.GAEConnectionManager;
032 import org.slf4j.Logger;
033 import org.tynamo.security.federatedaccounts.FederatedAccountSymbols;
034 import org.tynamo.security.federatedaccounts.components.FlashMessager;
035 import org.tynamo.security.federatedaccounts.oauth.FacebookAccessToken;
036 import org.tynamo.security.federatedaccounts.util.WindowMode;
037
038 public abstract class AbstractFacebookOauthPage extends FacebookOauthComponentBase {
039 @Inject
040 @Symbol(FederatedAccountSymbols.HTTPCLIENT_ON_GAE)
041 private boolean httpClientOnGae;
042
043 @Inject
044 @Symbol(FederatedAccountSymbols.SUCCESSURL)
045 private String successUrl;
046
047 @Inject
048 private Logger logger;
049
050 @Inject
051 private Request request;
052
053 @Component
054 private FlashMessager flashMessager;
055
056 @Inject
057 private PageRenderLinkSource linkSource;
058
059 private boolean fbAuthenticated;
060
061 @Property
062 private WindowMode windowMode;
063
064 protected void onActivate(String windowModeText) throws MalformedURLException {
065 try {
066 windowMode = WindowMode.valueOf(windowModeText);
067 } catch (IllegalArgumentException e) {
068 }
069
070 String code = request.getParameter("code");
071 if (code == null) {
072 flashMessager.setFailureMessage("No Oauth authentication code provided");
073 return;
074 }
075
076 // String accessTokenUri = "https://graph.facebook.com/oauth/access_token?client_id=" + clientId
077 // + "&redirect_uri=http://localhost:8080/oauth&client_secret=" + clientSecret + "&code=" + code;
078 // String accessTokenUri = "https://graph.facebook.com/oauth/access_token";
079
080 // String accessTokenUri = "https://graph.facebook.com/oauth/access_token?client_id=" + clientId
081 // + "&redirect_uri=http://localhost:8080/oauth&client_secret=" + clientSecret + "&code=" +
082 // code;
083 // https://graph.facebook.com/oauth/access_token?client_id=119507274749030&redirect_uri=http://localhost:8080/oauth&client_secret=d8b3b7dc6d5b6ddaebd68549002d643d&code=2._ViV33QBoP0Yhzmua_6gvA__.3600.1274994000-539598633|7_yFQgTwoBoD7DJYx8dqMhiXiP0.
084 // logger.info("access token uri " + accessTokenUri);
085
086 List<NameValuePair> qparams = new ArrayList<NameValuePair>();
087 qparams.add(new BasicNameValuePair("client_id", getOauthClientId()));
088 qparams.add(new BasicNameValuePair("redirect_uri", getOauthRedirectLink(windowMode)));
089 qparams.add(new BasicNameValuePair("client_secret", getOauthClientSecret()));
090 qparams.add(new BasicNameValuePair("code", code));
091 HttpGet get = null;
092 String accessToken = "";
093 long expires = 0L;
094 try {
095 URI uri = URIUtils
096 .createURI("https", "graph.facebook.com", -1, "/oauth/access_token", URLEncodedUtils.format(qparams, "UTF-8"), null);
097 get = new HttpGet(uri);
098
099 // HttpGet get = new HttpGet(accessTokenUri);
100 // NameValuePair[] queryString = new NameValuePair[4];
101 // queryString[0] = new NameValuePair("client_id", clientId);
102 // queryString[1] = new NameValuePair("redirect_uri", getOauthRedirectLink());
103 // queryString[2] = new NameValuePair("client_secret", clientSecret);
104 // queryString[3] = new NameValuePair("code", code);
105 // get.setQueryString(queryString);
106 // HttpClient httpClient = new DefaultHttpClient();
107 HttpClient httpClient = httpClientOnGae ? new DefaultHttpClient(new GAEConnectionManager(), new BasicHttpParams())
108 : new DefaultHttpClient();
109
110 HttpResponse response = httpClient.execute(get);
111 int status = response.getStatusLine().getStatusCode();
112 if (HttpStatus.SC_OK != status) {
113 logger.error("Facebook access_token request returned status code " + status);
114 flashMessager.setFailureMessage("Facebook access_token request failed with status code: " + status);
115 return;
116 }
117 HttpEntity entity = response.getEntity();
118 if (entity != null) {
119 long len = entity.getContentLength();
120 if (len != -1 && len < 2048) accessToken = EntityUtils.toString(entity);
121 }
122 } catch (Exception e) {
123 logger.error("Facebook access_token request failed because of:", e);
124 flashMessager.setFailureMessage("Facebook access_token request failed with message: " + e.getMessage());
125 return;
126 } finally {
127 if (get != null) get.abort();
128 }
129
130 try {
131 if (!accessToken.startsWith("access_token")) throw new IllegalArgumentException();
132 // access_token is of form:
133 // access_token=119507274749030|2.1AptZFp9__qW3k2PuG4bVA__.3600.1274914800-539598633|9aTyryhVl8vnn3ulLy2w6Txo92E.&expires=4059
134 accessToken = accessToken.substring(accessToken.indexOf("=") + 1);
135 expires = Long.valueOf(accessToken.substring(accessToken.lastIndexOf("=") + 1));
136 accessToken = accessToken.substring(0, accessToken.indexOf("&expires"));
137 } catch (Exception e) {
138 logger.error("access_token wasn't of right format");
139 flashMessager.setFailureMessage("Facebook access_token wasn't of right format");
140 return;
141 }
142
143 try {
144 SecurityUtils.getSubject().login(new FacebookAccessToken(accessToken, expires));
145 flashMessager.setSuccessMessage("User successfully authenticated");
146 fbAuthenticated = true;
147 } catch (AuthenticationException e) {
148 logger.error("Using access token " + accessToken + "\nCould not sign in a Facebook federated user because of: ", e);
149 // FIXME Deal with other account exception types like expired and
150 // locked
151 flashMessager.setFailureMessage("A Facebook federated user cannot be signed in, report this to support.\n " + e.getMessage());
152 }
153 }
154
155 @Inject
156 private BaseURLSource baseURLSource;
157
158 public String getSuccessLink() {
159 return "".equals(successUrl) ? "" : baseURLSource.getBaseURL(request.isSecure()) + successUrl;
160 }
161
162 @Environmental
163 private JavaScriptSupport javaScriptSupport;
164
165 protected void afterRender() {
166 if (fbAuthenticated) javaScriptSupport.addScript("onAuthenticationSuccess('" + getSuccessLink() + "', '" + windowMode.name() + "');");
167 }
168 }