Search code examples
iosswiftxcode10.2swift5devicecheck

Device check API: Error: Unable to verify authorization token


I am using Xcode10, Swift 5 and SwiftJWT to generate JSON Web token in order to authenticate Device Check API. Below is the method

let key8 = """
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCq...
-----END PRIVATE KEY-----
"""

let myHeader = Header(kid: m_key)
struct MyClaims: Claims {
  let iat: Date
  let iss: String
}

var jwt = JWT(header: myHeader, claims: MyClaims(iat: Date(timeIntervalSinceNow: 3600), iss: m_iss))
    let privateKey = key8.data(using: .utf8)!
    let rsaJWTEncoder = JWTEncoder(jwtSigner: JWTSigner.es256(privateKey: privateKey))
    do {
        let jwtString = try rsaJWTEncoder.encodeToString(jwt)

        print("endcode header: \(jwtString)")
    } catch {
        print("Failed to encode JWT: \(error)")
    }
}

getting the result something like eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIs...

Using the above header to connect the Apple server. I keep getting this response from Apple’s server: 401 Unable to verify authorization token.

I am also trying to create JSON Web token from jwt

but same error. Any help would be appreciated. Thanks in advance


Solution

  • I tried multiple ways to resolve the issue. But what worked for me, describing here step by step how to get device two bits status using DeviceCheck API.

    Step 1- Generate Token

    let currentDevice = DCDevice.current
    if currentDevice.isSupported {
    currentDevice.generateToken(completionHandler: { (data, error) in
        if let tokenData = data {
            print("Token: \(tokenData)")
        } else {
            print("Error: \(error?.localizedDescription ?? "")")
        }
    })
    }
    

    Step 2 - Accessing and Modifying Per-Device Data

    Create an APNs authentication token signing key

    1- In your developer account, go to Certificates, Identifiers & Profiles.

    2 - Under Keys, select All and click the Add button (+) in the upper-right corner.

    3- Under Key Description, enter a unique name for the signing key.

    4- Under Key Services, select the APNs checkbox, then click Continue.

    5- Review the key configuration, then click Confirm.

    6- Optionally, click Download to generate and download the key now. If you download the key, it is saved as a text file with a .p8 file extension. Save the file in a secure place because the key is not saved in your developer account and you won’t be able to download it again.

    7- Click Done.

    Step 3- Create the Payload for a Query Request

    func generateJWTToken() -> String{
    
         let key8 = """
         -----BEGIN PRIVATE KEY-----
    
        MIGTAgEAMBMGByqGSM49...
        -----END PRIVATE KEY-----
        """
    
        let myHeader = Header(kid: "2YHFSDF45")
    
        let timeStamp = Date.currentTimeStamp
        let teamId = "xyz"
    
        let jwt = JWT(header: myHeader, claims: MyClaims(iat:timeStamp, iss: teamId))
    
        let privateKey = key8.data(using: .utf8)!
        let rsaJWTEncoder = JWTEncoder(jwtSigner: JWTSigner.es256(privateKey: privateKey))
    
        do {
            let jwtString = try rsaJWTEncoder.encodeToString(jwt)
    
            //print(jwtString)
    
            return jwtString
        } catch {
            print("Failed to encode JWT: \(error)")
        }
    
        return ""
    }
    

    Step 4- Query DeviceCheckApi

    To get the two bits state of a device, we will make a HTTP request Using Alamofire to https://api.devicecheck.apple.com/v1/query_two_bits

        //MARK:- method call device check API
    func callAppleDeviceCheckApi(_ token: String){
    
        let mUrl : URL = NSURL(string: "https://api.devicecheck.apple.com/v1/query_two_bits") as! URL
    
        let mHeaders = generateJWTToken()
        let mDeviceToken = token
        let mTimeStampMili = Date.currentTimeStampMili
    
        let headers: HTTPHeaders = ["Authorization": "Bearer "+mHeaders,
                                    "Content-Type" : "application/json"]
    
        let params = ["device_token": mDeviceToken, "transaction_id": "dfgsdfgddfc", "timestamp": mTimeStampMili] as [String : Any]
    
        var request = URLRequest(url: mUrl)
        request.httpMethod = "POST"
    
        request.headers = headers
    
        request.httpBody = try! JSONSerialization.data(withJSONObject: params)
        AF.request(request).responseString(){
            response in
    
            switch response.result {
            case .success(let value):
    
                let json = JSON(value)
    
                //print("JSON: \(json)") 
    
            case .failure(let error):
                print("ERROR: \(error)")
    
            }
        }
    }
    

    Note- Go through this & this for better understanding.