chore: Extract authentication logic of the apps to their own classes

This commit is contained in:
Faruk AYDIN
2021-11-18 12:54:56 +01:00
committed by Ömer Faruk Aydın
parent 6b1dee053f
commit 46321f19f4
23 changed files with 707 additions and 617 deletions

View File

@@ -0,0 +1,91 @@
import { URLSearchParams } from 'url';
import DiscordApi from 'discord.js';
import axios, { AxiosInstance } from 'axios';
import App from '../../models/app';
import Field from '../../types/field';
export default class Authentication {
client?: any
connectionData: any
appData: any
scope: string[] = ['identify', 'email']
httpClient: AxiosInstance = axios.create({
baseURL: 'https://discord.com/api/'
})
constructor(connectionData: any) {
this.connectionData = connectionData;
this.appData = App.findOneByKey('discord');
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const searchParams = new URLSearchParams({
client_id: this.connectionData.consumerKey,
redirect_uri: this.oauthRedirectUrl,
response_type: 'code',
scope: this.scope.join(' '),
});
const url = `https://discord.com/api/oauth2/authorize?${searchParams.toString()}`;
return { url };
}
async verifyCredentials() {
const params = new URLSearchParams({
client_id: this.connectionData.consumerKey,
redirect_uri: this.oauthRedirectUrl,
response_type: 'code',
scope: this.scope.join(' '),
client_secret: this.connectionData.consumerSecret,
code: this.connectionData.oauthVerifier,
grant_type: 'authorization_code',
});
const { data: verifiedCredentials }: any = await this.httpClient.post('/oauth2/token', params.toString());
const {
access_token: accessToken,
refresh_token: refreshToken,
expires_in: expiresIn,
scope: scope,
token_type: tokenType,
} = verifiedCredentials;
const { data: user }: any = await this.httpClient.get('/users/@me', {
headers: {
Authorization: `${tokenType} ${accessToken}`,
},
});
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken,
refreshToken,
expiresIn,
scope,
tokenType,
userId: user.id,
screenName: user.username,
email: user.email,
};
}
async isStillVerified() {
try {
await this.httpClient.get('/users/@me', {
headers: {
Authorization: `${this.connectionData.tokenType} ${this.connectionData.accessToken}`,
},
});
return true;
} catch {
return false
}
}
}

View File

@@ -1,91 +1,9 @@
import { URLSearchParams } from 'url'; import Authentication from './authentication';
import DiscordApi from 'discord.js';
import axios, { AxiosInstance } from 'axios';
import App from '../../models/app';
import Field from '../../types/field';
export default class Discord { export default class Discord {
client?: any authenticationClient: any
connectionData: any
appData: any
scope: string[] = ['identify', 'email']
httpClient: AxiosInstance = axios.create({
baseURL: 'https://discord.com/api/'
})
constructor(connectionData: any) { constructor(connectionData: any) {
this.connectionData = connectionData; this.authenticationClient = new Authentication(connectionData);
this.appData = App.findOneByKey('discord');
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const searchParams = new URLSearchParams({
client_id: this.connectionData.consumerKey,
redirect_uri: this.oauthRedirectUrl,
response_type: 'code',
scope: this.scope.join(' '),
});
const url = `https://discord.com/api/oauth2/authorize?${searchParams.toString()}`;
return { url };
}
async verifyCredentials() {
const params = new URLSearchParams({
client_id: this.connectionData.consumerKey,
redirect_uri: this.oauthRedirectUrl,
response_type: 'code',
scope: this.scope.join(' '),
client_secret: this.connectionData.consumerSecret,
code: this.connectionData.oauthVerifier,
grant_type: 'authorization_code',
});
const { data: verifiedCredentials }: any = await this.httpClient.post('/oauth2/token', params.toString());
const {
access_token: accessToken,
refresh_token: refreshToken,
expires_in: expiresIn,
scope: scope,
token_type: tokenType,
} = verifiedCredentials;
const { data: user }: any = await this.httpClient.get('/users/@me', {
headers: {
Authorization: `${tokenType} ${accessToken}`,
},
});
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken,
refreshToken,
expiresIn,
scope,
tokenType,
userId: user.id,
screenName: user.username,
email: user.email,
};
}
async isStillVerified() {
try {
await this.httpClient.get('/users/@me', {
headers: {
Authorization: `${this.connectionData.tokenType} ${this.connectionData.accessToken}`,
},
});
return true;
} catch {
return false
}
} }
} }

View File

@@ -0,0 +1,78 @@
import { google, google as GoogleApi } from 'googleapis';
import App from '../../models/app';
import Field from '../../types/field';
export default class Authentication {
oauthClient: any
connectionData: any
appData: any
scopes: string[] = [
'https://www.googleapis.com/auth/datastore',
'https://www.googleapis.com/auth/firebase',
'https://www.googleapis.com/auth/user.emails.read',
'profile',
]
constructor(connectionData: any) {
this.appData = App.findOneByKey('firebase');
this.connectionData = connectionData;
this.oauthClient = new GoogleApi.auth.OAuth2(
connectionData.consumerKey,
connectionData.consumerSecret,
this.oauthRedirectUrl,
);
GoogleApi.options({ auth: this.oauthClient });
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const url = this.oauthClient.generateAuthUrl({
access_type: 'offline',
scope: this.scopes
});
return { url };
}
async verifyCredentials() {
const { tokens } = await this.oauthClient.getToken(this.connectionData.oauthVerifier);
this.oauthClient.setCredentials(tokens);
const people = GoogleApi.people('v1');
const { data } = await people.people.get({
resourceName: 'people/me',
personFields: 'emailAddresses',
});
const { emailAddresses, resourceName: userId } = data;
const primaryEmailAddress = emailAddresses.find(emailAddress => emailAddress.metadata.primary);
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
tokenType: tokens.token_type,
expiryDate: tokens.expiry_date,
scope: tokens.scope,
screenName: primaryEmailAddress.value,
userId,
}
}
async isStillVerified() {
try {
await this.oauthClient.getTokenInfo(this.connectionData.accessToken);
return true;
} catch {
return false
}
}
}

View File

@@ -1,78 +1,9 @@
import { google, google as GoogleApi } from 'googleapis'; import Authentication from './authentication'
import App from '../../models/app';
import Field from '../../types/field';
export default class Firebase { export default class Firebase {
oauthClient: any authenticationClient: any
connectionData: any
appData: any
scopes: string[] = [
'https://www.googleapis.com/auth/datastore',
'https://www.googleapis.com/auth/firebase',
'https://www.googleapis.com/auth/user.emails.read',
'profile',
]
constructor(connectionData: any) { constructor(connectionData: any) {
this.appData = App.findOneByKey('firebase'); this.authenticationClient = new Authentication(connectionData);
this.connectionData = connectionData;
this.oauthClient = new GoogleApi.auth.OAuth2(
connectionData.consumerKey,
connectionData.consumerSecret,
this.oauthRedirectUrl,
);
GoogleApi.options({ auth: this.oauthClient });
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const url = this.oauthClient.generateAuthUrl({
access_type: 'offline',
scope: this.scopes
});
return { url };
}
async verifyCredentials() {
const { tokens } = await this.oauthClient.getToken(this.connectionData.oauthVerifier);
this.oauthClient.setCredentials(tokens);
const people = GoogleApi.people('v1');
const { data } = await people.people.get({
resourceName: 'people/me',
personFields: 'emailAddresses',
});
const { emailAddresses, resourceName: userId } = data;
const primaryEmailAddress = emailAddresses.find(emailAddress => emailAddress.metadata.primary);
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
tokenType: tokens.token_type,
expiryDate: tokens.expiry_date,
scope: tokens.scope,
screenName: primaryEmailAddress.value,
userId,
}
}
async isStillVerified() {
try {
await this.oauthClient.getTokenInfo(this.connectionData.accessToken);
return true;
} catch {
return false
}
} }
} }

View File

@@ -0,0 +1,68 @@
import FlickrApi from 'flickr-sdk';
import App from '../../models/app';
import Field from '../../types/field';
export default class Authentication {
oauthClient: any
client: any
connectionData: any
appData: any
constructor(connectionData: any) {
this.oauthClient = new FlickrApi.OAuth(connectionData.consumerKey, connectionData.consumerSecret);
if (connectionData.accessToken && connectionData.accessSecret) {
this.client = new FlickrApi(
FlickrApi.OAuth.createPlugin(
connectionData.consumerKey,
connectionData.consumerSecret,
connectionData.accessToken,
connectionData.accessSecret,
)
);
}
this.connectionData = connectionData;
this.appData = App.findOneByKey('flickr');
}
async createAuthData() {
const appFields = this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl')
const callbackUrl = appFields.value;
const oauthData = (await this.oauthClient.request(callbackUrl)).body;
const url = await this.oauthClient.authorizeUrl(oauthData.oauth_token, 'delete');
return {
accessToken: oauthData.oauth_token,
accessSecret: oauthData.oauth_token_secret,
url: url,
};
}
async verifyCredentials() {
const verifiedCredentials = (await this.oauthClient.verify(
this.connectionData.accessToken,
this.connectionData.oauthVerifier,
this.connectionData.accessSecret
)).body;
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: verifiedCredentials.oauth_token,
accessSecret: verifiedCredentials.oauth_token_secret,
userId: verifiedCredentials.user_nsid,
screenName: verifiedCredentials.fullname
}
}
async isStillVerified() {
try {
await this.client.test.login();
return true;
} catch {
return false;
}
}
}

View File

@@ -1,68 +1,9 @@
import FlickrApi from 'flickr-sdk'; import Authentication from './authentication'
import App from '../../models/app';
import Field from '../../types/field';
export default class Flickr { export default class Flickr {
oauthClient: any authenticationClient: any
client: any
connectionData: any
appData: any
constructor(connectionData: any) { constructor(connectionData: any) {
this.oauthClient = new FlickrApi.OAuth(connectionData.consumerKey, connectionData.consumerSecret); this.authenticationClient = new Authentication(connectionData);
if (connectionData.accessToken && connectionData.accessSecret) {
this.client = new FlickrApi(
FlickrApi.OAuth.createPlugin(
connectionData.consumerKey,
connectionData.consumerSecret,
connectionData.accessToken,
connectionData.accessSecret,
)
);
}
this.connectionData = connectionData;
this.appData = App.findOneByKey('flickr');
}
async createAuthData() {
const appFields = this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl')
const callbackUrl = appFields.value;
const oauthData = (await this.oauthClient.request(callbackUrl)).body;
const url = await this.oauthClient.authorizeUrl(oauthData.oauth_token, 'delete');
return {
accessToken: oauthData.oauth_token,
accessSecret: oauthData.oauth_token_secret,
url: url,
};
}
async verifyCredentials() {
const verifiedCredentials = (await this.oauthClient.verify(
this.connectionData.accessToken,
this.connectionData.oauthVerifier,
this.connectionData.accessSecret
)).body;
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: verifiedCredentials.oauth_token,
accessSecret: verifiedCredentials.oauth_token_secret,
userId: verifiedCredentials.user_nsid,
screenName: verifiedCredentials.fullname
}
}
async isStillVerified() {
try {
await this.client.test.login();
return true;
} catch {
return false;
}
} }
} }

View File

@@ -0,0 +1,77 @@
import {
getWebFlowAuthorizationUrl,
exchangeWebFlowCode,
checkToken,
} from '@octokit/oauth-methods';
import App from '../../models/app';
import Field from '../../types/field';
export default class Authentication {
connectionData: any
appData: any
scopes: string[] = ['repo']
constructor(connectionData: any) {
this.connectionData = connectionData;
this.appData = App.findOneByKey('github');
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const { url } = await getWebFlowAuthorizationUrl({
clientType: "oauth-app",
clientId: this.connectionData.consumerKey,
redirectUrl: this.oauthRedirectUrl,
scopes: this.scopes,
});
return {
url: url,
};
}
async verifyCredentials() {
const { data, authentication } = await exchangeWebFlowCode({
clientType: "oauth-app",
clientId: this.connectionData.consumerKey,
clientSecret: this.connectionData.consumerSecret,
code: this.connectionData.oauthVerifier,
});
this.connectionData.accessToken = data.access_token;
const tokenInfo = await this.getTokenInfo();
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: data.access_token,
scope: data.scope,
tokenType: data.token_type,
userId: tokenInfo.data.user.id,
screenName: tokenInfo.data.user.login,
}
}
async getTokenInfo() {
return checkToken({
clientType: "oauth-app",
clientId: this.connectionData.consumerKey,
clientSecret: this.connectionData.consumerSecret,
token: this.connectionData.accessToken,
});
}
async isStillVerified() {
try {
await this.getTokenInfo();
return true;
} catch {
return false;
}
}
}

View File

@@ -1,77 +1,9 @@
import { import Authentication from './authentication';
getWebFlowAuthorizationUrl,
exchangeWebFlowCode,
checkToken,
} from '@octokit/oauth-methods';
import App from '../../models/app';
import Field from '../../types/field';
export default class Github { export default class Github {
connectionData: any authenticationClient: any
appData: any
scopes: string[] = ['repo']
constructor(connectionData: any) { constructor(connectionData: any) {
this.connectionData = connectionData; this.authenticationClient = new Authentication(connectionData);
this.appData = App.findOneByKey('github');
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const { url } = await getWebFlowAuthorizationUrl({
clientType: "oauth-app",
clientId: this.connectionData.consumerKey,
redirectUrl: this.oauthRedirectUrl,
scopes: this.scopes,
});
return {
url: url,
};
}
async verifyCredentials() {
const { data, authentication } = await exchangeWebFlowCode({
clientType: "oauth-app",
clientId: this.connectionData.consumerKey,
clientSecret: this.connectionData.consumerSecret,
code: this.connectionData.oauthVerifier,
});
this.connectionData.accessToken = data.access_token;
const tokenInfo = await this.getTokenInfo();
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: data.access_token,
scope: data.scope,
tokenType: data.token_type,
userId: tokenInfo.data.user.id,
screenName: tokenInfo.data.user.login,
}
}
async getTokenInfo() {
return checkToken({
clientType: "oauth-app",
clientId: this.connectionData.consumerKey,
clientSecret: this.connectionData.consumerSecret,
token: this.connectionData.accessToken,
});
}
async isStillVerified() {
try {
await this.getTokenInfo();
return true;
} catch {
return false;
}
} }
} }

View File

@@ -0,0 +1,38 @@
import { Client } from 'pg';
import App from '../../models/app';
export default class Authentication {
client: any
connectionData: any
appData: any
constructor(connectionData: any) {
this.client = new Client({
host: connectionData.host,
port: connectionData.port,
database: connectionData.database,
user: connectionData.username,
password: connectionData.password,
})
this.connectionData = connectionData;
this.appData = App.findOneByKey('postgresql');
}
async verifyCredentials() {
await this.client.connect()
return {
screenName: this.connectionData.database
}
}
async isStillVerified() {
try {
await this.client.connect()
return true;
} catch(error) {
return false
}
}
}

View File

@@ -1,38 +1,9 @@
import { Client } from 'pg'; import Authentication from './authentication'
import App from '../../models/app';
export default class PostgreSQL { export default class PostgreSQL {
client: any authenticationClient: any;
connectionData: any
appData: any
constructor(connectionData: any) { constructor(connectionData: any) {
this.client = new Client({ this.authenticationClient = new Authentication(connectionData);
host: connectionData.host,
port: connectionData.port,
database: connectionData.database,
user: connectionData.username,
password: connectionData.password,
})
this.connectionData = connectionData;
this.appData = App.findOneByKey('postgresql');
}
async verifyCredentials() {
await this.client.connect()
return {
screenName: this.connectionData.database
}
}
async isStillVerified() {
try {
await this.client.connect()
return true;
} catch(error) {
return false
}
} }
} }

View File

@@ -0,0 +1,40 @@
import nodemailer from 'nodemailer';
import App from '../../models/app';
export default class Authentication {
client: any
connectionData: any
appData: any
constructor(connectionData: any) {
this.client = nodemailer.createTransport({
host: connectionData.host,
port: connectionData.port,
secure: connectionData.useTls,
auth: {
user: connectionData.username,
pass: connectionData.password,
},
});
this.connectionData = connectionData;
this.appData = App.findOneByKey('smtp');
}
async verifyCredentials() {
await this.client.verify()
return {
screenName: this.connectionData.username
}
}
async isStillVerified() {
try {
await this.client.verify()
return true;
} catch(error) {
return false
}
}
}

View File

@@ -1,40 +1,9 @@
import nodemailer from 'nodemailer'; import Authentication from './authentication';
import App from '../../models/app';
export default class SMTP { export default class SMTP {
client: any authenticationClient: any
connectionData: any
appData: any
constructor(connectionData: any) { constructor(connectionData: any) {
this.client = nodemailer.createTransport({ this.authenticationClient = new Authentication(connectionData);
host: connectionData.host,
port: connectionData.port,
secure: connectionData.useTls,
auth: {
user: connectionData.username,
pass: connectionData.password,
},
});
this.connectionData = connectionData;
this.appData = App.findOneByKey('smtp');
}
async verifyCredentials() {
await this.client.verify()
return {
screenName: this.connectionData.username
}
}
async isStillVerified() {
try {
await this.client.verify()
return true;
} catch(error) {
return false
}
} }
} }

View File

@@ -0,0 +1,37 @@
import TwilioApi from 'twilio';
import App from '../../models/app';
export default class Authentication {
client: any
connectionData: any
appData: any
constructor(connectionData: any) {
this.client = TwilioApi(connectionData.accountSid, connectionData.authToken);
this.connectionData = connectionData;
this.appData = App.findOneByKey('twilio');
}
async verifyCredentials() {
await this.verify();
return {
screenName: this.connectionData.accountSid
}
}
async verify() {
try {
await this.client.keys.list({ limit: 1 })
return true;
} catch(error) {
// Test credentials throw HTTP 403 and thus, we need to have an exception.
return error?.status === 403;
}
}
async isStillVerified() {
return this.verify();
}
}

View File

@@ -1,37 +1,9 @@
import TwilioApi from 'twilio'; import Authentication from './authentication';
import App from '../../models/app';
export default class Twilio { export default class Twilio {
client: any authenticationClient: any;
connectionData: any
appData: any
constructor(connectionData: any) { constructor(connectionData: any) {
this.client = TwilioApi(connectionData.accountSid, connectionData.authToken); this.authenticationClient = new Authentication(connectionData);
this.connectionData = connectionData;
this.appData = App.findOneByKey('twilio');
}
async verifyCredentials() {
await this.verify();
return {
screenName: this.connectionData.accountSid
}
}
async verify() {
try {
await this.client.keys.list({ limit: 1 })
return true;
} catch(error) {
// Test credentials throw HTTP 403 and thus, we need to have an exception.
return error?.status === 403;
}
}
async isStillVerified() {
return this.verify();
} }
} }

View File

@@ -0,0 +1,96 @@
import TwitchApi, { ApiVersions } from 'twitch-js';
import fetchUtil from 'twitch-js/lib/utils/fetch';
import App from '../../models/app';
import Field from '../../types/field';
type TwitchTokenResponse = {
accessToken: string;
refreshToken: string;
expiresIn: string;
tokenType: string;
};
export default class Authentication {
client: any
connectionData: any
appData: any
constructor(connectionData: any) {
this.connectionData = connectionData;
this.appData = App.findOneByKey('twitch');
if (this.clientOptions.token) {
this.client = new TwitchApi(this.clientOptions);
}
}
get clientOptions() {
return {
token: this.connectionData.accessToken,
clientId: this.connectionData.consumerKey,
log: { enabled: true }
};
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const { url } = await fetchUtil('https://id.twitch.tv/oauth2/authorize', {
search: {
client_id: this.connectionData.consumerKey,
redirect_uri: this.oauthRedirectUrl,
response_type: 'code',
scope: 'user:read:email',
}
});
return { url };
}
async verifyCredentials() {
const verifiedCredentials = await fetchUtil('https://id.twitch.tv/oauth2/token', {
method: 'post',
search: {
client_id: this.connectionData.consumerKey,
client_secret: this.connectionData.consumerSecret,
code: this.connectionData.oauthVerifier,
grant_type: 'authorization_code',
redirect_uri: this.oauthRedirectUrl
}
}) as TwitchTokenResponse;
this.connectionData.accessToken = verifiedCredentials.accessToken;
const { api } = new TwitchApi(this.clientOptions);
const { data } = await api.get('users');
const [user] = data;
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: verifiedCredentials.accessToken,
refreshToken: verifiedCredentials.refreshToken,
expiresIn: verifiedCredentials.expiresIn,
tokenType: verifiedCredentials.tokenType,
userId: user.id,
screenName: user.displayName,
}
}
async isStillVerified() {
try {
await fetchUtil('https://id.twitch.tv/oauth2/userinfo', {
headers: {
Authorization: `Bearer ${this.connectionData.accessToken}`,
},
});
return true;
} catch (err) {
return false
}
}
}

View File

@@ -1,96 +1,9 @@
import TwitchApi, { ApiVersions } from 'twitch-js'; import Authentication from './authentication';
import fetchUtil from 'twitch-js/lib/utils/fetch';
import App from '../../models/app';
import Field from '../../types/field';
type TwitchTokenResponse = {
accessToken: string;
refreshToken: string;
expiresIn: string;
tokenType: string;
};
export default class Twitch { export default class Twitch {
client: any authenticationClient: any
connectionData: any
appData: any
constructor(connectionData: any) { constructor(connectionData: any) {
this.connectionData = connectionData; this.authenticationClient = new Authentication(connectionData);
this.appData = App.findOneByKey('twitch');
if (this.clientOptions.token) {
this.client = new TwitchApi(this.clientOptions);
}
}
get clientOptions() {
return {
token: this.connectionData.accessToken,
clientId: this.connectionData.consumerKey,
log: { enabled: true }
};
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const { url } = await fetchUtil('https://id.twitch.tv/oauth2/authorize', {
search: {
client_id: this.connectionData.consumerKey,
redirect_uri: this.oauthRedirectUrl,
response_type: 'code',
scope: 'user:read:email',
}
});
return { url };
}
async verifyCredentials() {
const verifiedCredentials = await fetchUtil('https://id.twitch.tv/oauth2/token', {
method: 'post',
search: {
client_id: this.connectionData.consumerKey,
client_secret: this.connectionData.consumerSecret,
code: this.connectionData.oauthVerifier,
grant_type: 'authorization_code',
redirect_uri: this.oauthRedirectUrl
}
}) as TwitchTokenResponse;
this.connectionData.accessToken = verifiedCredentials.accessToken;
const { api } = new TwitchApi(this.clientOptions);
const { data } = await api.get('users');
const [user] = data;
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: verifiedCredentials.accessToken,
refreshToken: verifiedCredentials.refreshToken,
expiresIn: verifiedCredentials.expiresIn,
tokenType: verifiedCredentials.tokenType,
userId: user.id,
screenName: user.displayName,
}
}
async isStillVerified() {
try {
await fetchUtil('https://id.twitch.tv/oauth2/userinfo', {
headers: {
Authorization: `Bearer ${this.connectionData.accessToken}`,
},
});
return true;
} catch (err) {
return false
}
} }
} }

View File

@@ -0,0 +1,56 @@
import TwitterApi from 'twitter-api-v2';
import App from '../../models/app';
import Field from '../../types/field';
export default class Authentication {
client: any
connectionData: any
appData: any
constructor(connectionData: any) {
this.client = new TwitterApi({
appKey: connectionData.consumerKey,
appSecret: connectionData.consumerSecret,
accessToken: connectionData.accessToken,
accessSecret: connectionData.accessSecret
});
this.connectionData = connectionData;
this.appData = App.findOneByKey('twitter');
}
async createAuthData() {
const appFields = this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl')
const callbackUrl = appFields.value;
const authLink = await this.client.generateAuthLink(callbackUrl);
return {
url: authLink.url,
accessToken: authLink.oauth_token,
accessSecret: authLink.oauth_token_secret,
}
}
async verifyCredentials() {
const verifiedCredentials = await this.client.login(this.connectionData.oauthVerifier)
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: verifiedCredentials.accessToken,
accessSecret: verifiedCredentials.accessSecret,
userId: verifiedCredentials.userId,
screenName: verifiedCredentials.screenName
}
}
async isStillVerified() {
try {
await this.client.currentUser();
return true;
} catch {
return false
}
}
}

View File

@@ -1,56 +1,9 @@
import TwitterApi from 'twitter-api-v2'; import Authentication from './authentication';
import App from '../../models/app';
import Field from '../../types/field';
export default class Twitter { export default class Twitter {
client: any authenticationClient: any
connectionData: any
appData: any
constructor(connectionData: any) { constructor(connectionData: any) {
this.client = new TwitterApi({ this.authenticationClient = new Authentication(connectionData);
appKey: connectionData.consumerKey,
appSecret: connectionData.consumerSecret,
accessToken: connectionData.accessToken,
accessSecret: connectionData.accessSecret
});
this.connectionData = connectionData;
this.appData = App.findOneByKey('twitter');
}
async createAuthData() {
const appFields = this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl')
const callbackUrl = appFields.value;
const authLink = await this.client.generateAuthLink(callbackUrl);
return {
url: authLink.url,
accessToken: authLink.oauth_token,
accessSecret: authLink.oauth_token_secret,
}
}
async verifyCredentials() {
const verifiedCredentials = await this.client.login(this.connectionData.oauthVerifier)
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken: verifiedCredentials.accessToken,
accessSecret: verifiedCredentials.accessSecret,
userId: verifiedCredentials.userId,
screenName: verifiedCredentials.screenName
}
}
async isStillVerified() {
try {
await this.client.currentUser();
return true;
} catch {
return false
}
} }
} }

View File

@@ -0,0 +1,93 @@
import { URLSearchParams } from 'url';
import axios, { AxiosInstance } from
'axios';
import App from '../../models/app';
import Field from '../../types/field';
export default class Authentication {
client?: any
connectionData: any
appData: any
scope: string[] = [
'forms:read',
'forms:write',
'webhooks:read',
'webhooks:write',
'responses:read',
'accounts:read',
'workspaces:read',
]
httpClient: AxiosInstance = axios.create({
baseURL: 'https://api.typeform.com'
})
constructor(connectionData: any) {
this.connectionData = connectionData;
this.appData = App.findOneByKey('typeform');
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const searchParams = new URLSearchParams({
client_id: this.connectionData.consumerKey,
redirect_uri: this.oauthRedirectUrl,
scope: this.scope.join(' '),
});
const url = `https://api.typeform.com/oauth/authorize?${searchParams.toString()}`;
return { url };
}
async verifyCredentials() {
const params = new URLSearchParams({
grant_type: 'authorization_code',
code: this.connectionData.oauthVerifier,
client_id: this.connectionData.consumerKey,
client_secret: this.connectionData.consumerSecret,
redirect_uri: this.oauthRedirectUrl,
});
const { data: verifiedCredentials }: any = await this.httpClient.post('/oauth/token', params.toString());
const {
access_token: accessToken,
expires_in: expiresIn,
token_type: tokenType,
} = verifiedCredentials;
const { data: user }: any = await this.httpClient.get('/me', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken,
expiresIn,
tokenType,
userId: user.user_id,
screenName: user.alias,
email: user.email,
};
}
async isStillVerified() {
try {
await this.httpClient.get('/me', {
headers: {
Authorization: `Bearer ${this.connectionData.accessToken}`,
},
});
return true;
} catch {
return false
}
}
}

View File

@@ -1,93 +1,9 @@
import { URLSearchParams } from 'url'; import Authentication from './authentication';
import axios, { AxiosInstance } from
'axios';
import App from '../../models/app';
import Field from '../../types/field';
export default class Typeform { export default class Typeform {
client?: any authenticationClient: any;
connectionData: any
appData: any
scope: string[] = [
'forms:read',
'forms:write',
'webhooks:read',
'webhooks:write',
'responses:read',
'accounts:read',
'workspaces:read',
]
httpClient: AxiosInstance = axios.create({
baseURL: 'https://api.typeform.com'
})
constructor(connectionData: any) { constructor(connectionData: any) {
this.connectionData = connectionData; this.authenticationClient = new Authentication(connectionData);
this.appData = App.findOneByKey('typeform');
}
get oauthRedirectUrl() {
return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value;
}
async createAuthData() {
const searchParams = new URLSearchParams({
client_id: this.connectionData.consumerKey,
redirect_uri: this.oauthRedirectUrl,
scope: this.scope.join(' '),
});
const url = `https://api.typeform.com/oauth/authorize?${searchParams.toString()}`;
return { url };
}
async verifyCredentials() {
const params = new URLSearchParams({
grant_type: 'authorization_code',
code: this.connectionData.oauthVerifier,
client_id: this.connectionData.consumerKey,
client_secret: this.connectionData.consumerSecret,
redirect_uri: this.oauthRedirectUrl,
});
const { data: verifiedCredentials }: any = await this.httpClient.post('/oauth/token', params.toString());
const {
access_token: accessToken,
expires_in: expiresIn,
token_type: tokenType,
} = verifiedCredentials;
const { data: user }: any = await this.httpClient.get('/me', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return {
consumerKey: this.connectionData.consumerKey,
consumerSecret: this.connectionData.consumerSecret,
accessToken,
expiresIn,
tokenType,
userId: user.user_id,
screenName: user.alias,
email: user.email,
};
}
async isStillVerified() {
try {
await this.httpClient.get('/me', {
headers: {
Authorization: `Bearer ${this.connectionData.accessToken}`,
},
});
return true;
} catch {
return false
}
} }
} }

View File

@@ -19,7 +19,7 @@ const createAuthDataResolver = async (params: Params, req: RequestWithCurrentUse
consumerSecret: connection.data.consumerSecret consumerSecret: connection.data.consumerSecret
}); });
const authLink = await appInstance.createAuthData(); const authLink = await appInstance.authenticationClient.createAuthData();
await connection.$query().patch({ await connection.$query().patch({
data: { data: {

View File

@@ -15,7 +15,7 @@ const verifyConnectionResolver = async (params: Params, req: RequestWithCurrentU
const appClass = (await import(`../../apps/${connection.key}`)).default; const appClass = (await import(`../../apps/${connection.key}`)).default;
const appInstance = new appClass(connection.data) const appInstance = new appClass(connection.data)
const verifiedCredentials = await appInstance.verifyCredentials(); const verifiedCredentials = await appInstance.authenticationClient.verifyCredentials();
connection = await connection.$query().patchAndFetch({ connection = await connection.$query().patchAndFetch({
data: { data: {

View File

@@ -16,7 +16,7 @@ const testConnectionResolver = async (params: Params, req: RequestWithCurrentUse
const appClass = (await import(`../../apps/${connection.key}`)).default; const appClass = (await import(`../../apps/${connection.key}`)).default;
const appInstance = new appClass(connection.data); const appInstance = new appClass(connection.data);
const isStillVerified = await appInstance.isStillVerified(); const isStillVerified = await appInstance.authenticationClient.isStillVerified();
connection = await connection.$query().patchAndFetch({ connection = await connection.$query().patchAndFetch({
verified: isStillVerified verified: isStillVerified