Skip to content

Commit 364cc56

Browse files
committed
feat(login): user login/auth through API Bridge
1 parent db86b57 commit 364cc56

File tree

3 files changed

+48
-59
lines changed

3 files changed

+48
-59
lines changed

bin/clever.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import * as Application from '../src/models/application.js';
1616
import { AVAILABLE_ZONES } from '../src/models/application.js';
1717
import { EXPERIMENTAL_FEATURES } from '../src/experimental-features.js';
1818
import { getExitOnOption, getOutputFormatOption, getSameCommitPolicyOption } from '../src/command-options.js';
19+
import { getHostAndTokens } from '../src/models/send-to-api.js';
1920
import { getFeatures, conf } from '../src/models/configuration.js';
2021

2122
import * as Addon from '../src/models/addon.js';
@@ -1005,12 +1006,22 @@ async function run () {
10051006
description: 'Revoke an API token',
10061007
args: [args.apiTokenId],
10071008
}, tokens.revoke);
1008-
const tokensCommands = cliparse.command('tokens', {
1009+
let tokensCommands = cliparse.command('tokens', {
10091010
description: `Manage API tokens to query Clever Cloud API from ${conf.AUTH_BRIDGE_HOST}`,
10101011
commands: [apiTokenCreateCommand, apiTokenRevokeCommand],
10111012
privateOptions: [opts.humanJsonOutputFormat],
10121013
}, tokens.list);
10131014

1015+
// If the user is logged in through the API bridge, we remove list/revoke tokens commands
1016+
const data = await getHostAndTokens();
1017+
if (data.tokens.apiToken != null) {
1018+
tokensCommands = cliparse.command('tokens', {
1019+
description: `Manage API tokens to query Clever Cloud API from ${conf.AUTH_BRIDGE_HOST}`,
1020+
commands: [apiTokenCreateCommand],
1021+
privateOptions: [opts.humanJsonOutputFormat],
1022+
}, tokens.showApiTokenUser);
1023+
}
1024+
10141025
// UNLINK COMMAND
10151026
const appUnlinkCommand = cliparse.command('unlink', {
10161027
description: 'Unlink this repo from an existing application',

src/commands/login.js

Lines changed: 32 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,40 @@
1-
import crypto from 'node:crypto';
2-
import util from 'node:util';
3-
41
import colors from 'colors/safe.js';
5-
import open from 'open';
6-
import superagent from 'superagent';
7-
8-
import { Logger } from '../logger.js';
92
import * as User from '../models/user.js';
10-
import { conf, writeOAuthConf } from '../models/configuration.js';
11-
12-
import { getPackageJson } from '../load-package-json.cjs';
13-
14-
const delay = util.promisify(setTimeout);
15-
const pkg = getPackageJson();
163

17-
// 20 random bytes as Base64URL
18-
function randomToken () {
19-
return crypto.randomBytes(20).toString('base64').replace(/\//g, '-').replace(/\+/g, '_').replace(/=/g, '');
20-
}
21-
22-
const POLLING_INTERVAL = 2000;
23-
const POLLING_MAX_TRY_COUNT = 60;
24-
25-
function pollOauthData (url, tryCount = 0) {
26-
27-
if (tryCount >= POLLING_MAX_TRY_COUNT) {
28-
throw new Error('Something went wrong while trying to log you in.');
29-
}
30-
if (tryCount > 1 && tryCount % 10 === 0) {
31-
Logger.println("We're still waiting for the login process (in your browser) to be completed…");
32-
}
33-
34-
return superagent
35-
.get(url)
36-
.send()
37-
.then(({ body }) => body)
38-
.catch(async (e) => {
39-
if (e.status === 404) {
40-
await delay(POLLING_INTERVAL);
41-
return pollOauthData(url, tryCount + 1);
42-
}
43-
throw new Error('Something went wrong while trying to log you in.');
44-
});
45-
}
4+
import { Logger } from '../logger.js';
5+
import { promptEmail, promptPassword } from '../prompt.js';
6+
import { sendToAuthBridge } from '../models/send-to-api.js';
7+
import { writeOAuthConf } from '../models/configuration.js';
8+
import { createApiToken } from '../clever-client/auth-bridge.js';
469

4710
async function loginViaConsole () {
48-
49-
const cliToken = randomToken();
50-
51-
const consoleUrl = new URL(conf.CONSOLE_TOKEN_URL);
52-
consoleUrl.searchParams.set('cli_version', pkg.version);
53-
consoleUrl.searchParams.set('cli_token', cliToken);
54-
55-
const cliPollUrl = new URL(conf.API_HOST);
56-
cliPollUrl.pathname = '/v2/self/cli_tokens';
57-
cliPollUrl.searchParams.set('cli_token', cliToken);
58-
59-
Logger.debug('Try to login to Clever Cloud…');
60-
Logger.println(`Opening ${colors.green(consoleUrl.toString())} in your browser to log you in…`);
61-
await open(consoleUrl.toString(), { wait: false });
62-
63-
return pollOauthData(cliPollUrl.toString());
11+
const dateObject = new Date();
12+
dateObject.setFullYear(dateObject.getFullYear() + 1);
13+
const expirationDate = dateObject;
14+
15+
const name = `Clever Tools - ${dateObject.getTime()}`;
16+
const email = await promptEmail('Enter your email:');
17+
const password = await promptPassword('Enter your password:');
18+
const mfaCode = await promptPassword('Enter your 2FA code (press Enter if none):');
19+
20+
const tokenData = {
21+
email,
22+
password,
23+
mfaCode,
24+
name,
25+
expirationDate: expirationDate.toISOString(),
26+
};
27+
28+
return createApiToken(tokenData).then(sendToAuthBridge).catch((error) => {
29+
const errorCode = error?.cause?.responseBody?.code;
30+
if (errorCode === 'invalid-credential') {
31+
throw new Error('Invalid credentials, check your password');
32+
}
33+
if (errorCode === 'invalid-mfa-code') {
34+
throw new Error('Invalid credentials, check your 2FA code');
35+
}
36+
throw error;
37+
});
6438
}
6539

6640
export async function login (params) {

src/commands/tokens.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,7 @@ export async function revoke (params) {
135135
function formatDate (dateInput) {
136136
return new Date(dateInput).toISOString().substring(0, 16).replace('T', ' ');
137137
}
138+
139+
export async function showApiTokenUser () {
140+
Logger.println(`You're logged in with an API Token, you can just create a new one with ${colors.blue('clever tokens create')} command`);
141+
}

0 commit comments

Comments
 (0)