Cloud Print

Developer Guide for Printers and Connectors

This guide will help developers building printers and printing software integrate their solutions with Google Cloud Print services to allow their printers to receive jobs from the Google cloud. For the purpose of simplicity this guide is framed for developing a single cloud-ready printer, but many of the same techniques and API calls apply to more abstract printing solutions such as proxies and virtual queues. The Python Code Samples provide a valuable walkthrough to help you begin making use of the GCP service interfaces.

For a printer to integrate with Google Cloud Print, it needs to be able to handle the three steps detailed in this document:

  1. Register itself to a user's Google Account
  2. Sync its status and capabilities to the cloud
  3. Handle any print jobs queued in the cloud

Contents

  1. Registering a Printer
    1. What is a Robot Account, and Why Must I Use It?
    2. Anonymous Registration
      1. Cloud-Ready Printer
      2. Connector-based Printer
      3. Requesting the registration_token (/register)
      4. Claiming the registration_token
      5. Obtaining the OAuth2 authorization_code
      6. Obtaining the OAuth2 authorization tokens
  2. Handling Printers
    1. Syncing with the GCP registry (/list)
    2. Updating a Printer (/update)
  3. Handling Print Jobs
    1. Receiving Print Notifications (XMPP)
    2. Fetching Print Jobs (/fetch)
    3. Processing Print Jobs (/control)

Note: Relevant service interfaces are indicated in parentheses.

Step #1: Registering a Printer

The first step in making the printer available to a user is to register it with GCP. During this step, the printer provides semantic information (e.g. name, capabilities, state) about itself so that GCP will know how to best use and display it. By the end of this step, the printer will get credentials for future communication with GCP (e.g. printer ID, robot account ID, OAuth2 authorization code).

This step can either be conducted from firmware located on the printer, or it can be conducted by a software application that "proxies" or "connects" the printer. The latter example is used by Google Chrome's GCP connector to register classic printers which don't have native GCP support. It's possible (and likely) that a single instance of the software application might represent many printers and process notifications and print jobs for each of them.

Registration is accomplished in 3 general steps:

  1. Printer (or connector software) makes an unauthenticated register call with semantic information about the printer and receives a registration token. This must be initiated by a user action (either by manual interaction or by the Privet Local Discovery protocol).
  2. User claims the printer by providing the registration token in an authenticated request to a specified URL.
  3. Printer makes an unauthenticated request to finalize the registration process and receives credentials for subsequent GCP communication. These credentials do not belong to the user account but belong instead to a special service account (commonly referred to as a "robot account").

In the connector application case, the application must use the robot account credentials from step 3 to register any other printers that it might represent by making authenticated calls to the /register interface.

What is a Robot Account, and Why Must I Use It?

A robot account is a special Google account that is owned by a human Google account. The robot and human accounts do not share identity, let alone credentials. The main purpose of a robot account is to better scope access to user data and to separate the identities of the user and the printer. There are a few interesting advantages to this approach:

  1. If the robot account's credentials get compromised, only printers owned by the robot are exposed, not the user's other printers.
  2. For the connector case, the connector will only see the printers it has registered, even if the user has registered other cloud-ready printers or even other instances of the connector.
  3. GCP can disambiguate between different instances of connector software to more accurately determine when a printer is online. For example, if GCP would like to determine if a printer is online, it queries the GTalk (XMPP) status of the identity associated with the printer. If that identity is shared with another instance of the connector software, then there would be a false positive if the other instance of the connector had an active XMPP connection to the GTalk service.
  4. API request quotas are better distributed. If all of a user's printers identified as the user when making API requests, then the API requests would be charged to that one identity and result in undesirable rate limiting for users with many printers. Having separate identities for separate printers (or groups of printers) helps mitigate this issue.
  5. The privileges of the robot account can be limited to those needed by its role. For example, a robot account has privileges to download print jobs, update printer state, and register printers, but not share printers. Sharing should require identity of the human user.

Anonymous Registration

Now that the registration process and robot accounts have been introduced, this section will detail the specific steps required to register the printer in two scenarios: when registering a cloud-ready printer, and when using a connector-based approach.

Cloud-Ready Printer

In this scenario, the registration request is conducted by the printer's firmware and does not have access to a user's credentials.

  1. Printer makes an unauthenticated request to the /register interface.
  2. GCP responds with a printer id, registration_token, polling_url, and some claim URL options. The registration_token is only valid for token_duration seconds, so step 4 must be completed before this token expires.
  3. Printer communicates one of the claim URLs to the user:
    1. If the printer has an LCD screen and doesn't want to waste a printout, it must show the invite_url and the registration_token on the LCD screen.
    2. If the printer uses a printout, it must download the document at invite_page_url and print it out to the user.
  4. User navigates their browser to the claim page and claims the printer.
  5. Meanwhile, printer polls polling_url every 5 seconds for 15 minutes or until after user completes step 4 (more details below).
  6. GCP responds with credentials of the robot account (more details below).

Connector-based Printer

In this scenario, the registration request is conducted by a software application that may have access to a user's credentials.

  1. Software makes an unauthenticated request to the /register interface.
  2. GCP responds with a printer id, registration_token, polling_url, and some claim URL options.
  3. Software claims the printer on behalf of the user by making an authenticated request (authenticated with the user's credentials) to the URL given by the automated_invite_url received in step 2.
  4. Software polls polling_url (only one poll is needed since software knows that step 3 has already been completed). More details about polling below.
  5. GCP responds with credentials of the robot account (more details below).
  6. If more printers need to be registered, robot account should be used to make authenticated requests to the /register interface.

Important: The printer must also allow the user to de-register a printer and register it again, if needed. This is important in case of sale returns or second-hand sales. De-registration consists of deleting the authentication data from the printer, and it doesn't require any interaction with the cloud. This action may be coupled with step #1, if desired.

Note: A diagram summarizing the anonymous registration sequence is available here.

Requesting the registration_token

The first step in the anonymous registration process is to request the registration_token from the Google Cloud Print server. To request the token, the printer must make a call to the GCP /register interface at https://www.google.com/cloudprint/register. Calls to the /register interface should include the following:

  • a system name (required) and a default display name (optional)
  • your printer's proxy ID (required)
  • a set of capabilities (required) and a set of defaults in CDD format (for GCP 2.0 compliant printers), or either XPS or PPD format (for older printers)
  • a hash value over the set of capabilities (this is used to compare capability sets to see if they've changed since the last update)

Important: Please see the /register interface specification for additional parameters which are required to register GCP 2.0 compliant printers.

Normally a call to /register would include a user's authentication credentials, but when those credentials are not included the request is assumed to be an anonymous registration request, as detailed in this document. In addition, the following additional requirements should be considered when making an anonymous registration request:

  • For security reasons, all anonymous registration calls must be made over HTTPS.
  • This process must be initiated by a user interaction, and not automatically.

Here's an example registration request with minimal parameters:

curl "https://www.google.com/cloudprint/register" \
  -H "X-CloudPrint-Proxy" \
  -d "proxy={$proxy_id}&printer={$printer_name}&capabilities={\"version\": \"1.0\"}&use_cdd=true"

Where {$proxy_id} should be replaced by the proxy identifier you wish to use and {$printer_name} should be replaced by the name of the printer.

In addition to the fields normally returned by /register (which includes the assigned printer ID), anonymous registration calls will return:

  • registration_token: a human readable string the user will need to claim printer ownership
  • token_duration: the lifetime of the registration_token, in seconds (the whole registration has to finish within this time frame)
  • invite_url: the url that a user will need to visit to claim ownership of the printer
  • complete_invite_url: same thing of invite_url but already containing the registration_token, so that the user doesn't have to insert it manually
  • invite_page_url: the url of a printable page containing the user's registration_token and url. (The page can be retrieved by the printer in PDF or PWG-raster format based on the HTTP header of the request, as for getting print jobs. At the moment the page size is letter and the resolution for the raster format is 300dpi. In the near future the page will have the page size and resolution based on the printer defaults.)
  • polling_url: the url that the printer will need to poll for the OAuth2 authorization_code

Claiming the registration_token

Once the printer has obtained the registration_token, it will need to make that information available to the user somehow, either by displaying the information on the device or by printing a page containing all the relevant information, such as the one provided in the invite_page_url field.

To claim ownership of the anonymously registered printer, the user will need to visit the relevant URL (invite_url or complete_invite_url), sign into their Google Account and (in case of invite_url) input their registration_token, all before the token expires. The printer will not be directly involved in this step of the process, and the printer doesn't show up in the list of owned printer until the next step is successfully performed.

Obtaining the OAuth2 authorization_code

In this last step, the printer must obtain the OAuth2 authorization_code by polling the URL provided in the polling_url field obtained at step #1. The polling_url is not complete, as a client ID needs to be appended to it. The client ID can be obtained as explained here. Client IDs don't need to be unique per printer: in fact, we expect one client ID per printer manufacturer.

Polling should not be more frequent than once every 5 seconds. The printer can either start polling after providing the user with the registration token or, preferably and to avoid the user having to wait for a new cycle, it may start upon user request (once the user is done with the previous step).

Here's an example polling request:

curl "https://www.google.com/cloudprint/getauthcode?printerid={$printer_id}&oauth_client_id={$oauth_client_id}" \
  -H "X-CloudPrint-Proxy"

Where {$printer_id} should be replaced by the ID you received in the registration step and {$oauth_client_id} should be replaced by the client ID obtained from the API console.

The polling_url request will return one of two possible responses:

  • If the user has successfully claimed the token:
    • success: set to true
    • authorization_code: the OAuth2 authorization_code to be used to get OAuth2 refresh_token and access_token. See details below.
    • xmpp_jid: this is the jabber ID or email address that needs to be used with Google Talk to subscribe for print notifications. This needs to be retained in the printer memory forever.
    • user_email: the email address of the user that claimed the registration_token at the previous step.
    • confirmation_page_url: the url of a printable page that confirms the user that the printer has been registered to him/herself. The same notes relative to retrieving the invite_page_url above apply here too. See below for its usage.
  • If the user has not yet claimed the printer, or the token is unknown or it has elapsed:
    • success: set to false
    • message: set to a generic - possibly internationalized - string “unknown id”. (For security reasons, Google will not provide precise information about the failure. The printer knows when a token is expired, and in this case it should stop polling and return to Step #1.)

Once the printer has obtained the OAuth2 authorization_code, the printer appears in the user's printer list. The printer must confirm to the user that it is successfully registered to that user, either by retrieving and printing the page at confirmation_page_url, or - for printers with the necessary hardware - showing a message like “Printer [printer name] has been successfully registered to user [email]”, where [email] is user_email. Showing the email address is very important as it is the ultimate defense against printer hijacking.

Obtaining the OAuth2 authorization tokens

As a final step, the printer must use the authorization_code to obtain OAuth2 Auth tokens, themselves used to authenticate subsequent API calls to Google Cloud Print. There are two types of tokens involved:

  • The refresh_token should be retained in printer memory forever. It can then be used to retrieve a temporary access_token.
  • The access_token needs to be refreshed every hour, and is used as authentication credentials in subsequent API calls.

The printer can initially retrieve both tokens together by POSTing the authorization_code to the OAuth2 token endpoint at https://accounts.google.com/o/oauth2/token, along with the following parameters:

  • client_id (the same that you appended to polling_url when fetching the authorization_code)
  • redirect_uri (must be set to oob)
  • client_secret (obtained along with client_id as part of your client credentials)
  • grant_type=authorization_code
  • scope=https://www.googleapis.com/auth/cloudprint (scope identifies the Google service being accessed, in this case GCP)

Important: You must use oob for redirect_uri instead of the OAuth recommended urn:ietf:wg:oauth:2.0:oob since you are working with robot accounts.

Here's an example of the OAuth authorization request:

curl "https://accounts.google.com/o/oauth2/token" \
  -d "client_id={$oauth_client_id}&redirect_uri=oob&client_secret={$client_secret}&grant_type=authorization_code&scope=https://www.googleapis.com/auth/cloudprint&code={$authorization_code}"

Where {$oauth_client_id} and {$client_secret} should be replaced by the client ID and client secret (respectively) obtained from the API console and {$authorization_code} should be replaced by the authorization_code you obtained in the polling step.

If this request succeeds, a refresh token and short-lived access token will be returned via JSON. You can then use the access token to make API calls by attaching the following Authorization HTTP header to each of your API calls: Authorization: OAuth YOUR_ACCESS_TOKEN. You can retrieve additional access tokens once the first expires (after an hour) by using the token endpoint with your refresh token, client credentials, and the parameter grant_type=refresh_token.

Note: Additional details about obtaining OAuth2 tokens, including sample requests and responses, can be found here.

Step #2: Handling Printers

Syncing with the GCP Registry (/list)

As a first step whenever the printer comes online, it should check in with the CloudPrint service and sync the printer's status and capabilities with the listing in the cloud. This way the client does not need to maintain state with regard to what has been registered, and needs to retain only the Auth token provided when the user authenticated.

The printer should call the /list interface using the proxy ID the printer used during registration. The proxy ID should be a unique attribute of the printer (such as the MAC address) and is the only piece of information besides the Auth token that the printer needs to retain state with. The /list call will return a response with a success flag, and if the request was successful a list of printers.

Note: In order to support certain third party hardware and virtual printer solutions, the GCP service allows a printer proxy to register more than one physical printer. In those circumstances, the printer list may be more than one item long. On the other hand, a Cloud Ready printer acting on its own behalf should always use a unique proxy ID.

The printer(s) returned by the /list interface will contain attributes corresponding to display name(s) and ID(s). The printer ID is the key provided by Google Cloud Print by which you will fetch and manage jobs in the cloud for a specific printer. Optionally, the /list interface will also accept an extra fields parameter to request that printer fields such as queuedJobsCount be included in the response object. The /printer interface must be used in order to retrieve a printer's capabilities.

Updating a Printer

If a printer has already been registered but information about it has changed because of errors, upgrades or breakdowns, you can use the /update interface to rewrite any of the fields that were set on registration. The call to /update is the same as a call to /register, plus an additional parameter specifying the printer ID to be updated.

Warning: Do not use /register to update a printer with new information. The result will be a duplicate printer with the same display name registered on all GCP services.

Step #3: Handling Print Jobs

Receiving Print Notifications (XMPP)

Notifications of waiting jobs are distributed to printers using a persistent XMPP connection. The printer should initiate this connection with the Google Talk XMPP servers. The OAuth2 access token received during the registration mechanism will also work for authenticating with Google Talk. For development purposes or for printers with at-device authentication, the notification step can be skipped. Final production printers should not rely on polling – the /fetch command should only be called as a result of a notification or on explicit user action.

To maintain a persistent XMPP connection, we recommend using Google's libjingle library. You can find libjingle documentation here. Using libjingle, open a persistent connection to talk.google.com and subscribe to Google Cloud Print notifications by sending a subscription stanza to the Google Push Notifications service. Following is an example stanza:

<iq from="{FULL_JID}" to="{BARE_JID}" type="set" id="1">
 <subscribe xmlns="google:push">
   <item channel="cloudprint.google.com" from="cloudprint.google.com"/>
 </subscribe>
</iq>

FULL_JID is the full XMPP JID (e.g. username@gmail.com/canon234), BARE_JID is the bare XMPP JID (e.g. username@gmail.com).

The service will acknowledge your subscription by returning the following:

<iq from="{BARE_JID}" to="{FULL_JID}" type="result" id="1"/>

Finally, whenever a new job has arrived at the queue, subscribing clients will receive the following data push:

<message from="cloudprint.google.com" to="{FULL_JID}">
 <push xmlns="google:push" channel="cloudprint.google.com">
   <recipient to="{BARE_JID}">{raw data, ignore}</recipient>
   <data>{base-64 encoded printer id}</data>
 </push>
</message>

Note: If you are not using the libjingle library and are instead building the XMPP handshake yourself, see here for documentation listing the typical stanzas used in the GCP XMPP handshake flow. The XMPP Standards Foundation lists more information on the protocol and on XML schemas here.

See here for example code from the Chromium Project demonstrating persistent XMPP connections and notification subscriptions, or here for the code necessary to parse notifications.

Fetching Print Jobs

Whenever the printer comes online or receives notification of a waiting job, it should use the /fetch interface to grab any available jobs in the queue. The /fetch call takes the printer ID (required) as a parameter and returns the list of available jobs in the QUEUED state. Each job has an ID, a title, a status, and two URLs: one for the job ticket (in either XPS, PPD, or CJT format, determined by the format in which the printer originally registered its capabilities) and one for the job file itself. The file is available in PDF, PWG-raster (March 2011 draft), or in the original format of the document. The document type served is specified via the accept header of the request made to the second URL of the print job: the URL of the job file itself. Not specifying this header will result in a PDF printout.

A list of common MIME types, sorted by applicable file extensions, is available here. A list which is more complete but omits Microsoft Office 2007 formats is available here.

Processing Print Jobs

As the printer completes jobs or encounters errors it can use the /control interface to update the status of jobs for relay back to the user. The /control request takes as parameters a job ID (required) and a semantic state diff (for GCP 2.0 compliant printers), or a status, error code and error message (for older printers). The /fetch protocol will only return jobs in the QUEUED state. The response will contain a success flag and an explanatory message.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.