/*
 * Copyright 2011 the original author or authors.
 *
 * 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.springframework.social.facebook.web;

import static org.springframework.web.bind.annotation.RequestMethod.*;

import java.io.Serializable;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.social.connect.support.ConnectionRepository;
import org.springframework.social.web.connect.SignInControllerGateway;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Controller that enables a user to authenticate to an application by signing into Facebook.
 * @author Craig Walls
 */
@Controller
@RequestMapping("/signin/")
public class FacebookSigninController {

	private final ConnectionRepository connectionRepository;

	private final SignInControllerGateway signinGateway;

	private String noConnectionView = "redirect:/signup";

	private final String apiKey;

	private final String appSecret;

	/**
	 * Constructs the FacebookSigninController.
	 * @param connectionRepository a connection repository used to lookup the account ID connected to the Facebook profile.
	 * @param signinGateway the signin strategy used to authenticate the user with the application.
	 * @param apiKey the Facebook API key used to retrieve the Facebook cookie containing the access token.
	 */
	public FacebookSigninController(ConnectionRepository connectionRepository, SignInControllerGateway signinGateway,
			String apiKey, String appSecret) {
		this.connectionRepository = connectionRepository;
		this.signinGateway = signinGateway;
		// TODO: The Facebook service provider could be looked up here and could expose its API key as a
		// property. Then this controller could just get the API key and app secret from the provider.
		this.apiKey = apiKey;
		this.appSecret = appSecret;
	}

	/**
	 * Sets the view that will be displayed should no connection be found for the Twitter profile.
	 * 
	 * @param noConnectionView the view to display when no connection can be found
	 */
	public void setNoConnectionView(String noConnectionView) {
		this.noConnectionView = noConnectionView;
	}

	/**
	 * Retrieves the user's Facebook access token from a cookie written after a successful login using Facebook's &lt;fb:login-button&gt; tag.
	 * Uses that access token to lookup the connected account ID and attempts to authenticate to the application for that account.
	 * If there is no connection for access token, the flow will transition to the no-connection view, "redirect:/signup" by default.
	 */
	@RequestMapping(value = FACEBOOK_PROVIDER_ID, method = POST)
	public String signin(HttpServletRequest request) {
		String accessToken = resolveAccessTokenValue(request);
		Serializable accountId = connectionRepository.findAccountIdByConnectionAccessToken(FACEBOOK_PROVIDER_ID, accessToken);

		if (accountId == null) {
			return noConnectionView;
		}

		signinGateway.signIn(accountId);
		return "redirect:/";
	}

	private String resolveAccessTokenValue(HttpServletRequest request) {
		Map<String, String> cookieData = FacebookCookieParser.getFacebookCookieData(request.getCookies(), apiKey,
				appSecret);
		String accessToken = cookieData.get("access_token");
		if (accessToken != null) {
			return accessToken;
		}

		throw new IllegalStateException("FacebookSigninController cannot find an access token in the Facebook cookie.");
	}

	private static final String FACEBOOK_PROVIDER_ID = "facebook";

}

@@ -861,51 +861,230 @@
 ork.
-social.connect.support.ConnectionRepository
+core.annotation.AnnotationUtils;%0Aimport org.springframework.social.connect.support.ConnectionRepository;%0Aimport org.springframework.social.oauth2.AccessGrant;%0Aimport org.springframework.social.web.connect.ConnectController
 ;%0Aim
@@ -1707,16 +1707,59 @@
 ecret;%0A%0A
+%09private String baseConnectControllerUrl;%0A%0A
 %09/**%0A%09 *
@@ -2249,24 +2249,47 @@
 ,%0A%09%09%09String 
+applicationUrl, String 
 apiKey, Stri
@@ -2644,16 +2644,157 @@
 Secret;%0A
+%09%09this.baseConnectControllerUrl = applicationUrl + AnnotationUtils.findAnnotation(ConnectController.class, RequestMapping.class).value()%5B0%5D;%0A
 %09%7D%0A%0A%09/**
@@ -3826,91 +3826,471 @@
 %09%09re
-turn noConnectionView;%0A%09%09%7D%0A%0A%09%09signinGateway.signIn(accountId);%0A%09%09return %22redirect:/
+quest.getSession().setAttribute(ConnectController.DEFERRED_CONNECTION_ACCESS_TOKEN_ATTRIBUTE, new AccessGrant(accessToken, null));%0A%09%09%09request.getSession().setAttribute(ConnectController.DEFERRED_CONNECTION_REDIRECT_URI_ATTRIBUTE, deferredConnectionUrl());%0A%09%09%09return noConnectionView;%0A%09%09%7D%0A%0A%09%09signinGateway.signIn(accountId);%0A%09%09return %22redirect:/%22;%0A%09%7D%0A%09%0A%09private String deferredConnectionUrl() %7B%0A%09%09return baseConnectControllerUrl + FACEBOOK_PROVIDER_ID + %22?deferred
 %22;%0A%09

Support deferred connection creation in ConnectController. Used by Twitter and Facebook signin controllers to store the access token received at signin time and then later use it to create a connection once the account has been created.