@@ -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,33 +142,32 @@ 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 {
105
- delegate? . onTokenRegistrationFailed ( " auth token was nil, scheduling auth token retrieval in 10 seconds " )
153
+ if retrievedAuthToken != nil {
154
+ let isRefreshQueued = queueAuthTokenExpirationRefresh ( retrievedAuthToken, onSuccess: onSuccess)
155
+ if !isRefreshQueued {
156
+ onSuccess ? ( authToken)
157
+ }
158
+ } else {
159
+ delegate? . onTokenRegistrationFailed ( " auth token was nil, scheduling auth token retrieval in \( getNextRetryInterval ( ) ) seconds " )
106
160
107
161
/// by default, schedule a refresh for 10s
108
- scheduleAuthTokenRefreshTimer ( 10 )
109
-
110
- return
162
+ scheduleAuthTokenRefreshTimer ( interval: getNextRetryInterval ( ) , successCallback: onSuccess)
111
163
}
112
164
113
165
authToken = retrievedAuthToken
114
166
115
167
storeAuthToken ( )
116
-
117
- queueAuthTokenExpirationRefresh ( authToken)
118
-
119
- onSuccess ? ( authToken)
120
168
}
121
169
122
- private func queueAuthTokenExpirationRefresh( _ authToken: String ? ) {
170
+ private func queueAuthTokenExpirationRefresh( _ authToken: String ? , onSuccess : AuthTokenRetrievalHandler ? = nil ) -> Bool {
123
171
ITBInfo ( )
124
172
125
173
clearRefreshTimer ( )
@@ -128,32 +176,47 @@ class AuthManager: IterableAuthManagerProtocol {
128
176
delegate? . onTokenRegistrationFailed ( " auth token was nil or could not decode an expiration date, scheduling auth token retrieval in 10 seconds " )
129
177
130
178
/// schedule a default timer of 10 seconds if we fall into this case
131
- scheduleAuthTokenRefreshTimer ( 10 )
179
+ scheduleAuthTokenRefreshTimer ( interval : getNextRetryInterval ( ) , successCallback : onSuccess )
132
180
133
- return
181
+ return true
134
182
}
135
183
136
184
let timeIntervalToRefresh = TimeInterval ( expirationDate) - dateProvider. currentDate. timeIntervalSince1970 - expirationRefreshPeriod
137
-
138
- scheduleAuthTokenRefreshTimer ( timeIntervalToRefresh)
185
+ if timeIntervalToRefresh > 0 {
186
+ scheduleAuthTokenRefreshTimer ( interval: timeIntervalToRefresh, isScheduledRefresh: true , successCallback: onSuccess)
187
+ return true
188
+ }
189
+ return false
139
190
}
140
191
141
- private func scheduleAuthTokenRefreshTimer( _ interval: TimeInterval ) {
192
+ func scheduleAuthTokenRefreshTimer( interval: TimeInterval , isScheduledRefresh : Bool = false , successCallback : AuthTokenRetrievalHandler ? = nil ) {
142
193
ITBInfo ( )
194
+ if shouldSkipTokenRefresh ( isScheduledRefresh: isScheduledRefresh) {
195
+ // we only stop schedule token refresh if it is called from retry (in case of failure). The normal auth token refresh schedule would work
196
+ return
197
+ }
143
198
144
199
expirationRefreshTimer = Timer . scheduledTimer ( withTimeInterval: interval, repeats: false ) { [ weak self] _ in
200
+ self ? . isTimerScheduled = false
145
201
if self ? . localStorage. email != nil || self ? . localStorage. userId != nil {
146
- self ? . requestNewAuthToken ( hasFailedPriorAuth: false )
202
+ self ? . requestNewAuthToken ( hasFailedPriorAuth: false , onSuccess : successCallback , shouldIgnoreRetryPolicy : isScheduledRefresh )
147
203
} else {
148
204
ITBDebug ( " Email or userId is not available. Skipping token refresh " )
149
205
}
150
206
}
207
+
208
+ isTimerScheduled = true
209
+ }
210
+
211
+ private func shouldSkipTokenRefresh( isScheduledRefresh: Bool ) -> Bool {
212
+ return ( pauseAuthRetry && !isScheduledRefresh) || isTimerScheduled
151
213
}
152
214
153
215
private func clearRefreshTimer( ) {
154
216
ITBInfo ( )
155
217
156
218
expirationRefreshTimer? . invalidate ( )
219
+ isTimerScheduled = false
157
220
expirationRefreshTimer = nil
158
221
}
159
222
0 commit comments