Chrome Device Token API Samples

Python

import jwt
import json
import requests
import sys
import time
import codecs

SERVICE_ACCOUNT_FILE = './service_account_key.json'

JWT_AUDIENCE = 'https://oauth2.googleapis.com/token'
JWT_SCOPE = 'https://www.googleapis.com/auth/chromeosdevicetoken'
TOKEN_REQUEST_URL = 'https://oauth2.googleapis.com/token'
INVALIDATE_TOKEN_API_URL = 'https://chromedevicetoken.googleapis.com/v1/users:invalidateToken'

def get_access_token_for_user(service_account_email, private_key_id, private_key, user_email):
    '''
    See instructions for getting a signed JWT here
    https://developers.google.com/identity/protocols/oauth2/service-account#jwt-auth
    '''
    iat = time.time()
    exp = iat + 3600
    payload = {'iss': service_account_email,
               'sub': user_email,
               'aud': JWT_AUDIENCE,
               'scope': JWT_SCOPE,
               'iat': iat,
               'exp': exp
               }
    additional_headers = {'kid': private_key_id}
    signed_jwt = jwt.encode(payload, private_key, headers=additional_headers,
                            algorithm='RS256')

    grant_type = codecs.decode(
        'urn:ietf:params:oauth:grant-type:jwt-bearer', 'unicode_escape')
    payload = 'grant_type=' + str(grant_type) + '&assertion=' + signed_jwt
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}

    response = requests.post(TOKEN_REQUEST_URL, data=payload, headers=headers)

    print(f'Response HTTP status code: {response.status_code}')
    print(json.dumps(response.json(), indent=2))

    return response.json()['access_token']

def invalidate_token(user_email):
    print(f'invalidate token for user {user_email}')

    with open(SERVICE_ACCOUNT_FILE, 'r') as service_account_key_json_file:
        service_account_key = json.load(service_account_key_json_file)

        service_account_email = service_account_key['client_email']
        private_key_id = service_account_key['private_key_id']
        private_key = service_account_key['private_key']
        print(f'using service account {service_account_email}')
        print(f'using private key id {private_key_id}')

        access_token = get_access_token_for_user(service_account_email, private_key_id,
                               private_key, user_email)

        response = requests.post(
            INVALIDATE_TOKEN_API_URL,
            headers={
                'Authorization': 'Bearer ' + access_token,
                'Content-Type': 'application/json'
            },
            json={
                'token_type': 'SAML_PASSWORD'
            })

        # Successful call should have HTTP status code 200 and an empty response body.
        print(f'Response HTTP status code: {response.status_code}')
        print(json.dumps(response.json(), indent=2))

if __name__ == '__main__':
    invalidate_token(sys.argv[1])

C#

private async Task<string> InvalidatePasswordTokenForUser(string userAccessToken)
{
    const string tokenRequestURI = "https://chromedevicetoken.googleapis.com/v1/users:invalidateToken";

    var jsSerializer = new JavaScriptSerializer();
    var body = new Dictionary<string, object>();
    body["token_type"] = "SAML_PASSWORD";
    string jsonString = jsSerializer.Serialize(body);

    HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenRequestURI);
    tokenRequest.Method = "POST";
    tokenRequest.Headers.Add(string.Format("Authorization: Bearer {0}", userAccessToken));
    tokenRequest.ContentType = "application/json";
    tokenRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
    byte[] _byteVersion = Encoding.ASCII.GetBytes(jsonString);
    tokenRequest.ContentLength = _byteVersion.Length;
    Stream stream = tokenRequest.GetRequestStream();
    await stream.WriteAsync(_byteVersion, 0, _byteVersion.Length);
    stream.Close();

    WebResponse tokenResponse = await tokenRequest.GetResponseAsync();
    using (StreamReader reader = new StreamReader(tokenResponse.GetResponseStream()))
    {
        string responseText = await reader.ReadToEndAsync();

        if (responseText != null && responseText.Length > 0)
        {
            return responseText;
        }
        throw new FormatException("Unexpected response format: " + responseText);
    }
}

Appscript

function runInvalidateToken() {
  var url = 'https://chromedevicetoken.googleapis.com/v1/users:invalidateToken';
  var userToImpersonate = '...@....';
  var service = getOAuth2Service(userToImpersonate, 'https://www.googleapis.com/auth/chromeosdevicetoken');

  if (service.hasAccess()) {
    var param = {
      method: 'post',
      contentType: 'application/json',
      headers: {
        Authorization: 'Bearer ' + service.getAccessToken()
      },
      payload : JSON.stringify({
        token_type : 'SAML_PASSWORD'
      }),
      muteHttpExceptions: true
    };

    var req = UrlFetchApp.getRequest(url, param);
    Logger.log('Request : ');
    Logger.log(req);

    var response = UrlFetchApp.fetch(url, param);
    Logger.log('ResponseCode : ' + response.getResponseCode());
    Logger.log('ResponseBody : ' + response.getContentText());
  } else {
    Logger.log(service.getLastError());
  }
}

/*
 * This sample demonstrates how to configure the library for Google APIs, using
 * domain-wide delegation (Service Account flow).
 * https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
 */

// Private key and client email of the service account.
var PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----\n....\n-----END PRIVATE KEY-----\n';
var CLIENT_EMAIL = '....iam.gserviceaccount.com';

/**
 * Reset the authorization state, so that it can be re-tested.
 */
function reset() {
  getService().reset();
}

/**
 * Configures the service.
 */
function getOAuth2Service(user, scopes) {
  return OAuth2.createService('ChromeDeviceTokens:' + user)
      // Set the endpoint URL.
      .setTokenUrl('https://oauth2.googleapis.com/token')

      // Set the private key and issuer.
      .setPrivateKey(PRIVATE_KEY)
      .setIssuer(CLIENT_EMAIL)

      // Set the name of the user to impersonate. This will only work for
      // Google Apps for Work/EDU accounts whose admin has setup domain-wide
      // delegation:
      // https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
      .setSubject(user)

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getScriptProperties())

      // Set the scope. This must match one of the scopes configured during the
      // setup of domain-wide delegation.
      .setScope(scopes);
}