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";
}
}