@deploily/deploily-cli 0.1.0 → 0.1.2
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/.env.example +14 -7
- package/.github/workflows/publish.yml +20 -11
- package/README.md +105 -56
- package/dist/auth/index.js +10 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +19 -8
- package/dist/config/constants.js.map +1 -1
- package/dist/index.js +0 -0
- package/package.json +3 -3
- package/pnpm-workspace.yaml +0 -2
- package/src/auth/index.ts +11 -11
- package/src/config/constants.ts +28 -8
package/.env.example
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
# Optional overrides. The CLI defaults to the public Deploily Keycloak instance.
|
|
2
|
+
# No .env file is required for normal use.
|
|
3
|
+
|
|
4
|
+
KEYCLOAK_URL="http://localhost:8080"
|
|
5
|
+
KEYCLOAK_REALM="deploily"
|
|
2
6
|
KEYCLOAK_CLIENT_ID="deploily"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
KEYCLOAK_REDIRECT_URI="http://localhost:8080/callback"
|
|
8
|
+
KEYCLOAK_CLIENT_SECRET=""
|
|
9
|
+
|
|
10
|
+
# Advanced overrides (normally not needed)
|
|
11
|
+
# KEYCLOAK_REALM_URL="https://auth.deploily.cloud/realms/deploily"
|
|
12
|
+
# KEYCLOAK_AUTH_ENDPOINT="https://auth.deploily.cloud/realms/deploily/protocol/openid-connect/auth"
|
|
13
|
+
# KEYCLOAK_TOKEN_ENDPOINT="https://auth.deploily.cloud/realms/deploily/protocol/openid-connect/token"
|
|
14
|
+
# KEYCLOAK_LOGOUT_ENDPOINT="https://auth.deploily.cloud/realms/deploily/protocol/openid-connect/logout"
|
|
15
|
+
# KEYCLOAK_USERINFO_ENDPOINT="https://auth.deploily.cloud/realms/deploily/protocol/openid-connect/userinfo"
|
|
@@ -10,30 +10,39 @@ jobs:
|
|
|
10
10
|
publish:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
if: github.repository_owner == 'deploily'
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
id-token: write
|
|
16
|
+
env:
|
|
17
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET_TOKEN }}
|
|
13
18
|
|
|
14
19
|
steps:
|
|
15
20
|
- name: Checkout repository
|
|
16
21
|
uses: actions/checkout@v4
|
|
17
22
|
|
|
23
|
+
- name: Set up pnpm
|
|
24
|
+
uses: pnpm/action-setup@v4
|
|
25
|
+
|
|
18
26
|
- name: Set up Node.js
|
|
19
27
|
uses: actions/setup-node@v4
|
|
20
28
|
with:
|
|
21
|
-
node-version:
|
|
29
|
+
node-version: 24
|
|
30
|
+
cache: pnpm
|
|
22
31
|
registry-url: "https://registry.npmjs.org/"
|
|
23
32
|
|
|
24
33
|
- name: Install dependencies
|
|
25
|
-
run:
|
|
34
|
+
run: pnpm install --frozen-lockfile
|
|
35
|
+
|
|
36
|
+
- name: Build project
|
|
37
|
+
run: pnpm run build
|
|
26
38
|
|
|
27
|
-
- name:
|
|
39
|
+
- name: Verify npm authentication
|
|
28
40
|
run: |
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
run: npm whoami
|
|
35
|
-
env:
|
|
36
|
-
NPM_SECRET_TOKEN: ${{ secrets.NPM_SECRET_TOKEN }}
|
|
41
|
+
printf "//registry.npmjs.org/:_authToken=%s\n" "${NODE_AUTH_TOKEN}" > ~/.npmrc
|
|
42
|
+
npm whoami
|
|
43
|
+
|
|
44
|
+
- name: Dry-run publish
|
|
45
|
+
run: npm publish --access public --dry-run
|
|
37
46
|
|
|
38
47
|
- name: Publish package
|
|
39
48
|
run: npm publish --access public
|
package/README.md
CHANGED
|
@@ -1,69 +1,58 @@
|
|
|
1
1
|
# deploily-cli
|
|
2
2
|
|
|
3
|
-
Deploily CLI is a
|
|
3
|
+
Deploily CLI is a Node.js command-line tool for authenticating against a Keycloak-backed Deploily environment.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- OAuth2 login flow with PKCE
|
|
8
8
|
- Local callback server for browser-based authentication
|
|
9
|
-
- Secure credential storage via `keytar` with file fallback
|
|
9
|
+
- Secure credential storage via `keytar` with a file fallback
|
|
10
10
|
- `login`, `logout`, and `whoami` commands
|
|
11
11
|
- TypeScript + native ESM project setup
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Section 1: Using the CLI
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
- pnpm
|
|
17
|
-
- A running Keycloak instance
|
|
18
|
-
|
|
19
|
-
## Quick Start
|
|
20
|
-
|
|
21
|
-
1. Install dependencies:
|
|
15
|
+
This is the recommended section for developers who want to install and use the published CLI without contributing to the repository.
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
pnpm install
|
|
25
|
-
```
|
|
17
|
+
### Requirements
|
|
26
18
|
|
|
27
|
-
|
|
19
|
+
- Node.js 20+
|
|
20
|
+
- npm (or pnpm)
|
|
21
|
+
- A browser for the OAuth login flow
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
docker compose up -d
|
|
31
|
-
```
|
|
23
|
+
### Default behavior
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
- Create a new realm
|
|
35
|
-
- Create a new client and add `http://localhost:8976/callback` as a valid redirect URI
|
|
36
|
-
- Create a new user and assign them to the realm
|
|
25
|
+
The CLI connects to the public Deploily Keycloak instance by default:
|
|
37
26
|
|
|
38
|
-
|
|
27
|
+
- URL: `https://auth.deploily.cloud`
|
|
28
|
+
- realm: `deploily`
|
|
29
|
+
- client ID: `deploily`
|
|
39
30
|
|
|
40
|
-
|
|
31
|
+
No `.env` file is required for standard use. If you need to override the defaults for a custom environment, you can set `KEYCLOAK_URL`, `KEYCLOAK_REALM`, or `KEYCLOAK_CLIENT_ID`.
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
pnpm build
|
|
44
|
-
node dist/index.js --help
|
|
45
|
-
```
|
|
33
|
+
### Install from npm
|
|
46
34
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
export PATH="$HOME/.local/share/pnpm/bin:$PATH"
|
|
51
|
-
pnpm setup
|
|
52
|
-
pnpm link --global .
|
|
53
|
-
deploily --help
|
|
54
|
-
```
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g @deploily/deploily-cli
|
|
37
|
+
```
|
|
55
38
|
|
|
56
|
-
|
|
39
|
+
Or run it without installing globally:
|
|
57
40
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
41
|
+
```bash
|
|
42
|
+
npx @deploily/deploily-cli --help
|
|
43
|
+
```
|
|
61
44
|
|
|
62
|
-
|
|
45
|
+
### Basic usage
|
|
63
46
|
|
|
64
|
-
|
|
47
|
+
```bash
|
|
48
|
+
deploily --help
|
|
49
|
+
deploily --version
|
|
50
|
+
deploily login
|
|
51
|
+
deploily logout
|
|
52
|
+
deploily whoami
|
|
53
|
+
```
|
|
65
54
|
|
|
66
|
-
|
|
55
|
+
### Commands
|
|
67
56
|
|
|
68
57
|
```bash
|
|
69
58
|
deploily login
|
|
@@ -73,28 +62,69 @@ deploily --help
|
|
|
73
62
|
deploily --version
|
|
74
63
|
```
|
|
75
64
|
|
|
76
|
-
###
|
|
65
|
+
### Authentication flow
|
|
77
66
|
|
|
78
|
-
|
|
67
|
+
1. The CLI uses the default Keycloak base URL `https://auth.deploily.cloud` when no environment variables are set.
|
|
68
|
+
2. It uses the `deploily` realm and the `deploily` client ID by default.
|
|
69
|
+
3. It opens the Keycloak authorization URL in your browser.
|
|
70
|
+
4. Keycloak redirects back to the local callback server on port `8976`.
|
|
71
|
+
5. The CLI exchanges the authorization code for tokens.
|
|
72
|
+
6. User info is fetched from the Keycloak userinfo endpoint.
|
|
73
|
+
7. Credentials are stored with `keytar`, or in `~/.config/deploily/config.json` if `keytar` is unavailable.
|
|
79
74
|
|
|
80
|
-
|
|
75
|
+
## Section 2: Contributing to the project
|
|
81
76
|
|
|
82
|
-
|
|
77
|
+
This section is for contributors working from the repository source.
|
|
83
78
|
|
|
84
|
-
###
|
|
79
|
+
### Requirements
|
|
85
80
|
|
|
86
|
-
|
|
81
|
+
- Node.js 20+
|
|
82
|
+
- pnpm
|
|
83
|
+
- Docker (optional, for the local Keycloak container)
|
|
84
|
+
|
|
85
|
+
### Clone and install
|
|
87
86
|
|
|
88
|
-
|
|
87
|
+
```bash
|
|
88
|
+
git clone <repository-url>
|
|
89
|
+
cd deploily-cli
|
|
90
|
+
pnpm install
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Build the CLI from source
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pnpm build
|
|
97
|
+
node dist/index.js --help
|
|
98
|
+
```
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
2. It opens the Keycloak authorization URL in your browser.
|
|
92
|
-
3. Keycloak redirects back to the local callback server on port `8976`.
|
|
93
|
-
4. The CLI exchanges the authorization code for tokens.
|
|
94
|
-
5. User info is fetched from the Keycloak userinfo endpoint.
|
|
95
|
-
6. Credentials are stored with `keytar`, or in `~/.config/deploily/config.json` if `keytar` is unavailable.
|
|
100
|
+
### Run the local CLI binary
|
|
96
101
|
|
|
97
|
-
|
|
102
|
+
If you want to invoke the project as `deploily` from your shell while developing locally:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
export PATH="$HOME/.local/share/pnpm/bin:$PATH"
|
|
106
|
+
pnpm setup
|
|
107
|
+
pnpm link --global .
|
|
108
|
+
deploily --help
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If the command is still not found, use the direct entry point instead:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
node dist/index.js --help
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Local development flow
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pnpm build
|
|
121
|
+
node dist/index.js --help
|
|
122
|
+
deploily login
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The project devcontainer already exposes the Keycloak service, so no separate Docker Compose startup is required.
|
|
126
|
+
|
|
127
|
+
### Project structure
|
|
98
128
|
|
|
99
129
|
- `src/index.ts` - CLI entry point and command routing
|
|
100
130
|
- `src/commands/` - CLI command handlers
|
|
@@ -103,3 +133,22 @@ Shows the current authenticated user when credentials are available.
|
|
|
103
133
|
- `src/storage/` - credential persistence
|
|
104
134
|
- `src/utils/` - PKCE helpers
|
|
105
135
|
- `src/types/` - shared TypeScript types
|
|
136
|
+
|
|
137
|
+
### Publishing checks
|
|
138
|
+
|
|
139
|
+
Before publishing a release, validate the package and the npm auth flow:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
export NPM_TOKEN="npm_your_token_here"
|
|
143
|
+
npm config set //registry.npmjs.org/:_authToken "$NPM_TOKEN"
|
|
144
|
+
npm whoami
|
|
145
|
+
npm publish --access public --dry-run
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
In GitHub Actions, configure a repository secret named `NPM_SECRET_TOKEN` and the publish workflow will:
|
|
149
|
+
|
|
150
|
+
- install dependencies
|
|
151
|
+
- build the CLI
|
|
152
|
+
- validate authentication with `npm whoami`
|
|
153
|
+
- run a dry-run publish
|
|
154
|
+
- publish the package to npm on a successful run
|
package/dist/auth/index.js
CHANGED
|
@@ -9,11 +9,11 @@ class AuthenticationService {
|
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
buildClientAuthHeaders() {
|
|
12
|
-
const clientSecret =
|
|
12
|
+
const clientSecret = AUTH_CONFIG.CLIENT_SECRET;
|
|
13
13
|
if (!clientSecret) {
|
|
14
14
|
return {};
|
|
15
15
|
}
|
|
16
|
-
const clientId =
|
|
16
|
+
const clientId = AUTH_CONFIG.CLIENT_ID;
|
|
17
17
|
const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
18
18
|
return {
|
|
19
19
|
Authorization: `Basic ${basicAuth}`,
|
|
@@ -45,11 +45,11 @@ class AuthenticationService {
|
|
|
45
45
|
const body = new URLSearchParams({
|
|
46
46
|
grant_type: "authorization_code",
|
|
47
47
|
code,
|
|
48
|
-
client_id:
|
|
49
|
-
redirect_uri:
|
|
48
|
+
client_id: AUTH_CONFIG.CLIENT_ID,
|
|
49
|
+
redirect_uri: AUTH_CONFIG.REDIRECT_URI,
|
|
50
50
|
code_verifier: codeVerifier,
|
|
51
51
|
});
|
|
52
|
-
const response = await this.client.post(
|
|
52
|
+
const response = await this.client.post(AUTH_CONFIG.KEYCLOAK_TOKEN_ENDPOINT, body, {
|
|
53
53
|
headers: {
|
|
54
54
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
55
55
|
...this.buildClientAuthHeaders(),
|
|
@@ -77,9 +77,9 @@ class AuthenticationService {
|
|
|
77
77
|
const body = new URLSearchParams({
|
|
78
78
|
grant_type: "refresh_token",
|
|
79
79
|
refresh_token: refreshToken,
|
|
80
|
-
client_id:
|
|
80
|
+
client_id: AUTH_CONFIG.CLIENT_ID,
|
|
81
81
|
});
|
|
82
|
-
const response = await this.client.post(
|
|
82
|
+
const response = await this.client.post(AUTH_CONFIG.KEYCLOAK_TOKEN_ENDPOINT, body, {
|
|
83
83
|
headers: {
|
|
84
84
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
85
85
|
...this.buildClientAuthHeaders(),
|
|
@@ -94,7 +94,7 @@ class AuthenticationService {
|
|
|
94
94
|
// Get user information from access token
|
|
95
95
|
async getUserInfo(accessToken) {
|
|
96
96
|
try {
|
|
97
|
-
const response = await this.client.get(
|
|
97
|
+
const response = await this.client.get(AUTH_CONFIG.KEYCLOAK_USERINFO_ENDPOINT, {
|
|
98
98
|
headers: {
|
|
99
99
|
Authorization: `Bearer ${accessToken}`,
|
|
100
100
|
},
|
|
@@ -143,11 +143,11 @@ class AuthenticationService {
|
|
|
143
143
|
if (credentials) {
|
|
144
144
|
try {
|
|
145
145
|
const body = new URLSearchParams({
|
|
146
|
-
client_id:
|
|
146
|
+
client_id: AUTH_CONFIG.CLIENT_ID,
|
|
147
147
|
refresh_token: credentials.refresh_token,
|
|
148
148
|
});
|
|
149
149
|
// Optionally call Keycloak logout endpoint
|
|
150
|
-
await this.client.post(
|
|
150
|
+
await this.client.post(AUTH_CONFIG.KEYCLOAK_LOGOUT_ENDPOINT, body, {
|
|
151
151
|
headers: {
|
|
152
152
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
153
153
|
...this.buildClientAuthHeaders(),
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAA;AAC5C,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,GACd,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAQ7D,MAAM,qBAAqB;IAGzB;QACE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YACzB,OAAO,EAAE,KAAK;SACf,CAAC,CAAA;IACJ,CAAC;IAEO,sBAAsB;QAC5B,MAAM,YAAY,GAAG,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AActB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;CA2BvB,CAAA;AAED,eAAO,MAAM,cAAc;;;;CAI1B,CAAA"}
|
package/dist/config/constants.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
|
+
const defaultKeycloakBaseUrl = process.env.KEYCLOAK_URL ??
|
|
3
|
+
process.env.KEYCLOAK_BASE_URL ??
|
|
4
|
+
"https://auth.deploily.cloud";
|
|
5
|
+
const defaultRealm = process.env.KEYCLOAK_REALM ?? "deploily";
|
|
6
|
+
const defaultClientId = process.env.KEYCLOAK_CLIENT_ID ?? "deploily";
|
|
7
|
+
const defaultRedirectUri = process.env.KEYCLOAK_REDIRECT_URI ?? "http://localhost:8080/callback";
|
|
8
|
+
const realmUrl = `${defaultKeycloakBaseUrl.replace(/\/$/, "")}/realms/${defaultRealm}`;
|
|
2
9
|
export const AUTH_CONFIG = {
|
|
3
10
|
// Keycloak Configuration
|
|
4
|
-
KEYCLOAK_REALM_URL: process.env.KEYCLOAK_REALM_URL,
|
|
5
|
-
KEYCLOAK_TOKEN_ENDPOINT: process.env.KEYCLOAK_TOKEN_ENDPOINT
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
KEYCLOAK_REALM_URL: process.env.KEYCLOAK_REALM_URL ?? realmUrl,
|
|
12
|
+
KEYCLOAK_TOKEN_ENDPOINT: process.env.KEYCLOAK_TOKEN_ENDPOINT ??
|
|
13
|
+
`${realmUrl}/protocol/openid-connect/token`,
|
|
14
|
+
KEYCLOAK_AUTH_ENDPOINT: process.env.KEYCLOAK_AUTH_ENDPOINT ??
|
|
15
|
+
`${realmUrl}/protocol/openid-connect/auth`,
|
|
16
|
+
KEYCLOAK_LOGOUT_ENDPOINT: process.env.KEYCLOAK_LOGOUT_ENDPOINT ??
|
|
17
|
+
`${realmUrl}/protocol/openid-connect/logout`,
|
|
18
|
+
KEYCLOAK_USERINFO_ENDPOINT: process.env.KEYCLOAK_USERINFO_ENDPOINT ??
|
|
19
|
+
`${realmUrl}/protocol/openid-connect/userinfo`,
|
|
9
20
|
// CLI Configuration
|
|
10
|
-
CLIENT_ID:
|
|
11
|
-
CLIENT_SECRET: process.env.KEYCLOAK_CLIENT_SECRET,
|
|
12
|
-
REDIRECT_URI:
|
|
21
|
+
CLIENT_ID: defaultClientId,
|
|
22
|
+
CLIENT_SECRET: process.env.KEYCLOAK_CLIENT_SECRET ?? "",
|
|
23
|
+
REDIRECT_URI: defaultRedirectUri,
|
|
13
24
|
CALLBACK_PORT: 8976,
|
|
14
25
|
SCOPE: "openid profile email",
|
|
15
26
|
RESPONSE_TYPE: "code",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,MAAM,sBAAsB,GAC1B,OAAO,CAAC,GAAG,CAAC,YAAY;IACxB,OAAO,CAAC,GAAG,CAAC,iBAAiB;IAC7B,6BAA6B,CAAA;AAE/B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,CAAA;AAC7D,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,UAAU,CAAA;AACpE,MAAM,kBAAkB,GACtB,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,gCAAgC,CAAA;AAEvE,MAAM,QAAQ,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,YAAY,EAAE,CAAA;AAEtF,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,yBAAyB;IACzB,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,QAAQ;IAC9D,uBAAuB,EACrB,OAAO,CAAC,GAAG,CAAC,uBAAuB;QACnC,GAAG,QAAQ,gCAAgC;IAC7C,sBAAsB,EACpB,OAAO,CAAC,GAAG,CAAC,sBAAsB;QAClC,GAAG,QAAQ,+BAA+B;IAC5C,wBAAwB,EACtB,OAAO,CAAC,GAAG,CAAC,wBAAwB;QACpC,GAAG,QAAQ,iCAAiC;IAC9C,0BAA0B,EACxB,OAAO,CAAC,GAAG,CAAC,0BAA0B;QACtC,GAAG,QAAQ,mCAAmC;IAEhD,oBAAoB;IACpB,SAAS,EAAE,eAAe;IAC1B,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE;IACvD,YAAY,EAAE,kBAAkB;IAChC,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.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deploily/deploily-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Deploily CLI - Deploy and manage your applications with ease",
|
|
5
5
|
"license": "APACHE-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"start": "node dist/index.js"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@deploily/deploily-cli": "link:",
|
|
23
22
|
"@hono/node-server": "^2.0.6",
|
|
24
23
|
"axios": "^1.18.1",
|
|
25
24
|
"chalk": "^5.6.2",
|
|
@@ -33,5 +32,6 @@
|
|
|
33
32
|
"@types/node": "^25.9.3",
|
|
34
33
|
"tsx": "^4.22.4",
|
|
35
34
|
"typescript": "^6.0.3"
|
|
36
|
-
}
|
|
35
|
+
},
|
|
36
|
+
"packageManager": "pnpm@11.23.0"
|
|
37
37
|
}
|
package/pnpm-workspace.yaml
CHANGED
package/src/auth/index.ts
CHANGED
|
@@ -23,13 +23,13 @@ class AuthenticationService {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
private buildClientAuthHeaders(): Record<string, string> {
|
|
26
|
-
const clientSecret =
|
|
26
|
+
const clientSecret = AUTH_CONFIG.CLIENT_SECRET
|
|
27
27
|
|
|
28
28
|
if (!clientSecret) {
|
|
29
29
|
return {}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const clientId =
|
|
32
|
+
const clientId = AUTH_CONFIG.CLIENT_ID
|
|
33
33
|
const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString(
|
|
34
34
|
"base64",
|
|
35
35
|
)
|
|
@@ -72,13 +72,13 @@ class AuthenticationService {
|
|
|
72
72
|
const body = new URLSearchParams({
|
|
73
73
|
grant_type: "authorization_code",
|
|
74
74
|
code,
|
|
75
|
-
client_id:
|
|
76
|
-
redirect_uri:
|
|
75
|
+
client_id: AUTH_CONFIG.CLIENT_ID,
|
|
76
|
+
redirect_uri: AUTH_CONFIG.REDIRECT_URI,
|
|
77
77
|
code_verifier: codeVerifier,
|
|
78
78
|
})
|
|
79
79
|
|
|
80
80
|
const response = await this.client.post<TokenResponse>(
|
|
81
|
-
|
|
81
|
+
AUTH_CONFIG.KEYCLOAK_TOKEN_ENDPOINT,
|
|
82
82
|
body,
|
|
83
83
|
{
|
|
84
84
|
headers: {
|
|
@@ -97,7 +97,7 @@ class AuthenticationService {
|
|
|
97
97
|
: JSON.stringify(error.response?.data ?? {})
|
|
98
98
|
const authHint =
|
|
99
99
|
error.response?.status === 401 &&
|
|
100
|
-
|
|
100
|
+
responseData.includes("unauthorized_client")
|
|
101
101
|
? " This usually means the Keycloak client is confidential and KEYCLOAK_CLIENT_SECRET is missing or invalid."
|
|
102
102
|
: ""
|
|
103
103
|
|
|
@@ -118,11 +118,11 @@ class AuthenticationService {
|
|
|
118
118
|
const body = new URLSearchParams({
|
|
119
119
|
grant_type: "refresh_token",
|
|
120
120
|
refresh_token: refreshToken,
|
|
121
|
-
client_id:
|
|
121
|
+
client_id: AUTH_CONFIG.CLIENT_ID,
|
|
122
122
|
})
|
|
123
123
|
|
|
124
124
|
const response = await this.client.post<TokenResponse>(
|
|
125
|
-
|
|
125
|
+
AUTH_CONFIG.KEYCLOAK_TOKEN_ENDPOINT,
|
|
126
126
|
body,
|
|
127
127
|
{
|
|
128
128
|
headers: {
|
|
@@ -144,7 +144,7 @@ class AuthenticationService {
|
|
|
144
144
|
async getUserInfo(accessToken: string): Promise<UserInfo> {
|
|
145
145
|
try {
|
|
146
146
|
const response = await this.client.get<UserInfo>(
|
|
147
|
-
|
|
147
|
+
AUTH_CONFIG.KEYCLOAK_USERINFO_ENDPOINT,
|
|
148
148
|
{
|
|
149
149
|
headers: {
|
|
150
150
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -206,12 +206,12 @@ class AuthenticationService {
|
|
|
206
206
|
if (credentials) {
|
|
207
207
|
try {
|
|
208
208
|
const body = new URLSearchParams({
|
|
209
|
-
client_id:
|
|
209
|
+
client_id: AUTH_CONFIG.CLIENT_ID,
|
|
210
210
|
refresh_token: credentials.refresh_token,
|
|
211
211
|
})
|
|
212
212
|
|
|
213
213
|
// Optionally call Keycloak logout endpoint
|
|
214
|
-
await this.client.post(
|
|
214
|
+
await this.client.post(AUTH_CONFIG.KEYCLOAK_LOGOUT_ENDPOINT, body, {
|
|
215
215
|
headers: {
|
|
216
216
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
217
217
|
...this.buildClientAuthHeaders(),
|
package/src/config/constants.ts
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
import "dotenv/config"
|
|
2
2
|
|
|
3
|
+
const defaultKeycloakBaseUrl =
|
|
4
|
+
process.env.KEYCLOAK_URL ??
|
|
5
|
+
process.env.KEYCLOAK_BASE_URL ??
|
|
6
|
+
"https://auth.deploily.cloud"
|
|
7
|
+
|
|
8
|
+
const defaultRealm = process.env.KEYCLOAK_REALM ?? "deploily"
|
|
9
|
+
const defaultClientId = process.env.KEYCLOAK_CLIENT_ID ?? "deploily"
|
|
10
|
+
const defaultRedirectUri =
|
|
11
|
+
process.env.KEYCLOAK_REDIRECT_URI ?? "http://localhost:8080/callback"
|
|
12
|
+
|
|
13
|
+
const realmUrl = `${defaultKeycloakBaseUrl.replace(/\/$/, "")}/realms/${defaultRealm}`
|
|
14
|
+
|
|
3
15
|
export const AUTH_CONFIG = {
|
|
4
16
|
// Keycloak Configuration
|
|
5
|
-
KEYCLOAK_REALM_URL: process.env.KEYCLOAK_REALM_URL
|
|
6
|
-
KEYCLOAK_TOKEN_ENDPOINT:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
17
|
+
KEYCLOAK_REALM_URL: process.env.KEYCLOAK_REALM_URL ?? realmUrl,
|
|
18
|
+
KEYCLOAK_TOKEN_ENDPOINT:
|
|
19
|
+
process.env.KEYCLOAK_TOKEN_ENDPOINT ??
|
|
20
|
+
`${realmUrl}/protocol/openid-connect/token`,
|
|
21
|
+
KEYCLOAK_AUTH_ENDPOINT:
|
|
22
|
+
process.env.KEYCLOAK_AUTH_ENDPOINT ??
|
|
23
|
+
`${realmUrl}/protocol/openid-connect/auth`,
|
|
24
|
+
KEYCLOAK_LOGOUT_ENDPOINT:
|
|
25
|
+
process.env.KEYCLOAK_LOGOUT_ENDPOINT ??
|
|
26
|
+
`${realmUrl}/protocol/openid-connect/logout`,
|
|
27
|
+
KEYCLOAK_USERINFO_ENDPOINT:
|
|
28
|
+
process.env.KEYCLOAK_USERINFO_ENDPOINT ??
|
|
29
|
+
`${realmUrl}/protocol/openid-connect/userinfo`,
|
|
10
30
|
|
|
11
31
|
// CLI Configuration
|
|
12
|
-
CLIENT_ID:
|
|
13
|
-
CLIENT_SECRET: process.env.KEYCLOAK_CLIENT_SECRET
|
|
14
|
-
REDIRECT_URI:
|
|
32
|
+
CLIENT_ID: defaultClientId,
|
|
33
|
+
CLIENT_SECRET: process.env.KEYCLOAK_CLIENT_SECRET ?? "",
|
|
34
|
+
REDIRECT_URI: defaultRedirectUri,
|
|
15
35
|
CALLBACK_PORT: 8976,
|
|
16
36
|
SCOPE: "openid profile email",
|
|
17
37
|
RESPONSE_TYPE: "code",
|