@deerdesign/deertools 1.2.6 → 1.2.7
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/package.json +1 -1
- package/src/login.js +39 -8
package/package.json
CHANGED
package/src/login.js
CHANGED
|
@@ -33,9 +33,9 @@ function normalizeScope(input) {
|
|
|
33
33
|
return VALID_SCOPES.filter((s) => set.has(s)).join(' ');
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function buildAuthorizeUrl(authorizationEndpoint, { redirectUri, state, scope, clientName, challenge }) {
|
|
36
|
+
function buildAuthorizeUrl(authorizationEndpoint, { redirectUri, state, scope, clientName, challenge, clientId = CLIENT_ID }) {
|
|
37
37
|
const u = new URL(authorizationEndpoint);
|
|
38
|
-
u.searchParams.set('client_id',
|
|
38
|
+
u.searchParams.set('client_id', clientId);
|
|
39
39
|
u.searchParams.set('redirect_uri', redirectUri);
|
|
40
40
|
u.searchParams.set('response_type', 'code');
|
|
41
41
|
u.searchParams.set('code_challenge', challenge);
|
|
@@ -67,13 +67,13 @@ function evaluateCallback(query, { expectedState }) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/** Wymiana kodu na klucz: bezpośrednie zapytanie CLI -> serwer (klucz nigdy nie przechodzi przez przeglądarkę). */
|
|
70
|
-
async function exchangeCode(product, tokenEndpoint, { code, verifier, redirectUri, fetchImpl = fetch }) {
|
|
70
|
+
async function exchangeCode(product, tokenEndpoint, { code, verifier, redirectUri, clientId = CLIENT_ID, fetchImpl = fetch }) {
|
|
71
71
|
let res;
|
|
72
72
|
try {
|
|
73
73
|
res = await fetchImpl(tokenEndpoint, {
|
|
74
74
|
method: 'POST',
|
|
75
75
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
76
|
-
body: JSON.stringify({ grant_type: 'authorization_code', code, code_verifier: verifier, redirect_uri: redirectUri, client_id:
|
|
76
|
+
body: JSON.stringify({ grant_type: 'authorization_code', code, code_verifier: verifier, redirect_uri: redirectUri, client_id: clientId }),
|
|
77
77
|
});
|
|
78
78
|
} catch (e) {
|
|
79
79
|
throw new Error(`Nie udało się połączyć z serwerem produktu ${product.name}: ${e.message}`);
|
|
@@ -162,6 +162,28 @@ function openBrowser(url) {
|
|
|
162
162
|
exec(cmd, () => { /* gdy się nie uda, użytkownik ma adres wypisany w terminalu */ });
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
async function registerDynamicClient(registrationEndpoint, { redirectUri, clientName, fetchImpl = fetch }) {
|
|
166
|
+
if (!registrationEndpoint) return null;
|
|
167
|
+
try {
|
|
168
|
+
const res = await fetchImpl(registrationEndpoint, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
171
|
+
body: JSON.stringify({
|
|
172
|
+
client_name: clientName || 'DeerTools CLI',
|
|
173
|
+
redirect_uris: [redirectUri],
|
|
174
|
+
token_endpoint_auth_method: 'none',
|
|
175
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
176
|
+
response_types: ['code'],
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
if (!res.ok) return null;
|
|
180
|
+
const json = await res.json();
|
|
181
|
+
return json.client_id || null;
|
|
182
|
+
} catch (_) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
165
187
|
/**
|
|
166
188
|
* Logowanie OAuth 2.1 dla aplikacji natywnej (RFC 8252): kod autoryzacyjny + PKCE, powrót na 127.0.0.1 (losowy port).
|
|
167
189
|
* opts.metadata pozwala podać adresy bez pobierania (testy). Zwraca wpis poświadczeń zapisany na dysku.
|
|
@@ -179,6 +201,7 @@ async function login(product, opts = {}) {
|
|
|
179
201
|
return new Promise((resolve, reject) => {
|
|
180
202
|
let finished = false;
|
|
181
203
|
let redirectUri = null;
|
|
204
|
+
let activeClientId = CLIENT_ID;
|
|
182
205
|
|
|
183
206
|
const server = http.createServer(async (req, res) => {
|
|
184
207
|
const url = new URL(req.url, 'http://127.0.0.1');
|
|
@@ -192,7 +215,7 @@ async function login(product, opts = {}) {
|
|
|
192
215
|
return;
|
|
193
216
|
}
|
|
194
217
|
try {
|
|
195
|
-
const entry = await exchangeCode(product, meta.token_endpoint, { code: result.code, verifier: pkce.verifier, redirectUri, fetchImpl: opts.fetchImpl });
|
|
218
|
+
const entry = await exchangeCode(product, meta.token_endpoint, { code: result.code, verifier: pkce.verifier, redirectUri, clientId: activeClientId, fetchImpl: opts.fetchImpl });
|
|
196
219
|
credentials.saveProduct(product.id, entry);
|
|
197
220
|
res.writeHead(200, headers);
|
|
198
221
|
res.end(page('Zalogowano', `Klucz do produktu ${product.name} został zapisany w terminalu. Możesz zamknąć to okno.`, true));
|
|
@@ -215,9 +238,17 @@ async function login(product, opts = {}) {
|
|
|
215
238
|
}
|
|
216
239
|
|
|
217
240
|
server.on('error', (e) => done(e));
|
|
218
|
-
server.listen(opts.port || 0, '127.0.0.1', () => {
|
|
241
|
+
server.listen(opts.port || 0, '127.0.0.1', async () => {
|
|
219
242
|
redirectUri = `http://127.0.0.1:${server.address().port}/callback`;
|
|
220
|
-
|
|
243
|
+
let authScope = scope;
|
|
244
|
+
if (meta.registration_endpoint) {
|
|
245
|
+
const dynId = await registerDynamicClient(meta.registration_endpoint, { redirectUri, clientName, fetchImpl: opts.fetchImpl });
|
|
246
|
+
if (dynId) {
|
|
247
|
+
activeClientId = dynId;
|
|
248
|
+
authScope = 'openid email profile offline_access';
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const url = buildAuthorizeUrl(meta.authorization_endpoint, { redirectUri, state, scope: authScope, clientName, challenge: pkce.challenge, clientId: activeClientId });
|
|
221
252
|
log(`Zakres: ${scope}. Otwieram przeglądarkę — zaloguj się w produkcie ${product.name} i kliknij „Zezwól na dostęp”.`);
|
|
222
253
|
log(`Jeśli przeglądarka się nie otworzy, wejdź na:\n${url}\n`);
|
|
223
254
|
if (open) open(url);
|
|
@@ -225,4 +256,4 @@ async function login(product, opts = {}) {
|
|
|
225
256
|
});
|
|
226
257
|
}
|
|
227
258
|
|
|
228
|
-
module.exports = { CLIENT_ID, createPkce, normalizeScope, buildAuthorizeUrl, evaluateCallback, exchangeCode, discoverOAuth, login };
|
|
259
|
+
module.exports = { CLIENT_ID, createPkce, normalizeScope, buildAuthorizeUrl, evaluateCallback, exchangeCode, discoverOAuth, login, registerDynamicClient };
|