Non-interactive authorization with Google OAuth2

2019-05-14 19:47发布

问题:

I have created a Java application that performs syncing of MS Active Directory with Google Groups. It is non-interactive application that suppose to be executed by the cronjob at night time. It does work perfectly on my laptop (DEV environment). My problem is that on the first run it pops up browser window with the dialog asking to authorize access to the Google API. After I click on "Allow" button it happily proceeds to the end. Now I need to move it to production server which runs CentOS and does not have a browser. When I run my application in this environment it prints the following message:

Please open the following address in your browser:  
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=520105804541-c7d7bfki88qr8dbkv6oahp22i3oq1jq0.apps.googleusercontent.com&redirect_uri=http://localhost:50089/Callback&response_type=code&scope=https://www.googleapis.com/auth/admin.directory.group.member%20https://www.googleapis.com/auth/admin.directory.orgunit%20https://www.googleapis.com/auth/admin.directory.device.mobile.action%20https://www.googleapis.com/auth/admin.directory.orgunit.readonly%20https://www.googleapis.com/auth/admin.directory.userschema%20https://www.googleapis.com/auth/admin.directory.device.chromeos%20https://www.googleapis.com/auth/admin.directory.notifications%20https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly%20https://www.googleapis.com/auth/admin.directory.device.mobile%20https://www.googleapis.com/auth/admin.directory.group.readonly%20https://www.googleapis.com/auth/admin.directory.group.member.readonly%20https://www.googleapis.com/auth/admin.directory.userschema.readonly%20https://www.googleapis.com/auth/admin.directory.user.alias%20https://www.googleapis.com/auth/admin.directory.user.security%20https://www.googleapis.com/auth/admin.directory.device.mobile.readonly%20https://www.googleapis.com/auth/admin.directory.user%20https://www.googleapis.com/auth/admin.directory.user.readonly%20https://www.googleapis.com/auth/admin.directory.user.alias.readonly%20https://www.googleapis.com/auth/admin.directory.group%20https://www.googleapis.com/auth/apps.groups.settings

There is no browser on this machine but if I try to run it on another box I am getting error 400 - invalid request. The reason is clear, because it redirecting to localhost:50089 and nobody is listening on that port on another machine. Going through the multiple documents and samples on developers.google.com I found a way to bypass the dialog by creating the service account and generating a primary key for it.

    try {
        GoogleCredential cr = GoogleCredential
             .fromStream(new FileInputStream(keyFile))
             .createScoped(SCOPES);
        Directory directory = new Directory.Builder(httpTransport, jsonFactory, cr)
.setApplicationName(config.getProperty("google.application.name")).build();
    } catch (IOException ex) {
       LOG.error("Failed to establish access credentials : ", ex.getMessage());
    }

It does read the key and does create Directory instance, but any request to it results in 403 response code

{ "code" : 403, "errors" : [ { "domain" : "global", "message" : "Not Authorized to access this resource/api", "reason" : "forbidden"
} ], "message" : "Not Authorized to access this resource/api" }"

But I have delegated all the privileges for this account to the service account. Don't know what else I can do. If anyone can help me out of this jam, I appreciate it greatly.

回答1:

I found solution. The problem is that Google generated JSON key does not set service account user. You have to do it manually like this:

        GoogleCredential cr = GoogleCredential
             .fromStream(new FileInputStream(keyFile))
             .createScoped(SCOPES);
        GoogleCredential.Builder builder = new GoogleCredential.Builder()
             .setTransport(httpTransport)
             .setJsonFactory(jsonFactory)
             .setServiceAccountScopes(SCOPES)
             .setServiceAccountId(cr.getServiceAccountId())
             .setServiceAccountPrivateKey(cr.getServiceAccountPrivateKey())
             .setServiceAccountPrivateKeyId(cr.getServiceAccountPrivateKeyId())
             .setTokenServerEncodedUrl(cr.getTokenServerEncodedUrl())
             .setServiceAccountUser(user);
        Directory directory = new Directory.Builder(
              httpTransport, jsonFactory, builder.build()) 
             .setApplicationName(config.getProperty("google.application.name")).build();