Server-to-server OAuth with the Google OAuth Client Library for Java

This post describes how to validate a JWT token using the Google OAuth library for making server-to-server OAuth requests.

First, there is a prerequisite of being able to read a key file from your local file system. This key file is obtained from the system that you wish to authorize against and contains the private-key pair authorizing your server with the other system.

/**
 * Return private key from a file. Must be a valid PEM file with PKCS#8 encoding standard.
 *
 * @return a private key
 */
PrivateKey loadPrivateKey(File keyFile) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
  byte[] content = Files.toByteArray(keyFile);
  PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(content);
  return KeyFactory.getInstance("RSA").generatePrivate(ks);
}

Now, assuming we have a valid private key, authenticating with an OAuth end-point using a JWT token is a matter of mapping the JWT token properties with the correct GoogleCredential methods. When GoogleCredential calls the API to obtain a new access token, it converts the methods set on the credential to the correct JWT token properties according to the following table.

JWT property Google Credential
issuer serviceAccountId
subject serviceAccountUser

Using this table, we can construct a GoogleCredential object. To obtain a new access token, call refreshToken() on the credential. This will call the API configured by setTokenServerEncodedUrl with a JWT token request to obtain a new access token.

/**
 * Obtain an access token.
 *
 * GoogleCredential provides an interface for JWT requests.
 */
void authorize() {
  try {
    credential = new GoogleCredential.Builder()
        .setTransport(GoogleNetHttpTransport.newTrustedTransport())
        .setJsonFactory(JacksonFactory.getDefaultInstance())
        .setServiceAccountPrivateKey(privateKey)
        .setTokenServerEncodedUrl(tokenProviderUri)
        .setServiceAccountId(issuer)
        .setServiceAccountUser(subject)
        .setServiceAccountScopes(scopes)
        .build();
    credential.refreshToken();
  } catch (IOException e) {
    // invalidate credential
    credential = null;
  }
}

You can obtain an access token from the credential via a helper method that will initialize the credential if it is invalid and refresh the token if it is expired.

public String getAccessToken() {
  try {
    if (credential == null) {
      authorize();
    }

    // If expired
    if (credential.getExpirationTimeMilliseconds() < new Date().getTime()) {
      credential.refreshToken();
    }
    return credential.getAccessToken();
  } catch (IOException e) {
    return "UNAUTHORIZED";
  }
}
java  oauth 
comments powered by Disqus