Skip to content

Commit 2c8224d

Browse files
[MFA-2681] add Auth0TelemetryInfo to track app version usage via sdk (#120)
1 parent 50c1483 commit 2c8224d

File tree

10 files changed

+95
-43
lines changed

10 files changed

+95
-43
lines changed

Guardian.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@
883883
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
884884
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
885885
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
886-
CLANG_WARN_STRICT_PROTOTYPES = YES;
886+
CLANG_WARN_STRICT_PROTOTYPES = NO;
887887
CLANG_WARN_SUSPICIOUS_MOVE = YES;
888888
CLANG_WARN_UNREACHABLE_CODE = YES;
889889
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -942,7 +942,7 @@
942942
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
943943
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
944944
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
945-
CLANG_WARN_STRICT_PROTOTYPES = YES;
945+
CLANG_WARN_STRICT_PROTOTYPES = NO;
946946
CLANG_WARN_SUSPICIOUS_MOVE = YES;
947947
CLANG_WARN_UNREACHABLE_CODE = YES;
948948
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;

Guardian/API/APIClient.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ import Foundation
2525
struct APIClient: API {
2626

2727
let baseUrl: URL
28+
let telemetryInfo: Auth0TelemetryInfo?
2829

29-
init(baseUrl: URL) {
30+
init(baseUrl: URL, telemetryInfo: Auth0TelemetryInfo? = nil) {
3031
self.baseUrl = baseUrl
32+
self.telemetryInfo = telemetryInfo
3133
}
3234

3335
func enroll(withTicket enrollmentTicket: String, identifier: String, name: String, notificationToken: String, verificationKey: VerificationKey) -> Request<Device, Enrollment> {
@@ -39,7 +41,7 @@ struct APIClient: API {
3941
}
4042

4143
let device = Device(identifier: identifier, name: name, pushCredentials: PushCredentials(token: notificationToken), publicKey: jwk)
42-
return Request.new(method: .post, url: url, headers: headers, body: device)
44+
return Request.new(method: .post, url: url, headers: headers, body: device, telemetryInfo: self.telemetryInfo)
4345
}
4446
catch let error {
4547
return Request(method: .post, url: url, error: error)
@@ -49,11 +51,11 @@ struct APIClient: API {
4951
func resolve(transaction transactionToken: String, withChallengeResponse challengeResponse: String) -> Request<Transaction, NoContent> {
5052
let transaction = Transaction(challengeResponse: challengeResponse)
5153
let url = self.baseUrl.appendingPathComponent("api/resolve-transaction")
52-
return Request.new(method: .post, url: url, headers: ["Authorization": "Bearer \(transactionToken)"], body: transaction)
54+
return Request.new(method: .post, url: url, headers: ["Authorization": "Bearer \(transactionToken)"], body: transaction, telemetryInfo: self.telemetryInfo)
5355
}
5456

5557
func device(forEnrollmentId id: String, token: String) -> DeviceAPI {
56-
return DeviceAPIClient(baseUrl: baseUrl, id: id, token: token)
58+
return DeviceAPIClient(baseUrl: baseUrl, id: id, token: token, telemetryInfo: self.telemetryInfo)
5759
}
5860

5961
func device(forEnrollmentId enrollmentId: String, userId: String, signingKey: SigningKey) -> DeviceAPI {
@@ -68,7 +70,7 @@ struct APIClient: API {
6870
)
6971
let jwt = try? JWT(claimSet: claims, key: signingKey.secKey)
7072

71-
return DeviceAPIClient(baseUrl: baseUrl, id: enrollmentId, token: jwt?.string ?? "")
73+
return DeviceAPIClient(baseUrl: baseUrl, id: enrollmentId, token: jwt?.string ?? "", telemetryInfo: self.telemetryInfo)
7274
}
7375

7476
}

Guardian/API/DeviceAPIClient.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@ struct DeviceAPIClient: DeviceAPI {
2727

2828
let url: URL
2929
let token: String
30+
let telemetryInfo: Auth0TelemetryInfo?
3031

31-
init(baseUrl: URL, id: String, token: String) {
32+
init(baseUrl: URL, id: String, token: String, telemetryInfo: Auth0TelemetryInfo? = nil) {
3233
self.url = baseUrl.appendingPathComponent(DeviceAPIClient.path).appendingPathComponent(id)
3334
self.token = token
35+
self.telemetryInfo = telemetryInfo
3436
}
3537

3638
func delete() -> Request<NoContent, NoContent> {
37-
return Request.new(method: .delete, url: url, headers: ["Authorization": "Bearer \(token)"])
39+
return Request.new(method: .delete, url: url, headers: ["Authorization": "Bearer \(token)"], telemetryInfo: self.telemetryInfo)
3840
}
3941

4042
func update(localIdentifier identifier: String? = nil, name: String? = nil, notificationToken: String? = nil) -> Request<UpdatedDevice, UpdatedDevice> {
@@ -45,6 +47,6 @@ struct DeviceAPIClient: DeviceAPI {
4547
credentials = nil
4648
}
4749
let update = UpdatedDevice(identifier: identifier, name: name, pushCredentials: credentials)
48-
return Request.new(method: .patch, url: url, headers: ["Authorization": "Bearer \(token)"], body: update)
50+
return Request.new(method: .patch, url: url, headers: ["Authorization": "Bearer \(token)"], body: update, telemetryInfo: self.telemetryInfo)
4951
}
5052
}

Guardian/API/Models.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,12 @@ public struct UpdatedDevice: Codable {
9494
}
9595
}
9696

97+
public struct Auth0TelemetryInfo: Codable, Equatable {
98+
let appName: String
99+
let appVersion: String
100+
101+
public init(name: String, version: String) {
102+
self.appName = name
103+
self.appVersion = version
104+
}
105+
}

Guardian/API/Request.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ let errorBuilder = { (response: HTTPURLResponse, data: Data?) -> Swift.Error? in
3131
public struct Request<T: Encodable, E: Decodable>: Operation {
3232
let request: NetworkOperation<T, E>
3333

34-
static func new(method: HTTPMethod, url: URL, headers: [String: String] = [:], body: T? = nil) -> Request<T, E>{
34+
static func new(method: HTTPMethod, url: URL, headers: [String: String] = [:], body: T? = nil, telemetryInfo: Auth0TelemetryInfo? = nil) -> Request<T, E>{
3535
do {
36-
let request: NetworkOperation<T, E> = try NetworkOperation(method: method, url: url, headers: headers, body: body).mapError(transform: errorBuilder)
36+
let request: NetworkOperation<T, E> = try NetworkOperation(method: method, url: url, headers: headers, body: body, telemetryInfo: telemetryInfo).mapError(transform: errorBuilder)
3737
return Request(request: request)
3838
} catch let error {
3939
return Request(method: method, url: url, error: error)

Guardian/Guardian.swift

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ import Foundation
3030
```
3131

3232
- parameter forDomain: domain or URL of your Guardian server
33+
- parameter telemetryInfo: information about the app, used for internal auth0 analitycs purposes
3334

3435
- returns: an Guardian API client
3536

3637
- seealso: Guardian.API
3738
*/
38-
public func api(forDomain domain: String) -> API {
39-
return api(url: url(from: domain)!)
39+
public func api(forDomain domain: String, telemetryInfo: Auth0TelemetryInfo? = nil) -> API {
40+
return api(url: url(from: domain)!, telemetryInfo: telemetryInfo)
4041
}
4142

4243
/**
@@ -47,13 +48,14 @@ public func api(forDomain domain: String) -> API {
4748
```
4849

4950
- parameter url: URL of your Guardian server
51+
- parameter telemetryInfo: information about the app, used for internal auth0 analitycs purposes
5052

5153
- returns: an Guardian API client
5254

5355
- seealso: Guardian.API
5456
*/
55-
public func api(url: URL) -> API {
56-
return APIClient(baseUrl: url)
57+
public func api(url: URL, telemetryInfo: Auth0TelemetryInfo? = nil) -> API {
58+
return APIClient(baseUrl: url, telemetryInfo: telemetryInfo)
5759
}
5860

5961
/**
@@ -67,13 +69,14 @@ public func api(url: URL) -> API {
6769

6870
- parameter forDomain: domain or URL of your Guardian server
6971
- parameter device: the enrolled device that will be used to handle authentication
72+
- parameter telemetryInfo: information about the app, used for internal auth0 analitycs purposes
7073

7174
- returns: an `Authentication` instance
7275

7376
- seealso: Guardian.Authentication
7477
*/
75-
public func authentication(forDomain domain: String, device: AuthenticationDevice) -> Authentication {
76-
let client = api(forDomain: domain)
78+
public func authentication(forDomain domain: String, device: AuthenticationDevice, telemetryInfo: Auth0TelemetryInfo? = nil) -> Authentication {
79+
let client = api(forDomain: domain, telemetryInfo: telemetryInfo)
7780
return RSAAuthentication(api: client, device: device)
7881
}
7982

@@ -89,14 +92,15 @@ public func authentication(forDomain domain: String, device: AuthenticationDevic
8992

9093
- parameter url: URL of your Guardian server
9194
- parameter device: the enrolled device that will be used to handle authentication
95+
- parameter telemetryInfo: information about the app, used for internal auth0 analitycs purposes
9296

9397

9498
- returns: an `Authentication` instance
9599

96100
- seealso: Guardian.Authentication
97101
*/
98-
public func authentication(url: URL, device: AuthenticationDevice) -> Authentication {
99-
let client = api(url: url)
102+
public func authentication(url: URL, device: AuthenticationDevice, telemetryInfo: Auth0TelemetryInfo? = nil) -> Authentication {
103+
let client = api(url: url, telemetryInfo: telemetryInfo)
100104
return RSAAuthentication(api: client, device: device)
101105
}
102106

@@ -153,11 +157,12 @@ public func authentication(url: URL, device: AuthenticationDevice) -> Authentica
153157
- parameter notificationToken: the APNS token of the device
154158
- parameter signingKey: the signing key for Guardian AuthN responses
155159
- parameter verificationKey: the verification key for Guardian AuthN responses
156-
160+
- parameter telemetryInfo: information about the app, used for internal auth0 analitycs purposes
161+
157162
- returns: a request to create an enrollment
158163
*/
159-
public func enroll(forDomain domain: String, usingUri uri: String, notificationToken: String, signingKey: SigningKey, verificationKey: VerificationKey) -> EnrollRequest {
160-
let client = api(forDomain: domain)
164+
public func enroll(forDomain domain: String, usingUri uri: String, notificationToken: String, signingKey: SigningKey, verificationKey: VerificationKey, telemetryInfo: Auth0TelemetryInfo? = nil) -> EnrollRequest {
165+
let client = api(forDomain: domain, telemetryInfo: telemetryInfo)
161166
return EnrollRequest(api: client, enrollmentUri: uri, notificationToken: notificationToken, verificationKey: verificationKey, signingKey: signingKey)
162167
}
163168

@@ -214,11 +219,12 @@ public func enroll(forDomain domain: String, usingUri uri: String, notificationT
214219
- parameter notificationToken: the APNS token of the device
215220
- parameter signingKey: the signing key for Guardian AuthN responses
216221
- parameter verificationKey: the verification key for Guardian AuthN responses
222+
- parameter telemetryInfo: information about the app, used for internal auth0 analitycs purposes
217223

218224
- returns: a request to create an enrollment
219225
*/
220-
public func enroll(url: URL, usingUri uri: String, notificationToken: String, signingKey: SigningKey, verificationKey: VerificationKey) -> EnrollRequest {
221-
let client = api(url: url)
226+
public func enroll(url: URL, usingUri uri: String, notificationToken: String, signingKey: SigningKey, verificationKey: VerificationKey, telemetryInfo: Auth0TelemetryInfo? = nil) -> EnrollRequest {
227+
let client = api(url: url, telemetryInfo: telemetryInfo)
222228
return EnrollRequest(api: client, enrollmentUri: uri, notificationToken: notificationToken, verificationKey: verificationKey, signingKey: signingKey)
223229
}
224230

@@ -275,11 +281,12 @@ public func enroll(url: URL, usingUri uri: String, notificationToken: String, si
275281
- parameter notificationToken: the APNS token of the device
276282
- parameter signingKey: the signing key for Guardian AuthN responses
277283
- parameter verificationKey: the verification key for Guardian AuthN responses
284+
- parameter telemetryInfo: information about the app, used for internal auth0 analitycs purposes
278285

279286
- returns: a request to create an enrollment
280287
*/
281-
public func enroll(forDomain domain: String, usingTicket ticket: String, notificationToken: String, signingKey: SigningKey, verificationKey: VerificationKey) -> EnrollRequest {
282-
let client = api(forDomain: domain)
288+
public func enroll(forDomain domain: String, usingTicket ticket: String, notificationToken: String, signingKey: SigningKey, verificationKey: VerificationKey, telemetryInfo: Auth0TelemetryInfo? = nil) -> EnrollRequest {
289+
let client = api(forDomain: domain, telemetryInfo: telemetryInfo)
283290
return EnrollRequest(api: client, enrollmentTicket: ticket, notificationToken: notificationToken, verificationKey: verificationKey, signingKey: signingKey)
284291
}
285292

@@ -337,11 +344,12 @@ public func enroll(forDomain domain: String, usingTicket ticket: String, notific
337344
- parameter notificationToken: the APNS token of the device
338345
- parameter signingKey: the signing key for Guardian AuthN responses
339346
- parameter verificationKey: the verification key for Guardian AuthN responses
347+
- parameter telemetryInfo: information about the app, used for internal auth0 analitycs purposes
340348

341349
- returns: a request to create an enrollment
342350
*/
343-
public func enroll(url: URL, usingTicket ticket: String, notificationToken: String, signingKey: SigningKey, verificationKey: VerificationKey) -> EnrollRequest {
344-
let client = api(url: url)
351+
public func enroll(url: URL, usingTicket ticket: String, notificationToken: String, signingKey: SigningKey, verificationKey: VerificationKey, telemetryInfo: Auth0TelemetryInfo? = nil) -> EnrollRequest {
352+
let client = api(url: url, telemetryInfo: telemetryInfo)
345353
return EnrollRequest(api: client, enrollmentTicket: ticket, notificationToken: notificationToken, verificationKey: verificationKey, signingKey: signingKey)
346354
}
347355

Guardian/Networking/ClientInfo.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,17 @@ private let infoEntryName = "CFBundleShortVersionString"
2929
struct ClientInfo: Codable, Equatable {
3030
let name: String
3131
let version: String
32+
let env: Auth0TelemetryInfo?
3233

33-
init(name: String, version: String) {
34+
init(name: String, version: String, telemetryInfo: Auth0TelemetryInfo? = nil) {
3435
self.name = name
3536
self.version = version
37+
self.env = telemetryInfo
3638
}
3739

38-
init?(info: [String: Any]) {
40+
init?(info: [String: Any], telemetryInfo: Auth0TelemetryInfo? = nil) {
3941
guard let version = info[infoEntryName] as? String else { return nil }
40-
self.init(name: libraryName, version: version)
42+
self.init(name: libraryName, version: version, telemetryInfo: telemetryInfo)
4143
}
4244

4345
func asHeader() throws -> [String: String] {

Guardian/Networking/NetworkOperation.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ public struct NetworkOperation<T: Encodable, E: Decodable>: Operation {
3939
self.error = error
4040
}
4141

42-
init(method: HTTPMethod, url: URL, headers: [String: String] = [:], body: T? = nil) throws {
42+
init(method: HTTPMethod, url: URL, headers: [String: String] = [:], body: T? = nil, telemetryInfo: Auth0TelemetryInfo? = nil) throws {
4343
var request = URLRequest(url: url)
4444
request.httpMethod = method.rawValue.uppercased()
4545
headers
46-
.merging(try defaultHeaders(hasBody: body != nil)) { old, _ in return old }
46+
.merging(try defaultHeaders(hasBody: body != nil, telemetryInfo: telemetryInfo)) { old, _ in return old }
4747
.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
4848

4949
if let body = body { // Fail if its 'GET'
@@ -200,9 +200,9 @@ private let privateSession: URLSession = {
200200
return URLSession.init(configuration: config)
201201
}()
202202

203-
func defaultHeaders(hasBody: Bool) throws -> [String: String] {
203+
func defaultHeaders(hasBody: Bool, telemetryInfo: Auth0TelemetryInfo? = nil) throws -> [String: String] {
204204
let info = Bundle(for: _BundleGrapple.classForCoder()).infoDictionary ?? [:]
205-
let clientInfo = ClientInfo(info: info)
205+
let clientInfo = ClientInfo(info: info, telemetryInfo: telemetryInfo)
206206
let telemetry = try clientInfo?.asHeader() ?? [:]
207207
let content = hasBody ? ["Content-Type": "application/json"] : [:]
208208
return telemetry.merging(content) { _, new in new }

GuardianTests/Networking/ClientInfoSpec.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,18 @@ class ClientInfoSpec: QuickSpec {
6060
}
6161

6262
it("should have the correct header value") {
63-
expect(try? clientInfo.asHeader().values).to(contain("eyJuYW1lIjoiR3VhcmRpYW4uc3dpZnQiLCJ2ZXJzaW9uIjoiMS4wLjAifQ"))
64-
}
63+
let decoder = JSONDecoder()
6564

65+
let value = try? clientInfo.asHeader().values.first
66+
67+
expect(value).toNot(beNil())
68+
69+
let data = Data(base64URLEncoded: value!!)
70+
let decoded = try? decoder.decode(ClientInfo.self, from: data!)
71+
72+
expect(decoded?.name).to(equal("Guardian.swift"))
73+
expect(decoded?.version).to(equal("1.0.0"))
74+
}
6675
}
6776
}
6877
}

GuardianTests/Networking/NetworkOperationSpec.swift

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ import Nimble
2626

2727
let url = URL(string: "https://auth0.com")!
2828
class NetworkOperationSpec: QuickSpec {
29-
3029
override class func spec() {
30+
31+
let mockAuth0TelemetryInfo = Auth0TelemetryInfo(name: "Guardian", version: "1.2.3")
3132

32-
func new(method: HTTPMethod = .get, url: URL = url, headers: [String: String] = [:], body: [String: String]? = nil) -> NetworkOperation<[String: String], String> {
33-
return try! NetworkOperation(method: method, url: url, headers: headers, body: body)
33+
func new(method: HTTPMethod = .get, url: URL = url, headers: [String: String] = [:], body: [String: String]? = nil, telemetryInfo: Auth0TelemetryInfo? = mockAuth0TelemetryInfo) -> NetworkOperation<[String: String], String> {
34+
return try! NetworkOperation(method: method, url: url, headers: headers, body: body, telemetryInfo: telemetryInfo)
3435
}
3536

3637
var basicJSON: [String: String]!
@@ -87,11 +88,30 @@ class NetworkOperationSpec: QuickSpec {
8788
expect(new(body: ["KEY": "VALUE"]).request.value(forHTTPHeaderField: key)).to(equal("application/json"))
8889
}
8990

90-
it("should have a request with telemetry header and client info") {
91+
it("should have a request with telemetry header and client info [1]") {
92+
expect({
93+
let key = "Auth0-Client"
94+
let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
95+
let expected = ClientInfo(name: "Guardian.swift", version: version)
96+
let decoder = JSONDecoder()
97+
guard let value = new(telemetryInfo: nil).request.value(forHTTPHeaderField: key) else {
98+
return .failed(reason: "missing auth0-client value")
99+
}
100+
guard let data = Data(base64URLEncoded: value), let actual = try? decoder.decode(ClientInfo.self, from: data) else {
101+
return .failed(reason: "invalid auth0-client value \(value)")
102+
}
103+
guard actual == expected else {
104+
return .failed(reason: "expected auth0-client with \(expected) but got \(actual)")
105+
}
106+
return .succeeded
107+
}).to(succeed())
108+
}
109+
110+
it("should have a request with telemetry header and client info [2]") {
91111
expect({
92112
let key = "Auth0-Client"
93113
let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
94-
let expected = ClientInfo(name: "Guardian.swift", version: version)
114+
let expected = ClientInfo(name: "Guardian.swift", version: version, telemetryInfo: mockAuth0TelemetryInfo)
95115
let decoder = JSONDecoder()
96116
guard let value = new().request.value(forHTTPHeaderField: key) else {
97117
return .failed(reason: "missing auth0-client value")

0 commit comments

Comments
 (0)