Search code examples
apple-push-notifications

How to send APNs push messages using APNs Auth Key and standard CLI tools?


Apple recently added a new authentication method to APNS ( Apple Push Notification Authentication Key (Sandbox & Production)).

enter image description here

The downloaded key is a .p8 file with a private key:

$ cat APNSAuthKey_3HHEB343FX.p8
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBH...Already.Revoked...lHEjCX1v51W
-----END PRIVATE KEY-----

I am using APNs messages using the old method - adding them to keychain, asking for a certificate and using OpenSSL to send messages to gateway.production.push.apple.com:2195.

How do I send push notifications using standard CLI Linux tools (OpenSSL, Python etc.) using the new format?


Solution

  • If you have curl with HTTP/2 support and openssl with ECDSA support installed on your machine, you can use the following script to test push notifications using an APNs Auth Key:

    #!/bin/bash
    
    deviceToken=b27371497b85611baf9052b4ccfb9641ab7fea1d01c91732149c99cc3ed9342f
    
    authKey="./APNSAuthKey_ABC1234DEF.p8"
    authKeyId=ABC1234DEF
    teamId=TEAM123456
    bundleId=com.example.myapp
    endpoint=https://api.development.push.apple.com
    
    read -r -d '' payload <<-'EOF'
    {
       "aps": {
          "badge": 2,
          "category": "mycategory",
          "alert": {
             "title": "my title",
             "subtitle": "my subtitle",
             "body": "my body text message"
          }
       },
       "custom": {
          "mykey": "myvalue"
       }
    }
    EOF
    
    # --------------------------------------------------------------------------
    
    base64() {
       openssl base64 -e -A | tr -- '+/' '-_' | tr -d =
    }
    
    sign() {
       printf "$1" | openssl dgst -binary -sha256 -sign "$authKey" | base64
    }
    
    time=$(date +%s)
    header=$(printf '{ "alg": "ES256", "kid": "%s" }' "$authKeyId" | base64)
    claims=$(printf '{ "iss": "%s", "iat": %d }' "$teamId" "$time" | base64)
    jwt="$header.$claims.$(sign $header.$claims)"
    
    curl --verbose \
       --header "content-type: application/json" \
       --header "authorization: bearer $jwt" \
       --header "apns-topic: $bundleId" \
       --data "$payload" \
       $endpoint/3/device/$deviceToken
    

    NOTE: I use a slight variation of this script for testing on macOS with homebrew versions of curl and openssl: http://thrysoee.dk/apns/

    Apple now have documentation for this method to Send a Push Notification Using a Token.