@deploily/deploily-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/devcontainer.json +25 -0
- package/.devcontainer/docker-compose.yml +18 -0
- package/.env.example +8 -0
- package/.github/dependabot.yml +12 -0
- package/.github/workflows/publish.yml +39 -0
- package/LICENSE +201 -0
- package/README.md +105 -0
- package/dist/auth/callback.d.ts +3 -0
- package/dist/auth/callback.d.ts.map +1 -0
- package/dist/auth/callback.js +143 -0
- package/dist/auth/callback.js.map +1 -0
- package/dist/auth/index.d.ts +23 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +173 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/commands/auth.d.ts +4 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +90 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/config/constants.d.ts +22 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +25 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/credentials.d.ts +47 -0
- package/dist/storage/credentials.d.ts.map +1 -0
- package/dist/storage/credentials.js +160 -0
- package/dist/storage/credentials.js.map +1 -0
- package/dist/utils/pkce.d.ts +5 -0
- package/dist/utils/pkce.d.ts.map +1 -0
- package/dist/utils/pkce.js +30 -0
- package/dist/utils/pkce.js.map +1 -0
- package/package.json +37 -0
- package/pnpm-workspace.yaml +5 -0
- package/src/auth/callback.ts +159 -0
- package/src/auth/index.ts +238 -0
- package/src/commands/auth.ts +114 -0
- package/src/config/constants.ts +28 -0
- package/src/index.ts +69 -0
- package/src/storage/credentials.ts +188 -0
- package/src/types/index.d.ts +35 -0
- package/src/utils/pkce.ts +36 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { generateCodeVerifier, generateCodeChallenge, generateState, } from "../utils/pkce.js";
|
|
3
|
+
import { AUTH_CONFIG } from "../config/constants.js";
|
|
4
|
+
import { credentialStorage } from "../storage/credentials.js";
|
|
5
|
+
class AuthenticationService {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.client = axios.create({
|
|
8
|
+
timeout: 10000,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
buildClientAuthHeaders() {
|
|
12
|
+
const clientSecret = process.env.KEYCLOAK_CLIENT_SECRET;
|
|
13
|
+
if (!clientSecret) {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
const clientId = process.env.KEYCLOAK_CLIENT_ID;
|
|
17
|
+
const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
18
|
+
return {
|
|
19
|
+
Authorization: `Basic ${basicAuth}`,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// Generate authorization URL for user login
|
|
23
|
+
generateAuthorizationUrl() {
|
|
24
|
+
const state = generateState();
|
|
25
|
+
const codeVerifier = generateCodeVerifier();
|
|
26
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
27
|
+
const params = new URLSearchParams({
|
|
28
|
+
client_id: AUTH_CONFIG.CLIENT_ID,
|
|
29
|
+
response_type: AUTH_CONFIG.RESPONSE_TYPE,
|
|
30
|
+
redirect_uri: AUTH_CONFIG.REDIRECT_URI,
|
|
31
|
+
scope: AUTH_CONFIG.SCOPE,
|
|
32
|
+
code_challenge: codeChallenge,
|
|
33
|
+
code_challenge_method: AUTH_CONFIG.CODE_CHALLENGE_METHOD,
|
|
34
|
+
state,
|
|
35
|
+
});
|
|
36
|
+
const url = `${AUTH_CONFIG.KEYCLOAK_AUTH_ENDPOINT}?${params.toString()}`;
|
|
37
|
+
return {
|
|
38
|
+
url,
|
|
39
|
+
context: { state, codeVerifier, codeChallenge },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Exchange authorization code for tokens
|
|
43
|
+
async exchangeCodeForToken(code, codeVerifier) {
|
|
44
|
+
try {
|
|
45
|
+
const body = new URLSearchParams({
|
|
46
|
+
grant_type: "authorization_code",
|
|
47
|
+
code,
|
|
48
|
+
client_id: process.env.KEYCLOAK_CLIENT_ID,
|
|
49
|
+
redirect_uri: process.env.KEYCLOAK_REDIRECT_URI,
|
|
50
|
+
code_verifier: codeVerifier,
|
|
51
|
+
});
|
|
52
|
+
const response = await this.client.post(process.env.KEYCLOAK_TOKEN_ENDPOINT, body, {
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
55
|
+
...this.buildClientAuthHeaders(),
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
return response.data;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (axios.isAxiosError(error)) {
|
|
62
|
+
const responseData = typeof error.response?.data === "string"
|
|
63
|
+
? error.response.data
|
|
64
|
+
: JSON.stringify(error.response?.data ?? {});
|
|
65
|
+
const authHint = error.response?.status === 401 &&
|
|
66
|
+
responseData.includes("unauthorized_client")
|
|
67
|
+
? " This usually means the Keycloak client is confidential and KEYCLOAK_CLIENT_SECRET is missing or invalid."
|
|
68
|
+
: "";
|
|
69
|
+
throw new Error(`Failed to exchange code for token: ${error.message}${responseData ? ` (${responseData})` : ""}${authHint}`);
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`Failed to exchange code for token: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Refresh access token using refresh token
|
|
75
|
+
async refreshAccessToken(refreshToken) {
|
|
76
|
+
try {
|
|
77
|
+
const body = new URLSearchParams({
|
|
78
|
+
grant_type: "refresh_token",
|
|
79
|
+
refresh_token: refreshToken,
|
|
80
|
+
client_id: process.env.KEYCLOAK_CLIENT_ID,
|
|
81
|
+
});
|
|
82
|
+
const response = await this.client.post(process.env.KEYCLOAK_TOKEN_ENDPOINT, body, {
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
85
|
+
...this.buildClientAuthHeaders(),
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return response.data;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
throw new Error(`Failed to refresh token: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Get user information from access token
|
|
95
|
+
async getUserInfo(accessToken) {
|
|
96
|
+
try {
|
|
97
|
+
const response = await this.client.get(process.env.KEYCLOAK_USERINFO_ENDPOINT, {
|
|
98
|
+
headers: {
|
|
99
|
+
Authorization: `Bearer ${accessToken}`,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
return response.data;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw new Error(`Failed to fetch user info: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Store tokens after successful authentication
|
|
109
|
+
async storeTokens(token, userEmail) {
|
|
110
|
+
const expiresAt = Math.floor(Date.now() / 1000) + token.expires_in;
|
|
111
|
+
const credentials = {
|
|
112
|
+
access_token: token.access_token,
|
|
113
|
+
refresh_token: token.refresh_token,
|
|
114
|
+
expires_at: expiresAt,
|
|
115
|
+
user_email: userEmail,
|
|
116
|
+
};
|
|
117
|
+
await credentialStorage.save(credentials);
|
|
118
|
+
}
|
|
119
|
+
// Get valid access token
|
|
120
|
+
async getValidAccessToken() {
|
|
121
|
+
const credentials = await credentialStorage.load();
|
|
122
|
+
if (!credentials) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
// Check if token is expired
|
|
126
|
+
if (credentials.expires_at < Date.now() / 1000) {
|
|
127
|
+
try {
|
|
128
|
+
const newToken = await this.refreshAccessToken(credentials.refresh_token);
|
|
129
|
+
await this.storeTokens(newToken, credentials.user_email);
|
|
130
|
+
return newToken.access_token;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
// Token refresh failed, need to re-authenticate
|
|
134
|
+
await credentialStorage.clear();
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return credentials.access_token;
|
|
139
|
+
}
|
|
140
|
+
// Logout and clear stored tokens
|
|
141
|
+
async logout() {
|
|
142
|
+
const credentials = await credentialStorage.load();
|
|
143
|
+
if (credentials) {
|
|
144
|
+
try {
|
|
145
|
+
const body = new URLSearchParams({
|
|
146
|
+
client_id: process.env.KEYCLOAK_CLIENT_ID,
|
|
147
|
+
refresh_token: credentials.refresh_token,
|
|
148
|
+
});
|
|
149
|
+
// Optionally call Keycloak logout endpoint
|
|
150
|
+
await this.client.post(process.env.KEYCLOAK_LOGOUT_ENDPOINT, body, {
|
|
151
|
+
headers: {
|
|
152
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
153
|
+
...this.buildClientAuthHeaders(),
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
// Continue with local logout even if server call fails
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Clear local credentials
|
|
162
|
+
await credentialStorage.clear();
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if user is authenticated
|
|
166
|
+
*/
|
|
167
|
+
async isAuthenticated() {
|
|
168
|
+
const credentials = await credentialStorage.load();
|
|
169
|
+
return credentials !== null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export const authService = new AuthenticationService();
|
|
173
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAMA,wBAAsB,WAAW,kBAyChC;AAED,wBAAsB,YAAY,kBAejC;AAED,wBAAsB,YAAY,kBA+CjC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import open from "open";
|
|
2
|
+
import { authService } from "../auth/index.js";
|
|
3
|
+
import { startCallbackServer } from "../auth/callback.js";
|
|
4
|
+
import { credentialStorage } from "../storage/credentials.js";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
export async function handleLogin() {
|
|
7
|
+
try {
|
|
8
|
+
// 1. Check if logged in
|
|
9
|
+
const isAuth = await authService.isAuthenticated();
|
|
10
|
+
if (isAuth) {
|
|
11
|
+
console.log("Already logged in. Use 'deploily logout' to log out first.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
console.log("Opening browser for authentication...");
|
|
15
|
+
// 2. Generate authorization URL
|
|
16
|
+
const { url, context } = authService.generateAuthorizationUrl();
|
|
17
|
+
// 3. Start callback server
|
|
18
|
+
const callbackPromise = startCallbackServer(context.state);
|
|
19
|
+
// 4. Open browser
|
|
20
|
+
await open(url);
|
|
21
|
+
console.log("Waiting for authentication...");
|
|
22
|
+
// 5. Wait for callback
|
|
23
|
+
const callback = await callbackPromise;
|
|
24
|
+
// 6. Exchange code for token
|
|
25
|
+
const tokens = await authService.exchangeCodeForToken(callback.code, context.codeVerifier);
|
|
26
|
+
// 7. Get user info
|
|
27
|
+
const userInfo = await authService.getUserInfo(tokens.access_token);
|
|
28
|
+
// 8. Store tokens
|
|
29
|
+
await authService.storeTokens(tokens, userInfo.email);
|
|
30
|
+
console.log("Successfully authenticated");
|
|
31
|
+
console.log(`Welcome, ${userInfo.email}!`);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
console.error("Error occurred while logging in:", error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function handleLogout() {
|
|
39
|
+
try {
|
|
40
|
+
const isAuth = await authService.isAuthenticated();
|
|
41
|
+
if (!isAuth) {
|
|
42
|
+
console.log("You are not logged in.");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
await authService.logout();
|
|
46
|
+
console.log("Successfully logged out");
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error("Error occurred while logging in:", error);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export async function handleWhoami() {
|
|
54
|
+
try {
|
|
55
|
+
const isAuth = await authService.isAuthenticated();
|
|
56
|
+
if (!isAuth) {
|
|
57
|
+
console.log("You are not logged in. Use `deploily login` to authenticate.");
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
// Get stored credentials
|
|
61
|
+
const credentials = await credentialStorage.load();
|
|
62
|
+
if (!credentials) {
|
|
63
|
+
console.log("You are not logged in. Use `deploily login` to authenticate.");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
// Get user info from API
|
|
67
|
+
const userInfo = await authService.getUserInfo(credentials.access_token);
|
|
68
|
+
console.log("Current User:");
|
|
69
|
+
console.log(`├─ Email: ${userInfo.email}`);
|
|
70
|
+
console.log(`├─ Name: ${userInfo.name}`);
|
|
71
|
+
console.log(`├─ Username: ${userInfo.preferred_username}`);
|
|
72
|
+
// Calculate token expiry
|
|
73
|
+
const expiresAt = new Date(credentials.expires_at * 1000);
|
|
74
|
+
const now = new Date();
|
|
75
|
+
const timeRemaining = Math.floor((credentials.expires_at * 1000 - now.getTime()) / 1000);
|
|
76
|
+
if (timeRemaining > 0) {
|
|
77
|
+
const hours = Math.floor(timeRemaining / 3600);
|
|
78
|
+
const minutes = Math.floor((timeRemaining % 3600) / 60);
|
|
79
|
+
console.log(chalk.gray(`└─ Token expires in: ${hours}h ${minutes}m`));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
console.log(chalk.gray(`└─ Token expires in: Soon (will auto-refresh)`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error("Error occurred while logging in:", error);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
export declare const AUTH_CONFIG: {
|
|
3
|
+
KEYCLOAK_REALM_URL: string;
|
|
4
|
+
KEYCLOAK_TOKEN_ENDPOINT: string;
|
|
5
|
+
KEYCLOAK_AUTH_ENDPOINT: string;
|
|
6
|
+
KEYCLOAK_LOGOUT_ENDPOINT: string;
|
|
7
|
+
KEYCLOAK_USERINFO_ENDPOINT: string;
|
|
8
|
+
CLIENT_ID: string;
|
|
9
|
+
CLIENT_SECRET: string;
|
|
10
|
+
REDIRECT_URI: string;
|
|
11
|
+
CALLBACK_PORT: number;
|
|
12
|
+
SCOPE: string;
|
|
13
|
+
RESPONSE_TYPE: string;
|
|
14
|
+
CODE_CHALLENGE_METHOD: string;
|
|
15
|
+
TOKEN_EXPIRY_BUFFER: number;
|
|
16
|
+
};
|
|
17
|
+
export declare const STORAGE_CONFIG: {
|
|
18
|
+
SERVICE_NAME: string;
|
|
19
|
+
CONFIG_DIR: string;
|
|
20
|
+
CONFIG_FILE: string;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;CAmBvB,CAAA;AAED,eAAO,MAAM,cAAc;;;;CAI1B,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
export const AUTH_CONFIG = {
|
|
3
|
+
// Keycloak Configuration
|
|
4
|
+
KEYCLOAK_REALM_URL: process.env.KEYCLOAK_REALM_URL,
|
|
5
|
+
KEYCLOAK_TOKEN_ENDPOINT: process.env.KEYCLOAK_TOKEN_ENDPOINT,
|
|
6
|
+
KEYCLOAK_AUTH_ENDPOINT: process.env.KEYCLOAK_AUTH_ENDPOINT,
|
|
7
|
+
KEYCLOAK_LOGOUT_ENDPOINT: process.env.KEYCLOAK_LOGOUT_ENDPOINT,
|
|
8
|
+
KEYCLOAK_USERINFO_ENDPOINT: process.env.KEYCLOAK_USERINFO_ENDPOINT,
|
|
9
|
+
// CLI Configuration
|
|
10
|
+
CLIENT_ID: process.env.KEYCLOAK_CLIENT_ID,
|
|
11
|
+
CLIENT_SECRET: process.env.KEYCLOAK_CLIENT_SECRET,
|
|
12
|
+
REDIRECT_URI: process.env.KEYCLOAK_REDIRECT_URI,
|
|
13
|
+
CALLBACK_PORT: 8976,
|
|
14
|
+
SCOPE: "openid profile email",
|
|
15
|
+
RESPONSE_TYPE: "code",
|
|
16
|
+
CODE_CHALLENGE_METHOD: "S256",
|
|
17
|
+
// Token Configuration
|
|
18
|
+
TOKEN_EXPIRY_BUFFER: 60,
|
|
19
|
+
};
|
|
20
|
+
export const STORAGE_CONFIG = {
|
|
21
|
+
SERVICE_NAME: "deploily-cli",
|
|
22
|
+
CONFIG_DIR: "~/.config/deploily",
|
|
23
|
+
CONFIG_FILE: "config.json",
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,yBAAyB;IACzB,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAmB;IACnD,uBAAuB,EAAE,OAAO,CAAC,GAAG,CAAC,uBAAwB;IAC7D,sBAAsB,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAuB;IAC3D,wBAAwB,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAyB;IAC/D,0BAA0B,EAAE,OAAO,CAAC,GAAG,CAAC,0BAA2B;IAEnE,oBAAoB;IACpB,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAmB;IAC1C,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAuB;IAClD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAsB;IAChD,aAAa,EAAE,IAAI;IACnB,KAAK,EAAE,sBAAsB;IAC7B,aAAa,EAAE,MAAM;IACrB,qBAAqB,EAAE,MAAM;IAE7B,sBAAsB;IACtB,mBAAmB,EAAE,EAAE;CACxB,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,YAAY,EAAE,cAAc;IAC5B,UAAU,EAAE,oBAAoB;IAChC,WAAW,EAAE,aAAa;CAC3B,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { handleLogin, handleLogout, handleWhoami } from "./commands/auth.js";
|
|
3
|
+
const VERSION = "1.0.0"; // TODO: read from package.json
|
|
4
|
+
async function main() {
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const command = args[0];
|
|
7
|
+
if (!command || command === "--help" || command === "-h") {
|
|
8
|
+
showHelp();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (command === "--version" || command === "-v") {
|
|
12
|
+
console.log(`deploily version ${VERSION}`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
switch (command) {
|
|
17
|
+
case "login":
|
|
18
|
+
await handleLogin();
|
|
19
|
+
break;
|
|
20
|
+
case "logout":
|
|
21
|
+
await handleLogout();
|
|
22
|
+
break;
|
|
23
|
+
case "whoami":
|
|
24
|
+
await handleWhoami();
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
console.error(`Unknown command: ${command}`);
|
|
28
|
+
showHelp();
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
console.error(`Error executing command '${command}':`, err);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function showHelp() {
|
|
38
|
+
console.log(`
|
|
39
|
+
Usage:
|
|
40
|
+
deploily [command]
|
|
41
|
+
|
|
42
|
+
Commands:
|
|
43
|
+
login Authenticate with Keycloak
|
|
44
|
+
logout Clear authentication tokens
|
|
45
|
+
whoami Show current user information
|
|
46
|
+
|
|
47
|
+
Options:
|
|
48
|
+
--help, -h Show this help message
|
|
49
|
+
--version Show version number
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
deploily login
|
|
53
|
+
deploily logout
|
|
54
|
+
deploily whoami
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
main().catch((err) => {
|
|
58
|
+
console.error(err);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAE5E,MAAM,OAAO,GAAG,OAAO,CAAA,CAAC,+BAA+B;AAEvD,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAClC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IAEvB,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACzD,QAAQ,EAAE,CAAA;QACV,OAAM;IACR,CAAC;IAED,IAAI,OAAO,KAAK,WAAW,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAA;QAC1C,OAAM;IACR,CAAC;IAED,IAAI,CAAC;QACH,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,OAAO;gBACV,MAAM,WAAW,EAAE,CAAA;gBACnB,MAAK;YAEP,KAAK,QAAQ;gBACX,MAAM,YAAY,EAAE,CAAA;gBACpB,MAAK;YAEP,KAAK,QAAQ;gBACX,MAAM,YAAY,EAAE,CAAA;gBACpB,MAAK;YAEP;gBACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAA;gBAC5C,QAAQ,EAAE,CAAA;gBACV,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACnB,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,OAAO,IAAI,EAAE,GAAG,CAAC,CAAA;QAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;CAiBb,CAAC,CAAA;AACF,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { StoredCredentials } from "../types/index.js";
|
|
2
|
+
declare class CredentialStorage {
|
|
3
|
+
private serviceName;
|
|
4
|
+
private accountName;
|
|
5
|
+
private configDir;
|
|
6
|
+
private configFilePath;
|
|
7
|
+
constructor();
|
|
8
|
+
/**
|
|
9
|
+
* Expand ~ to home directory
|
|
10
|
+
*/
|
|
11
|
+
private expandPath;
|
|
12
|
+
/**
|
|
13
|
+
* Save credentials using keytar (primary) or fallback to config file
|
|
14
|
+
*/
|
|
15
|
+
save(credentials: StoredCredentials): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Load credentials using keytar (primary) or fallback to config file
|
|
18
|
+
*/
|
|
19
|
+
load(): Promise<StoredCredentials | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Clear credentials from keytar (primary) and config file
|
|
22
|
+
*/
|
|
23
|
+
clear(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Save to config file (fallback method)
|
|
26
|
+
*/
|
|
27
|
+
private saveToConfigFile;
|
|
28
|
+
/**
|
|
29
|
+
* Load from config file (fallback method)
|
|
30
|
+
*/
|
|
31
|
+
private loadFromConfigFile;
|
|
32
|
+
/**
|
|
33
|
+
* Clear config file
|
|
34
|
+
*/
|
|
35
|
+
private clearConfigFile;
|
|
36
|
+
/**
|
|
37
|
+
* Check if credentials exist
|
|
38
|
+
*/
|
|
39
|
+
exists(): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Check if credentials are expired
|
|
42
|
+
*/
|
|
43
|
+
isExpired(): Promise<boolean>;
|
|
44
|
+
}
|
|
45
|
+
export declare const credentialStorage: CredentialStorage;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/storage/credentials.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAoBrD,cAAM,iBAAiB;IACrB,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,cAAc,CAAQ;;IAO9B;;OAEG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACG,IAAI,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBzD;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAsB/C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B;;OAEG;YACW,gBAAgB;IAmB9B;;OAEG;YACW,kBAAkB;IAahC;;OAEG;YACW,eAAe;IAU7B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAKhC;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAUpC;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAA"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import { STORAGE_CONFIG } from "../config/constants.js";
|
|
5
|
+
// Lazy load keytar - it's optional and may not be available
|
|
6
|
+
let keytar = null;
|
|
7
|
+
let keytarLoaded = false;
|
|
8
|
+
async function getKeytar() {
|
|
9
|
+
if (keytarLoaded) {
|
|
10
|
+
return keytar;
|
|
11
|
+
}
|
|
12
|
+
keytarLoaded = true;
|
|
13
|
+
try {
|
|
14
|
+
keytar = await import("keytar");
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
// keytar not available, will use fallback
|
|
18
|
+
keytar = null;
|
|
19
|
+
}
|
|
20
|
+
return keytar;
|
|
21
|
+
}
|
|
22
|
+
class CredentialStorage {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.serviceName = STORAGE_CONFIG.SERVICE_NAME;
|
|
25
|
+
this.accountName = "deploily-auth";
|
|
26
|
+
this.configDir = this.expandPath(STORAGE_CONFIG.CONFIG_DIR);
|
|
27
|
+
this.configFilePath = path.join(this.configDir, STORAGE_CONFIG.CONFIG_FILE);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Expand ~ to home directory
|
|
31
|
+
*/
|
|
32
|
+
expandPath(filepath) {
|
|
33
|
+
if (filepath.startsWith("~")) {
|
|
34
|
+
return path.join(os.homedir(), filepath.slice(1));
|
|
35
|
+
}
|
|
36
|
+
return filepath;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Save credentials using keytar (primary) or fallback to config file
|
|
40
|
+
*/
|
|
41
|
+
async save(credentials) {
|
|
42
|
+
const kt = await getKeytar();
|
|
43
|
+
try {
|
|
44
|
+
// Try keytar first
|
|
45
|
+
if (kt) {
|
|
46
|
+
await kt.setPassword(this.serviceName, this.accountName, JSON.stringify(credentials));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
// Fallback to config file
|
|
52
|
+
}
|
|
53
|
+
// Fallback to config file
|
|
54
|
+
console.warn("Keytar unavailable, using config file for token storage");
|
|
55
|
+
await this.saveToConfigFile(credentials);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Load credentials using keytar (primary) or fallback to config file
|
|
59
|
+
*/
|
|
60
|
+
async load() {
|
|
61
|
+
const kt = await getKeytar();
|
|
62
|
+
try {
|
|
63
|
+
// Try keytar first
|
|
64
|
+
if (kt) {
|
|
65
|
+
const password = await kt.getPassword(this.serviceName, this.accountName);
|
|
66
|
+
if (password) {
|
|
67
|
+
return JSON.parse(password);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// Fallback to config file
|
|
73
|
+
console.warn("Keytar unavailable, checking config file");
|
|
74
|
+
}
|
|
75
|
+
// Try loading from config file
|
|
76
|
+
return await this.loadFromConfigFile();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Clear credentials from keytar (primary) and config file
|
|
80
|
+
*/
|
|
81
|
+
async clear() {
|
|
82
|
+
const kt = await getKeytar();
|
|
83
|
+
try {
|
|
84
|
+
// Clear from keytar
|
|
85
|
+
if (kt) {
|
|
86
|
+
await kt.deletePassword(this.serviceName, this.accountName);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
// Fallback to config file
|
|
91
|
+
console.warn("Could not clear keytar credentials");
|
|
92
|
+
}
|
|
93
|
+
// Clear from config file
|
|
94
|
+
await this.clearConfigFile();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Save to config file (fallback method)
|
|
98
|
+
*/
|
|
99
|
+
async saveToConfigFile(credentials) {
|
|
100
|
+
try {
|
|
101
|
+
// Ensure config directory exists
|
|
102
|
+
if (!fs.existsSync(this.configDir)) {
|
|
103
|
+
fs.mkdirSync(this.configDir, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
fs.writeFileSync(this.configFilePath, JSON.stringify(credentials, null, 2), { mode: 0o600 });
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
throw new Error(`Failed to save credentials: ${error}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Load from config file (fallback method)
|
|
113
|
+
*/
|
|
114
|
+
async loadFromConfigFile() {
|
|
115
|
+
try {
|
|
116
|
+
if (!fs.existsSync(this.configFilePath)) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const content = fs.readFileSync(this.configFilePath, "utf-8");
|
|
120
|
+
return JSON.parse(content);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Clear config file
|
|
128
|
+
*/
|
|
129
|
+
async clearConfigFile() {
|
|
130
|
+
try {
|
|
131
|
+
if (fs.existsSync(this.configFilePath)) {
|
|
132
|
+
fs.unlinkSync(this.configFilePath);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// Silently fail if file doesn't exist
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Check if credentials exist
|
|
141
|
+
*/
|
|
142
|
+
async exists() {
|
|
143
|
+
const credentials = await this.load();
|
|
144
|
+
return credentials !== null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check if credentials are expired
|
|
148
|
+
*/
|
|
149
|
+
async isExpired() {
|
|
150
|
+
const credentials = await this.load();
|
|
151
|
+
if (!credentials) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
// Check if token expires in less than 5 minutes
|
|
155
|
+
const bufferSeconds = 300;
|
|
156
|
+
return credentials.expires_at < Date.now() / 1000 + bufferSeconds;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export const credentialStorage = new CredentialStorage();
|
|
160
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/storage/credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function generateRandomString(length?: number): string;
|
|
2
|
+
export declare function generateCodeVerifier(): string;
|
|
3
|
+
export declare function generateCodeChallenge(codeVerifier: string): string;
|
|
4
|
+
export declare function generateState(): string;
|
|
5
|
+
//# sourceMappingURL=pkce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../src/utils/pkce.ts"],"names":[],"mappings":"AAGA,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM,CAEhE;AAGD,wBAAgB,oBAAoB,IAAI,MAAM,CAY7C;AAGD,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAOlE;AAGD,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|