| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- // Zoho OAuth2 Authentication Module
- // EU data center: accounts.zoho.eu
- var ACCOUNTS_BASE = 'https://accounts.zoho.eu';
- var SCOPE = 'ZohoCliq.Chats.READ,ZohoCliq.Messages.READ,ZohoCliq.Webhooks.CREATE,ZohoCliq.Channels.READ,ZohoCliq.Users.READ';
- function setupAuth(grantToken) {
- var clientId = shadowman.config.value('client_id');
- var clientSecret = shadowman.config.value('client_secret');
- if (!clientId || !clientSecret) {
- return { error: 'Missing OAuth credentials. Set client_id and client_secret in plugin config.' };
- }
- if (!grantToken) {
- return { error: 'Missing grant_token parameter. Generate one from Zoho Developer Console (Self Client).' };
- }
- shadowman.log.info('Exchanging Zoho grant token for access + refresh tokens...');
- var body = 'grant_type=authorization_code' +
- '&client_id=' + encodeURIComponent(clientId) +
- '&client_secret=' + encodeURIComponent(clientSecret) +
- '&redirect_uri=oob' +
- '&code=' + encodeURIComponent(grantToken);
- var resp = shadowman.http.post(ACCOUNTS_BASE + '/oauth/v2/token', body, {
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded'
- }
- });
- if (resp.status !== 200) {
- shadowman.log.error('Token exchange failed: ' + resp.status + ' ' + resp.body);
- var errBody;
- try {
- errBody = JSON.parse(resp.body);
- } catch (e) {
- errBody = resp.body;
- }
- return { error: 'Token exchange failed: ' + (errBody.error || errBody.error_description || resp.body) };
- }
- var tokens;
- try {
- tokens = JSON.parse(resp.body);
- } catch (e) {
- return { error: 'Invalid response from Zoho: ' + resp.body };
- }
- shadowman.storage.set('tokens', {
- access_token: tokens.access_token,
- refresh_token: tokens.refresh_token,
- expires_at: Date.now() + (tokens.expires_in * 1000),
- api_domain: tokens.api_domain || 'https://cliq.zoho.eu',
- token_type: tokens.token_type || 'Bearer'
- });
- shadowman.log.info('Zoho OAuth tokens stored successfully. Refresh token does not expire.');
- var result = {
- success: true,
- message: 'Authentication successful. Refresh token stored permanently.',
- expires_in: tokens.expires_in
- };
- var convId = shadowman.storage.get('auth_conversation_id');
- if (convId) {
- shadowman.events.emit('message', {
- text: 'Zoho Cliq OAuth successful! You can now use chat tools.',
- conversationId: convId
- });
- shadowman.storage.del('auth_conversation_id');
- }
- return result;
- }
- function getStatus() {
- var tokens = shadowman.storage.get('tokens');
- if (!tokens) {
- return { authenticated: false, message: 'Not authenticated. Use setup_auth with a grant token.' };
- }
- if (Date.now() >= tokens.expires_at) {
- var newTokens = refreshTokenInternal();
- if (newTokens.error) {
- return { authenticated: false, message: 'Token expired and refresh failed. Re-run setup_auth.' };
- }
- return { authenticated: true, message: 'Authenticated (token refreshed)', domain: newTokens.api_domain };
- }
- var remaining = Math.round((tokens.expires_at - Date.now()) / 1000);
- return {
- authenticated: true,
- message: 'Authenticated',
- expires_in_seconds: remaining,
- domain: tokens.api_domain
- };
- }
- function getValidToken() {
- var tokens = shadowman.storage.get('tokens');
- if (!tokens) {
- return null;
- }
- if (Date.now() >= (tokens.expires_at - 300000)) {
- var newTokens = refreshTokenInternal();
- if (newTokens.error) {
- shadowman.log.error('Token refresh failed: ' + newTokens.error);
- if (Date.now() < tokens.expires_at) {
- return tokens.access_token;
- }
- return null;
- }
- return newTokens.access_token;
- }
- return tokens.access_token;
- }
- function refreshTokenInternal() {
- var tokens = shadowman.storage.get('tokens');
- if (!tokens || !tokens.refresh_token) {
- return { error: 'No refresh token available' };
- }
- var clientId = shadowman.config.value('client_id');
- var clientSecret = shadowman.config.value('client_secret');
- if (!clientId || !clientSecret) {
- return { error: 'OAuth client credentials missing' };
- }
- shadowman.log.info('Refreshing Zoho access token...');
- var body = 'grant_type=refresh_token' +
- '&client_id=' + encodeURIComponent(clientId) +
- '&client_secret=' + encodeURIComponent(clientSecret) +
- '&refresh_token=' + encodeURIComponent(tokens.refresh_token);
- var resp = shadowman.http.post(ACCOUNTS_BASE + '/oauth/v2/token', body, {
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded'
- }
- });
- if (resp.status !== 200) {
- shadowman.log.error('Token refresh failed: ' + resp.status + ' ' + resp.body);
- return { error: 'Refresh failed: ' + resp.body };
- }
- var newTokens;
- try {
- newTokens = JSON.parse(resp.body);
- } catch (e) {
- return { error: 'Invalid refresh response: ' + resp.body };
- }
- var updated = {
- access_token: newTokens.access_token,
- refresh_token: tokens.refresh_token,
- expires_at: Date.now() + (newTokens.expires_in * 1000),
- api_domain: newTokens.api_domain || tokens.api_domain,
- token_type: newTokens.token_type || 'Bearer'
- };
- shadowman.storage.set('tokens', updated);
- shadowman.log.info('Zoho token refreshed successfully');
- return updated;
- }
- module.exports = {
- setupAuth: setupAuth,
- getStatus: getStatus,
- getValidToken: getValidToken
- };
|