1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | Object.defineProperty(exports, "__esModule", { value: true });
|
11 | exports.DeviceOauthService = void 0;
|
12 |
|
13 | const Transport = require("jsforce/lib/transport");
|
14 | const kit_1 = require("@salesforce/kit");
|
15 | const ts_types_1 = require("@salesforce/ts-types");
|
16 | const logger_1 = require("./logger");
|
17 | const authInfo_1 = require("./authInfo");
|
18 | const sfdxError_1 = require("./sfdxError");
|
19 | const connection_1 = require("./connection");
|
20 | async function wait(ms = 1000) {
|
21 | return new Promise((resolve) => {
|
22 | setTimeout(resolve, ms);
|
23 | });
|
24 | }
|
25 | async function makeRequest(options) {
|
26 | const rawResponse = await new Transport().httpRequest(options);
|
27 | const response = kit_1.parseJsonMap(rawResponse.body);
|
28 | if (response.error) {
|
29 | const err = new sfdxError_1.SfdxError('Request Failed.');
|
30 | err.data = Object.assign(response, { status: rawResponse.statusCode });
|
31 | throw err;
|
32 | }
|
33 | else {
|
34 | return response;
|
35 | }
|
36 | }
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | class DeviceOauthService extends kit_1.AsyncCreatable {
|
54 | constructor(options) {
|
55 | super(options);
|
56 | this.pollingCount = 0;
|
57 | this.options = options;
|
58 | if (!this.options.clientId)
|
59 | this.options.clientId = authInfo_1.DEFAULT_CONNECTED_APP_INFO.clientId;
|
60 | if (!this.options.loginUrl)
|
61 | this.options.loginUrl = authInfo_1.AuthInfo.getDefaultInstanceUrl();
|
62 | }
|
63 | |
64 |
|
65 |
|
66 |
|
67 |
|
68 | async requestDeviceLogin() {
|
69 | const deviceFlowRequestUrl = this.getDeviceFlowRequestUrl();
|
70 | const loginOptions = this.getLoginOptions(deviceFlowRequestUrl);
|
71 | return makeRequest(loginOptions);
|
72 | }
|
73 | |
74 |
|
75 |
|
76 |
|
77 |
|
78 | async awaitDeviceApproval(loginData) {
|
79 | const deviceFlowRequestUrl = this.getDeviceFlowRequestUrl();
|
80 | const pollingOptions = this.getPollingOptions(deviceFlowRequestUrl, loginData.device_code);
|
81 | const interval = kit_1.Duration.seconds(loginData.interval).milliseconds;
|
82 | const response = await this.pollForDeviceApproval(pollingOptions, interval);
|
83 | return response;
|
84 | }
|
85 | |
86 |
|
87 |
|
88 |
|
89 |
|
90 | async authorizeAndSave(approval) {
|
91 | const authInfo = await authInfo_1.AuthInfo.create({
|
92 | oauth2Options: {
|
93 | loginUrl: approval.instance_url,
|
94 | refreshToken: approval.refresh_token,
|
95 | clientSecret: this.options.clientSecret,
|
96 | clientId: this.options.clientId,
|
97 | },
|
98 | });
|
99 | await authInfo.save();
|
100 | return authInfo;
|
101 | }
|
102 | async init() {
|
103 | this.logger = await logger_1.Logger.child(this.constructor.name);
|
104 | this.logger.debug(`this.options.clientId: ${this.options.clientId}`);
|
105 | this.logger.debug(`this.options.loginUrl: ${this.options.loginUrl}`);
|
106 | }
|
107 | getLoginOptions(url) {
|
108 | return {
|
109 | url,
|
110 | headers: connection_1.SFDX_HTTP_HEADERS,
|
111 | method: 'POST',
|
112 | form: {
|
113 | client_id: ts_types_1.ensureString(this.options.clientId),
|
114 | response_type: DeviceOauthService.RESPONSE_TYPE,
|
115 | scope: DeviceOauthService.SCOPE,
|
116 | },
|
117 | };
|
118 | }
|
119 | getPollingOptions(url, code) {
|
120 | return {
|
121 | url,
|
122 | headers: connection_1.SFDX_HTTP_HEADERS,
|
123 | method: 'POST',
|
124 | form: {
|
125 | code,
|
126 | grant_type: DeviceOauthService.GRANT_TYPE,
|
127 | client_id: ts_types_1.ensureString(this.options.clientId),
|
128 | },
|
129 | };
|
130 | }
|
131 | getDeviceFlowRequestUrl() {
|
132 | return `${ts_types_1.ensureString(this.options.loginUrl)}/services/oauth2/token`;
|
133 | }
|
134 | async poll(pollingOptions) {
|
135 | this.logger.debug(`polling for device approval (attempt ${this.pollingCount} of ${DeviceOauthService.POLLING_COUNT_MAX})`);
|
136 | try {
|
137 | return await makeRequest(pollingOptions);
|
138 | }
|
139 | catch (e) {
|
140 | const err = e.data;
|
141 | if (err.error && err.status === 400 && err.error === 'authorization_pending') {
|
142 |
|
143 | }
|
144 | else {
|
145 | if (err.error && err.error_description) {
|
146 | this.logger.error(`Polling error: ${err.error}: ${err.error_description}`);
|
147 | }
|
148 | else {
|
149 | this.logger.error('Unknown Polling Error:');
|
150 | this.logger.error(err);
|
151 | }
|
152 | throw err;
|
153 | }
|
154 | }
|
155 | }
|
156 | shouldContinuePolling() {
|
157 | return this.pollingCount < DeviceOauthService.POLLING_COUNT_MAX;
|
158 | }
|
159 | async pollForDeviceApproval(pollingOptions, interval) {
|
160 | this.logger.debug('BEGIN POLLING FOR DEVICE APPROVAL');
|
161 | let result;
|
162 | while (this.shouldContinuePolling()) {
|
163 | result = await this.poll(pollingOptions);
|
164 | if (result) {
|
165 | this.logger.debug('POLLING FOR DEVICE APPROVAL SUCCESS');
|
166 | break;
|
167 | }
|
168 | else {
|
169 | this.logger.debug(`waiting ${interval} ms...`);
|
170 | await wait(interval);
|
171 | this.pollingCount += 1;
|
172 | }
|
173 | }
|
174 | if (this.pollingCount >= DeviceOauthService.POLLING_COUNT_MAX) {
|
175 |
|
176 | this.logger.error(`Polling timed out because max polling was hit: ${this.pollingCount}`);
|
177 | throw sfdxError_1.SfdxError.create('@salesforce/core', 'auth', 'pollingTimeout');
|
178 | }
|
179 | return result;
|
180 | }
|
181 | }
|
182 | exports.DeviceOauthService = DeviceOauthService;
|
183 | DeviceOauthService.RESPONSE_TYPE = 'device_code';
|
184 | DeviceOauthService.GRANT_TYPE = 'device';
|
185 | DeviceOauthService.SCOPE = 'refresh_token web api';
|
186 | DeviceOauthService.POLLING_COUNT_MAX = 100;
|
187 |
|
\ | No newline at end of file |