play-authenticate icon indicating copy to clipboard operation
play-authenticate copied to clipboard

Customize where users are redirected to with each provider

Open smola opened this issue 11 years ago • 11 comments

In my Play! Authenticate, I need to redirect users who login with Facebook (or other social network) for the first time to be redirected to a custom URL. That way, I can get users to import their friends on their first Facebook login (i.e. account creation).

Is that possible currently? Maybe it would have to be a new setting or group of settings for each provider?

smola avatar Feb 18 '13 13:02 smola

You might be able to do that by using a temporary variable in the session, but it definitely isn't the best solution. I actually understand and agree with the use case, so we should look into it on how to implement this. Off the top of my head I could think of defining a method in the Router class that gets called before jumping back to the initial URL for example. If you have already implemented something like that in your application that you can refactor and provide as a pull request (preferably as a non-breaking change, that should be possible) I would be more than happy to look into merging that.

joscha avatar Feb 18 '13 15:02 joscha

Ok. Thank you. At the moment I juse use a custom auth action with an alternative to PlayAuthenticate.handleAuthentication. My approach is storing in context.args the action that is being performed (login, signup, link, merge) and my loginAndRedirect goes like this:

    private static Result loginAndRedirect(final Context context, final AuthUser loginUser) {
        storeUser(context.session(), loginUser);
        final String action = (String)context.args.get("pa-action");
        if (action != null && action.equals("link")) {
            final String provider = loginUser.getProvider();
            if (provider.equals("facebook")) {
                return Controller.redirect("/facebook/import");
            }
        }
        return Controller.redirect(getJumpUrl(context));

So, do you think that providing a hook in Resolver for use in loginAndRedirect is the way to go? I think it could look like this:

    private static Result loginAndRedirect(final Context context, final AuthUser loginUser) {
        storeUser(context.session(), loginUser);
                final String redirectUrl = getResolver().getRedirect(
                    (String)context.args.get("pa-action"), 
                     loginUser.getProvider()
                );
                if (redirectUrl != null) {
                    return Controller.redirect(redirectUrl);
                }
        return Controller.redirect(getJumpUrl(context));

Or maybe the hook should be for getJumlUrl. I can't make my mind with a good API for this...

smola avatar Feb 26 '13 15:02 smola

Ideally there would be method in the Router that gets both the provider name as a String and the return value of getJumpUrl (from within loginAndRedirect) and returns a Call - by default it would always return the value from getJumpUrl() (to stay backwards compatible) but in your case you could basically override it and return a Call object pointing virtually anywhere - how does that sound?

joscha avatar Feb 28 '13 20:02 joscha

At the moment I just reworked my code so I can just redirect to Facebook auth and use PlayAuthenticate.storeOriginalUrl to go back to the page I want. This works for me at the moment (no explicit Facebook login for the user, I just log in the user into Facebook as needed for certain capabilities such as importing friends). I'm not sure if I'll implement explicit Facebook login in my app any time soon, so I do not need this cutomization at the moment.

smola avatar Mar 05 '13 08:03 smola

I've come across this again when implementing a welcome page where users are redirected on their first login. At the moment I'm using a new PlayAuthenticate.storeUrl, but let's see what are the different possibilies:

Store redirect URL in session

My current approach:

In PlayAuthenticate.java:


public static void storeRedirectUrl(final Http.Context context, final String url) {
    context.session.put(ORIGINAL_URL, url);
}

Then I call it when needed. For example, if I want to redirect any new user to this page, I modify my MyUserServicePlugin.java with this:

    @Override
    public Object save(final AuthUser authUser) {
        final boolean isLinked = User.existsByAuthUserIdentity(authUser);
        if (!isLinked) {    

            // Redirect to the Welcome page
            PlayAuthenticate.storeUrl(Context.current(), "/welcome");

            return User.create(authUser).id;        
        } else {
            // we have this user already, so return null
            return null;
        }
    }

I could also put this in MyUsernamePasswordAuthProvider.java if I wanted to do a specific redirect for this provider with finer-grained control.

Or I could add a custom action for /authenticate/facebook route to do this in a specific way of Facebook.

This API might not be very beautiful, although it seems very versatile for the use cases I considered.

Add a method to the Resolver API

Something like you described:

    public abstract Call redirectAfterLogin();

For this to work flexibly enough, it should be possible to have enough information to do the decision here. For example, which provider are we using and if this is a signup, link, merge o login.

Do everything behind the scenes and conf-driven

Eventually, I guess it could be possible to just make PlayAuthenticate.java and all core providers support enough setting options to support all these use cases just setting some keys in the conf. Maybe this would be more sensible once more people have spoken up about their use cases?

What do you think? Whatever you think is better API-wise, I'm up for implementing it.

smola avatar Mar 11 '13 11:03 smola

This is quite similar to issue #63

joscha avatar Mar 11 '13 20:03 joscha

@smola I agree with the usecase - this is actually something more users want - not only for signup but also on an error case (like a user exists already). How about a resolver driven solution with conf fallbacks like for the other pages. As long as it will work for all providers and is versatile enough this should be the easiest solution, as the resolver is there already and could come with a default (that can be overwritten by a subclass) out of the box. What do you think?

joscha avatar Mar 11 '13 20:03 joscha

That works for me. So that resolver method would need easy access to:

  • Current action (signup/login/link/merge)
  • Redirect reason (success/error/...). I don't need this in my use case, but it might be needed for issue #63?
  • AuthUser

Would those be arguments for the resolver method or should they be fetched with Context.current().session(), PlayAuthenticate.getUser(), etc?

smola avatar Mar 11 '13 21:03 smola

I would really love to see the resolver driven solution. I try to login with an ajax request and just want to write out the new user as json after successfully signup and currently I have to redirect the user. With the resolver driven solution I could just print out the json file and go :)

Since its 19 days ago since something happend here is there any kind of eta?

ssachtleben avatar Mar 30 '13 18:03 ssachtleben

@ssachtleben as this is an Open Source project, there's no fix release cycle or anything. Unfortunately I am quite busy these days, so I won't be able to implement this, but I am happy to look at any pull request

joscha avatar Apr 04 '13 17:04 joscha

+1

mkurz avatar Sep 22 '14 23:09 mkurz