Skip to content
This repository was archived by the owner on Jun 11, 2025. It is now read-only.

Commit f37904d

Browse files
author
Eric Koleda
authored
Add sample for public Xero apps.
1 parent ba3e823 commit f37904d

File tree

1 file changed

+109
-0
lines changed

1 file changed

+109
-0
lines changed

samples/XeroPublic.gs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Xero public applications guide:
3+
* https://developer.xero.com/documentation/auth-and-limits/public-applications
4+
*
5+
* This script must be published as a web app (Publish > Deploy as web app) in
6+
* order to function. The web app URL is used instead of the normal callback
7+
* URL to work around a limitation in the Xero API's OAuth implementation
8+
* (callback URLs are limited to 250 characters). Make sure to republish the
9+
* web app after updating the code.
10+
*/
11+
12+
var CONSUMER_KEY = '...';
13+
var CONSUMER_SECRET = '...';
14+
15+
/**
16+
* Authorizes and makes a request to the Xero API.
17+
*/
18+
function run() {
19+
var service = getService();
20+
if (service.hasAccess()) {
21+
var url = 'https://api.xero.com/api.xro/2.0/Organisations';
22+
var response = service.fetch(url, {
23+
headers: {
24+
Accept: 'application/json'
25+
}
26+
});
27+
var result = JSON.parse(response.getContentText());
28+
Logger.log(JSON.stringify(result, null, 2));
29+
} else {
30+
var authorizationUrl = service.authorize();
31+
Logger.log('Open the following URL and re-run the script: %s',
32+
authorizationUrl);
33+
}
34+
}
35+
36+
/**
37+
* Reset the authorization state, so that it can be re-tested.
38+
*/
39+
function reset() {
40+
var service = getService();
41+
service.reset();
42+
}
43+
44+
/**
45+
* Configures the service.
46+
*/
47+
function getService() {
48+
var service = OAuth1.createService('Xero')
49+
// Set the endpoint URLs.
50+
.setRequestTokenUrl('https://api.xero.com/oauth/RequestToken')
51+
.setAuthorizationUrl('https://api.xero.com/oauth/Authorize')
52+
.setAccessTokenUrl('https://api.xero.com/oauth/AccessToken')
53+
54+
// Set the consumer key and secret.
55+
.setConsumerKey(CONSUMER_KEY)
56+
.setConsumerSecret(CONSUMER_SECRET)
57+
58+
// Set the name of the callback function in the script referenced
59+
// above that should be invoked to complete the OAuth flow.
60+
.setCallbackFunction('authCallback')
61+
62+
// Set the property store where authorized tokens should be persisted.
63+
.setPropertyStore(PropertiesService.getUserProperties());
64+
65+
// Override the callback URL method to use the web app URL instead.
66+
service.getCallbackUrl = function() {
67+
return ScriptApp.getService().getUrl();
68+
};
69+
70+
// Override the parseToken_ method to record the time granted.
71+
var originalParseToken = service.parseToken_;
72+
service.parseToken_ = function(content) {
73+
var token = originalParseToken.apply(this, [content]);
74+
token.granted_time = Math.floor((new Date()).getTime() / 1000);
75+
return token;
76+
}
77+
78+
// Override the hasAccess method to handle token expiration.
79+
var orginalHasAccess = service.hasAccess;
80+
service.hasAccess = function() {
81+
// First do the normal check.
82+
if (!orginalHasAccess.apply(this)) return false;
83+
84+
// Check to see if the access token has expired
85+
// (or will expire in the next 60 seconds).
86+
var token = this.getToken_();
87+
var expiresTime = token.granted_time + Number(token.oauth_expires_in);
88+
var now = Math.floor((new Date()).getTime() / 1000);
89+
return (expiresTime - now) > 60;
90+
}
91+
92+
return service;
93+
}
94+
95+
/**
96+
* Handles GET requests to the web app.
97+
*/
98+
function doGet(request) {
99+
// Determine if the request is part of an OAuth callback.
100+
if (request.parameter.oauth_token) {
101+
var service = getService();
102+
var authorized = service.handleCallback(request);
103+
if (authorized) {
104+
return HtmlService.createHtmlOutput('Success!');
105+
} else {
106+
return HtmlService.createHtmlOutput('Denied');
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)