0

I've created a self hosted Java applicataion and I would like to use Google sign in to log in into. I followed the follwong example:

https://developers.google.com/identity/sign-in/web/

That of course work, but now I'm getting a little confuse on how I can authorize the calls on the server. In the backend I'm using Grizzly+Jersey.

Felice Pollano
  • 32,832
  • 9
  • 75
  • 115
  • Have you seen [this](https://developers.google.com/identity/sign-in/web/backend-auth)? – cassiomolin Nov 20 '15 at 11:14
  • @CássioMazzochiMolin It seems to be the answer, thanks. – Felice Pollano Nov 20 '15 at 11:15
  • I'm writing an answer about it, basic integrating the `GoogleIdTokenVerifier` described [here](https://developers.google.com/identity/sign-in/web/backend-auth) with a `ContainerRequestFilter` as I decribed [here](http://stackoverflow.com/a/26778123/1426227). – cassiomolin Nov 20 '15 at 11:17
  • @CássioMazzochiMolin please expamd the concept, to authenticate *all* the other requests after the logon. – Felice Pollano Nov 20 '15 at 11:23
  • Please try my solution and let me know if it works for you. To be honest, I haven't tested it completely, but it should work. – cassiomolin Nov 20 '15 at 12:02

1 Answers1

5

As described on the Google Sig-In documentation, you can use Google API Client Library for Java in order to check the authentication token on server side.

Client side

After a user successfully signs in, get the user's ID token:

function onSignIn(googleUser) {
    var idToken = googleUser.getAuthResponse().id_token;
    ...
}

And send the idToken to the server in every request using the standard HTTP Authorization header.

Server side

You can use a filter to perform authentication and/or authorization.

To bind filters to your REST endpoints, JAX-RS provides the meta-annotation @NameBinding and can be used as following:

@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured { }

The @Secured annotation will be used to decorate a filter class, which implements ContainerRequestFilter, allowing you to handle the request, get and validate the token.

The ContainerRequestContext helps you to extract information from the HTTP request.

The @Provider annotation marks an implementation of an extension interface that should be discoverable by JAX-RS/Jersey runtime during a provider scanning phase.

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the token header from the HTTP Authorization request header
        String token = 
            requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);

        // Check if the token is present
        if (token == null || token.isEmpty()) {
            throw new NotAuthorizedException("Token must be provided");
        }

        // Validate the token
        validateToken(token);
    }

    private void validateToken(String token) {

        GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier
            .Builder(new NetHttpTransport(), new GsonFactory())
                .setAudience(Arrays.asList(CLIENT_ID))
                .build();

        GoogleIdToken idToken = verifier.verify(token);
        if (idToken != null) {
            Payload payload = idToken.getPayload();
            System.out.println("User ID: " + payload.getSubject());
        } else {
            throw new NotAuthorizedException("Invalid token.");
        }
    }
}

To bind the filter to your endpoints methods or classes, annotate them with the @Secured annotation created above. For the methods and/or classes which are annotated, the filter will be executed.

@Path("/example")
public class MyEndpoint {

    @GET
    @Path("{id}")
    @Produces("application/json")
    public Response myUnsecuredMethod(@PathParam("id") Long id) {
        // This method is not annotated with @Secured
        // The security filter won't be executed before invoking this method
        ...
    }

    @DELETE
    @Secured
    @Path("{id}")
    @Produces("application/json")
    public Response mySecuredMethod(@PathParam("id") Long id) {
        // This method is annotated with @Secured
        // The security filter will be executed before invoking this method
        ...
    }
}

In the example above, the security filter will be executed only for mySecuredMethod(Long) because it's annotated with @Secured.

cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • A point is not clear to me, who block some attacker intercept my calls and use my token t simulate my activity? – Felice Pollano Nov 20 '15 at 15:45
  • 1
    @FelicePollano Using the **HTTPS** is the way to prevent the [Man-in-the-middle attack](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). This vulnerability is present in every application that sends the session ID over the wire, for example. – cassiomolin Nov 20 '15 at 15:50
  • 1
    Thank you very much this complete the useful answer. – Felice Pollano Nov 20 '15 at 15:55
  • How could this code be targetted to authorise against _any_ of several Oauth2/Social login providers? Would the [Scribe-Java library](https://github.com/scribejava/scribejava) be useful towards that end? – Big Rich Nov 14 '18 at 11:02