Working support for the cli and cloud auth

parent e0fce7a4
......@@ -78,8 +78,9 @@ export default class Submit extends Command {
*/
const cloudAuth = new CloudAuth();
const hasToken = await cloudAuth.hasToken();
let email = '';
if (!await cloudAuth.hasToken()) {
if (!hasToken) {
const cloudAccount: any = await inquirer.prompt([{
type: 'confirm',
name: 'hasAccount',
......@@ -90,12 +91,10 @@ export default class Submit extends Command {
if (cloudAccount.hasAccount) {
try {
cli.log(chalk.green('*') + ' ' + chalk.gray('waiting for authorization...'));
const r = await cloudAuth.executeAuthFlow();
// tslint:disable-next-line:no-console
console.log('result:', r);
await cloudAuth.executeAuthFlow();
} catch (e) {
// tslint:disable-next-line:no-console
console.log(e);
cli.action.stop('failure to authenticate.');
return;
}
} else {
const result: any = await inquirer.prompt([{
......
......@@ -8,30 +8,40 @@ import { cpu, mem, osInfo, system } from 'systeminformation';
import { v4 as uuidv4 } from 'uuid';
const cloudUrl = 'https://cloud-beta.rocket.chat';
const clientId = '5d8e59c5d48080ef5497e522';
const scope = 'offline_access marketplace';
export interface ICloudToken {
access_token: string;
expires_in: number;
scope: string;
refresh_token: string;
token_type: string;
}
export interface ICloudAuthResult {
token: string;
export interface ICloudAuthStorage {
token: ICloudToken;
expiresAt: Date;
}
export class CloudAuth {
private config: Conf;
private codeVerifier: string;
private port = 3005;
private server: Server;
private redirectUri: string;
constructor() {
this.redirectUri = `http://localhost:${ this.port }/callback`;
this.codeVerifier = uuidv4() + uuidv4();
}
public async executeAuthFlow(): Promise<ICloudAuthResult> {
public async executeAuthFlow(): Promise<string> {
await this.initialize();
return new Promise((resolve, reject) => {
const port = 3005;
try {
this.redirectUri = `http://localhost:${ port }/callback`;
this.server = new Server({ host: 'localhost', port });
this.server = new Server({ host: 'localhost', port: this.port });
this.server.route({
method: 'GET',
path: '/callback',
......@@ -40,7 +50,7 @@ export class CloudAuth {
const code = request.query.code;
const token = await this.fetchToken(code);
resolve({ token });
resolve(token.access_token);
return 'Thank you. You can close this tab.';
} catch (err) {
reject(err);
......@@ -65,35 +75,81 @@ export class CloudAuth {
public async hasToken(): Promise<boolean> {
await this.initialize();
return this.config.has('our.a.token');
return this.config.has('rcc.token.access_token');
}
public async getToken(): Promise<string> {
await this.initialize();
return this.config.get('our.a.token', '') as string;
const item: ICloudAuthStorage = this.config.get('rcc');
if (new Date() < new Date(item.expiresAt)) {
return item.token.access_token;
}
await this.refreshToken();
return this.config.get('rcc.token.access_token', '') as string;
}
private async fetchToken(code: string | Array<string>): Promise<string> {
private async fetchToken(code: string | Array<string>): Promise<ICloudToken> {
try {
const request = {
grant_type: 'authorization_code',
redirect_uri: this.redirectUri,
client_id: '5d8e59c5d48080ef5497e522',
client_id: clientId,
code,
code_verifier: this.codeVerifier,
};
const url = `${ cloudUrl }/api/oauth/token`;
const data = stringify(request);
const res = await axios.post(url, data);
const res = await axios.post(`${ cloudUrl }/api/oauth/token`, stringify(request));
const tokenInfo: ICloudToken = res.data;
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + tokenInfo.expires_in);
const storageItem: ICloudAuthStorage = {
token: tokenInfo,
expiresAt,
};
this.config.set('rcc', storageItem);
return tokenInfo;
} catch (err) {
// tslint:disable-next-line:no-console
console.log(`[${ err.res.statusCode }] error getting token: ${ err.data.error } (${ err.data.requestId })`);
throw err;
}
}
private async refreshToken(): Promise<void> {
const refreshToken = this.config.get('rcc.token.refresh_token', '');
const request = {
client_id: clientId,
refresh_token: refreshToken,
scope,
grant_type: 'refresh_token',
redirect_uri: this.redirectUri,
};
try {
const res = await axios.post(`${ cloudUrl }/api/oauth/token`, stringify(request));
const tokenInfo: ICloudToken = res.data;
this.config.set('our.a.token', res.data);
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + tokenInfo.expires_in);
return res.data;
this.config.set('rcc.token.access_token', tokenInfo.access_token);
this.config.set('rcc.token.expires_in', tokenInfo.expires_in);
this.config.set('rcc.token.scope', tokenInfo.scope);
this.config.set('rcc.token.token_type', tokenInfo.token_type);
this.config.set('rcc.expiresAt', expiresAt);
} catch (err) {
const d = err.data;
// tslint:disable-next-line:no-console
console.log('error getting token', err);
console.log(`[${ err.res.statusCode }] error getting token refreshed: ${ d.error } (${ d.requestId })`);
throw err;
}
......@@ -101,9 +157,9 @@ export class CloudAuth {
private buildAuthorizeUrl(codeChallenge: string) {
const data = {
client_id: '5d8d40d44c43effadb77a351',
client_id: clientId,
response_type: 'code',
scope: 'offline_access marketplace',
scope,
redirect_uri: this.redirectUri,
state: uuidv4(),
code_challenge_method: 'S256',
......@@ -121,7 +177,7 @@ export class CloudAuth {
}
this.config = new Conf({
projectName: 'Rocket.Chat_apps-cli',
projectName: 'chat.rocket.apps-cli',
encryptionKey: await this.getEncryptionKey(),
});
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment