             return nil
         }
     }
+    
+    private func deserializeToken(_ data: Data) -> String? {
+        do {
+            let token = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? Dictionary<String, Any>
+            return token?["access_token"] as? String
+        } catch {
+            print("json error: \(error.localizedDescription)")
+            return nil
+        }
+    }
 }

        
        let escapedUser = user.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        let escapedPassword = password.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        let requestBody = String(format: "email=%@&pass=%@", escapedUser, escapedPassword).data(using: .utf8)
        
        sendRequest(url: UserTicketsRetriever.userTicketsApi, method: "POST", body: requestBody, completionHandler: { (data, error) in
            guard
                let unwrappedData = data,
                let tickets = self.deserialize(unwrappedData)
            else {
                callback?(Tickets(), error ?? .unknown)
                return;
            }

            let intTickets = tickets.filter({Int($0) != nil}).map({Int($0)!})

            self.sendRequest(url: UserTicketsRetriever.userIdApi, method: "POST", body: requestBody, completionHandler: { (data, error) in
                guard
                    let unwrappedData = data,
                    let userId = String(data: unwrappedData, encoding: .utf8)
                else {
                    callback?(Tickets(), error)
                    return;
                }
                
                let qrApi = UserTicketsRetriever.qrApi.appendingPathComponent(user)
                self.sendRequest(url: qrApi, method: "GET", body: nil, completionHandler: { (data, error) in
                    guard
                        let qrData = data
                    else {
                        callback?(Tickets(userId: userId, eventIds: intTickets, qrData: nil), error)
                        return;
                    }
                    
                    callback?(Tickets(userId: userId, eventIds: intTickets, qrData: qrData), error)
                })
            })
        })
    }
    
    private func sendRequest(url: URL, method: String, body: Data?, completionHandler: @escaping (_ data: Data?, _ error: Error?) -> Void) {
        var request = URLRequest(url: url)
        request.httpMethod = method
        request.httpBody = body
        
        URLSession.shared.dataTask(with:request , completionHandler:{(data, response, error) -> Void in
            DispatchQueue.main.async {
                if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 {
                    switch httpResponse.statusCode {

import Foundation

class UserTicketsRetriever {
    private static let getTokenApi = URL(string: "https://sso.sf-f.org.il/auth/realms/sf-f/protocol/openid-connect/token")!
    private static let userTicketsApi = URL(string: "https://api.sf-f.org.il/program/cod3/events_per_user_sso?slug=" + Convention.name)!
    private static let userIdApi = URL(string: "https://api.sf-f.org.il/program/cod3/get_user_id_sso?slug=" + Convention.name)!
    private static let qrApi = URL(string: "https://api.sf-f.org.il/cons/qr/login")!
    
    private static let clientId = "con_apps"
    private static let clientSecret = "d1f36c7e-83c1-4008-843c-8853f8e3c8ea" // hardcoded in the app since the server only supports password grant type
    
    enum Error {
        case badUsername,badPassword,unknown
