2

I am issuing a pass and the receiving device gives me this information in a register request {"device_id":"9b39b68fa1e4b01485857160ecdde0e0","pass_serial":"4LTCAg","push_token":"7c8a5c327bce2a6d18710aebabdc5225f3b3a252e7ff8027906b1243ad498687"}I use that to attempt to send a push notification with APNS and I'm getting this response: {403 Forbidden 403 HTTP/2.0 2 0 map[Apns-Id:[7731E8A4-D049-A982-5B31-11894BA891D4]] {0xc000178900} -1 [] false false map[] 0xc000602100 0xc00049ce70} I'm at a loss of what to do with that information. 403 would imply that something is wrong with the auth, but it isn't giving me any specific information on what.

My code to send an APNs push is in Go and looks like this:

func getAppleBearerAuth() (*string, error) {
    kid := os.Getenv("APPLE_PUSH_KEY")
    iss := os.Getenv("APPLE_TEAM_ID")

    // Create a new JWT token
    token := jwt.New(jwt.SigningMethodRS256)

    // Add claims
    claims := token.Claims.(jwt.MapClaims)
    claims["iss"] = iss
    claims["iat"] = time.Now().Unix()

    // Add kid to the header
    token.Header["kid"] = kid
    token.Header["alg"] = "ES256"

    // Load your ES256 key
    key, err := loadPrivateKey()
    if err != nil {
        fmt.Println("failure to load private key")
        return nil, err
    }
    rsaKey, ok := key.(*rsa.PrivateKey)
    if !ok {
        return nil, fmt.Errorf("private key is not of type *rsa.PrivateKey")
    }

    // Sign and get the complete encoded token as a string
    tokenString, err := token.SignedString(rsaKey)
    if err != nil {
        fmt.Println("Failure to sign key")
        return nil, err
    }

    // Add "Bearer " to the token string to make it a bearer token
    bearer := "Bearer " + tokenString
    fmt.Println(bearer)
    return &bearer, nil
}

func loadPrivateKey() (interface{}, error) {
    certPath := os.Getenv("APPLE_CERT")
    certPassword := os.Getenv("PASSKEYPASS")

    p12Bytes, err := ioutil.ReadFile(certPath)
    if err != nil {
        fmt.Println("failure to read cert file")
        return nil, err
    }

    blocks, err := pkcs12.ToPEM(p12Bytes, certPassword)
    if err != nil {
        fmt.Println("failure to convert to PEM file")
        return nil, err
    }

    var pemData []byte
    for _, block := range blocks {
        if block.Type == "PRIVATE KEY" || block.Type == "RSA PRIVATE KEY" {
            pemData = pem.EncodeToMemory(&pem.Block{
                Type:  block.Type,
                Bytes: block.Bytes,
            })
            break
        }
    }

    if pemData == nil {
        fmt.Println("no private key found")
        return nil, err
    }

    block, _ := pem.Decode(pemData)
    if block == nil {
        fmt.Println("failed to parse PEM block containing the key")
        return nil, err
    }

    key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
        fmt.Println("failure parse private key")
        return nil, err
    }
    return key, nil
}
func SendPushNotification(device_id string, push_token string) error {
    authorization, err := getAppleBearerAuth()
    if err != nil {
        fmt.Println("Failure to get apple auth: " + err.Error())
        return err
    }
    endpoint := fmt.Sprintf("https://api.sandbox.push.apple.com/3/device/%s", device_id) //APNs dev endpoint

    //Empty payload as documented.
    payload := map[string]interface{}{"pushToken": push_token}
    payloadJSON, err := json.Marshal(payload)
    if err != nil {
        fmt.Println("Failed to marshal payload:", err)
        return err
    }
    fmt.Println("payload: " + string(payloadJSON))
    expiration := time.Now().Add(24 * time.Hour).Unix()
    // Set the headers for the request
    headers := map[string]string{
        "Authorization":   *authorization,
        "Content-Type":    "application/json",
        "apns-push-type":  "background",
        "apns-topic":      os.Getenv("APPLE_PASS_TYPE_IDENTIFIER"),
        "apns-expiration": fmt.Sprintf("%d", expiration),
    }

    httpClient := &http.Client{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{
                InsecureSkipVerify: true, // Set to false in production
            },
            ForceAttemptHTTP2: true,
        },
    }

    request, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(payloadJSON))

    if err != nil {
        fmt.Println("Failed to create request:", err)
        return err
    }

    for key, value := range headers {
        request.Header.Set(key, value)
    }

    response, err := httpClient.Do(request)
    if err != nil {
        fmt.Println("Failed to send request:", err)
        return err
    }
    fmt.Println("Sent push notification!")
    defer response.Body.Close()

    if response.StatusCode == http.StatusOK {
        fmt.Println("Push notification sent successfully.")
    } else {
        fmt.Println("Failed to send the push notification. Status code:", response.StatusCode)
        fmt.Println("response content ", response)
    }
    return nil
}

The cert being used is the same one I used to sign the apple wallet pass when it was created. I've added SendPushNotification("9b39b68fa1e4b01485857160ecdde0e0", "7c8a5c327bce2a6d18710aebabdc5225f3b3a252e7ff8027906b1243ad498687") is what I call to attempt to send a test. And it spits out this response:

Failed to send the push notification. Status code: 403
response content  &{403 Forbidden 403 HTTP/2.0 2 0 map[Apns-Id:[7731E8A4-D049-A982-5B31-11894BA891D4]] {0xc000178900} -1 [] false false map[] 0xc000602100 0xc00049ce70}

Things I've tried:

  • Adding the port :443 to the url. results unchanged.
  • removing device piece from url and adding it to content. result 404.
  • adding/removing each header. and testing each combination of headers. Have tried both "authorization" and "Authorization". results unchanged
  • several different devices and deviceId, push_token pairs

The bearer token looks normal example: "Bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IkFQQzU5V1JUV0oiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE2ODczODI5MzEsImlzcyI6IjUyVzZXSE1YWkwifQ.l16IhiXr__Pjf_rsFP16CeyDyzsdVrgLHA1DRLvD3mB_I_fGtCemhgtO-ibx9SjXD078sqFsHGHTpGqspJfPo0Ed2feWgvfP-ak1fN0wRwjh-4JTOqCHoI8US_AE0Yp2eNBIvnT4ZjB61s5Bu-cv-dvYpLJVZ254m872raIlIoOW9eQtV_0GbRMstlWQo2Euy4XKyUtOqzzF4g_wCPLLlgsYf4_SogWhyEYvrH82UlrqM1Fd7-ZPnLqioj844GpzU0oHga1Wb8qHZARu9bhnq7wIj4KvUnlkGMYMspVXW-QLlsC91jK0IakeEK6ap3ZP1LLL0gWX0C8gL0Emi3PTgQ"

I'm not sure what to try next. My credentials work fine for issuing the pass but I'm getting this oddly formatted 403 for this push regardless of if I'm sending an auth or not.

Has anyone gotten Apple wallet pass updates to work, and know what I'm doing wrong or have working example code for this pass update APNS push?

0 Answers0