ring-client-api 11.7.7 → 11.8.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/lib/api.js +26 -9
- package/lib/rest-client.d.ts +4 -0
- package/lib/rest-client.js +68 -17
- package/lib/ring-camera.js +6 -2
- package/lib/ring-types.js +7 -7
- package/lib/streaming/webrtc-connection.js +1 -1
- package/lib/test/rest-client.spec.js +18 -12
- package/lib/util.d.ts +2 -0
- package/lib/util.js +11 -1
- package/package.json +12 -12
package/lib/api.js
CHANGED
|
@@ -123,7 +123,8 @@ class RingApi extends subscribed_1.Subscribed {
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
async registerPushReceiver(cameras, intercoms) {
|
|
126
|
-
const pushReceiver = new push_receiver_1.default({
|
|
126
|
+
const credentials = this.restClient._internalOnly_pushNotificationCredentials, pushReceiver = new push_receiver_1.default({
|
|
127
|
+
credentials,
|
|
127
128
|
logLevel: 'NONE',
|
|
128
129
|
senderId: '876313859327', // for Ring android app. 703521446232 for ring-site
|
|
129
130
|
}), devicesById = {}, sendToDevice = (id, notification) => {
|
|
@@ -135,10 +136,15 @@ class RingApi extends subscribed_1.Subscribed {
|
|
|
135
136
|
for (const intercom of intercoms) {
|
|
136
137
|
devicesById[intercom.id] = intercom;
|
|
137
138
|
}
|
|
138
|
-
pushReceiver.onCredentialsChanged(({ newCredentials
|
|
139
|
+
pushReceiver.onCredentialsChanged(({ newCredentials }) => {
|
|
140
|
+
// Store the new credentials in the rest client so that it can be used for subsequent restarts
|
|
141
|
+
this.restClient._internalOnly_pushNotificationCredentials = newCredentials;
|
|
142
|
+
// Send the new credentials to the server
|
|
143
|
+
onPushNotificationToken.next(newCredentials.fcm.token);
|
|
144
|
+
});
|
|
139
145
|
this.addSubscriptions((0, rxjs_1.combineLatest)([
|
|
140
146
|
onPushNotificationToken,
|
|
141
|
-
this.restClient.onSession, // combined but not used here, just to trigger another request when session is updated
|
|
147
|
+
this.restClient.onSession.pipe((0, operators_1.startWith)(undefined)), // combined but not used here, just to trigger another request when session is updated
|
|
142
148
|
]).subscribe(async ([token]) => {
|
|
143
149
|
try {
|
|
144
150
|
await this.restClient.request({
|
|
@@ -160,7 +166,21 @@ class RingApi extends subscribed_1.Subscribed {
|
|
|
160
166
|
(0, util_1.logError)(e);
|
|
161
167
|
}
|
|
162
168
|
}));
|
|
169
|
+
try {
|
|
170
|
+
await pushReceiver.connect();
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
(0, util_1.logError)('Failed to connect push notification receiver');
|
|
174
|
+
(0, util_1.logError)(e);
|
|
175
|
+
}
|
|
176
|
+
const startTime = Date.now();
|
|
163
177
|
pushReceiver.onNotification(({ message }) => {
|
|
178
|
+
// Ignore messages received in the first two seconds after connecting
|
|
179
|
+
// These are likely duplicates, and we aren't currently storying persistent ids anywhere to avoid re-processing them
|
|
180
|
+
if (Date.now() - startTime < 2000) {
|
|
181
|
+
(0, util_1.logInfo)('Ignoring push notification received in first two seconds after starting up');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
164
184
|
const dataJson = message.data?.gcmData;
|
|
165
185
|
try {
|
|
166
186
|
const notification = (0, json_bigint_1.default)({ storeAsString: true }).parse(dataJson);
|
|
@@ -176,12 +196,9 @@ class RingApi extends subscribed_1.Subscribed {
|
|
|
176
196
|
(0, util_1.logError)(e);
|
|
177
197
|
}
|
|
178
198
|
});
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
catch (e) {
|
|
183
|
-
(0, util_1.logError)('Failed to connect push notification receiver');
|
|
184
|
-
(0, util_1.logError)(e);
|
|
199
|
+
// If we already have credentials, use them immediately
|
|
200
|
+
if (credentials) {
|
|
201
|
+
onPushNotificationToken.next(credentials.fcm.token);
|
|
185
202
|
}
|
|
186
203
|
}
|
|
187
204
|
async fetchRawLocations() {
|
package/lib/rest-client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Options as RequestOptions } from 'got';
|
|
2
2
|
import { AuthTokenResponse, SessionResponse } from './ring-types';
|
|
3
3
|
import { ReplaySubject } from 'rxjs';
|
|
4
|
+
import type { Credentials } from '@eneris/push-receiver/dist/types';
|
|
4
5
|
export declare function clientApi(path: string): string;
|
|
5
6
|
export declare function deviceApi(path: string): string;
|
|
6
7
|
export declare function commandsApi(path: string): string;
|
|
@@ -24,6 +25,7 @@ export interface SessionOptions {
|
|
|
24
25
|
export declare class RingRestClient {
|
|
25
26
|
private authOptions;
|
|
26
27
|
refreshToken: string | undefined;
|
|
28
|
+
private authConfig;
|
|
27
29
|
private hardwareIdPromise;
|
|
28
30
|
private _authPromise;
|
|
29
31
|
private timeouts;
|
|
@@ -54,4 +56,6 @@ export declare class RingRestClient {
|
|
|
54
56
|
}): Promise<T & ExtendedResponse>;
|
|
55
57
|
getCurrentAuth(): Promise<AuthTokenResponse>;
|
|
56
58
|
clearTimeouts(): void;
|
|
59
|
+
get _internalOnly_pushNotificationCredentials(): Credentials | undefined;
|
|
60
|
+
set _internalOnly_pushNotificationCredentials(credentials: Credentials | undefined);
|
|
57
61
|
}
|
package/lib/rest-client.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.RingRestClient = exports.appApi = exports.commandsApi = exports.deviceAp
|
|
|
7
7
|
const got_1 = __importDefault(require("got"));
|
|
8
8
|
const util_1 = require("./util");
|
|
9
9
|
const rxjs_1 = require("rxjs");
|
|
10
|
+
const assert_1 = __importDefault(require("assert"));
|
|
10
11
|
const defaultRequestOptions = {
|
|
11
12
|
responseType: 'json',
|
|
12
13
|
method: 'GET',
|
|
@@ -64,6 +65,22 @@ async function requestWithRetry(requestOptions) {
|
|
|
64
65
|
throw e;
|
|
65
66
|
}
|
|
66
67
|
}
|
|
68
|
+
function parseAuthConfig(rawRefreshToken) {
|
|
69
|
+
if (!rawRefreshToken) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const config = JSON.parse((0, util_1.fromBase64)(rawRefreshToken));
|
|
74
|
+
(0, assert_1.default)(config);
|
|
75
|
+
(0, assert_1.default)(config.rt);
|
|
76
|
+
return config;
|
|
77
|
+
}
|
|
78
|
+
catch (_) {
|
|
79
|
+
return {
|
|
80
|
+
rt: rawRefreshToken,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
67
84
|
class RingRestClient {
|
|
68
85
|
clearPreviousAuth() {
|
|
69
86
|
this._authPromise = undefined;
|
|
@@ -90,6 +107,11 @@ class RingRestClient {
|
|
|
90
107
|
}
|
|
91
108
|
constructor(authOptions) {
|
|
92
109
|
this.authOptions = authOptions;
|
|
110
|
+
this.refreshToken = 'refreshToken' in this.authOptions
|
|
111
|
+
? this.authOptions.refreshToken
|
|
112
|
+
: undefined;
|
|
113
|
+
this.authConfig = parseAuthConfig(this.refreshToken);
|
|
114
|
+
this.hardwareIdPromise = this.authConfig?.hid || (0, util_1.getHardwareId)(this.authOptions.systemId);
|
|
93
115
|
this.timeouts = [];
|
|
94
116
|
this.sessionPromise = undefined;
|
|
95
117
|
this.using2fa = false;
|
|
@@ -99,17 +121,12 @@ class RingRestClient {
|
|
|
99
121
|
api_version: apiVersion,
|
|
100
122
|
device_model: this.authOptions.controlCenterDisplayName ?? 'ring-client-api',
|
|
101
123
|
};
|
|
102
|
-
this.refreshToken =
|
|
103
|
-
'refreshToken' in this.authOptions
|
|
104
|
-
? this.authOptions.refreshToken
|
|
105
|
-
: undefined;
|
|
106
|
-
this.hardwareIdPromise = (0, util_1.getHardwareId)(this.authOptions.systemId);
|
|
107
124
|
}
|
|
108
125
|
getGrantData(twoFactorAuthCode) {
|
|
109
|
-
if (this.
|
|
126
|
+
if (this.authConfig?.rt && !twoFactorAuthCode) {
|
|
110
127
|
return {
|
|
111
128
|
grant_type: 'refresh_token',
|
|
112
|
-
refresh_token: this.
|
|
129
|
+
refresh_token: this.authConfig.rt,
|
|
113
130
|
};
|
|
114
131
|
}
|
|
115
132
|
const { authOptions } = this;
|
|
@@ -125,7 +142,7 @@ class RingRestClient {
|
|
|
125
142
|
async getAuth(twoFactorAuthCode) {
|
|
126
143
|
const grantData = this.getGrantData(twoFactorAuthCode);
|
|
127
144
|
try {
|
|
128
|
-
const response = await requestWithRetry({
|
|
145
|
+
const hardwareId = await this.hardwareIdPromise, response = await requestWithRetry({
|
|
129
146
|
url: 'https://oauth.ring.com/oauth/token',
|
|
130
147
|
json: {
|
|
131
148
|
client_id: 'ring_official_android',
|
|
@@ -136,21 +153,33 @@ class RingRestClient {
|
|
|
136
153
|
headers: {
|
|
137
154
|
'2fa-support': 'true',
|
|
138
155
|
'2fa-code': twoFactorAuthCode || '',
|
|
139
|
-
hardware_id:
|
|
156
|
+
hardware_id: hardwareId,
|
|
140
157
|
'User-Agent': 'android:com.ringapp',
|
|
141
158
|
},
|
|
142
|
-
});
|
|
159
|
+
}), oldRefreshToken = this.refreshToken;
|
|
160
|
+
// Store the new refresh token and auth config
|
|
161
|
+
this.authConfig = {
|
|
162
|
+
...this.authConfig,
|
|
163
|
+
rt: response.refresh_token,
|
|
164
|
+
hid: hardwareId,
|
|
165
|
+
};
|
|
166
|
+
this.refreshToken = (0, util_1.toBase64)(JSON.stringify(this.authConfig));
|
|
167
|
+
// Emit an event with the new token
|
|
143
168
|
this.onRefreshTokenUpdated.next({
|
|
144
|
-
oldRefreshToken
|
|
145
|
-
newRefreshToken:
|
|
169
|
+
oldRefreshToken,
|
|
170
|
+
newRefreshToken: this.refreshToken,
|
|
146
171
|
});
|
|
147
|
-
|
|
148
|
-
|
|
172
|
+
return {
|
|
173
|
+
...response,
|
|
174
|
+
// Override the refresh token in the response so that consumers of this data get the wrapped version
|
|
175
|
+
refresh_token: this.refreshToken,
|
|
176
|
+
};
|
|
149
177
|
}
|
|
150
178
|
catch (requestError) {
|
|
151
179
|
if (grantData.refresh_token) {
|
|
152
180
|
// failed request with refresh token
|
|
153
181
|
this.refreshToken = undefined;
|
|
182
|
+
this.authConfig = undefined;
|
|
154
183
|
(0, util_1.logError)(requestError);
|
|
155
184
|
return this.getAuth();
|
|
156
185
|
}
|
|
@@ -250,9 +279,6 @@ class RingRestClient {
|
|
|
250
279
|
});
|
|
251
280
|
}
|
|
252
281
|
async request(options) {
|
|
253
|
-
if (!this.sessionPromise) {
|
|
254
|
-
this.refreshSession();
|
|
255
|
-
}
|
|
256
282
|
const hardwareId = await this.hardwareIdPromise, url = options.url, initialSessionPromise = this.sessionPromise;
|
|
257
283
|
try {
|
|
258
284
|
await initialSessionPromise;
|
|
@@ -319,5 +345,30 @@ class RingRestClient {
|
|
|
319
345
|
clearTimeouts() {
|
|
320
346
|
this.timeouts.forEach(clearTimeout);
|
|
321
347
|
}
|
|
348
|
+
get _internalOnly_pushNotificationCredentials() {
|
|
349
|
+
return this.authConfig?.pnc;
|
|
350
|
+
}
|
|
351
|
+
set _internalOnly_pushNotificationCredentials(credentials) {
|
|
352
|
+
if (!this.refreshToken || !this.authConfig) {
|
|
353
|
+
throw new Error('Cannot set push notification credentials without a refresh token');
|
|
354
|
+
}
|
|
355
|
+
const oldRefreshToken = this.refreshToken;
|
|
356
|
+
this.authConfig = {
|
|
357
|
+
...this.authConfig,
|
|
358
|
+
pnc: credentials,
|
|
359
|
+
};
|
|
360
|
+
// SOMEDAY: refactor the conversion from auth config to refresh token - DRY from above
|
|
361
|
+
const newRefreshToken = (0, util_1.toBase64)(JSON.stringify(this.authConfig));
|
|
362
|
+
if (newRefreshToken === oldRefreshToken) {
|
|
363
|
+
// No change, so we don't need to emit an updated refresh token
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
// Save and emit the updated refresh token
|
|
367
|
+
this.refreshToken = newRefreshToken;
|
|
368
|
+
this.onRefreshTokenUpdated.next({
|
|
369
|
+
oldRefreshToken,
|
|
370
|
+
newRefreshToken,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
322
373
|
}
|
|
323
374
|
exports.RingRestClient = RingRestClient;
|
package/lib/ring-camera.js
CHANGED
|
@@ -99,9 +99,13 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
99
99
|
this.onInHomeDoorbellStatus = this.onData.pipe((0, operators_1.map)(({ settings: { chime_settings } }) => {
|
|
100
100
|
return Boolean(chime_settings?.enable);
|
|
101
101
|
}), (0, operators_1.distinctUntilChanged)());
|
|
102
|
-
this.addSubscriptions(this.restClient.onSession
|
|
102
|
+
this.addSubscriptions(this.restClient.onSession
|
|
103
|
+
.pipe((0, operators_1.startWith)(undefined), (0, operators_1.throttleTime)(1000)) // Force this to run immediately, but don't double run if a session is created due to these api calls
|
|
104
|
+
.subscribe(() => {
|
|
103
105
|
this.subscribeToDingEvents().catch((e) => {
|
|
104
|
-
(0, util_1.logError)('Failed to subscribe ' +
|
|
106
|
+
(0, util_1.logError)('Failed to subscribe ' +
|
|
107
|
+
initialData.description +
|
|
108
|
+
' to ding events');
|
|
105
109
|
(0, util_1.logError)(e);
|
|
106
110
|
});
|
|
107
111
|
this.subscribeToMotionEvents().catch((e) => {
|
package/lib/ring-types.js
CHANGED
|
@@ -47,7 +47,7 @@ var RingDeviceType;
|
|
|
47
47
|
RingDeviceType["OnvifCamera"] = "onvif_camera";
|
|
48
48
|
RingDeviceType["ThirdPartyGarageDoorOpener"] = "third_party_gdo";
|
|
49
49
|
RingDeviceType["IntercomHandsetAudio"] = "intercom_handset_audio";
|
|
50
|
-
})(RingDeviceType
|
|
50
|
+
})(RingDeviceType || (exports.RingDeviceType = RingDeviceType = {}));
|
|
51
51
|
// eslint-disable-next-line no-shadow
|
|
52
52
|
var RingDeviceCategory;
|
|
53
53
|
(function (RingDeviceCategory) {
|
|
@@ -68,7 +68,7 @@ var RingDeviceCategory;
|
|
|
68
68
|
RingDeviceCategory[RingDeviceCategory["Keypads"] = 33] = "Keypads";
|
|
69
69
|
RingDeviceCategory[RingDeviceCategory["Sirens"] = 34] = "Sirens";
|
|
70
70
|
RingDeviceCategory[RingDeviceCategory["PanicButtons"] = 35] = "PanicButtons";
|
|
71
|
-
})(RingDeviceCategory
|
|
71
|
+
})(RingDeviceCategory || (exports.RingDeviceCategory = RingDeviceCategory = {}));
|
|
72
72
|
// eslint-disable-next-line no-shadow
|
|
73
73
|
var RingCameraKind;
|
|
74
74
|
(function (RingCameraKind) {
|
|
@@ -102,7 +102,7 @@ var RingCameraKind;
|
|
|
102
102
|
RingCameraKind["cocoa_floodlight"] = "cocoa_floodlight";
|
|
103
103
|
RingCameraKind["stickup_cam_mini"] = "stickup_cam_mini";
|
|
104
104
|
RingCameraKind["onvif_camera"] = "onvif_camera";
|
|
105
|
-
})(RingCameraKind
|
|
105
|
+
})(RingCameraKind || (exports.RingCameraKind = RingCameraKind = {}));
|
|
106
106
|
exports.RingCameraModel = {
|
|
107
107
|
doorbot: 'Doorbell',
|
|
108
108
|
doorbell: 'Doorbell',
|
|
@@ -162,7 +162,7 @@ var DoorbellType;
|
|
|
162
162
|
DoorbellType[DoorbellType["Mechanical"] = 0] = "Mechanical";
|
|
163
163
|
DoorbellType[DoorbellType["Digital"] = 1] = "Digital";
|
|
164
164
|
DoorbellType[DoorbellType["None"] = 2] = "None";
|
|
165
|
-
})(DoorbellType
|
|
165
|
+
})(DoorbellType || (exports.DoorbellType = DoorbellType = {}));
|
|
166
166
|
// eslint-disable-next-line no-shadow
|
|
167
167
|
var NotificationDetectionType;
|
|
168
168
|
(function (NotificationDetectionType) {
|
|
@@ -173,7 +173,7 @@ var NotificationDetectionType;
|
|
|
173
173
|
NotificationDetectionType["OtherMotion"] = "other_motion";
|
|
174
174
|
NotificationDetectionType["NotAvailable"] = "null";
|
|
175
175
|
NotificationDetectionType["StreamBroken"] = "stream_broken";
|
|
176
|
-
})(NotificationDetectionType
|
|
176
|
+
})(NotificationDetectionType || (exports.NotificationDetectionType = NotificationDetectionType = {}));
|
|
177
177
|
// eslint-disable-next-line no-shadow
|
|
178
178
|
var PushNotificationAction;
|
|
179
179
|
(function (PushNotificationAction) {
|
|
@@ -181,13 +181,13 @@ var PushNotificationAction;
|
|
|
181
181
|
PushNotificationAction["Motion"] = "com.ring.push.HANDLE_NEW_motion";
|
|
182
182
|
PushNotificationAction["LowBattery"] = "com.ring.push.LOW_BATTERY_ALERT";
|
|
183
183
|
PushNotificationAction["IntercomUnlock"] = "com.ring.push.INTERCOM_UNLOCK_FROM_APP";
|
|
184
|
-
})(PushNotificationAction
|
|
184
|
+
})(PushNotificationAction || (exports.PushNotificationAction = PushNotificationAction = {}));
|
|
185
185
|
// eslint-disable-next-line no-shadow
|
|
186
186
|
var DispatchSignalType;
|
|
187
187
|
(function (DispatchSignalType) {
|
|
188
188
|
DispatchSignalType["Burglar"] = "user-verified-burglar-xa";
|
|
189
189
|
DispatchSignalType["Fire"] = "user-verified-fire-xa";
|
|
190
|
-
})(DispatchSignalType
|
|
190
|
+
})(DispatchSignalType || (exports.DispatchSignalType = DispatchSignalType = {}));
|
|
191
191
|
exports.disabledLocationModes = ['disabled', 'unset'];
|
|
192
192
|
function isWebSocketSupportedAsset({ kind }) {
|
|
193
193
|
return kind.startsWith('base_station') || kind.startsWith('beams_bridge');
|
|
@@ -5,7 +5,7 @@ const ws_1 = require("ws");
|
|
|
5
5
|
const streaming_connection_base_1 = require("./streaming-connection-base");
|
|
6
6
|
const util_1 = require("../util");
|
|
7
7
|
function parseLiveCallSession(sessionId) {
|
|
8
|
-
const encodedSession = sessionId.split('.')[1],
|
|
8
|
+
const encodedSession = sessionId.split('.')[1], text = (0, util_1.fromBase64)(encodedSession);
|
|
9
9
|
return JSON.parse(text);
|
|
10
10
|
}
|
|
11
11
|
class WebrtcConnection extends streaming_connection_base_1.StreamingConnectionBase {
|
|
@@ -106,6 +106,12 @@ const email = 'some@one.com', password = 'abc123!', phone = '+1xxxxxxxx89', twoF
|
|
|
106
106
|
},
|
|
107
107
|
}));
|
|
108
108
|
}));
|
|
109
|
+
async function wrapRefreshToken(rt) {
|
|
110
|
+
return (0, util_1.toBase64)(JSON.stringify({
|
|
111
|
+
rt,
|
|
112
|
+
hid: await hardwareIdPromise,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
109
115
|
beforeEach(() => {
|
|
110
116
|
sessionCreatedCount = 0;
|
|
111
117
|
});
|
|
@@ -143,9 +149,9 @@ describe('getAuth', () => {
|
|
|
143
149
|
const auth = await client.getAuth(twoFactorAuthCode);
|
|
144
150
|
expect(auth).toMatchObject({
|
|
145
151
|
access_token: accessToken,
|
|
146
|
-
refresh_token: refreshToken,
|
|
152
|
+
refresh_token: await wrapRefreshToken(refreshToken),
|
|
147
153
|
});
|
|
148
|
-
expect(client.refreshToken).toEqual(refreshToken);
|
|
154
|
+
expect(client.refreshToken).toEqual(await wrapRefreshToken(refreshToken));
|
|
149
155
|
});
|
|
150
156
|
test('it should handle invalid credentials', async () => {
|
|
151
157
|
client = new rest_client_1.RingRestClient({
|
|
@@ -171,9 +177,9 @@ describe('getAuth', () => {
|
|
|
171
177
|
});
|
|
172
178
|
expect(await client.getCurrentAuth()).toMatchObject({
|
|
173
179
|
access_token: accessToken,
|
|
174
|
-
refresh_token: secondRefreshToken,
|
|
180
|
+
refresh_token: await wrapRefreshToken(secondRefreshToken),
|
|
175
181
|
});
|
|
176
|
-
expect(client.refreshToken).toEqual(secondRefreshToken);
|
|
182
|
+
expect(client.refreshToken).toEqual(await wrapRefreshToken(secondRefreshToken));
|
|
177
183
|
});
|
|
178
184
|
test('it should emit an event when a new refresh token is created', async () => {
|
|
179
185
|
client = new rest_client_1.RingRestClient({
|
|
@@ -182,11 +188,11 @@ describe('getAuth', () => {
|
|
|
182
188
|
const refreshedPromise = (0, rxjs_1.firstValueFrom)(client.onRefreshTokenUpdated), auth = await client.getAuth();
|
|
183
189
|
expect(auth).toMatchObject({
|
|
184
190
|
access_token: accessToken,
|
|
185
|
-
refresh_token: secondRefreshToken,
|
|
191
|
+
refresh_token: await wrapRefreshToken(secondRefreshToken),
|
|
186
192
|
});
|
|
187
193
|
expect(await refreshedPromise).toEqual({
|
|
188
194
|
oldRefreshToken: refreshToken,
|
|
189
|
-
newRefreshToken: secondRefreshToken,
|
|
195
|
+
newRefreshToken: await wrapRefreshToken(secondRefreshToken),
|
|
190
196
|
});
|
|
191
197
|
});
|
|
192
198
|
});
|
|
@@ -194,7 +200,7 @@ describe('fetch', () => {
|
|
|
194
200
|
let invalidateFirstAccessToken = false;
|
|
195
201
|
beforeEach(() => {
|
|
196
202
|
invalidateFirstAccessToken = false;
|
|
197
|
-
server.use(msw_1.rest.get('https://api.ring.com/
|
|
203
|
+
server.use(msw_1.rest.get('https://api.ring.com/clients_api/some_endpoint', (req, res, ctx) => {
|
|
198
204
|
const authHeader = req.headers.get('Authorization');
|
|
199
205
|
if (invalidateFirstAccessToken &&
|
|
200
206
|
authHeader === `Bearer ${accessToken}`) {
|
|
@@ -207,10 +213,10 @@ describe('fetch', () => {
|
|
|
207
213
|
return res(ctx.status(401));
|
|
208
214
|
}
|
|
209
215
|
if (sessionCreatedCount === 0) {
|
|
210
|
-
// eslint-disable-next-line no-console
|
|
211
|
-
console.error('Request received before session was created');
|
|
212
216
|
// Session not created yet
|
|
213
|
-
return res(ctx.status(404), ctx.
|
|
217
|
+
return res(ctx.status(404), ctx.json({
|
|
218
|
+
error: 'Session not found for ' + req.headers.get('hardware_id'),
|
|
219
|
+
}));
|
|
214
220
|
}
|
|
215
221
|
return res(ctx.json([]));
|
|
216
222
|
}));
|
|
@@ -220,7 +226,7 @@ describe('fetch', () => {
|
|
|
220
226
|
refreshToken,
|
|
221
227
|
});
|
|
222
228
|
const response = await client.request({
|
|
223
|
-
url: 'https://api.ring.com/
|
|
229
|
+
url: 'https://api.ring.com/clients_api/some_endpoint',
|
|
224
230
|
});
|
|
225
231
|
expect(response).toEqual([]);
|
|
226
232
|
});
|
|
@@ -230,7 +236,7 @@ describe('fetch', () => {
|
|
|
230
236
|
});
|
|
231
237
|
invalidateFirstAccessToken = true;
|
|
232
238
|
const response = await client.request({
|
|
233
|
-
url: 'https://api.ring.com/
|
|
239
|
+
url: 'https://api.ring.com/clients_api/some_endpoint',
|
|
234
240
|
});
|
|
235
241
|
expect(response).toEqual([]);
|
|
236
242
|
});
|
package/lib/util.d.ts
CHANGED
|
@@ -19,4 +19,6 @@ export declare function randomString(length: number): string;
|
|
|
19
19
|
export type DeepPartial<T> = {
|
|
20
20
|
[K in keyof T]?: T[K] extends Array<infer U> ? Array<DeepPartial<U>> : T[K] extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : DeepPartial<T[K]>;
|
|
21
21
|
};
|
|
22
|
+
export declare function fromBase64(encodedInput: string): string;
|
|
23
|
+
export declare function toBase64(input: string): string;
|
|
22
24
|
export {};
|
package/lib/util.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.randomString = exports.randomInteger = exports.mapAsync = exports.stringify = exports.requestInput = exports.getHardwareId = exports.generateUuid = exports.enableDebug = exports.useLogger = exports.logError = exports.logInfo = exports.logDebug = exports.delay = exports.clearTimeouts = void 0;
|
|
6
|
+
exports.toBase64 = exports.fromBase64 = exports.randomString = exports.randomInteger = exports.mapAsync = exports.stringify = exports.requestInput = exports.getHardwareId = exports.generateUuid = exports.enableDebug = exports.useLogger = exports.logError = exports.logInfo = exports.logDebug = exports.delay = exports.clearTimeouts = void 0;
|
|
7
7
|
const debug_1 = __importDefault(require("debug"));
|
|
8
8
|
const colors_1 = require("colors");
|
|
9
9
|
const readline_1 = require("readline");
|
|
@@ -134,3 +134,13 @@ function logPushReceiver(...args) {
|
|
|
134
134
|
logDebug(args[0]);
|
|
135
135
|
}
|
|
136
136
|
logger_1.default.error = logPushReceiver;
|
|
137
|
+
function fromBase64(encodedInput) {
|
|
138
|
+
const buff = Buffer.from(encodedInput, 'base64');
|
|
139
|
+
return buff.toString('ascii');
|
|
140
|
+
}
|
|
141
|
+
exports.fromBase64 = fromBase64;
|
|
142
|
+
function toBase64(input) {
|
|
143
|
+
const buff = Buffer.from(input);
|
|
144
|
+
return buff.toString('base64');
|
|
145
|
+
}
|
|
146
|
+
exports.toBase64 = toBase64;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ring-client-api",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.8.0",
|
|
4
4
|
"description": "Unofficial API for Ring doorbells, cameras, security alarm system and smart lighting",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -27,29 +27,29 @@
|
|
|
27
27
|
"got": "^11.8.5",
|
|
28
28
|
"json-bigint": "^1.0.0",
|
|
29
29
|
"msw": "^1.2.1",
|
|
30
|
-
"rxjs": "^7.8.
|
|
30
|
+
"rxjs": "^7.8.1",
|
|
31
31
|
"socket.io-client": "^2.5.0",
|
|
32
|
-
"systeminformation": "^5.
|
|
32
|
+
"systeminformation": "^5.18.2",
|
|
33
33
|
"uuid": "^9.0.0",
|
|
34
34
|
"webcrypto-core": "^1.7.7",
|
|
35
|
-
"werift": "0.18.
|
|
35
|
+
"werift": "0.18.3",
|
|
36
36
|
"ws": "^8.13.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@types/debug": "4.1.
|
|
40
|
-
"@types/jest": "29.5.
|
|
39
|
+
"@types/debug": "4.1.8",
|
|
40
|
+
"@types/jest": "29.5.2",
|
|
41
41
|
"@types/json-bigint": "^1.0.1",
|
|
42
|
-
"@types/node": "
|
|
43
|
-
"@types/uuid": "9.0.
|
|
44
|
-
"@types/ws": "^8.5.
|
|
42
|
+
"@types/node": "20.2.5",
|
|
43
|
+
"@types/uuid": "9.0.2",
|
|
44
|
+
"@types/ws": "^8.5.5",
|
|
45
45
|
"eslint-config-shared": "*",
|
|
46
46
|
"jest": "29.5.0",
|
|
47
|
-
"ts-jest": "29.0
|
|
47
|
+
"ts-jest": "29.1.0",
|
|
48
48
|
"tsconfig": "*",
|
|
49
|
-
"typescript": "5.
|
|
49
|
+
"typescript": "5.1.3"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
|
-
"node": "^16 || ^18"
|
|
52
|
+
"node": "^16 || ^18 || ^20"
|
|
53
53
|
},
|
|
54
54
|
"repository": {
|
|
55
55
|
"type": "git",
|