@cloudflare/workers-auth 0.5.6 → 0.6.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/dist/cf/index.d.mts +3 -3
- package/dist/cf/index.mjs +6 -3
- package/dist/{chunk-7ZPLVMOI.mjs → chunk-7AYQ2BAF.mjs} +379 -17
- package/dist/{chunk-CEH7NWR7.mjs → chunk-AEOSGWMT.mjs} +5 -2
- package/dist/{file-format-B1KgHuUR.d.mts → file-format-Dbmn5hhd.d.mts} +22 -0
- package/dist/index.d.mts +22 -3
- package/dist/index.mjs +1 -1
- package/dist/{keyring-preference-BgjU5DM9.d.mts → keyring-preference-CeP6MLyb.d.mts} +25 -2
- package/dist/metafile-esm.json +1 -1
- package/dist/wrangler/index.d.mts +2 -2
- package/dist/wrangler/index.mjs +6 -3
- package/package.json +2 -2
package/dist/cf/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as ConfigStorage, A as AuthConfigStorage, U as UserAuthConfig, b as OAuthFlowContext, k as ProfileStore } from '../file-format-
|
|
2
|
-
import { C as CliDescriptor, U as UserPreferences, A as AuthContext, a as CloudflareAuth, K as KeyringPreferenceContext, S as SetKeyringPreferenceResult } from '../keyring-preference-
|
|
3
|
-
export { b as CloudflareLoginProps } from '../keyring-preference-
|
|
1
|
+
import { a as ConfigStorage, A as AuthConfigStorage, U as UserAuthConfig, b as OAuthFlowContext, k as ProfileStore } from '../file-format-Dbmn5hhd.mjs';
|
|
2
|
+
import { C as CliDescriptor, U as UserPreferences, A as AuthContext, a as CloudflareAuth, K as KeyringPreferenceContext, S as SetKeyringPreferenceResult } from '../keyring-preference-CeP6MLyb.mjs';
|
|
3
|
+
export { b as CloudflareLoginProps } from '../keyring-preference-CeP6MLyb.mjs';
|
|
4
4
|
import '@cloudflare/workers-utils';
|
|
5
5
|
|
|
6
6
|
/**
|
package/dist/cf/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createAuthConfigFileHelpers, createPreferences, createKeyringPreference, createFileStorage, createCloudflareAuth, createCloudflareProfileStore } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
import { createAuthConfigFileHelpers, createPreferences, createKeyringPreference, createFileStorage, createCloudflareAuth, createCloudflareProfileStore } from '../chunk-AEOSGWMT.mjs';
|
|
2
|
+
import '../chunk-7AYQ2BAF.mjs';
|
|
3
3
|
import { __name } from '../chunk-O6YSETKJ.mjs';
|
|
4
4
|
import { getEnvironmentVariableFactory, getGlobalConfigPath, getCloudflareApiEnvironmentFromEnv } from '@cloudflare/workers-utils';
|
|
5
5
|
import path from 'node:path';
|
|
@@ -7,6 +7,7 @@ import path from 'node:path';
|
|
|
7
7
|
// src/cf/constants.ts
|
|
8
8
|
var CF_KEYRING_SERVICE_NAME = "cloudflare";
|
|
9
9
|
var CF_CLI_NAME = "cf";
|
|
10
|
+
var CF_DISPLAY_NAME = "cf";
|
|
10
11
|
var CF_OAUTH_CALLBACK_URL = "http://localhost:8877/oauth/callback";
|
|
11
12
|
var CF_CONSENT_PAGES = {
|
|
12
13
|
granted: {
|
|
@@ -158,10 +159,12 @@ var {
|
|
|
158
159
|
// src/cf/index.ts
|
|
159
160
|
var CF_CLI = {
|
|
160
161
|
cliName: CF_CLI_NAME,
|
|
162
|
+
displayName: CF_DISPLAY_NAME,
|
|
161
163
|
commands: {
|
|
162
164
|
login: "cf auth login",
|
|
163
165
|
whoami: "cf auth whoami",
|
|
164
|
-
createProfile: "cf auth create"
|
|
166
|
+
createProfile: "cf auth create",
|
|
167
|
+
deviceLogin: "cf auth login --device"
|
|
165
168
|
},
|
|
166
169
|
keyringServiceName: CF_KEYRING_SERVICE_NAME,
|
|
167
170
|
clientId: getClientIdFromEnv,
|
|
@@ -143,6 +143,11 @@ function getCloudflareAccountIdFromEnv() {
|
|
|
143
143
|
);
|
|
144
144
|
}
|
|
145
145
|
__name(getCloudflareAccountIdFromEnv, "getCloudflareAccountIdFromEnv");
|
|
146
|
+
var DEVICE_AUTH_PATH = "/oauth2/device/auth";
|
|
147
|
+
function getDeviceAuthUrl() {
|
|
148
|
+
return `https://${getAuthDomainFromEnv()}${DEVICE_AUTH_PATH}`;
|
|
149
|
+
}
|
|
150
|
+
__name(getDeviceAuthUrl, "getDeviceAuthUrl");
|
|
146
151
|
var getAccessClientIdFromEnv = getEnvironmentVariableFactory({
|
|
147
152
|
variableName: "CLOUDFLARE_ACCESS_CLIENT_ID"
|
|
148
153
|
});
|
|
@@ -320,6 +325,36 @@ async function generatePKCECodes() {
|
|
|
320
325
|
return { codeChallenge, codeVerifier };
|
|
321
326
|
}
|
|
322
327
|
__name(generatePKCECodes, "generatePKCECodes");
|
|
328
|
+
var generateVerificationUrl = /* @__PURE__ */ __name(({
|
|
329
|
+
verificationUri,
|
|
330
|
+
userCode
|
|
331
|
+
}) => {
|
|
332
|
+
const url2 = new URL(verificationUri);
|
|
333
|
+
url2.searchParams.set("user_code", userCode);
|
|
334
|
+
return url2.toString();
|
|
335
|
+
}, "generateVerificationUrl");
|
|
336
|
+
function parseUrl(value) {
|
|
337
|
+
try {
|
|
338
|
+
return new URL(value);
|
|
339
|
+
} catch {
|
|
340
|
+
return void 0;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
__name(parseUrl, "parseUrl");
|
|
344
|
+
function assertTrustedVerificationUrl(verificationUrl, options) {
|
|
345
|
+
const url2 = parseUrl(verificationUrl);
|
|
346
|
+
const expected = parseUrl(`https://${options.authDomain}`);
|
|
347
|
+
const trusted = url2 !== void 0 && expected !== void 0 && url2.protocol === "https:" && url2.username === "" && url2.password === "" && // `host` rather than `hostname` so a port in either value is significant.
|
|
348
|
+
url2.host === expected.host;
|
|
349
|
+
if (!trusted) {
|
|
350
|
+
throw new UserError(
|
|
351
|
+
`The authorization server returned an untrusted \`${options.field}\`: ${verificationUrl}
|
|
352
|
+
Expected an \`https\` URL on \`${options.authDomain}\`. Refusing to display or open it.`,
|
|
353
|
+
{ telemetryMessage: "user device-flow untrusted verification url" }
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
__name(assertTrustedVerificationUrl, "assertTrustedVerificationUrl");
|
|
323
358
|
|
|
324
359
|
// src/generate-auth-url.ts
|
|
325
360
|
var generateAuthUrl = /* @__PURE__ */ __name(({
|
|
@@ -1201,6 +1236,318 @@ If you have changed the callback host or port because you are running in a conta
|
|
|
1201
1236
|
return Promise.race([timerPromise, loginPromise]);
|
|
1202
1237
|
}
|
|
1203
1238
|
__name(getOauthToken, "getOauthToken");
|
|
1239
|
+
var DEVICE_FLOW_MAX_DURATION_SECONDS = 300;
|
|
1240
|
+
var DEVICE_FLOW_MIN_POLL_INTERVAL_SECONDS = 1;
|
|
1241
|
+
function getStringMember(body, key) {
|
|
1242
|
+
if (typeof body !== "object" || body === null) {
|
|
1243
|
+
return void 0;
|
|
1244
|
+
}
|
|
1245
|
+
const value = body[key];
|
|
1246
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1247
|
+
}
|
|
1248
|
+
__name(getStringMember, "getStringMember");
|
|
1249
|
+
function getPositiveNumberMember(body, key) {
|
|
1250
|
+
if (typeof body !== "object" || body === null) {
|
|
1251
|
+
return void 0;
|
|
1252
|
+
}
|
|
1253
|
+
const value = body[key];
|
|
1254
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
1255
|
+
}
|
|
1256
|
+
__name(getPositiveNumberMember, "getPositiveNumberMember");
|
|
1257
|
+
function describeUnusableResponse(response, body) {
|
|
1258
|
+
const status = `HTTP ${response.status} ${response.statusText}`.trim();
|
|
1259
|
+
const description = getStringMember(body, "error_description");
|
|
1260
|
+
return description ? `${status}: ${description}` : status;
|
|
1261
|
+
}
|
|
1262
|
+
__name(describeUnusableResponse, "describeUnusableResponse");
|
|
1263
|
+
function isRetryableStatus(status) {
|
|
1264
|
+
return status >= 500 || status === 429;
|
|
1265
|
+
}
|
|
1266
|
+
__name(isRetryableStatus, "isRetryableStatus");
|
|
1267
|
+
function asDeviceAuthorizationResponse(body) {
|
|
1268
|
+
const deviceCode = getStringMember(body, "device_code");
|
|
1269
|
+
const userCode = getStringMember(body, "user_code");
|
|
1270
|
+
const verificationUri = getStringMember(body, "verification_uri");
|
|
1271
|
+
const expiresIn = getPositiveNumberMember(body, "expires_in");
|
|
1272
|
+
if (!deviceCode || !userCode || !verificationUri || expiresIn === void 0) {
|
|
1273
|
+
return void 0;
|
|
1274
|
+
}
|
|
1275
|
+
return {
|
|
1276
|
+
device_code: deviceCode,
|
|
1277
|
+
user_code: userCode,
|
|
1278
|
+
verification_uri: verificationUri,
|
|
1279
|
+
verification_uri_complete: getStringMember(
|
|
1280
|
+
body,
|
|
1281
|
+
"verification_uri_complete"
|
|
1282
|
+
),
|
|
1283
|
+
expires_in: expiresIn,
|
|
1284
|
+
interval: getPositiveNumberMember(body, "interval")
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
__name(asDeviceAuthorizationResponse, "asDeviceAuthorizationResponse");
|
|
1288
|
+
async function readJson(response, logger) {
|
|
1289
|
+
const text = await response.text();
|
|
1290
|
+
try {
|
|
1291
|
+
return JSON.parse(text);
|
|
1292
|
+
} catch (e) {
|
|
1293
|
+
if (text.match(/<!DOCTYPE html>/)) {
|
|
1294
|
+
logger.debug(
|
|
1295
|
+
"The body of the response was HTML rather than JSON. Check the debug logs to see the full body of the response."
|
|
1296
|
+
);
|
|
1297
|
+
if (text.match(/challenge-platform/)) {
|
|
1298
|
+
logger.debug(
|
|
1299
|
+
`It looks like you might have hit a bot challenge page. This may be transient but if not, please contact Cloudflare to find out what can be done. When you contact Cloudflare, please provide your Ray ID: ${response.headers.get(
|
|
1300
|
+
"cf-ray"
|
|
1301
|
+
)}`
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
logger.debug("Full body of response\n\n", text);
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
`Invalid JSON in response: status: ${response.status} ${response.statusText}`,
|
|
1308
|
+
{ cause: e }
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
__name(readJson, "readJson");
|
|
1313
|
+
async function buildDeviceAuthHeaders(logger, isNonInteractiveOrCI2) {
|
|
1314
|
+
const headers = {
|
|
1315
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1316
|
+
};
|
|
1317
|
+
if (await domainUsesAccess(getAuthDomainFromEnv(), logger)) {
|
|
1318
|
+
logger.debug(
|
|
1319
|
+
"Using Cloudflare Access to get an access token for the device authorization request"
|
|
1320
|
+
);
|
|
1321
|
+
Object.assign(
|
|
1322
|
+
headers,
|
|
1323
|
+
await getCloudflareAccessHeaders({ logger, isNonInteractiveOrCI: isNonInteractiveOrCI2 })
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
return headers;
|
|
1327
|
+
}
|
|
1328
|
+
__name(buildDeviceAuthHeaders, "buildDeviceAuthHeaders");
|
|
1329
|
+
async function requestDeviceAuthorization(scopes, clientId, logger, isNonInteractiveOrCI2) {
|
|
1330
|
+
const params = new URLSearchParams({
|
|
1331
|
+
client_id: clientId,
|
|
1332
|
+
scope: [...scopes, "offline_access"].join(" ")
|
|
1333
|
+
});
|
|
1334
|
+
const headers = await buildDeviceAuthHeaders(logger, isNonInteractiveOrCI2);
|
|
1335
|
+
const deviceAuthUrl = getDeviceAuthUrl();
|
|
1336
|
+
logger.debug("Fetching device authorization from", deviceAuthUrl);
|
|
1337
|
+
const response = await fetch(deviceAuthUrl, {
|
|
1338
|
+
method: "POST",
|
|
1339
|
+
body: params.toString(),
|
|
1340
|
+
headers
|
|
1341
|
+
});
|
|
1342
|
+
if (!response.ok) {
|
|
1343
|
+
const body2 = await readJson(response, logger).catch(() => void 0);
|
|
1344
|
+
const rawError = body2 && typeof body2 === "object" && "error" in body2 ? String(body2.error) : `HTTP ${response.status} ${response.statusText}`;
|
|
1345
|
+
throw toErrorClass(rawError);
|
|
1346
|
+
}
|
|
1347
|
+
const body = await readJson(response, logger);
|
|
1348
|
+
const deviceAuth = asDeviceAuthorizationResponse(body);
|
|
1349
|
+
if (deviceAuth === void 0) {
|
|
1350
|
+
logger.debug("Unusable device authorization response body:", body);
|
|
1351
|
+
throw new UserError(
|
|
1352
|
+
`The device authorization endpoint returned a response that could not be interpreted (${describeUnusableResponse(response, body)}).
|
|
1353
|
+
Enable debug logging to see the full response.`,
|
|
1354
|
+
{
|
|
1355
|
+
telemetryMessage: "user device-flow unusable device authorization response"
|
|
1356
|
+
}
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
return deviceAuth;
|
|
1360
|
+
}
|
|
1361
|
+
__name(requestDeviceAuthorization, "requestDeviceAuthorization");
|
|
1362
|
+
async function pollDeviceToken(deviceCode, clientId, logger, isNonInteractiveOrCI2) {
|
|
1363
|
+
const params = new URLSearchParams({
|
|
1364
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
1365
|
+
device_code: deviceCode,
|
|
1366
|
+
client_id: clientId
|
|
1367
|
+
});
|
|
1368
|
+
const response = await fetchAuthToken(params, logger, isNonInteractiveOrCI2);
|
|
1369
|
+
const body = await readJson(response, logger);
|
|
1370
|
+
const error = getStringMember(body, "error");
|
|
1371
|
+
if (error !== void 0) {
|
|
1372
|
+
return { kind: "oauth-error", error };
|
|
1373
|
+
}
|
|
1374
|
+
const accessToken = getStringMember(body, "access_token");
|
|
1375
|
+
const expiresIn = getPositiveNumberMember(body, "expires_in");
|
|
1376
|
+
if (!response.ok || accessToken === void 0 || expiresIn === void 0) {
|
|
1377
|
+
logger.debug("Unusable token response body:", body);
|
|
1378
|
+
return {
|
|
1379
|
+
kind: "unexpected",
|
|
1380
|
+
detail: describeUnusableResponse(response, body),
|
|
1381
|
+
// A 2xx that carries no usable grant is not going to become one on the
|
|
1382
|
+
// next attempt, so only the retryable statuses keep the loop alive.
|
|
1383
|
+
retryable: !response.ok && isRetryableStatus(response.status)
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
return {
|
|
1387
|
+
kind: "token",
|
|
1388
|
+
grant: {
|
|
1389
|
+
access_token: accessToken,
|
|
1390
|
+
expires_in: expiresIn,
|
|
1391
|
+
refresh_token: getStringMember(body, "refresh_token"),
|
|
1392
|
+
scope: getStringMember(body, "scope")
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
__name(pollDeviceToken, "pollDeviceToken");
|
|
1397
|
+
function sleep(seconds) {
|
|
1398
|
+
return new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
|
|
1399
|
+
}
|
|
1400
|
+
__name(sleep, "sleep");
|
|
1401
|
+
async function getOauthTokenViaDeviceFlow(options, ctx) {
|
|
1402
|
+
const { logger, openInBrowser, isNonInteractiveOrCI: isNonInteractiveOrCI2 } = ctx;
|
|
1403
|
+
const deviceAuth = await requestDeviceAuthorization(
|
|
1404
|
+
options.scopes,
|
|
1405
|
+
options.clientId,
|
|
1406
|
+
logger,
|
|
1407
|
+
isNonInteractiveOrCI2
|
|
1408
|
+
);
|
|
1409
|
+
const authDomain = getAuthDomainFromEnv();
|
|
1410
|
+
assertTrustedVerificationUrl(deviceAuth.verification_uri, {
|
|
1411
|
+
field: "verification_uri",
|
|
1412
|
+
authDomain
|
|
1413
|
+
});
|
|
1414
|
+
if (deviceAuth.verification_uri_complete !== void 0) {
|
|
1415
|
+
assertTrustedVerificationUrl(deviceAuth.verification_uri_complete, {
|
|
1416
|
+
field: "verification_uri_complete",
|
|
1417
|
+
authDomain
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
const verificationUrl = deviceAuth.verification_uri_complete ?? generateVerificationUrl({
|
|
1421
|
+
verificationUri: deviceAuth.verification_uri,
|
|
1422
|
+
userCode: deviceAuth.user_code
|
|
1423
|
+
});
|
|
1424
|
+
const maxDurationSeconds = Math.min(
|
|
1425
|
+
deviceAuth.expires_in,
|
|
1426
|
+
DEVICE_FLOW_MAX_DURATION_SECONDS
|
|
1427
|
+
);
|
|
1428
|
+
logger.log(
|
|
1429
|
+
esm_default`
|
|
1430
|
+
To authorize ${ctx.displayName}, please visit:
|
|
1431
|
+
|
|
1432
|
+
${deviceAuth.verification_uri}
|
|
1433
|
+
|
|
1434
|
+
and enter the code:
|
|
1435
|
+
|
|
1436
|
+
${deviceAuth.user_code}
|
|
1437
|
+
|
|
1438
|
+
You have ${toHumanPeriod(maxDurationSeconds)} to approve this request.
|
|
1439
|
+
`
|
|
1440
|
+
);
|
|
1441
|
+
if (options.browser) {
|
|
1442
|
+
logger.log(`
|
|
1443
|
+
Opening a link in your default browser: ${verificationUrl}`);
|
|
1444
|
+
await openInBrowser(verificationUrl);
|
|
1445
|
+
}
|
|
1446
|
+
const deadline = Date.now() + maxDurationSeconds * 1e3;
|
|
1447
|
+
let intervalSeconds = Math.max(
|
|
1448
|
+
deviceAuth.interval ?? DEVICE_FLOW_MIN_POLL_INTERVAL_SECONDS,
|
|
1449
|
+
DEVICE_FLOW_MIN_POLL_INTERVAL_SECONDS
|
|
1450
|
+
);
|
|
1451
|
+
let firstPoll = true;
|
|
1452
|
+
let lastPollFailure;
|
|
1453
|
+
while (Date.now() < deadline) {
|
|
1454
|
+
if (!firstPoll) {
|
|
1455
|
+
await sleep(intervalSeconds);
|
|
1456
|
+
}
|
|
1457
|
+
firstPoll = false;
|
|
1458
|
+
let result;
|
|
1459
|
+
try {
|
|
1460
|
+
result = await pollDeviceToken(
|
|
1461
|
+
deviceAuth.device_code,
|
|
1462
|
+
options.clientId,
|
|
1463
|
+
logger,
|
|
1464
|
+
isNonInteractiveOrCI2
|
|
1465
|
+
);
|
|
1466
|
+
} catch (e) {
|
|
1467
|
+
logger.debug("Ignoring transient error while polling for token:", e);
|
|
1468
|
+
lastPollFailure = e instanceof Error ? e.message : String(e);
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
if (result.kind === "unexpected") {
|
|
1472
|
+
if (!result.retryable) {
|
|
1473
|
+
throw new UserError(
|
|
1474
|
+
`The token endpoint returned a response that could not be interpreted (${result.detail}).
|
|
1475
|
+
Enable debug logging to see the full response.`,
|
|
1476
|
+
{ telemetryMessage: "user device-flow unusable token response" }
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
logger.debug(
|
|
1480
|
+
"Ignoring transient response while polling for token:",
|
|
1481
|
+
result.detail
|
|
1482
|
+
);
|
|
1483
|
+
lastPollFailure = result.detail;
|
|
1484
|
+
continue;
|
|
1485
|
+
}
|
|
1486
|
+
lastPollFailure = void 0;
|
|
1487
|
+
if (result.kind === "oauth-error") {
|
|
1488
|
+
switch (result.error) {
|
|
1489
|
+
case "authorization_pending":
|
|
1490
|
+
continue;
|
|
1491
|
+
case "slow_down":
|
|
1492
|
+
intervalSeconds += 5;
|
|
1493
|
+
continue;
|
|
1494
|
+
case "access_denied":
|
|
1495
|
+
throw new UserError(
|
|
1496
|
+
`Consent denied. You must grant consent to ${ctx.displayName} in order to login.
|
|
1497
|
+
If you don't want to do this consider passing an API token via the \`CLOUDFLARE_API_TOKEN\` environment variable.`,
|
|
1498
|
+
{ telemetryMessage: "user device-flow consent denied" }
|
|
1499
|
+
);
|
|
1500
|
+
case "expired_token":
|
|
1501
|
+
throw new UserError(
|
|
1502
|
+
`Device code expired before the request was approved. Please run \`${ctx.deviceLoginCommand}\` again to obtain a new code.`,
|
|
1503
|
+
{ telemetryMessage: "user device-flow code expired" }
|
|
1504
|
+
);
|
|
1505
|
+
default:
|
|
1506
|
+
throw toErrorClass(result.error);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
const { grant } = result;
|
|
1510
|
+
const accessToken = {
|
|
1511
|
+
value: grant.access_token,
|
|
1512
|
+
expiry: new Date(Date.now() + grant.expires_in * 1e3).toISOString()
|
|
1513
|
+
};
|
|
1514
|
+
const scopes = grant.scope ? grant.scope.split(" ") : [];
|
|
1515
|
+
return {
|
|
1516
|
+
token: accessToken,
|
|
1517
|
+
scopes,
|
|
1518
|
+
refreshToken: grant.refresh_token ? { value: grant.refresh_token } : void 0
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
if (lastPollFailure !== void 0) {
|
|
1522
|
+
throw new UserError(
|
|
1523
|
+
`Gave up waiting for device authorization: the token endpoint kept returning responses that could not be interpreted (last: ${lastPollFailure}).
|
|
1524
|
+
Enable debug logging to see the full response, then run \`${ctx.deviceLoginCommand}\` to try again.`,
|
|
1525
|
+
{ telemetryMessage: "user device-flow poll failed" }
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
throw new UserError(
|
|
1529
|
+
`Device authorization timed out after ${toHumanPeriod(maxDurationSeconds)}. Please run \`${ctx.deviceLoginCommand}\` again to obtain a new code.`,
|
|
1530
|
+
{ telemetryMessage: "user device-flow authorization timeout" }
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
__name(getOauthTokenViaDeviceFlow, "getOauthTokenViaDeviceFlow");
|
|
1534
|
+
function toHumanPeriod(seconds) {
|
|
1535
|
+
if (seconds < 60) {
|
|
1536
|
+
return `${seconds} seconds`;
|
|
1537
|
+
}
|
|
1538
|
+
const minutes = Math.floor(seconds / 60);
|
|
1539
|
+
const minutesPostfix = minutes === 1 ? "minute" : "minutes";
|
|
1540
|
+
const remainingSeconds = seconds % 60;
|
|
1541
|
+
const secondsPostfix = remainingSeconds === 1 ? "second" : "seconds";
|
|
1542
|
+
if (remainingSeconds === 0) {
|
|
1543
|
+
return `${minutes} ${minutesPostfix}`;
|
|
1544
|
+
}
|
|
1545
|
+
if (remainingSeconds == 59) {
|
|
1546
|
+
return `${minutes + 1} ${minutesPostfix}`;
|
|
1547
|
+
}
|
|
1548
|
+
return `${minutes} ${minutesPostfix} and ${remainingSeconds} ${secondsPostfix}`;
|
|
1549
|
+
}
|
|
1550
|
+
__name(toHumanPeriod, "toHumanPeriod");
|
|
1204
1551
|
|
|
1205
1552
|
// src/flow.ts
|
|
1206
1553
|
function createOAuthFlow(ctx) {
|
|
@@ -1243,22 +1590,37 @@ function createOAuthFlow(ctx) {
|
|
|
1243
1590
|
}
|
|
1244
1591
|
);
|
|
1245
1592
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1593
|
+
let oauth;
|
|
1594
|
+
if (props.device) {
|
|
1595
|
+
ctx.logger.log(
|
|
1596
|
+
"Attempting to login via OAuth Device Authorization Grant..."
|
|
1597
|
+
);
|
|
1598
|
+
oauth = await getOauthTokenViaDeviceFlow(
|
|
1599
|
+
{
|
|
1600
|
+
browser: props.browser ?? true,
|
|
1601
|
+
scopes: props.scopes,
|
|
1602
|
+
clientId: getClientId()
|
|
1603
|
+
},
|
|
1604
|
+
ctx
|
|
1605
|
+
);
|
|
1606
|
+
} else {
|
|
1607
|
+
ctx.logger.log("Attempting to login via OAuth...");
|
|
1608
|
+
oauth = await getOauthToken(
|
|
1609
|
+
{
|
|
1610
|
+
browser: props.browser ?? true,
|
|
1611
|
+
scopes: props.scopes,
|
|
1612
|
+
clientId: getClientId(),
|
|
1613
|
+
redirectUri: ctx.redirectUri,
|
|
1614
|
+
denied: consent.denied,
|
|
1615
|
+
granted: consent.granted,
|
|
1616
|
+
callbackHost: props.callbackHost ?? defaultCallbackHost,
|
|
1617
|
+
callbackPort: props.callbackPort ?? defaultCallbackPort
|
|
1618
|
+
},
|
|
1619
|
+
oauthFlowState,
|
|
1620
|
+
ctx,
|
|
1621
|
+
generators
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1262
1624
|
getStorage(props.profile).write({
|
|
1263
1625
|
oauth_token: oauth.token?.value ?? "",
|
|
1264
1626
|
expiration_time: oauth.token?.expiry,
|
|
@@ -3748,4 +4110,4 @@ smol-toml/dist/index.js:
|
|
|
3748
4110
|
*)
|
|
3749
4111
|
*/
|
|
3750
4112
|
|
|
3751
|
-
export { EncryptedFileCredentialStore, FileCredentialStore, LinuxSecretToolKeyProvider, MacSecurityKeyProvider, NapiKeyringKeyProvider, PINNED_KEYRING_VERSION, PKCE_CHARSET, TEMPORARY_TERMS_NOTICE, TEMPORARY_TERMS_PROMPT, clearAccessCaches, clearCredentialStorageState, createCredentialStorageContext, createOAuthFlow, createProfileStore, decryptString, dedent, domainUsesAccess, encryptString, findKeyringBinding, generateAuthUrl, generateKey, generateRandomState, getAccessHeaders, getAuthConfigFilePath, getAuthFromEnv, getAuthUrlFromEnv, getCloudflareAPITokenFromEnv, getCloudflareAccountIdFromEnv, getCloudflareAuthUseKeyringFromEnv, getCloudflareGlobalAuthEmailFromEnv, getCloudflareGlobalAuthKeyFromEnv, getEncryptedAuthConfigFilePath, getKeyringAccountName, getKeyringInstallDir, installKeyringBindingSync, parseEncryptedEnvelope, parseFile, probeSecretTool, readStoredAuthState, resetCredentialStorageState, resolveKeyProvider, scrubEncryptedCredentials, setKeyProviderFactoryForTesting, setKeyringEntryFactory, setLinuxSecretToolRunner, setMacSecurityCommandRunner, setNpmRunner, stringifyFile, validateAccountId, validateProfileName };
|
|
4113
|
+
export { EncryptedFileCredentialStore, FileCredentialStore, LinuxSecretToolKeyProvider, MacSecurityKeyProvider, NapiKeyringKeyProvider, PINNED_KEYRING_VERSION, PKCE_CHARSET, TEMPORARY_TERMS_NOTICE, TEMPORARY_TERMS_PROMPT, clearAccessCaches, clearCredentialStorageState, createCredentialStorageContext, createOAuthFlow, createProfileStore, decryptString, dedent, domainUsesAccess, encryptString, findKeyringBinding, generateAuthUrl, generateKey, generateRandomState, generateVerificationUrl, getAccessHeaders, getAuthConfigFilePath, getAuthFromEnv, getAuthUrlFromEnv, getCloudflareAPITokenFromEnv, getCloudflareAccountIdFromEnv, getCloudflareAuthUseKeyringFromEnv, getCloudflareGlobalAuthEmailFromEnv, getCloudflareGlobalAuthKeyFromEnv, getEncryptedAuthConfigFilePath, getKeyringAccountName, getKeyringInstallDir, installKeyringBindingSync, parseEncryptedEnvelope, parseFile, probeSecretTool, readStoredAuthState, resetCredentialStorageState, resolveKeyProvider, scrubEncryptedCredentials, setKeyProviderFactoryForTesting, setKeyringEntryFactory, setLinuxSecretToolRunner, setMacSecurityCommandRunner, setNpmRunner, stringifyFile, validateAccountId, validateProfileName };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stringifyFile, parseFile, dedent, createCredentialStorageContext, createOAuthFlow, getAuthFromEnv, createProfileStore, validateAccountId, getCloudflareAccountIdFromEnv, getAuthConfigFilePath, getEncryptedAuthConfigFilePath, scrubEncryptedCredentials, validateProfileName, getCloudflareAuthUseKeyringFromEnv } from './chunk-
|
|
1
|
+
import { stringifyFile, parseFile, dedent, createCredentialStorageContext, createOAuthFlow, getAuthFromEnv, createProfileStore, validateAccountId, getCloudflareAccountIdFromEnv, getAuthConfigFilePath, getEncryptedAuthConfigFilePath, scrubEncryptedCredentials, validateProfileName, getCloudflareAuthUseKeyringFromEnv } from './chunk-7AYQ2BAF.mjs';
|
|
2
2
|
import { __name } from './chunk-O6YSETKJ.mjs';
|
|
3
3
|
import { existsSync, rmSync, mkdirSync, writeFileSync, chmodSync, readFileSync as readFileSync$1, readdirSync } from 'node:fs';
|
|
4
4
|
import path3 from 'node:path';
|
|
@@ -810,6 +810,8 @@ Run \`${descriptor.commands.whoami}\` to check your current authentication statu
|
|
|
810
810
|
purgeOnLoginOrLogout: configCache.purgeConfigCaches,
|
|
811
811
|
clientId: descriptor.clientId,
|
|
812
812
|
consent: descriptor.consent,
|
|
813
|
+
displayName: descriptor.displayName,
|
|
814
|
+
deviceLoginCommand: descriptor.commands.deviceLogin,
|
|
813
815
|
redirectUri: descriptor.redirectUri,
|
|
814
816
|
storageFactory: credentialStorage.storageFactory,
|
|
815
817
|
allowGlobalAuthKey,
|
|
@@ -855,7 +857,8 @@ Run \`${descriptor.commands.whoami}\` to check your current authentication statu
|
|
|
855
857
|
browser: props?.browser ?? true,
|
|
856
858
|
callbackHost: props?.callbackHost,
|
|
857
859
|
callbackPort: props?.callbackPort,
|
|
858
|
-
profile: props?.profile
|
|
860
|
+
profile: props?.profile,
|
|
861
|
+
device: props?.device
|
|
859
862
|
};
|
|
860
863
|
}
|
|
861
864
|
__name(withDefaultScopes, "withDefaultScopes");
|
|
@@ -174,6 +174,18 @@ interface OAuthFlowContext {
|
|
|
174
174
|
* or denies consent.
|
|
175
175
|
*/
|
|
176
176
|
consent: OAuthConsentPages;
|
|
177
|
+
/**
|
|
178
|
+
* The consuming CLI's branded name (e.g. `"Wrangler"`, `"cf"`), interpolated
|
|
179
|
+
* into the copy the flow prints to the user — "To authorize <name>, please
|
|
180
|
+
* visit ...". Consumer-specific, so it is required.
|
|
181
|
+
*/
|
|
182
|
+
displayName: string;
|
|
183
|
+
/**
|
|
184
|
+
* The command that restarts the device authorization flow (e.g.
|
|
185
|
+
* `"wrangler login --device"`), quoted when a device code is denied,
|
|
186
|
+
* expires, or the flow times out. Consumer-specific, so it is required.
|
|
187
|
+
*/
|
|
188
|
+
deviceLoginCommand: string;
|
|
177
189
|
/**
|
|
178
190
|
* The `redirect_uri` registered on the consumer's OAuth app
|
|
179
191
|
*/
|
|
@@ -271,6 +283,16 @@ interface LoginProps {
|
|
|
271
283
|
* Only for use by 'auth create', not exposed to the user
|
|
272
284
|
*/
|
|
273
285
|
profile?: string;
|
|
286
|
+
/**
|
|
287
|
+
* When `true`, authenticate using the OAuth 2.0 Device Authorization Grant
|
|
288
|
+
* (RFC 8628) instead of the authorization-code-with-PKCE callback flow. The
|
|
289
|
+
* device flow does not start a local callback server and works in
|
|
290
|
+
* environments where the consumer's loopback callback URL
|
|
291
|
+
* ({@link OAuthFlowContext.redirectUri}) is unreachable from the user's
|
|
292
|
+
* browser (containers, remote SSH sessions, Codespaces). `callbackHost` and
|
|
293
|
+
* `callbackPort` are ignored when this is set.
|
|
294
|
+
*/
|
|
295
|
+
device?: boolean;
|
|
274
296
|
}
|
|
275
297
|
/**
|
|
276
298
|
* Public surface returned by {@link createOAuthFlow}.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { O as OAuthFlowLogger, U as UserAuthConfig, A as AuthConfigStorage, C as CredentialStore, F as FileFormat } from './file-format-
|
|
2
|
-
export { a as ConfigStorage, D as DeactivateDirectoryResult, i as DirectoryBindingOperations, j as DirectoryBindingsStorage, L as LoginOrRefreshFailureReason, d as LoginOrRefreshResult, e as LoginProps, b as OAuthFlowContext, P as ProfileConfigOperations, k as ProfileStore, T as TemporaryPreviewAccount, c as createOAuthFlow, h as createProfileStore, g as generateAuthUrl, f as generateRandomState, v as validateProfileName } from './file-format-
|
|
1
|
+
import { O as OAuthFlowLogger, U as UserAuthConfig, A as AuthConfigStorage, C as CredentialStore, F as FileFormat } from './file-format-Dbmn5hhd.mjs';
|
|
2
|
+
export { a as ConfigStorage, D as DeactivateDirectoryResult, i as DirectoryBindingOperations, j as DirectoryBindingsStorage, L as LoginOrRefreshFailureReason, d as LoginOrRefreshResult, e as LoginProps, b as OAuthFlowContext, P as ProfileConfigOperations, k as ProfileStore, T as TemporaryPreviewAccount, c as createOAuthFlow, h as createProfileStore, g as generateAuthUrl, f as generateRandomState, v as validateProfileName } from './file-format-Dbmn5hhd.mjs';
|
|
3
3
|
import { ApiCredentials } from '@cloudflare/workers-utils';
|
|
4
4
|
import { SpawnSyncReturns } from 'node:child_process';
|
|
5
5
|
|
|
@@ -103,6 +103,25 @@ declare function getCloudflareAccountIdFromEnv(): string | undefined;
|
|
|
103
103
|
*/
|
|
104
104
|
declare const getCloudflareAuthUseKeyringFromEnv: () => boolean | undefined;
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Build the URL the user should visit to approve a device authorization
|
|
108
|
+
* request, with the `user_code` embedded as a query parameter so the user
|
|
109
|
+
* does not have to type it manually.
|
|
110
|
+
*
|
|
111
|
+
* Per RFC 8628 §3.3.1, this is the "verification_uri_complete" optimization
|
|
112
|
+
* for non-textual transmission (such as QR codes). Authorization servers may
|
|
113
|
+
* return their own `verification_uri_complete` in the device authorization
|
|
114
|
+
* response — prefer that when it is provided. Use this helper as a fallback
|
|
115
|
+
* when only `verification_uri` is available.
|
|
116
|
+
*
|
|
117
|
+
* Extracted into its own module (mirroring `generate-auth-url.ts`) so that
|
|
118
|
+
* tests can mock the generated URL deterministically.
|
|
119
|
+
*/
|
|
120
|
+
declare const generateVerificationUrl: ({ verificationUri, userCode, }: {
|
|
121
|
+
verificationUri: string;
|
|
122
|
+
userCode: string;
|
|
123
|
+
}) => string;
|
|
124
|
+
|
|
106
125
|
declare const TEMPORARY_TERMS_PROMPT: string;
|
|
107
126
|
declare const TEMPORARY_TERMS_NOTICE: string;
|
|
108
127
|
|
|
@@ -790,4 +809,4 @@ declare function decryptString(envelope: EncryptedEnvelope, key: Uint8Array): st
|
|
|
790
809
|
*/
|
|
791
810
|
declare function parseEncryptedEnvelope(raw: unknown): EncryptedEnvelope | undefined;
|
|
792
811
|
|
|
793
|
-
export { AuthConfigStorage, type CredentialStorageBundle, type CredentialStorageContext, CredentialStore, type EncryptedEnvelope, EncryptedFileCredentialStore, FileCredentialStore, type KeyProvider, type KeyProviderResolution, type KeyringEntry, type KeyringEntryFactory, LinuxSecretToolKeyProvider, type LinuxSecretToolRunner, type MacSecurityCommandRunner, MacSecurityKeyProvider, NapiKeyringKeyProvider, type NpmRunner, OAuthFlowLogger, PINNED_KEYRING_VERSION, PKCE_CHARSET, type ScrubEncryptedCredentialsResult, TEMPORARY_TERMS_NOTICE, TEMPORARY_TERMS_PROMPT, UserAuthConfig, clearAccessCaches, clearCredentialStorageState, createCredentialStorageContext, decryptString, domainUsesAccess, encryptString, findKeyringBinding, generateKey, getAccessHeaders, getAuthConfigFilePath, getAuthFromEnv, getAuthUrlFromEnv, getCloudflareAPITokenFromEnv, getCloudflareAccountIdFromEnv, getCloudflareAuthUseKeyringFromEnv, getCloudflareGlobalAuthEmailFromEnv, getCloudflareGlobalAuthKeyFromEnv, getEncryptedAuthConfigFilePath, getKeyringAccountName, getKeyringInstallDir, installKeyringBindingSync, parseEncryptedEnvelope, probeSecretTool, readStoredAuthState, resetCredentialStorageState, resolveKeyProvider, scrubEncryptedCredentials, setKeyProviderFactoryForTesting, setKeyringEntryFactory, setLinuxSecretToolRunner, setMacSecurityCommandRunner, setNpmRunner };
|
|
812
|
+
export { AuthConfigStorage, type CredentialStorageBundle, type CredentialStorageContext, CredentialStore, type EncryptedEnvelope, EncryptedFileCredentialStore, FileCredentialStore, type KeyProvider, type KeyProviderResolution, type KeyringEntry, type KeyringEntryFactory, LinuxSecretToolKeyProvider, type LinuxSecretToolRunner, type MacSecurityCommandRunner, MacSecurityKeyProvider, NapiKeyringKeyProvider, type NpmRunner, OAuthFlowLogger, PINNED_KEYRING_VERSION, PKCE_CHARSET, type ScrubEncryptedCredentialsResult, TEMPORARY_TERMS_NOTICE, TEMPORARY_TERMS_PROMPT, UserAuthConfig, clearAccessCaches, clearCredentialStorageState, createCredentialStorageContext, decryptString, domainUsesAccess, encryptString, findKeyringBinding, generateKey, generateVerificationUrl, getAccessHeaders, getAuthConfigFilePath, getAuthFromEnv, getAuthUrlFromEnv, getCloudflareAPITokenFromEnv, getCloudflareAccountIdFromEnv, getCloudflareAuthUseKeyringFromEnv, getCloudflareGlobalAuthEmailFromEnv, getCloudflareGlobalAuthKeyFromEnv, getEncryptedAuthConfigFilePath, getKeyringAccountName, getKeyringInstallDir, installKeyringBindingSync, parseEncryptedEnvelope, probeSecretTool, readStoredAuthState, resetCredentialStorageState, resolveKeyProvider, scrubEncryptedCredentials, setKeyProviderFactoryForTesting, setKeyringEntryFactory, setLinuxSecretToolRunner, setMacSecurityCommandRunner, setNpmRunner };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { EncryptedFileCredentialStore, FileCredentialStore, LinuxSecretToolKeyProvider, MacSecurityKeyProvider, NapiKeyringKeyProvider, PINNED_KEYRING_VERSION, PKCE_CHARSET, TEMPORARY_TERMS_NOTICE, TEMPORARY_TERMS_PROMPT, clearAccessCaches, clearCredentialStorageState, createCredentialStorageContext, createOAuthFlow, createProfileStore, decryptString, domainUsesAccess, encryptString, findKeyringBinding, generateAuthUrl, generateKey, generateRandomState, getAccessHeaders, getAuthConfigFilePath, getAuthFromEnv, getAuthUrlFromEnv, getCloudflareAPITokenFromEnv, getCloudflareAccountIdFromEnv, getCloudflareAuthUseKeyringFromEnv, getCloudflareGlobalAuthEmailFromEnv, getCloudflareGlobalAuthKeyFromEnv, getEncryptedAuthConfigFilePath, getKeyringAccountName, getKeyringInstallDir, installKeyringBindingSync, parseEncryptedEnvelope, probeSecretTool, readStoredAuthState, resetCredentialStorageState, resolveKeyProvider, scrubEncryptedCredentials, setKeyProviderFactoryForTesting, setKeyringEntryFactory, setLinuxSecretToolRunner, setMacSecurityCommandRunner, setNpmRunner, validateProfileName } from './chunk-
|
|
1
|
+
export { EncryptedFileCredentialStore, FileCredentialStore, LinuxSecretToolKeyProvider, MacSecurityKeyProvider, NapiKeyringKeyProvider, PINNED_KEYRING_VERSION, PKCE_CHARSET, TEMPORARY_TERMS_NOTICE, TEMPORARY_TERMS_PROMPT, clearAccessCaches, clearCredentialStorageState, createCredentialStorageContext, createOAuthFlow, createProfileStore, decryptString, domainUsesAccess, encryptString, findKeyringBinding, generateAuthUrl, generateKey, generateRandomState, generateVerificationUrl, getAccessHeaders, getAuthConfigFilePath, getAuthFromEnv, getAuthUrlFromEnv, getCloudflareAPITokenFromEnv, getCloudflareAccountIdFromEnv, getCloudflareAuthUseKeyringFromEnv, getCloudflareGlobalAuthEmailFromEnv, getCloudflareGlobalAuthKeyFromEnv, getEncryptedAuthConfigFilePath, getKeyringAccountName, getKeyringInstallDir, installKeyringBindingSync, parseEncryptedEnvelope, probeSecretTool, readStoredAuthState, resetCredentialStorageState, resolveKeyProvider, scrubEncryptedCredentials, setKeyProviderFactoryForTesting, setKeyringEntryFactory, setLinuxSecretToolRunner, setMacSecurityCommandRunner, setNpmRunner, validateProfileName } from './chunk-7AYQ2BAF.mjs';
|
|
2
2
|
import './chunk-O6YSETKJ.mjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as OAuthConsentPages, F as FileFormat, b as OAuthFlowContext, C as CredentialStore, d as LoginOrRefreshResult, U as UserAuthConfig } from './file-format-
|
|
1
|
+
import { l as OAuthConsentPages, F as FileFormat, b as OAuthFlowContext, C as CredentialStore, d as LoginOrRefreshResult, U as UserAuthConfig } from './file-format-Dbmn5hhd.mjs';
|
|
2
2
|
import { ApiCredentials, ComplianceConfig } from '@cloudflare/workers-utils';
|
|
3
3
|
|
|
4
4
|
/** Minimal shape of the interactive `select` prompt's options for account selection. */
|
|
@@ -42,13 +42,29 @@ interface AuthContext {
|
|
|
42
42
|
* rather than a fork of the factory.
|
|
43
43
|
*/
|
|
44
44
|
interface CliDescriptor {
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* The CLI's invocation name, used for keyring install-dir scoping and in
|
|
47
|
+
* messaging that refers to the executable (e.g. `"wrangler"`, `"cf"`).
|
|
48
|
+
*/
|
|
46
49
|
cliName: string;
|
|
50
|
+
/**
|
|
51
|
+
* The CLI's branded name, used in prose addressed to the user (e.g.
|
|
52
|
+
* `"Wrangler"` for wrangler, `"cf"` for cf). Distinct from
|
|
53
|
+
* {@link CliDescriptor.cliName}, which names the executable.
|
|
54
|
+
*/
|
|
55
|
+
displayName: string;
|
|
47
56
|
/** Commands surfaced in auth guidance. */
|
|
48
57
|
commands: {
|
|
49
58
|
login: string;
|
|
50
59
|
whoami: string;
|
|
51
60
|
createProfile: string;
|
|
61
|
+
/**
|
|
62
|
+
* The command that restarts the OAuth 2.0 Device Authorization Grant,
|
|
63
|
+
* surfaced when a device code is denied, expires, or times out. Spelled
|
|
64
|
+
* out per CLI rather than derived from {@link CliDescriptor.commands.login}
|
|
65
|
+
* so the flag name stays the CLI's business.
|
|
66
|
+
*/
|
|
67
|
+
deviceLogin: string;
|
|
52
68
|
};
|
|
53
69
|
/**
|
|
54
70
|
* OS-keyring service identifier. Becomes the `-s` arg to macOS `security`,
|
|
@@ -110,6 +126,13 @@ interface CloudflareLoginProps {
|
|
|
110
126
|
callbackHost?: string;
|
|
111
127
|
callbackPort?: number;
|
|
112
128
|
profile?: string;
|
|
129
|
+
/**
|
|
130
|
+
* When `true`, authenticate using the OAuth 2.0 Device Authorization Grant
|
|
131
|
+
* (RFC 8628) instead of the authorization-code-with-PKCE callback flow. The
|
|
132
|
+
* device flow does not start a local callback server, so `callbackHost` and
|
|
133
|
+
* `callbackPort` are ignored when this is set.
|
|
134
|
+
*/
|
|
135
|
+
device?: boolean;
|
|
113
136
|
}
|
|
114
137
|
/** A Cloudflare CLI's auth layer, returned by {@link createCloudflareAuth}. */
|
|
115
138
|
interface CloudflareAuth {
|