         }
         let origRequest = request.mutableCopy() as! NSURLRequest
         var actualRequest = request
-        if authenticated && getAuthKey() != nil {
+        if authenticated {
             actualRequest = signRequest(request)
         }
         return session.dataTaskWithRequest(actualRequest) { (data, resp, err) -> Void in
-            let end = NSDate()
-            let dur = Int(end.timeIntervalSinceDate(start) * 1000)
-            if let httpResp = resp as? NSHTTPURLResponse {
-                var errStr = ""
-                if httpResp.statusCode >= 400 {
-                    if let data = data, s = NSString(data: data, encoding: NSUTF8StringEncoding) {
-                        errStr = s as String
+            dispatch_async(dispatch_get_main_queue()) {
+                let end = NSDate()
+                let dur = Int(end.timeIntervalSinceDate(start) * 1000)
+                if let httpResp = resp as? NSHTTPURLResponse {
+                    var errStr = ""
+                    if httpResp.statusCode >= 400 {
+                        if let data = data, s = NSString(data: data, encoding: NSUTF8StringEncoding) {
+                            errStr = s as String
+                        }
                     }
-                }
-                
-                self.log("\(logLine) -> status=\(httpResp.statusCode) duration=\(dur)ms errStr=\(errStr)")
-                
-                if authenticated && isBreadChallenge(httpResp) {
-                    self.log("got authentication challenge from API - will attempt to get token")
-                    self.getToken({ (err) -> Void in
-                        if err != nil && retryCount < 1 { // retry once
-                            self.log("error retrieving token: \(err) - will retry")
-                            dispatch_after(1, dispatch_get_main_queue(), { () -> Void in
+                    
+                    self.log("\(logLine) -> status=\(httpResp.statusCode) duration=\(dur)ms errStr=\(errStr)")
+                    
+                    if authenticated && isBreadChallenge(httpResp) {
+                        self.log("\(logLine) got authentication challenge from API - will attempt to get token")
+                        self.getToken { err in
+                            if err != nil && retryCount < 1 { // retry once
+                                self.log("\(logLine) error retrieving token: \(err) - will retry")
+                                dispatch_after(1, dispatch_get_main_queue()) {
+                                    self.dataTaskWithRequest(
+                                        origRequest, authenticated: authenticated,
+                                        retryCount: retryCount + 1, handler: handler
+                                        ).resume()
+                                }
+                            } else if err != nil && retryCount > 0 { // fail if we already retried
+                                self.log("\(logLine) error retrieving token: \(err) - will no longer retry")
+                                handler(nil, nil, err)
+                            } else if retryCount < 1 { // no error, so attempt the request again
+                                self.log("\(logLine) retrieved token, so retrying the original request")
                                 self.dataTaskWithRequest(
                                     origRequest, authenticated: authenticated,
                                     retryCount: retryCount + 1, handler: handler).resume()
-                            })
-                        } else if err != nil && retryCount > 0 { // fail if we already retried
-                            self.log("error retrieving token: \(err) - will no longer retry")
-                            handler(nil, nil, err)
-                        } else if retryCount < 1 { // no error, so attempt the request again
-                            self.log("retrieved token, so retrying the original request")
-                            self.dataTaskWithRequest(
-                                origRequest, authenticated: authenticated,
-                                retryCount: retryCount + 1, handler: handler).resume()
-                        } else {
-                            self.log("retried token multiple times, will not retry again")
-                            handler(data, httpResp, err)
+                            } else {
+                                self.log("\(logLine) retried token multiple times, will not retry again")
+                                handler(data, httpResp, err)
+                            }
                         }
-                    })
+                    } else {
+                        handler(data, httpResp, err)
+                    }
                 } else {
-                    handler(data, httpResp, err)
-                }
-            } else {
-                self.log("\(logLine) encountered connection error \(err)")
-                handler(data, nil, err)
+                    self.log("\(logLine) encountered connection error \(err)")
+                    handler(data, nil, err)
+                }
             }
         }
     }
     
     // retrieve a token and save it in the keychain data for this account
-    func getToken(handler: (NSError?) -> Void) -> Void {
-        if getAuthKey() == nil {
+    func getToken(handler: (NSError?) -> Void) {
+        if isFetchingAuth {
+            log("already fetching auth, waiting...")
+            dispatch_group_notify(authFetchGroup, dispatch_get_main_queue()) {
+                handler(nil)
+            }
+            return
+        }
+        isFetchingAuth = true
+        log("auth: entering group")
+        dispatch_group_enter(authFetchGroup)
+        guard let authKey = getAuthKey(), authPubKey = authKey.publicKey else {
             return handler(NSError(domain: BRAPIClientErrorDomain, code: 500, userInfo: [
                 NSLocalizedDescriptionKey: NSLocalizedString("Wallet not ready", comment: "")]))
         }



@objc public class BRAPIClient: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, BRAPIAdaptor {
    var logEnabled = true
    var proto = "https"
    var host = "api.breadwallet.com"
    
    private var _session: NSURLSession? = nil
    var session: NSURLSession {
        if _session == nil {
            let config = NSURLSessionConfiguration.defaultSessionConfiguration()
            _session = NSURLSession(configuration: config, delegate: self, delegateQueue: queue)
        }
        return _session!
    }
    var queue = NSOperationQueue()
    
    var baseUrl: String {
        return "\(proto)://\(host)"
    }
    
    // the singleton
    static let sharedClient = BRAPIClient()
    
    func log(s: String) {
        if !logEnabled {
            return

            // add Date header if necessary
            mutableRequest.setValue(NSDate().RFC1123String(), forHTTPHeaderField: "Date")
        }
        if let manager = BRWalletManager.sharedInstance(),
            tokenData = manager.userAccount,
            token = tokenData["token"],
            authKey = getAuthKey(),
            signingData = buildRequestSigningString(mutableRequest).dataUsingEncoding(NSUTF8StringEncoding),
            sig = authKey.compactSign(signingData.SHA256_2()) {
            mutableRequest.setValue("bread \(token):\(sig.base58String())", forHTTPHeaderField: "Authorization")
        }
        return mutableRequest.copy() as! NSURLRequest
    }
