Search code examples
javagoogle-app-enginerestlet

Using Google authentication with Restlet


I have this very basic authentication for my app:

    MapVerifier mapVerifier = new MapVerifier();
    mapVerifier.getLocalSecrets().put("user", "pass".toCharArray());

    ChallengeAuthenticator guard= new ChallengeAuthenticator(null, ChallengeScheme.HTTP_BASIC, "Secured Resources");
    guard.setContext(getContext());
    guard.setVerifier(mapVerifier);

How do I adapt this to use Google authentication scheme? That, instead of showing the Username/Password browser popup, it will go to the Google authentication page.


Solution

  • I think that you aren't in the context of a challenge authentication and you need to leverage the authentication service of Google.

    Here is an implementation of this approach (not tested) if you want a custom Restlet Authenticator implementation:

    public class GoogleAuthenticator extends Authenticator {
        private UserService userService;
    
        public GoogleAuthenticator(Context context) {
            super(context);
            this.userService = UserServiceFactory.getUserService();
        }
    
        protected User createUser(com.google.appengine.api.users.User googleUser,
                                 Request request, Response response) {
            return new User(googleUser.getUserId());
        }
    
        protected boolean authenticate(Request request, Response response) {
            // Get current Google user
            com.google.appengine.api.users.User googleUser = userService.getCurrentUser();
    
            // Check if the user is authenticated
            if (googleUser!=null) {
                // Authenticated through Google authentication service
    
                request.getClientInfo().setUser(
                     createUser(googleUser, request, response));
                return true;
            } else {
                // Not authenticated. Redirect to the login URL
                response.redirectSeeOther(userService.createLoginURL(
                                          request.getRequestURI()));
                return false;
            }
        }
    }
    

    However such authenticator exists in the extension org.restlet.ext.gae for a while. It leverages the service UserService of GAE. So I think that you have it with the version of Restlet you use. Here is a sample of use below:

    public Restlet createInboundRoot() {
        Router router = new Router(getContext());
        (...)
    
        GaeAuthenticator guard= new GaeAuthenticator(getContext());
        guard.setNext(router);
    
        return guard;
    }
    

    Edited:

    You can notice that the GAE authenticator can use the GAE enroler for this purpose (i.e. if it's an admin one).

    To implement this, you simply need to instantiate such enroler and set it on your authenticator, as desribed below:

    GaeEnroler enroler = new GaeEnroler();
    GaeAuthenticator guard = new GaeAuthenticator(getContext());
    guard.setEnroler(enroler)
    guard.setNext(router);
    

    Within your server resource, you can then check the role, as described below:

    protected boolean hasAdminRole() {
        ClientInfo clientInfo = getClientInfo();
        List<Role> roles = clientInfo.getRoles();
        boolean isAdmin = false;
        for (Role role : roles) {
            if (role.getName().equals("admin")) {
                isAdmin = true;
                break;
            }
        }
        return isAdmin;
    }
    
    @Post
    public XX handlePost(YY content) {
        if (!hasAdminRole()) {
            throw new ResourceException(Status.CLIENT_ERROR_FORBIDDEN);
        }
    
        (...)
    }
    

    Hope it helps you, Thierry