iOS DeviceCheck API call returning 400 all the time

Hi,

I'm getting 400 Missing or badly formatted authorization token everytime I call the following API from my local Server ( I tried calling this API from my app itself as well)

curl --location 'https://api.development.devicecheck.apple.com/v1/query_two_bits' \
--header 'Authorization: Bearer <<JWT-token>>' \
--header 'Content-Type: application/json' \
--data '{
    "device_token": Token_fetched_from_Device_Check,
    "transaction_id":"c6bdb659-0ee6-443d-88cb-a8f036dfc551", 
    "timestamp": 1721300244267
}'

"device_token" - I have generated from DeviceCheck framework

JWT-token - I generated using key from .p8 file generated from Apple developer portal, keyId of the same and the team Id ( I have individual account)

IMP Points-

  1. I have created this .p8 file from apple developer account, and I did enable Device check option while creating the key.
  2. I also tried calling this API after 7 hours ( and more then that as well) of creating key from the developer portal as I have read somewhere that the key gets activated after few hours.
  3. I understand (again read somewhere) that the token created by DeviceCheck framework has some expiration time so I tried with freshly created token several times as well.

This is how I'm generating token using DeviceCheck -

if curDevice.isSupported{
     DCDevice.current.generateToken { (data, error) in
           if let data = data {
           }
     }
 }

JWT token generation -

func createJWTToken(privateKey: String, keyID: String, teamID: String) -> String? {
        // Set up the JWT header
        var jwtHeader = Header()
        jwtHeader.kid = keyID
        
        // Set up the JWT claims
        let jwtClaims = MyClaims(iss: teamID, iat: Date())
        
        // Create the JWT
        var jwt = JWT(header: jwtHeader, claims: jwtClaims)
        
        // Convert the private key to Data
        guard let privateKeyData = Data(base64Encoded: privateKey) else {
            print("Invalid private key")
            return nil
        }
        
        
        // Sign the JWT
        let jwtSigner = JWTSigner.es256(privateKey: privateKeyData)
        
        do {
            let signedJWT = try jwt.sign(using: jwtSigner)
            return signedJWT
        } catch {
            print("Failed to sign JWT: \(error)")
            return nil
        }
    }

But no luck, please suggest something. any sort of help is much appreciated.

Thank you

There could be many reasons that your JWT is malformed or has invalid information.

Have you checked it against the https://jwt.io Debugger and see if anything looks out of place?

Below is the all methods I have tried to create JWT but [https://jwt.io] (https://jwt.io) always giving me No valid signature error

Please have a look at the code and let me know if you find anything suspicious here -

1. Python method

import jwt
import time
import uuid


def generate_jwt(private_key, key_id, team_id):
    headers = {
        "alg": "ES256",
        "kid": key_id,
    }
    payload = {
        "iss": team_id,
        "iat": int(time.time()),
        "exp": int(time.time()) + 3600,  # Token valid for 1 hour
    }
    token = jwt.encode(payload, private_key, algorithm="ES256", headers=headers)
    return token

# Load your private key
with open('AuthKey_abc.p8', 'r') as key_file:
    private_key = key_file.read()



key_id = "########"  # Replace with your Key ID
team_id = "#########"  # Replace with your Team ID

jwt_token = generate_jwt(private_key, key_id, team_id).decode('utf-8')
transaction_id = str(uuid.uuid4())
timestamp = int(time.time() * 1000)

print("JWT Token:", jwt_token)
print("Transaction ID:", transaction_id)
print("Timestamp:", timestamp)

2. Node JS

const privateKey = fs.readFileSync('AuthKey_abc.p8, 'utf8');

  const payload = {
    iss: teamId,
    iat: curTime,
    exp: curTime + 3600 // 1 hour expiration
};

// Prepare the JWT headers
const headers = {
    kid: keyId,
    alg: 'ES256'
};

// Create and sign the JWT
var jwToken = jwt.sign(payload, privateKey, { 
    header: headers
});
console.log(jwToken)

3. Swift

import SwiftJWT

func createJWTToken(privateKey: String, keyID: String, teamID: String) -> String? {
        // Set up the JWT header
        var jwtHeader = Header()
        jwtHeader.kid = keyID
        
        // Set up the JWT claims
        let jwtClaims = MyClaims(iss: teamID, iat: Date())
        
        // Create the JWT
        var jwt = JWT(header: jwtHeader, claims: jwtClaims)
        
        // Convert the private key to Data
        guard let privateKeyData = Data(base64Encoded: privateKey) else {
            print("Invalid private key")
            return nil
        }
        
        // Sign the JWT
        let jwtSigner = JWTSigner.es256(privateKey: privateKeyData)
        
        do {
            let signedJWT = try jwt.sign(using: jwtSigner)
            return signedJWT
        } catch {
            print("Failed to sign JWT: \(error)")
            return nil
        }
    }


    func readFileFromBundle(fileName: String, fileType: String) -> String? {
        if let fileURL = Bundle.main.url(forResource: fileName, withExtension: fileType) {
            do {
                // Read the file contents
                let privateKey = try String(contentsOf: fileURL, encoding: .utf8)
                
                // Clean the private key by removing header, footer, and whitespace/newlines
                let cleanedKey = privateKey
                    .replacingOccurrences(of: "-----BEGIN PRIVATE KEY-----", with: "")
                    .replacingOccurrences(of: "-----END PRIVATE KEY-----", with: "")
                    .replacingOccurrences(of: "\n", with: "")
                    .replacingOccurrences(of: "\r", with: "")
                    .trimmingCharacters(in: .whitespacesAndNewlines)
                
                return cleanedKey
            } catch {
                print("Error reading private key: \(error)")
                return nil
            }
        } else {
            print("Private key file not found")
            return nil
        }
    }

iOS DeviceCheck API call returning 400 all the time
 
 
Q