@@ -6,12 +6,14 @@ import Foundation
6
6
7
7
class AuthManager : IterableAuthManagerProtocol {
8
8
init ( delegate: IterableAuthDelegate ? ,
9
+ authRetryPolicy: RetryPolicy ,
9
10
expirationRefreshPeriod: TimeInterval ,
10
11
localStorage: LocalStorageProtocol ,
11
12
dateProvider: DateProviderProtocol ) {
12
13
ITBInfo ( )
13
14
14
15
self . delegate = delegate
16
+ self . authRetryPolicy = authRetryPolicy
15
17
self . localStorage = localStorage
16
18
self . dateProvider = dateProvider
17
19
self . expirationRefreshPeriod = expirationRefreshPeriod
@@ -35,26 +37,44 @@ class AuthManager: IterableAuthManagerProtocol {
35
37
hasFailedPriorAuth = false
36
38
}
37
39
38
- func requestNewAuthToken( hasFailedPriorAuth: Bool = false , onSuccess: AuthTokenRetrievalHandler ? = nil ) {
40
+ func requestNewAuthToken( hasFailedPriorAuth: Bool = false ,
41
+ onSuccess: AuthTokenRetrievalHandler ? = nil ,
42
+ shouldIgnoreRetryPolicy: Bool ) {
39
43
ITBInfo ( )
40
44
41
- guard !pendingAuth else {
42
- return
43
- }
44
-
45
- guard !self . hasFailedPriorAuth || !hasFailedPriorAuth else {
45
+ if shouldPauseRetry ( shouldIgnoreRetryPolicy) || pendingAuth || hasFailedAuth ( hasFailedPriorAuth) {
46
46
return
47
47
}
48
48
49
49
self . hasFailedPriorAuth = hasFailedPriorAuth
50
-
51
50
pendingAuth = true
52
51
52
+ if shouldUseLastValidToken ( shouldIgnoreRetryPolicy) {
53
+ // if some JWT retry had valid token it will not fetch the auth token again from developer function
54
+ onAuthTokenReceived ( retrievedAuthToken: authToken, onSuccess: onSuccess)
55
+ return
56
+ }
57
+
53
58
delegate? . onAuthTokenRequested { [ weak self] retrievedAuthToken in
59
+ self ? . pendingAuth = false
60
+ self ? . retryCount+= 1
54
61
self ? . onAuthTokenReceived ( retrievedAuthToken: retrievedAuthToken, onSuccess: onSuccess)
55
62
}
56
63
}
57
64
65
+ private func hasFailedAuth( _ hasFailedPriorAuth: Bool ) -> Bool {
66
+ return self . hasFailedPriorAuth && hasFailedPriorAuth
67
+ }
68
+
69
+ private func shouldPauseRetry( _ shouldIgnoreRetryPolicy: Bool ) -> Bool {
70
+ return ( !shouldIgnoreRetryPolicy && pauseAuthRetry) ||
71
+ ( retryCount >= authRetryPolicy. maxRetry && !shouldIgnoreRetryPolicy)
72
+ }
73
+
74
+ private func shouldUseLastValidToken( _ shouldIgnoreRetryPolicy: Bool ) -> Bool {
75
+ return isLastAuthTokenValid && !shouldIgnoreRetryPolicy
76
+ }
77
+
58
78
func setNewToken( _ newToken: String ) {
59
79
ITBInfo ( )
60
80
@@ -69,6 +89,7 @@ class AuthManager: IterableAuthManagerProtocol {
69
89
storeAuthToken ( )
70
90
71
91
clearRefreshTimer ( )
92
+ isLastAuthTokenValid = false
72
93
}
73
94
74
95
// MARK: - Private/Internal
@@ -79,11 +100,39 @@ class AuthManager: IterableAuthManagerProtocol {
79
100
private var pendingAuth : Bool = false
80
101
private var hasFailedPriorAuth : Bool = false
81
102
103
+ private var authRetryPolicy : RetryPolicy
104
+ private var retryCount : Int = 0
105
+ private var isLastAuthTokenValid : Bool = false
106
+ private var pauseAuthRetry : Bool = false
107
+ private var isTimerScheduled : Bool = false
108
+
82
109
private weak var delegate : IterableAuthDelegate ?
83
110
private let expirationRefreshPeriod : TimeInterval
84
111
private var localStorage : LocalStorageProtocol
85
112
private let dateProvider : DateProviderProtocol
86
113
114
+ func pauseAuthRetries( _ pauseAuthRetry: Bool ) {
115
+ self . pauseAuthRetry = pauseAuthRetry
116
+ resetRetryCount ( )
117
+ }
118
+
119
+ func setIsLastAuthTokenValid( _ isValid: Bool ) {
120
+ isLastAuthTokenValid = isValid
121
+ }
122
+
123
+ func getNextRetryInterval( ) -> Double {
124
+ var nextRetryInterval = Double ( authRetryPolicy. retryInterval)
125
+ if authRetryPolicy. retryBackoff == . exponential {
126
+ nextRetryInterval = Double ( nextRetryInterval) * pow( Const . exponentialFactor, Double ( retryCount - 1 ) )
127
+ }
128
+
129
+ return nextRetryInterval
130
+ }
131
+
132
+ private func resetRetryCount( ) {
133
+ retryCount = 0
134
+ }
135
+
87
136
private func storeAuthToken( ) {
88
137
localStorage. authToken = authToken
89
138
}
@@ -93,37 +142,34 @@ class AuthManager: IterableAuthManagerProtocol {
93
142
94
143
authToken = localStorage. authToken
95
144
96
- queueAuthTokenExpirationRefresh ( authToken)
145
+ _ = queueAuthTokenExpirationRefresh ( authToken)
97
146
}
98
147
99
148
private func onAuthTokenReceived( retrievedAuthToken: String ? , onSuccess: AuthTokenRetrievalHandler ? = nil ) {
100
149
ITBInfo ( )
101
150
102
151
pendingAuth = false
103
152
104
- guard retrievedAuthToken != nil else {
153
+ if retrievedAuthToken != nil {
154
+ let isRefreshQueued = queueAuthTokenExpirationRefresh ( retrievedAuthToken, onSuccess: onSuccess)
155
+ if !isRefreshQueued {
156
+ onSuccess ? ( authToken)
157
+ }
158
+ } else {
105
159
handleAuthFailure ( failedAuthToken: nil , reason: . authTokenNull)
106
-
107
- /// by default, schedule a refresh for 10s
108
- scheduleAuthTokenRefreshTimer ( 10 )
109
-
110
- return
160
+ scheduleAuthTokenRefreshTimer ( interval: getNextRetryInterval ( ) , successCallback: onSuccess)
111
161
}
112
162
113
163
authToken = retrievedAuthToken
114
164
115
165
storeAuthToken ( )
116
-
117
- queueAuthTokenExpirationRefresh ( authToken)
118
-
119
- onSuccess ? ( authToken)
120
166
}
121
167
122
168
func handleAuthFailure( failedAuthToken: String ? , reason: AuthFailureReason ) {
123
169
delegate? . onAuthFailure ( AuthFailure ( userKey: IterableUtil . getEmailOrUserId ( ) , failedAuthToken: failedAuthToken, failedRequestTime: IterableUtil . secondsFromEpoch ( for: dateProvider. currentDate) , failureReason: reason) )
124
170
}
125
171
126
- private func queueAuthTokenExpirationRefresh( _ authToken: String ? ) {
172
+ private func queueAuthTokenExpirationRefresh( _ authToken: String ? , onSuccess : AuthTokenRetrievalHandler ? = nil ) -> Bool {
127
173
ITBInfo ( )
128
174
129
175
clearRefreshTimer ( )
@@ -132,32 +178,47 @@ class AuthManager: IterableAuthManagerProtocol {
132
178
handleAuthFailure ( failedAuthToken: authToken, reason: . authTokenPayloadInvalid)
133
179
134
180
/// schedule a default timer of 10 seconds if we fall into this case
135
- scheduleAuthTokenRefreshTimer ( 10 )
181
+ scheduleAuthTokenRefreshTimer ( interval : getNextRetryInterval ( ) , successCallback : onSuccess )
136
182
137
- return
183
+ return true
138
184
}
139
185
140
186
let timeIntervalToRefresh = TimeInterval ( expirationDate) - dateProvider. currentDate. timeIntervalSince1970 - expirationRefreshPeriod
141
-
142
- scheduleAuthTokenRefreshTimer ( timeIntervalToRefresh)
187
+ if timeIntervalToRefresh > 0 {
188
+ scheduleAuthTokenRefreshTimer ( interval: timeIntervalToRefresh, isScheduledRefresh: true , successCallback: onSuccess)
189
+ return true
190
+ }
191
+ return false
143
192
}
144
193
145
- private func scheduleAuthTokenRefreshTimer( _ interval: TimeInterval ) {
194
+ func scheduleAuthTokenRefreshTimer( interval: TimeInterval , isScheduledRefresh : Bool = false , successCallback : AuthTokenRetrievalHandler ? = nil ) {
146
195
ITBInfo ( )
196
+ if shouldSkipTokenRefresh ( isScheduledRefresh: isScheduledRefresh) {
197
+ // we only stop schedule token refresh if it is called from retry (in case of failure). The normal auth token refresh schedule would work
198
+ return
199
+ }
147
200
148
201
expirationRefreshTimer = Timer . scheduledTimer ( withTimeInterval: interval, repeats: false ) { [ weak self] _ in
202
+ self ? . isTimerScheduled = false
149
203
if self ? . localStorage. email != nil || self ? . localStorage. userId != nil {
150
- self ? . requestNewAuthToken ( hasFailedPriorAuth: false )
204
+ self ? . requestNewAuthToken ( hasFailedPriorAuth: false , onSuccess : successCallback , shouldIgnoreRetryPolicy : isScheduledRefresh )
151
205
} else {
152
206
ITBDebug ( " Email or userId is not available. Skipping token refresh " )
153
207
}
154
208
}
209
+
210
+ isTimerScheduled = true
211
+ }
212
+
213
+ private func shouldSkipTokenRefresh( isScheduledRefresh: Bool ) -> Bool {
214
+ return ( pauseAuthRetry && !isScheduledRefresh) || isTimerScheduled
155
215
}
156
216
157
217
private func clearRefreshTimer( ) {
158
218
ITBInfo ( )
159
219
160
220
expirationRefreshTimer? . invalidate ( )
221
+ isTimerScheduled = false
161
222
expirationRefreshTimer = nil
162
223
}
163
224
0 commit comments