26
26
27
27
import CryptoKit
28
28
import Foundation
29
+ import OSLog
29
30
30
31
/// Networking layer for OpenPass API Server
31
32
@available ( iOS 13 . 0 , tvOS 16 . 0 , * )
@@ -40,25 +41,31 @@ internal final class OpenPassClient {
40
41
41
42
let clientId : String
42
43
private let session = URLSession . shared
44
+ private let log : OSLog
43
45
44
46
convenience init ( configuration: OpenPassConfiguration ) {
45
47
self . init (
46
48
baseURL: configuration. baseURL,
47
49
sdkName: configuration. sdkName,
48
50
sdkVersion: configuration. sdkVersion,
49
- clientId: configuration. clientId
51
+ clientId: configuration. clientId,
52
+ isLoggingEnabled: configuration. isLoggingEnabled
50
53
)
51
54
}
52
55
53
56
init (
54
57
baseURL: String ,
55
58
sdkName: String ,
56
59
sdkVersion: String = openPassSdkVersion,
57
- clientId: String
60
+ clientId: String ,
61
+ isLoggingEnabled: Bool
58
62
) {
59
63
self . baseURL = baseURL
60
64
self . baseRequestParameters = BaseRequestParameters ( sdkName: sdkName, sdkVersion: sdkVersion)
61
65
self . clientId = clientId
66
+ self . log = isLoggingEnabled
67
+ ? . init( subsystem: " com.myopenpass " , category: " OpenPassClient " )
68
+ : . disabled
62
69
}
63
70
64
71
// MARK: - Tokens
@@ -75,25 +82,29 @@ internal final class OpenPassClient {
75
82
codeVerifier: String ,
76
83
redirectUri: String
77
84
) async throws -> OpenPassTokensResponse {
85
+ let logTag : StaticString = " Updating Tokens "
86
+ os_log ( logTag, log: log, type: . debug)
78
87
let request = Request . authorizationCode (
79
88
clientId: clientId,
80
89
code: code,
81
90
codeVerifier: codeVerifier,
82
91
redirectUri: redirectUri
83
92
)
84
- return try await execute ( request)
93
+ return try await execute ( request, String ( logTag ) )
85
94
}
86
95
87
96
/// Refresh tokens using an existing `refreshToken`
88
97
/// - Parameters:
89
98
/// - refreshToken: A refresh token
90
99
/// - Returns: Refreshed ``OpenPassTokensResponse``
91
100
func refreshTokens( _ refreshToken: String ) async throws -> OpenPassTokensResponse {
101
+ let logTag : StaticString = " Refreshing token "
102
+ os_log ( logTag, log: log, type: . debug)
92
103
let request = Request . refresh (
93
104
clientId: clientId,
94
105
refreshToken: refreshToken
95
106
)
96
- return try await execute ( request)
107
+ return try await execute ( request, String ( logTag ) )
97
108
}
98
109
99
110
// MARK: - Device Authorization Flow
@@ -102,8 +113,10 @@ internal final class OpenPassClient {
102
113
/// `/v1/api/authorize-device`
103
114
/// - Returns: ``DeviceAuthorizationResponse`` transfer object
104
115
func getDeviceCode( ) async throws -> DeviceAuthorizationResponse {
116
+ let logTag : StaticString = " Fetching device code "
117
+ os_log ( logTag, log: log, type: . debug)
105
118
let request = Request . authorizeDevice ( clientId: clientId)
106
- return try await execute ( request)
119
+ return try await execute ( request, String ( logTag ) )
107
120
}
108
121
109
122
/// Get Device Token from Endpoint
@@ -112,14 +125,16 @@ internal final class OpenPassClient {
112
125
/// - deviceCode: Device Code retrieved from `/v1/api/authorize-device`
113
126
/// - Returns: ``OpenPassTokens`` or an error if the request was not successful.
114
127
func getTokenFromDeviceCode( deviceCode: String ) async throws -> OpenPassTokensResponse {
128
+ let logTag : StaticString = " Fetching token from device code "
129
+ os_log ( logTag, log: log, type: . debug)
115
130
let request = Request . deviceToken ( clientId: clientId, deviceCode: deviceCode)
116
- return try await execute ( request)
131
+ return try await execute ( request, String ( logTag ) )
117
132
}
118
133
119
134
// MARK: - JWKS
120
135
121
136
func fetchJWKS( ) async throws -> JWKS {
122
- try await execute ( Request < JWKS > ( path: " /.well-known/jwks " ) )
137
+ try await execute ( Request < JWKS > ( path: " /.well-known/jwks " ) , " Fetching JWKS " )
123
138
}
124
139
125
140
// MARK: - Request Execution
@@ -152,14 +167,40 @@ internal final class OpenPassClient {
152
167
return Data ( query. utf8)
153
168
}
154
169
155
- private func execute< ResponseType: Decodable > ( _ request: Request < ResponseType > ) async throws -> ResponseType {
170
+ private func execute< ResponseType: Decodable > (
171
+ _ request: Request < ResponseType > ,
172
+ _ logTag: String
173
+ ) async throws -> ResponseType {
156
174
let urlRequest = urlRequest (
157
175
request
158
176
)
159
- let data = try await session. data ( for: urlRequest) . 0
177
+ let data : Data
178
+ let response : HTTPURLResponse
179
+ do {
180
+ ( data, response) = try await self . data ( for: urlRequest)
181
+ } catch {
182
+ os_log ( " Client request error %@ " , log: log, type: . error, logTag)
183
+ throw error
184
+ }
185
+ if response. statusCode != 200 {
186
+ os_log ( " Client request error (%d) %@ " , log: log, type: . error, response. statusCode, logTag)
187
+ }
160
188
let decoder = JSONDecoder ( )
161
189
decoder. keyDecodingStrategy = . convertFromSnakeCase
162
- return try decoder. decode ( ResponseType . self, from: data)
190
+ do {
191
+ return try decoder. decode ( ResponseType . self, from: data)
192
+ } catch {
193
+ os_log ( " Error parsing response %@ " , log: log, type: . error, logTag)
194
+ throw error
195
+ }
196
+ }
197
+
198
+ private func data( for request: URLRequest ) async throws -> ( Data , HTTPURLResponse ) {
199
+ let ( data, response) = try await session. data ( for: request)
200
+ // `URLResponse` is always `HTTPURLResponse` for HTTP requests
201
+ // https://developer.apple.com/documentation/foundation/urlresponse
202
+ // swiftlint:disable:next force_cast
203
+ return ( data, response as! HTTPURLResponse )
163
204
}
164
205
}
165
206
@@ -205,3 +246,12 @@ private func generateCodeChallengeFromVerifierCode(verifier: String) -> String {
205
246
206
247
return base64UrlEncodedHashed
207
248
}
249
+
250
+ private extension String {
251
+ init ( _ string: StaticString ) {
252
+ self = string. withUTF8Buffer {
253
+ // swiftlint:disable:next optional_data_string_conversion
254
+ String ( decoding: $0, as: UTF8 . self)
255
+ }
256
+ }
257
+ }
0 commit comments