1
1
import axios , { AxiosRequestConfig , ResponseType } from 'axios'
2
- import { delay , generateRandomId , logError , logInfo } from './util'
2
+ import {
3
+ delay ,
4
+ generateRandomId ,
5
+ logError ,
6
+ logInfo ,
7
+ requestInput
8
+ } from './util'
3
9
import * as querystring from 'querystring'
4
10
import { AuthTokenResponse , SessionResponse } from './ring-types'
5
11
@@ -63,14 +69,15 @@ export type RingAuth = EmailAuth | RefreshTokenAuth
63
69
64
70
export class RingRestClient {
65
71
// prettier-ignore
66
- private refreshToken = ( 'refreshToken' in this . authOptions ? this . authOptions . refreshToken : undefined )
72
+ public refreshToken = ( 'refreshToken' in this . authOptions ? this . authOptions . refreshToken : undefined )
67
73
private authPromise = this . getAuthToken ( )
68
74
private sessionPromise = this . getSession ( )
75
+ public using2fa = false
69
76
70
77
constructor ( private authOptions : RingAuth ) { }
71
78
72
- private getGrantData ( ) {
73
- if ( this . refreshToken ) {
79
+ private getGrantData ( twoFactorAuthCode ?: string ) {
80
+ if ( this . refreshToken && ! twoFactorAuthCode ) {
74
81
return {
75
82
grant_type : 'refresh_token' ,
76
83
refresh_token : this . refreshToken
@@ -91,8 +98,16 @@ export class RingRestClient {
91
98
)
92
99
}
93
100
94
- private async getAuthToken ( ) : Promise < AuthTokenResponse > {
95
- const grantData = this . getGrantData ( )
101
+ private async getAuthToken (
102
+ twoFactorAuthCode ?: string
103
+ ) : Promise < AuthTokenResponse > {
104
+ const grantData = this . getGrantData ( twoFactorAuthCode ) ,
105
+ twoFactorAuthHeaders = twoFactorAuthCode
106
+ ? {
107
+ '2fa-code' : twoFactorAuthCode ,
108
+ '2fa-Support' : 'true'
109
+ }
110
+ : { }
96
111
97
112
try {
98
113
const response = await requestWithRetry < AuthTokenResponse > ( {
@@ -104,7 +119,8 @@ export class RingRestClient {
104
119
} ,
105
120
method : 'POST' ,
106
121
headers : {
107
- 'content-type' : 'application/json'
122
+ 'content-type' : 'application/json' ,
123
+ ...twoFactorAuthHeaders
108
124
}
109
125
} )
110
126
@@ -118,8 +134,30 @@ export class RingRestClient {
118
134
return this . getAuthToken ( )
119
135
}
120
136
121
- const errorMessage =
122
- 'Failed to fetch oauth token from Ring. Verify that your email and password are correct.'
137
+ const response = requestError . response || { } ,
138
+ responseData = response . data || { } ,
139
+ responseError =
140
+ typeof responseData . error === 'string' ? responseData . error : ''
141
+
142
+ if (
143
+ response . status === 412 || // need 2fa code
144
+ ( response . status === 400 &&
145
+ responseError . startsWith ( 'Verification Code' ) ) // invalid 2fa code entered
146
+ ) {
147
+ const code = await requestInput (
148
+ 'Ring 2fa enabled. Please enter code from text message: '
149
+ )
150
+ this . using2fa = true
151
+ return this . getAuthToken ( code )
152
+ }
153
+
154
+ const authTypeMessage =
155
+ 'refreshToken' in this . authOptions
156
+ ? 'refresh token is'
157
+ : 'email and password are' ,
158
+ errorMessage =
159
+ `Failed to fetch oauth token from Ring. Verify that your ${ authTypeMessage } correct. ` +
160
+ responseError
123
161
logError ( requestError . response )
124
162
logError ( errorMessage )
125
163
throw new Error ( errorMessage )
0 commit comments