How to sign up new user via Spring Social Facebook?
Asked Answered
S

2

5

I am having a website (Angular App) which I can reach on https://localhost:4200 and a server (pure REST API) that is on https://localhost:8443.

Since the REST endpoints are secured, a user must login on my server. Obviously I want users to be able to signup using Facebook and further communicate with my server after login.


According to the docs

POST /signin/{providerId} - Initiates the sign in flow.

that is why there is a button that does just this:

<form ngNoForm name="fb_signin" id="fb_signin" action="https://localhost:8443/signin/facebook" method="POST">
  <input type="hidden" name="scope" value="email">
  <button type="submit">SIGNING</button>
</form>

From there, everything works fine for some time. The user gets redirected to Facebook's authorization page where the authorization button gets clicked. After that the user gets redirected to https://localhost:8443/signin/facebook.


It seems that per default, in case the user is unknown, there will be a another redirect to https://localhost:8443/signup (see docs)

If the provider user ID matches more than one existing connection, ProviderSignInController will redirect to the application’s sign in URL to offer the user a chance to sign in through another provider or with their username and password. The request to the sign in URL will have an "error" query parameter set to "multiple_users" to indicate the problem so that the page can communicate it to the user. The default sign in URL is "/signin" (relative to the application root), but can be customized by setting the signInUrl property.

On my serer this looks like this (borrowed from a sample application):

@RequestMapping(value = "/signup", method = RequestMethod.GET)
public SignUpForm signupForm(WebRequest request) {
    Connection<?> connection = providerSignInUtils.getConnectionFromSession(request);
    if (connection != null) {
        request.setAttribute("message", new Message(MessageType.INFO, "Your " + StringUtils.capitalize(connection.getKey().getProviderId()) + " account is not associated with a Spring Social Showcase account. If you're new, please sign up."), WebRequest.SCOPE_REQUEST);
        return SignUpForm.fromProviderUser(connection.fetchUserProfile());
    } else {
        return new SignUpForm();
    }
}

And here are my issues:

First of all I need to know what I am supposed to do at this endpoint. The user authorized my app, but my server does not know the user yet so do I

  • connection.fetchUserProfile() and save the new user to my database
  • something else?

Second, I do not know how I am supposed to redirect back to my website from here which, as explained, lies on https://localhost:4200. But of course, my server does not know that.

Is there a chance somebody can guide me through this?


This is the SocialConfig

@Configuration
@EnableSocial
public class SocialConfig implements SocialConfigurer {

    private final static Logger LOGGER = LogManager.getLogger(SocialConfig.class);

    @Value("${spring.social.facebook.appId}")
    String facebookAppId;

    @Value("${spring.social.facebook.appSecret}")
    String facebookSecret;

    @Autowired
    private DataSource dataSource;

    @Bean
    @Scope(value="singleton", proxyMode = ScopedProxyMode.INTERFACES)
    public ConnectionFactoryLocator connectionFactoryLocator() {
        ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();

        registry.addConnectionFactory(new FacebookConnectionFactory(
                facebookAppId,
                facebookSecret
        ));

        return registry;
    }

    @Bean
    @Scope(value="singleton", proxyMode=ScopedProxyMode.INTERFACES)
    public UsersConnectionRepository usersConnectionRepository() {
        return new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator(), Encryptors.noOpText());
    }

    @Override
    public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
        LOGGER.debug("Adding connection factories");
        cfConfig.addConnectionFactory(new FacebookConnectionFactory(
                env.getProperty("facebook.clientId"),
                env.getProperty("facebook.clientSecret")));

    }

    @Override
    public UserIdSource getUserIdSource() {
        return new AuthenticationNameUserIdSource();
    }

    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        return new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
    }

    @Bean
    public ProviderSignInController providerSignInController(
            ConnectionFactoryLocator connectionFactoryLocator,
            UsersConnectionRepository usersConnectionRepository) {
        ProviderSignInController controller = new ProviderSignInController(
                connectionFactoryLocator,
                usersConnectionRepository,
                new SimpleSignInAdapter(new HttpSessionRequestCache()));

        return controller;
    }

    @Bean
    public RequestCache requestCache() {
        return new HttpSessionRequestCache();
    }

    @Bean
    public SignInAdapter signInAdapter() {
        return new SimpleSignInAdapter(new HttpSessionRequestCache());
    }

}

Maven dependencies related to Spring Social:

<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-facebook</artifactId>
    <version>3.0.0.M1</version>
</dependency>
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-security</artifactId>
    <version>2.0.0.M4</version>
</dependency>
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-core</artifactId>
    <version>2.0.0.M2</version>
</dependency>
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-config</artifactId>
    <version>2.0.0.M2</version>
</dependency>
Scarecrow answered 7/5, 2018 at 21:5 Comment(2)
See if the tutorial in this SO question #49943140 helps?Dehumanize
@TarunLalwani Hm no, this is a different setup and I think the problem is also different from what I am having. I know why the redirects are coming - what I do not know is how I redirect back to a web client that gets hosted elsewhere.Scarecrow
S
0

After that the user gets redirected to https://localhost:8443/signin/facebook.

I think there is another best way of doing so .As your angular webapp (https://localhost:4200) comprises of webLayer so directly redirecting via form action is not a better way.You can use Proxy Routing click here

Now in case of you are using you are using proxy routing u need to configure your proxy port (8443) by command.

ng serve –proxy-config proxy.conf.json
where proxy.conf.json contain your configuration https://localhost:8443/

then you need to first authenticate your each user/new user to your server(service layer) & then pass every request through user service layer controller .This will lead to pass every request through your server and it solve your problem.

The user authorized my app, but my server does not know the user yet so do I

Stephan answered 16/5, 2018 at 7:37 Comment(5)
Sorry, I don't understand. localhost:8443/signin/facebook is my OAuth redirect from Facebook to my server. I'm not sure how proxy routing could help here. All the requests are already going through my server anyway.Scarecrow
The user authorized my app .how does your authorization working?if you are hitting your oauth request localhost:8443/signin/facebook ..after authorization it get back to server .where is the role of your angular webapp?Is it not required to make a authorize connection between web and service layer ? I only want to know what is the middleware layer of your angular webapp and services.Stephan
Have you used Spring Social yourself in one of your projects?Scarecrow
The thing is this: I don't see what this proxy-config does for me except externalizing the URL of my REST API - maybe I'm getting something wrong here? However, as stated in my question I submit a POST to https://localhost:8443/signin/facebook which triggers the Spring Social magic. The user gets redirected to the Facebook page and authorizes my app. Then I am receiving the callback from Facebook and somehow end up on https://localhost:8443/signup in my browser. I'm either doing something completely wrong or I have to adapt something due to the fact that the web client and the ..Scarecrow
.. REST API live on two different domains. This "proxy routing" sounds more like a fancy name for externalizing/centralizing other web endpoint URLs?Scarecrow
F
0

This is my signup PoC code , FYI

@Controller
class HomeController : AbstractController() {

  private var sessionStrategy: SessionStrategy = HttpSessionSessionStrategy()

  @Inject
  private lateinit var connectionFactoryLocator: ConnectionFactoryLocator

  @Inject
  private lateinit var usersConnectionRepository: FoodUsersConnectionRepository

  @RequestMapping("/")
  fun index(): String {
    logger.info("index")
    return "index"
  }

  @RequestMapping("/signup")
  fun signup(nativeWebRequest: NativeWebRequest) : String {
    sessionStrategy.getAttribute(nativeWebRequest , ProviderSignInAttempt.SESSION_ATTRIBUTE)
      .takeIf { it != null }
      .also { it ->
        val attempt = it as ProviderSignInAttempt
        val connection = attempt.getConnection(connectionFactoryLocator)
        logger.info("conn.key = {}" , connection.key)
        val user = userDao.save(User(RandomStringUtils.randomAlphanumeric(8) , RandomStringUtils.randomAlphanumeric(8)))
        val connRepo: ConnectionRepository = usersConnectionRepository.createConnectionRepository(user.id.toString())
        connRepo.addConnection(connection)
      }
    return "signup"
  }

}

Points to notes :

I retrieve the connection object set in ProviderSignInController.handleSignIn() , it contains freshy (not mapped in db) providerId/providerUserId pair. And I create a new local user , give it random username/password pair , and link the new providerId/providerUserId to the local user.

This is a simple PoC code , it should be modified to conform to your business logic.

Maybe you can display a form for user to fill-in his nickname or address or something. That's depend on your business logic.

Flyleaf answered 8/10, 2018 at 10:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.