cloudendpoints / endpoints-java

A Java framework for building RESTful APIs on Google App Engine
Apache License 2.0
32 stars 35 forks source link

Service Account authentication - Invalid JOSE Compact Serialization #179

Open emremogn opened 5 years ago

emremogn commented 5 years ago

According to https://cloud.google.com/endpoints/docs/frameworks/java/service-account-authentication you should set "authenticators" param to the value {EspAuthenticator.class}.

But if you use EspAuthenticator you get the following exception:

com.google.api.server.spi.auth.EspAuthenticator authenticate: Authentication failed: endpoints.repackaged.com.google.common.util.concurrent.UncheckedExecutionException: com.google.api.auth.UnauthenticatedException: endpoints.repackaged.org.jose4j.jwt.consumer.InvalidJwtException: JWT processing failed. Additional details: [[17] Unable to process JOSE object (cause: endpoints.repackaged.org.jose4j.lang.JoseException: Invalid JOSE Compact Serialization. Expecting either 3 or 5 parts for JWS or JWE respectively but was 2.): <token_here>] (EspAuthenticator.java:86)

I was able to make service-account-authentication work properly with the following config:

@Api(name = "apiNameHere", version = "v1",
        //
        authenticators = { EndpointsAuthenticator.class }, // notice the authenticator class
        //
        issuers = {
                //
                @ApiIssuer(
                        //
                        name = "serviceAccount",
                        //
                        issuer = "<app-default-service-account>@appspot.gserviceaccount.com",
                        //
                        jwksUri = "https://www.googleapis.com/robot/v1/metadata/x509/<app-default-service-account>@appspot.gserviceaccount.com") },
        //
        issuerAudiences = {
                //
                @ApiIssuerAudience(
                        //
                        name = "serviceAccount",
                        //
                        audiences = "https://www.googleapis.com/oauth2/v4/token") },
        //
        clientIds = { "<domain-wide-delegated-sa-client-id>" }) // notice the client ID here; I'm using a domain-wide delegated SA to impersonate users; the scope is userinfo.email

My Java SE client is something like this:

public static void main(String[] args) throws IOException {

        GoogleCredential googleCredentialsFromJsonStream = GoogleCredential.fromStream(YourClient.class.getResourceAsStream("/your-cert.json"))

                .createDelegated("end-user@domain") // used 1.28 api client here! 1.25 doesn't have this method

                .createScoped(Collections.singletonList(YourApiScopes.USERINFO_EMAIL));

        YourApi.Builder builder = new YourApi.Builder(googleCredentialsFromJsonStream.getTransport(), JacksonFactory.getDefaultInstance(), googleCredentialsFromJsonStream);

        TestRequest testRequest = new TestRequest();

        System.out.println(builder.build().YourApi().echoSa(testRequest).execute().getEchoedMessage());
    }

I was also able to call my endpoint from Apps Script / App Maker using OAuth2 lib:

function getService() {

  return OAuth2.createService('Test')

  .setTokenUrl('https://accounts.google.com/o/oauth2/token')

  .setPrivateKey(PK) // your key

  .setIssuer(SA) // your service account email

  .setSubject(Session.getActiveUser().getEmail()) // impersonated end user

  .setPropertyStore(PropertiesService.getUserProperties())

  .setScope('https://www.googleapis.com/auth/userinfo.email');
}
abhideep commented 3 years ago

Seeing the same exception. Were you able to resolve this? How?