google-oauth-jwt
Google OAuth 2.0 authentication for server-to-server applications with Node.js.
This library generates JWT tokens to establish
identity for an API, without an end-user being involved. This is the preferred scenario for server-side communications.
It can be used to interact with Google APIs requiring access to user data (such as Google Drive, Calendar, etc.) for
which URL-based callbacks and user authorization prompts are not appropriate.
Tokens are generated for a service account, which is created from the Google API console. Service accounts must also
be granted access to resources, using traditional assignation of permissions using the unique service account email
address.
The authentication process is implemented following the specifications found
here.
This package also integrates with request to seamlessly query Google RESTful APIs,
which is optional. Integration with request provides automatic requesting of
tokens, as well as built-in token caching.
Documentation
Installation
npm install google-oauth-jwt
How does it work?
When using Google APIs from the server (or any non-browser based application), authentication is performed through a
Service Account, which is a special account representing your application. This account has a unique email address that
can be used to grant permissions to. If a user wants to give access to his Google Drive to your application, he must share the files or folders with the Service Account using the supplied email address.
Now that the Service Account has permission to some user resources, the application can query the API with OAuth2.
When using OAuth2, authentication is performed using a token that has been obtained first by submitting a JSON Web
Token (JWT). The JWT identifies the user as well as the scope of the data he wants access to. The JWT is also signed
with a cryptographic key to prevent tampering. Google generates the key and keeps only the public key for validation.
You must keep the private key secure with your application so that you can sign the JWT in order to guarantee its authenticity.
The application requests a token that can be used for authentication in exchange with a valid JWT. The resulting token
can then be used for multiple API calls, until it expires and a new token must be obtained by submitting another JWT.
Creating a Service Account and generating the encryption key
-
From the Google API Console, create a service account.
-
Download the generated P12 key.
IMPORTANT: keep a copy of the key, Google keeps only the public key.
-
Convert the key to PEM, so we can use it from the Node crypto module.
To do this, run the following in Terminal:
openssl pkcs12 -in downloaded-key-file.p12 -out your-key-file.pem -nodes
The password for the key is notasecret
, as mentioned when you downloaded the key from Google.
Granting access to resources to be requested through an API
In order to query resources using the API, access must be granted to the Service Account. Each Google application that
has security settings must be configured individually. Access is granted by assigning permissions to the service
account, using its email address found in the API console.
For example, in order to list files in Google Drive, folders and files must be shared with the service account email
address. Likewise, to access a calendar, the calendar must be shared with the service account.
Querying Google APIs with "request"
In this example, we use a modified instance of request to query the
Google Drive API. request is a full-featured HTTP client which can be extended with Google OAuth2 capabilities by using the requestWithJWT
method. The modified module will request and cache tokens automatically when supplied with a jwt
setting in the options.
var request = require('google-oauth-jwt').requestWithJWT();
request({
url: 'https://www.googleapis.com/drive/v2/files',
jwt: {
email: 'my-service-account@developer.gserviceaccount.com',
keyFile: 'my-service-account-key.pem',
scopes: ['https://www.googleapis.com/auth/drive.readonly']
}
}, function (err, res, body) {
console.log(JSON.parse(body));
});
Note that the options
object includes a jwt
object we use to configure the JWT generation. The token will then
automatically be requested and inserted in the authorization header. It will also be cached and reused for subsequent calls using the same service account and scopes.
If you want to use a specific version of request
, simply pass it to the the requestWithJWT
method as such:
var request = require('request');
request = require('google-oauth-jwt').requestWithJWT(request);
Requesting the token manually
If you wish to simply request the token for use with a Google API, use the authenticate
method.
var googleAuth = require('google-oauth-jwt');
googleAuth.authenticate({
email: 'my-service-account@developer.gserviceaccount.com',
keyFile: 'my-service-account-key.pem',
scopes: ['https://www.googleapis.com/auth/drive.readonly']
}, function (err, token) {
console.log(token);
});
If you want to use the built-in token cache, use the TokenCache
class. Tokens are cached using the email address and
the scopes as the key.
var TokenCache = require('google-oauth-jwt').TokenCache,
tokens = new TokenCache();
tokens.get({
email: 'my-service-account@developer.gserviceaccount.com',
keyFile: 'my-service-account-key.pem',
scopes: ['https://www.googleapis.com/auth/drive.readonly']
}, function (err, token) {
console.log(token);
});
Using TokenCache
will request only one token for multiple concurrent requests to get
. A new token request will
automatically be issued if the token is expired.
Encoding the JWT manually
It is also possible to encode the JWT manually using the encodeJWT
method.
var googleAuth = require('google-oauth-jwt');
googleAuth.encodeJWT({
email: 'my-service-account@developer.gserviceaccount.com',
keyFile: 'my-service-account-key.pem',
scopes: ['https://www.googleapis.com/auth/drive.readonly']
}, function (err, jwt) {
console.log(jwt);
});
Specifying JWT generation options
The following options can be specified in order to generate the JWT used for authentication:
var options = {
email: 'my-service-account@developer.gserviceaccount.com',
scopes: [...],
key: 'KEY_CONTENTS',
keyFile: 'path/to/key.pem',
expiration: 3600000,
delegationEmail: 'email_address@mycompany.com'
};
For more information:
https://developers.google.com/accounts/docs/OAuth2ServiceAccount#formingclaimset
Options are used to encode the JWT that will be sent to Google OAuth servers in order to issue a token that can then be
used for authentification to Google APIs. The same options are used for authenticate
, TokenCache.get
or the jwt
setting passed to request
options.
Running the tests
Running the unit tests for google-oauth-jwt
requires a valid Service Account, its encryption key and a URL to test.
To launch the tests, first configure your account in "test/jwt-settings.json" using the sample file. Make sure your
test URL also matches with the requested scopes. The tests do not make any assumption on the results from the API, so
you can use any OAuth2 enabled API.
For example, to run the tests by listing Google Drive files, you can use the following configuration:
{
"email": "my-account@developer.gserviceaccount.com",
"scopes": ["https://www.googleapis.com/auth/drive.readonly"],
"keyFile": "./test/key.pem",
"test_url": "https://www.googleapis.com/drive/v2/files"
}
To run the tests:
npm test
or
mocha -t 5000
The 5 seconds timeout is required since some tests make multiple calls to the API. If you get timeout exceeded errors,
you can bump this value since not all Google APIs may respond with the same timings.
Debugging
To turn on debugging, add "google-oauth-jwt" to your DEBUG
variable. Debugging events include JWT generation, token
requests to the OAuth server and token expirations through TokenCache
.
For example, to turn on debugging while running the unit tests, use this:
DEBUG=google-oauth-jwt mocha -t 5000
Changelog
- 0.1.1: re-introduced debugging, now with debug
- 0.1.0: improved documentation, introduced unit tests and refactoring aimed at testability
- 0.0.7: fixed token expiration check
- 0.0.6: fixed request function call when using a URI string without options
- 0.0.5: token now passed using Authorization header (thank you jpd236)
- 0.0.4: fixed pending callbacks accumulating indefinitely in TokenCache
- 0.0.3: introduction of TokenCache
- 0.0.2: improved error handling and documentation
- 0.0.1: initial version
Compatibility
- Tested with Node 0.8
- Tested on Mac OS X 10.8
Dependencies
License
The MIT License (MIT)
Copyright (c) 2013, Nicolas Mercier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.