Skip to content

Commit

Permalink
Merge pull request #57 from auth0/add-debug-requests
Browse files Browse the repository at this point in the history
Add ability to log requests or add hooks on request/response/error
  • Loading branch information
hzalaz authored Jun 7, 2018
2 parents 65229af + 9794575 commit 8fe9868
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 42 deletions.
4 changes: 4 additions & 0 deletions Guardian.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
5F20380A1D5E40EE0005D2E2 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5F2038001D5E40C70005D2E2 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5F7969341DDABDDE006AC7BA /* AuthenticationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1DB45C1DA4750F00264437 /* AuthenticationSpec.swift */; };
5F7969371DDAC074006AC7BA /* JWTSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2331BFEC1DD52ED50047F1D4 /* JWTSpec.swift */; };
5FB4ECBC20C9A5050050B275 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FB4ECBB20C9A5050050B275 /* Hooks.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -193,6 +194,7 @@
5F20380F1D5E421F0005D2E2 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
5F2038111D5E42AD0005D2E2 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
5F2038181D5E46210005D2E2 /* Gemfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Gemfile; sourceTree = "<group>"; };
5FB4ECBB20C9A5050050B275 /* Hooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hooks.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -260,6 +262,7 @@
2374A9E21D670F5900737F2E /* Request.swift */,
2374A9EE1D672ABD00737F2E /* Result.swift */,
23AD869E1DEE10B500051F41 /* FailedRequest.swift */,
5FB4ECBB20C9A5050050B275 /* Hooks.swift */,
);
name = Networking;
sourceTree = "<group>";
Expand Down Expand Up @@ -592,6 +595,7 @@
233F75271D92F27000B8C15C /* Notification.swift in Sources */,
23C67AEE1D81EF5300A38A2E /* Enrollment.swift in Sources */,
235B4B221DA3F51D00CEB5DC /* A0HMAC.m in Sources */,
5FB4ECBC20C9A5050050B275 /* Hooks.swift in Sources */,
2331C01A1DD5FC6F0047F1D4 /* Data+Base64URL.swift in Sources */,
2331BFEB1DD5260D0047F1D4 /* JWT.swift in Sources */,
231687191DDA07FD00B09526 /* A0SHA.m in Sources */,
Expand Down
49 changes: 37 additions & 12 deletions Guardian/EnrollRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Foundation
- seealso: Guardian.enroll
- seealso: Guardian.Enrollment
*/
public struct EnrollRequest: Requestable {
public class EnrollRequest: Requestable {

typealias T = Enrollment

Expand All @@ -37,13 +37,48 @@ public struct EnrollRequest: Requestable {
private let enrollmentUri: String?
private let notificationToken: String
private let keyPair: RSAKeyPair
private var request: Request<[String: Any]>

init(api: API, enrollmentTicket: String? = nil, enrollmentUri: String? = nil, notificationToken: String, keyPair: RSAKeyPair) {
self.api = api
self.enrollmentTicket = enrollmentTicket
self.enrollmentUri = enrollmentUri
self.notificationToken = notificationToken
self.keyPair = keyPair
let ticket: String
if let enrollmentTicket = enrollmentTicket {
ticket = enrollmentTicket
} else if let enrollmentUri = enrollmentUri, let parameters = parameters(fromUri: enrollmentUri), let enrollmentTxId = parameters["enrollment_tx_id"] {
ticket = enrollmentTxId
} else {
self.request = FailedRequest(error: GuardianError.invalidEnrollmentUri)
return
}

self.request = api.enroll(withTicket: ticket, identifier: Enrollment.defaultDeviceIdentifier, name: Enrollment.defaultDeviceName, notificationToken: notificationToken, publicKey: keyPair.publicKey)
}

/// Registers hooks to be called on specific events:
/// * on request being sent
/// * on response recieved (successful or not)
/// * on network error
///
/// - Parameters:
/// - request: closure called with request information
/// - response: closure called with response and data
/// - error: closure called with network error
/// - Returns: itself for chaining
public func on(request: RequestHook? = nil, response: ResponseHook? = nil, error: ErrorHook? = nil) -> EnrollRequest {
let _ = self.request.on(request: request, response: response, error: error)
return self
}

public var description: String {
return self.request.description
}

public var debugDescription: String {
return self.request.debugDescription
}

/**
Expand All @@ -53,17 +88,7 @@ public struct EnrollRequest: Requestable {
received
*/
public func start(callback: @escaping (Result<Enrollment>) -> ()) {
let ticket: String
if let enrollmentTicket = enrollmentTicket {
ticket = enrollmentTicket
} else if let enrollmentUri = enrollmentUri, let parameters = parameters(fromUri: enrollmentUri), let enrollmentTxId = parameters["enrollment_tx_id"] {
ticket = enrollmentTxId
} else {
return callback(.failure(cause: GuardianError.invalidEnrollmentUri))
}

api.enroll(withTicket: ticket, identifier: Enrollment.defaultDeviceIdentifier, name: Enrollment.defaultDeviceName, notificationToken: notificationToken, publicKey: keyPair.publicKey)
.start { result in
self.request.start { result in
switch result {
case .failure(let cause):
callback(.failure(cause: cause))
Expand Down
28 changes: 28 additions & 0 deletions Guardian/Hooks.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Hooks.swift
// Guardian
//
// Created by Hernan Zalazar on 07/06/2018.
// Copyright © 2018 Auth0. All rights reserved.
//

import Foundation

/// Hook that will be called before request is sent
public typealias RequestHook = (URLRequest) -> ()
/// Hook that will be called when a response is received (any status code) along with data (if any)
public typealias ResponseHook = (HTTPURLResponse, Data?) -> ()
/// Hook that will be called when request fails with error
public typealias ErrorHook = (Error) -> ()

struct Hooks {
let request: RequestHook?
let response: ResponseHook?
let error: ErrorHook?

init(request: RequestHook? = nil, response: ResponseHook? = nil, error: ErrorHook? = nil) {
self.request = request
self.response = response
self.error = error
}
}
29 changes: 29 additions & 0 deletions Guardian/Notification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ class AuthenticationNotification: NSObject, Notification {

self.init(domain: domain, enrollmentId: enrollmentId, transactionToken: token, challenge: challenge, startedAt: startedAt, source: source, location: location)
}

override var description: String {
return "enrollmentId: <\(self.enrollmentId)> txToken: <\(self.transactionToken)> challenge: <\(self.challenge)> startedAt: \(self.startedAt)"
}

override var debugDescription: String {
return "domain: <\(self.domain)> enrollmentId: <\(self.enrollmentId)> txToken: <\(self.transactionToken)> challenge: <\(self.challenge)> source: <\(String(describing: self.source))> location: <\(String(describing: self.location))> startedAt: \(self.startedAt)"
}
}

class AuthenticationSource: NSObject, Source {
Expand Down Expand Up @@ -243,6 +251,18 @@ class AuthenticationSource: NSObject, Source {
self.os = os
self.browser = browser
}

override var description: String {
let osName = self.os?.name ?? "Unknown OS"
let osVersion = self.os?.version != nil ? "(\(String(describing: self.os?.version))" : ""
let browserName = self.browser?.name ?? "Unknown Browser"
let browserVersion = self.browser?.version != nil ? "(\(String(describing: self.browser?.version))" : ""
return "\(osName) \(osVersion)".trimmingCharacters(in: .whitespaces) + " \(browserName) \(browserVersion)".trimmingCharacters(in: .whitespaces)
}

override var debugDescription: String {
return self.description
}
}

class AuthenticationLocation: NSObject, Location {
Expand All @@ -268,4 +288,13 @@ class AuthenticationLocation: NSObject, Location {
longitude = longitudeValue as? NSNumber
}
}

override var description: String {
return "\(self.name ?? "Unknown") (\(String(describing: self.latitude)), \(String(describing: self.longitude)))"
}

override var debugDescription: String {
return self.description
}

}
81 changes: 62 additions & 19 deletions Guardian/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,74 @@ public class Request<T>: Requestable {
let method: String
let url: URL
let payload: [String: Any]?
let headers: [String: String]?
let headers: [String: String]
var hooks: Hooks

init(session: URLSession, method: String, url: URL, payload: [String: Any]? = nil, headers: [String: String]? = nil) {
self.session = session
self.method = method
self.url = url
self.payload = payload
self.hooks = Hooks()
let bundle = Bundle(for: _ObjectiveGuardian.classForCoder())
var headers = headers ?? [:]
if let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String,
let clientInfo = try? JSONSerialization.data(withJSONObject: [
"name": "Guardian.swift",
"version": version
])
{
headers["Auth0-Client"] = clientInfo.base64URLEncodedString()
}

if payload != nil {
headers["Content-Type"] = "application/json"
}
self.headers = headers
}

public var description: String {
return "\(self.method) \(self.url)"
}

public var debugDescription: String {
var description = "\(self.method) \(self.url)\n"
self.headers.forEach { description.append("\($0): \($1)") }
description.append("\n")
if let payload = self.payload,
let body = try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted),
let json = String(data: body, encoding: .utf8) {
description.append(json.replacingOccurrences(of: "\\n", with: "\n"))
}
return description
}

/// Registers hooks to be called on specific events:
/// * on request being sent
/// * on response recieved (successful or not)
/// * on network error
///
/// - Parameters:
/// - request: closure called with request information
/// - response: closure called with response and data
/// - error: closure called with network error
/// - Returns: itself for chaining
public func on(request: RequestHook? = nil, response: ResponseHook? = nil, error: ErrorHook? = nil) -> Request {
self.hooks = Hooks(request: request ?? self.hooks.request, response: response ?? self.hooks.response, error: error ?? self.hooks.error)
return self
}

/**
Executes the request in a background thread
- parameter callback: the termination callback, where the result is
received
*/
public func start(callback: @escaping (Result<T>) -> ()) {
let request = NSMutableURLRequest(url: url)
request.httpMethod = method

var request = URLRequest(url: url)
request.httpMethod = self.method
self.headers.forEach { request.setValue($1, forHTTPHeaderField: $0) }

if let payload = payload {
guard let body = try? JSONSerialization.data(withJSONObject: payload, options: []) else {
callback(.failure(cause: GuardianError.invalidPayload))
Expand All @@ -59,24 +107,19 @@ public class Request<T>: Requestable {
request.httpBody = body
}

let bundle = Bundle(for: _ObjectiveGuardian.classForCoder())
if let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String,
let clientInfo = try? JSONSerialization.data(withJSONObject: [
"name": "Guardian.swift",
"version": version
])
{
request.setValue(clientInfo.base64URLEncodedString(), forHTTPHeaderField: "Auth0-Client")
}
self.hooks.request?(request)

request.setValue("application/json", forHTTPHeaderField: "Content-Type")
headers?.forEach { request.setValue($1, forHTTPHeaderField: $0) }

let task = session.dataTask(with: request as URLRequest) { data, response, error in
if let error = error { return callback(.failure(cause: error)) }
let task = self.session.dataTask(with: request as URLRequest) { data, response, error in
if let error = error {
self.hooks.error?(error)
return callback(.failure(cause: error))
}
guard let httpResponse = response as? HTTPURLResponse else {
return callback(.failure(cause: GuardianError.invalidResponse))
let cause = GuardianError.invalidResponse
self.hooks.error?(cause)
return callback(.failure(cause: cause))
}
self.hooks.response?(httpResponse, data)
guard (200..<300).contains(httpResponse.statusCode) else {
guard let info: [String: Any] = json(data) else {
return callback(.failure(cause: GuardianError.invalidResponse(withStatus: httpResponse.statusCode)))
Expand Down
2 changes: 1 addition & 1 deletion Guardian/Requestable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import Foundation

protocol Requestable {
protocol Requestable: CustomDebugStringConvertible, CustomStringConvertible {

associatedtype T

Expand Down
10 changes: 6 additions & 4 deletions GuardianApp/NotificationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ class NotificationController: UIViewController {
guard let notification = notification, let enrollment = AppDelegate.enrollment else {
return self.dismiss(animated: true, completion: nil)
}
Guardian
let request = Guardian
.authentication(forDomain: AppDelegate.guardianDomain, andEnrollment: enrollment)
.allow(notification: notification)
.start { result in
debugPrint(request)
request.start { result in
print(result)
switch result {
case .success:
Expand All @@ -55,10 +56,11 @@ class NotificationController: UIViewController {
guard let notification = notification, let enrollment = AppDelegate.enrollment else {
return self.dismiss(animated: true, completion: nil)
}
Guardian
let request = Guardian
.authentication(forDomain: AppDelegate.guardianDomain, andEnrollment: enrollment)
.reject(notification: notification)
.start { result in
debugPrint(request)
request.start { result in
print(result)
switch result {
case .success:
Expand Down
10 changes: 6 additions & 4 deletions GuardianApp/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ class ViewController: UIViewController, QRCodeReaderViewControllerDelegate {
return
}

Guardian
let request = Guardian
.enroll(forDomain: AppDelegate.guardianDomain, usingUri: result.value, notificationToken: AppDelegate.pushToken!, keyPair: keyPair)
.start { result in
debugPrint(request)
request.start { result in
switch result {
case .failure(let cause):
self.showError("Enroll failed", cause)
Expand Down Expand Up @@ -110,11 +111,12 @@ class ViewController: UIViewController, QRCodeReaderViewControllerDelegate {

@IBAction func unenrollAction(_ sender: AnyObject) {
if let enrollment = AppDelegate.enrollment {
Guardian
let request = Guardian
.api(forDomain: AppDelegate.guardianDomain)
.device(forEnrollmentId: enrollment.id, token: enrollment.deviceToken)
.delete()
.start { [unowned self] result in
debugPrint(request)
request.start { [unowned self] result in
switch result {
case .failure(let cause):
self.showError("Unenroll failed", cause)
Expand Down
Loading

0 comments on commit 8fe9868

Please sign in to comment.