Skip to content

Commit 3643c4a

Browse files
committed
Re-worked login to get email, userId, claims directly from token. Switched to ES256 self signed tokens. Added codify edit command
1 parent 90b9578 commit 3643c4a

File tree

11 files changed

+116
-39
lines changed

11 files changed

+116
-39
lines changed

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"ink-gradient": "^3.0.0",
3030
"ink-select-input": "^6.0.0",
3131
"jju": "^1.4.0",
32+
"jose": "^6.1.0",
3233
"jotai": "^2.11.1",
3334
"js-yaml": "^4.1.0",
3435
"js-yaml-source-map": "^0.2.2",
@@ -42,8 +43,8 @@
4243
"semver": "^7.5.4",
4344
"socket.io": "^4.8.1",
4445
"supports-color": "^9.4.0",
45-
"ws": "^8.18.3",
46-
"uuid": "^10.0.0"
46+
"uuid": "^10.0.0",
47+
"ws": "^8.18.3"
4748
},
4849
"description": "Codify allows users to configure settings, install new packages, and automate their systems using code instead of the GUI. Get set up on a new laptop in one click, maintain a Codify file within your project so anyone can get started and never lose your cool apps or favourite settings again.",
4950
"devDependencies": {

src/api/dashboard/index.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const DashboardApiClient = {
1212

1313
const res = await fetch(
1414
`${API_BASE_URL}/api/v1/documents/${id}`,
15-
{ method: 'GET', headers: { 'Content-Type': 'application/json', 'authorization': login.accessToken } },
15+
{ method: 'GET', headers: { 'Content-Type': 'application/json', 'authorization': `Bearer ${login.accessToken}` } },
1616
);
1717

1818
if (!res.ok) {
@@ -24,23 +24,25 @@ export const DashboardApiClient = {
2424
return json;
2525
},
2626

27-
async getDefaultDocumentId(): Promise<string> {
27+
async getDefaultDocumentId(): Promise<null | string> {
2828
const login = LoginHelper.get()?.credentials;
2929
if (!login) {
3030
throw new Error('Not logged in');
3131
}
3232

33-
// const res = await fetch(
34-
// `${API_BASE_URL}/api/v1/documents/default/id`,
35-
// { method: 'GET', headers: { 'Content-Type': 'application/json', 'authorization': login.accessToken } },
36-
// );
33+
console.log('Token', login.accessToken);
3734

38-
// const json = await res.json();
39-
// if (!res.ok) {
40-
// throw new Error(JSON.stringify(json, null, 2));
41-
// }
35+
const res = await fetch(
36+
`${API_BASE_URL}/api/v1/documents/default/id`,
37+
{ method: 'GET', headers: { 'Content-Type': 'application/json', 'authorization': `Bearer ${login.accessToken}` } },
38+
);
39+
40+
const json = await res.json();
41+
if (!res.ok) {
42+
throw new Error(JSON.stringify(json, null, 2));
43+
}
4244

43-
return '1b80818e-5304-4158-80a3-82e17ff2c79e';
45+
return json.defaultDocumentId;
4446
},
4547

4648
async saveDocumentUpdate(id: string, contents: string): Promise<void> {

src/commands/edit.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { BaseCommand } from '../common/base-command.js';
22
import { ConnectOrchestrator } from '../orchestrators/connect.js';
3+
import { EditOrchestrator } from '../orchestrators/edit.js';
4+
import { LoginHelper } from '../connect/login-helper.js';
35

46
export default class Edit extends BaseCommand {
57
static description =
@@ -16,8 +18,10 @@ For more information, visit: https://docs.codifycli.com/commands/validate
1618
]
1719

1820
public async run(): Promise<void> {
19-
const { flags } = await this.parse(Edit)
21+
const { flags } = await this.parse(Edit);
22+
const config = this.config;
23+
24+
await EditOrchestrator.run(config);
2025

21-
// await ConnectOrchestrator.run();
2226
}
2327
}

src/config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@ export const config = {
66
'https://dashboard.codifycli.com',
77
'https://https://codify-dashboard.kevinwang5658.workers.dev',
88
'http://localhost:3000'
9-
]
9+
],
10+
11+
dashboardUrl: 'https://dashboard.codifycli.com',
12+
supabaseUrl: 'https://kdctbvqvqjfquplxhqrm.supabase.co',
1013
}

src/connect/http-routes/handlers/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const router = Router({
99
router.post('/session', (req, res) => {
1010
const { clientId } = req.body;
1111
if (!clientId) {
12-
throw new Error('connectionId is required');
12+
throw new Error('clientId is required');
1313
}
1414

1515
const socketServer = SocketServer.get();

src/connect/login-helper.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
import { createRemoteJWKSet, decodeJwt, jwtVerify } from 'jose';
12
import * as fs from 'node:fs/promises';
23
import * as os from 'node:os';
34
import * as path from 'node:path';
5+
import { config } from '../config.js';
46

57
interface Credentials {
68
accessToken: string;
79
email: string;
810
userId: string;
9-
expiry: string;
11+
expiry: number;
1012
}
1113

14+
const PROJECT_JWKS = createRemoteJWKSet(
15+
new URL(`${config.supabaseUrl}/auth/v1/.well-known/jwks.json`)
16+
)
17+
1218
export class LoginHelper {
1319
private static instance: LoginHelper;
1420

@@ -23,6 +29,8 @@ export class LoginHelper {
2329
}
2430

2531
const credentials = await LoginHelper.read();
32+
33+
console.log('My credentials', credentials);
2634
if (!credentials) {
2735
LoginHelper.instance = new LoginHelper(false);
2836
return LoginHelper.instance;
@@ -41,19 +49,34 @@ export class LoginHelper {
4149
return LoginHelper.instance;
4250
}
4351

44-
static async save(credentials: Credentials) {
52+
static async save(accessToken: string) {
4553
const credentialsPath = path.join(os.homedir(), '.codify', 'credentials.json');
4654
console.log(`Saving credentials to ${credentialsPath}`);
47-
await fs.writeFile(credentialsPath, JSON.stringify(credentials));
55+
await fs.writeFile(credentialsPath, JSON.stringify({ accessToken }));
4856
}
4957

5058
private static async read(): Promise<Credentials | undefined> {
5159
const credentialsPath = path.join(os.homedir(), '.codify', 'credentials.json');
5260
try {
5361
const credentialsStr = await fs.readFile(credentialsPath, 'utf8');
54-
return JSON.parse(credentialsStr);
55-
} catch {
62+
const { accessToken } = JSON.parse(credentialsStr);
63+
64+
await LoginHelper.verifyProjectJWT(accessToken);
65+
const decoded = decodeJwt(accessToken);
66+
67+
return {
68+
accessToken,
69+
email: decoded.email as string,
70+
userId: decoded.sub!,
71+
expiry: decoded.exp!,
72+
}
73+
} catch(e) {
74+
console.error(e);
5675
return undefined;
5776
}
5877
}
78+
79+
private static async verifyProjectJWT(jwt: string) {
80+
return jwtVerify(jwt, PROJECT_JWKS)
81+
}
5982
}

src/connect/socket-server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export class SocketServer {
124124
private handleClientConnected = (ws: WebSocket) => {
125125
const clientId = uuid();
126126
this.mainConnections.set(clientId, ws);
127-
ws.send(JSON.stringify({ key: 'opened', data: { clientId: uuid } }))
127+
ws.send(JSON.stringify({ key: 'opened', data: { clientId } }))
128128

129129
ws.on('close', () => {
130130
this.mainConnections.delete(clientId);

src/orchestrators/connect.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@ import open from 'open';
66

77
import { config } from '../config.js';
88
import router from '../connect/http-routes/router.js';
9+
import { LoginHelper } from '../connect/login-helper.js';
910
import { SocketServer } from '../connect/socket-server.js';
11+
import { LoginOrchestrator } from './login.js';
1012

1113
export class ConnectOrchestrator {
1214
static rootCommand: string;
1315

14-
static async run(oclifConfig: Config) {
16+
static async run(oclifConfig: Config, openBrowser = true, onOpen?: (connectionCode: string) => void) {
17+
const login = LoginHelper.get()?.isLoggedIn;
18+
if (!login) {
19+
await LoginOrchestrator.run();
20+
await LoginHelper.load();
21+
}
22+
1523
this.rootCommand = oclifConfig.options.root;
1624

1725
const connectionSecret = ConnectOrchestrator.tokenGenerate()
@@ -31,11 +39,15 @@ export class ConnectOrchestrator {
3139
throw error;
3240
}
3341

34-
open(`http://localhost:3000/connection/success?code=${connectionSecret}`)
35-
console.log(`Open browser window to store code.
42+
if (openBrowser) {
43+
open(`http://localhost:3000/connection/success?code=${connectionSecret}`)
44+
console.log(`Open browser window to store code.
3645
3746
If unsuccessful manually enter the code:
3847
${connectionSecret}`)
48+
}
49+
50+
onOpen?.(connectionSecret);
3951
});
4052

4153
// const wsManager = WsServerManager.init(server, connectionSecret)

src/orchestrators/edit.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Config } from '@oclif/core';
2+
import { randomBytes } from 'node:crypto';
3+
import open from 'open';
4+
5+
import { DashboardApiClient } from '../api/dashboard/index.js';
6+
import { config } from '../config.js';
7+
import { ConnectOrchestrator } from './connect.js';
8+
import { LoginHelper } from '../connect/login-helper.js';
9+
import { LoginOrchestrator } from './login.js';
10+
11+
export class EditOrchestrator {
12+
static rootCommand: string;
13+
14+
static async run(oclifConfig: Config) {
15+
const login = LoginHelper.get()?.isLoggedIn;
16+
if (!login) {
17+
await LoginOrchestrator.run();
18+
await LoginHelper.load();
19+
}
20+
21+
const defaultDocumentId = await DashboardApiClient.getDefaultDocumentId();
22+
const url = defaultDocumentId
23+
? `${config.dashboardUrl}/file/${defaultDocumentId}`
24+
: config.dashboardUrl;
25+
26+
await ConnectOrchestrator.run(oclifConfig, false, (code) => {
27+
open(`${url}?connection_code=${code}`);
28+
});
29+
}
30+
}

0 commit comments

Comments
 (0)