Develatio / django-walletpass

Django .pkpass build, sign, push for updates, serve and more...
Other
25 stars 13 forks source link

Can not register passes for push notifications. #26

Closed freeridre closed 10 months ago

freeridre commented 11 months ago

Hello! Can somebody proof that the push notification works? I tried it many times, but my passes never saved into the django_walletpass_registration table. It saved only in the django_walletpass_pass table, but as far as I understood when we add the pass to the wallet, then the wallet app tries to reach the api-endpoint, and then the pass will be inside the django_walletpass_registration table, but it never happened with me.

This is my views.py: I tried it with https://senitysecuritysystems.com/passes/ and https://senitysecuritysystems.com/api/passes/ and https://senitysecuritysystems.com/api/passes/v1/ but nothing happened.

@login_required(login_url='login')
def dashboard_user(request):
    if request.method == "POST":
        builder = PassBuilder(directory = settings.APPLE_PASS_JSON_PATH)
        builder.pass_data_required["serialNumber"] = secrets.token_urlsafe(20)
        builder.pass_data_required["authenticationToken"] = secrets.token_hex(32)
        builder.pass_data_required["webServiceURL"] = "https://senitysecuritysystems.com/api/passes/v1/"
        builder.pass_data["nfc"]["message"] = secrets.token_hex(4)
        pkpass_content = builder.build()

        pass_instance = builder.write_to_model()
        pass_instance.save()
        response = HttpResponse(pkpass_content, content_type="application/vnd.apple.pkpass")
        response['Content-Disposition'] = 'attachment; filename="senity.pkpass"'
        return response

    return render(request, "dashboard.html", {})

This is my settings.py:

WALLETPASS = {
    'CERT_PATH': APPLE_CER_PEM,
    'KEY_PATH': APPLE_KEY_PEM,
    'KEY_PASSWORD': APPLE_CERT_PASS_BYTES,
    'PASS_TYPE_ID': APPLE_PASS_TYPE_IDENTIFIER,
    'TEAM_ID': APPLE_TEAM_ID,
    'SERVICE_URL': 'https://senitysecuritysystems.com/api/passes/v1/',
    'PUSH_AUTH_STRATEGY': 'token',
    'TOKEN_AUTH_KEY_PATH': APPLE_TOKEN_AUTH_KEY_FILE,
    'TOKEN_AUTH_KEY_ID': APPLE_TOKEN_KEY_ID,
    "APPLE_WWDRCA_PEM_PATH": APPLE_WWDR_PEM,
    'STORAGE_CLASS': 'django.core.files.storage.FileSystemStorage',
    'UPLOAD_TO': 'passes',
    "STORAGE_HTTP_REDIRECT": True,
    'PUSH_SANDBOX': False
}

So the passes are saved, but not registered for push notifications. image image

patroqueeet commented 11 months ago

we're actively using push notifies and it's working.

reqs for us:

django-walletpass>=3.0

w/ Dj4.2 and py 3.11

freeridre commented 11 months ago

It's good to know. In that case the issue is with my inplementation. Do you have any idea?

patroqueeet commented 11 months ago

nope. but I would myself debug like this:

freeridre commented 11 months ago

In order to work the push_notifiaction firstly, my pass should be registered. Am I right? Are there any ways to debug why my pass is not registered?

patroqueeet commented 11 months ago

use the signal for pass registration to assert that you get the proper response from apple upon registration

freeridre commented 11 months ago

As far as I have recently understood, apple does not take a part of the pass registration process. Apple only takes part after the pass registered to your db.

patroqueeet commented 11 months ago

either one: if the device or apple themselves notifies you: the webhook is called and the Django signal fired.

freeridre commented 11 months ago

My django webapp is connected with apache2 where I can monitor the in-out coming requests, but I have never saw any request from apple wallet, after the pas added to the wallet. Then my webservice url could be inappropriate?!

freeridre commented 11 months ago

Do you use the master branch?

patroqueeet commented 11 months ago

My django webapp is connected with apache2 where I can monitor the in-out coming requests, but I have never saw any request from apple wallet, after the pas added to the wallet. Then my webservice url could be inappropriate?!

get a passbook file, unzip it, see the json data and validate the callback url is valid

patroqueeet commented 11 months ago

Do you use the master branch?

⬆️

freeridre commented 11 months ago

My django webapp is connected with apache2 where I can monitor the in-out coming requests, but I have never saw any request from apple wallet, after the pas added to the wallet. Then my webservice url could be inappropriate?!

get a passbook file, unzip it, see the json data and validate the callback url is valid

hmm very strange. If I try to open the "callback url" alias WebserviceUrl, then I get

your connection is not private NET::ERR_CERT_COMMON_NAME_INVALID

error, and if I proceed, then I get

Page not found 404 error

for www.senitysecuritysystems.com/passes

patroqueeet commented 11 months ago

then it must have https and you need to check the url setup according to docs (RTFM).

freeridre commented 11 months ago

Can you request www.example.com/passes in your webapp or in other words what happends if you request your website? I mean does it work for you? One of my colleague checked this www.senitysecuritysystems.com/passes and it's in https, and he got page not found 404 error.

patroqueeet commented 11 months ago

hey, will not access unknown urls and do your debugging. sending proper HTTP requests and inspecting the logs should be sufficient. in any other case write proper tests to learn what your code does.

freeridre commented 10 months ago

I realized that the communication happends, but it breaks with the ssl handshake process, so now I try to recreate the https certificate.

freeridre commented 10 months ago

I recreated the certificate now the communication works, and I see the requests when the Apple wallet tries to register the pass, but unfortunatelly I get this error every time: [Wed Jan 10 15:26:27.296571 2024] [wsgi:error] [pid 10109] [remote 37.76.10.117:16848] Unauthorized: /api/passes/v1/devices/58149c6696c2592240aa7ab4510cc4b7/registrations/pass.com.senity.application/Cu64anbaTCm-gyMzDbUp9SpNs_g

patroqueeet commented 10 months ago

using search feature of Gh reveals: https://github.com/search?q=repo%3ADevelatio%2Fdjango-walletpass%20Unauthorized&type=code - debug the http headers, if that code is not hit, the error is not related to this lib

freeridre commented 10 months ago

I still have not figured out what could be the problem. I still get these errors after the wallet tries to register the pass to push notification.

Unauthorized: /api/passes/v1/devices/b752fb2de40a42b0308e605cbaa33b23/registrations/pass.com.senity.application/1fU-M8l0U-QN4YYGNZ--5D4icQ_XzMNk

[2024-01-11 01:27:21 +0100] Register task (for device b752fb2de40a42b0308e605cbaa33b23, pass type pass.com.senity.application, serial number 1fU-M8l0U-QN4YYGNZ--5D4icQ_XzMNk; with web service url https://senitysecuritysystems.com/api/passes/) encountered error: Authentication failure

freeridre commented 10 months ago

Unfortunatelly, I'm still struggling in order to register the pass for push notification. I tried the "registration" link with the postman, and I also got unauthorized error.

(0.001) SELECT django_walletpass_pass.id, django_walletpass_pass.pass_type_identifier, django_walletpass_pass.serial_number, django_walletpass_pass.authentication_token, django_walletpass_pass.data, django_walletpass_pass.updated_at FROM django_walletpass_pass WHERE (django_walletpass_pass.pass_type_identifier = 'pass.com.senity.application' AND django_walletpass_pass.serial_number = 'Zcb7G_ZInUdjQDjfFVA4CbSupdBGTu1Q') LIMIT 21; args=('pass.com.senity.application', 'Zcb7G_ZInUdjQDjfFVA4CbSupdBGTu1Q') (0.001) SELECT django_walletpass_pass.id, django_walletpass_pass.pass_type_identifier, django_walletpass_pass.serial_number, django_walletpass_pass.authentication_token, django_walletpass_pass.data, django_walletpass_pass.updated_at FROM django_walletpass_pass WHERE (django_walletpass_pass.pass_type_identifier = 'pass.com.senity.application' AND django_walletpass_pass.serial_number = 'VESRSi1L7FceW4fL-ZDL277f7LuOI6lu') LIMIT 21; args=('pass.com.senity.application', 'VESRSi1L7FceW4fL-ZDL277f7LuOI6lu') Unauthorized: /api/passes/v1/devices/8037daf4905f21a1d91c17f7933ffdf9/registrations/pass.com.senity.application/VESRSi1L7FceW4fL-ZDL277f7LuOI6lu Exception while resolving variable 'name' in template 'unknown'. Traceback (most recent call last): File "/home/pi/WebService/passwebservice/.venv/lib/python3.7/site-packages/django/core/handlers/exception.py", line 47, in inner response = get_response(request) File "/home/pi/WebService/passwebservice/.venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 167, in _get_response callback, callback_args, callback_kwargs = self.resolve_request(request) File "/home/pi/WebService/passwebservice/.venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 290, in resolve_request resolver_match = resolver.resolve(request.path_info) File "/home/pi/WebService/passwebservice/.venv/lib/python3.7/site-packages/django/urls/resolvers.py", line 589, in resolve raise Resolver404({'tried': tried, 'path': new_path}) django.urls.exceptions.Resolver404: {'tried': [[<URLResolver <module 'django_walletpass.urls' from '/home/pi/WebService/passwebservice/.venv/lib/python3.7/site-packages/django_walletpass/urls.py'> (None:None) '^api/passes/'>], [<URLResolver (admin:admin) 'admin/'>], [<URLPattern '' [name='login']>], [<URLResolver <module 'UserHandler.urls' from '/home/pi/WebService/passwebservice/UserHandler/urls.py'> (None:None) 'UserHandler/'>]], 'path': 'passes/v1/devices/533e84c95651067bb2dddc4b70930696/registrations/pass.com.senity.application/_UmVM4AFHcVZ8XpSRZmVyOGWqwBxkCIE'}

patroqueeet commented 10 months ago

please always provide full test setup info. what was your postman request alike. what headers did you send?

freeridre commented 10 months ago

In the postman I tried this url with Bearer Token with this authkey: 901b71313d06d9ced98efae02e4a53b0c7fd3c6cccd23328421925c1d30eab7a0b7068f3 https://senitysecuritysystems.com/api/passes/v1/devices/511e5d2bf98ee2515d2faaa3a7f7de89/registrations/pass.com.senity.application/kwau0WmDxncH1dxtwLEX1VNaVPvpzVBx

Unfortunatelly, I got 401, unatuhorized. The problem is that I do not know what kind of authorization should I use. I chose the Bearer Token, because I found that it only needs the Token, however JWT Bearer needs more information that I do not know. Even I do not really know which authorization method should I choose from postman...

image

The webserver

is Apache2 under Raspbian OS with mod_wsgi

The webapp

is Django 3.2.16

The walletpas

s is django-walletpass 3.0

My https

created with certbot let's encrypt.

my settings.py:

APPLE_CER_PEM=os.environ.get("APPLE_CER_PEM")
APPLE_KEY_PEM=os.environ.get("APPLE_KEY_PEM")
APPLE_CERT_PASS=os.environ.get("APPLE_CERT_PASS")
APPLE_CERT_PASS_BYTES = APPLE_CERT_PASS.encode() if APPLE_CERT_PASS else None
APPLE_PASS_TYPE_IDENTIFIER=os.environ.get("APPLE_PASS_TYPE_IDENTIFIER")
APPLE_TEAM_ID=os.environ.get("APPLE_TEAM_ID")
APPLE_WWDR_PEM=os.environ.get("APPLE_WWDR_PEM")
APPLE_LOGO=os.environ.get("APPLE_LOGO")
APPLE_ICON=os.environ.get("APPLE_ICON")
APPLE_ICON_X2=os.environ.get("APPLE_ICON_X2")
APPLE_ICON_X3=os.environ.get("APPLE_ICON_X3")
APPLE_NFC_PUB_KEY=os.environ.get("APPLE_NFC_PUB_KEY")
APPLE_TOKEN_KEY_ID=os.environ.get("APPLE_TOKEN_KEY_ID")
APPLE_TOKEN_AUTH_KEY_FILE=os.environ.get("APPLE_TOKEN_AUTH_KEY_FILE")
APPLE_PASS_JSON_PATH=os.environ.get("APPLE_PASS_JSON_PATH")

WALLETPASS = {
    'CERT_PATH': APPLE_CER_PEM,
    'KEY_PATH': APPLE_KEY_PEM,
    'KEY_PASSWORD': APPLE_CERT_PASS_BYTES,
    'PASS_TYPE_ID': APPLE_PASS_TYPE_IDENTIFIER,
    'TEAM_ID': APPLE_TEAM_ID,
    'SERVICE_URL': 'https://senitysecuritysystems.com/passes/',
    'PUSH_AUTH_STRATEGY': 'token',
    'TOKEN_AUTH_KEY_PATH': APPLE_TOKEN_AUTH_KEY_FILE,
    'TOKEN_AUTH_KEY_ID': APPLE_TOKEN_KEY_ID,
    "APPLE_WWDRCA_PEM_PATH": APPLE_WWDR_PEM,
    'STORAGE_CLASS': 'django.core.files.storage.FileSystemStorage',
    'UPLOAD_TO': 'passes',
    "STORAGE_HTTP_REDIRECT": True,
    'PUSH_SANDBOX': False
}

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/home/pi/WebService/passwebservice/SenityProject/debug.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
        '__main__': {  # Allows to log from the main module (your views, models, etc.)
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

my .env file:

export APPLE_CER_PEM="/home/pi/WebService/passwebservice/certificates/certificate.pem"
export APPLE_KEY_PEM="/home/pi/WebService/passwebservice/certificates/private.pem"
export APPLE_CERT_PASS=46137928
export APPLE_PASS_TYPE_IDENTIFIER="pass.com.senity.application"
export APPLE_TEAM_ID="AC825JF3W2"
export APPLE_WWDR_PEM="/home/pi/WebService/passwebservice/certificates/APPLEWWDRCAG4.pem"
export APPLE_LOGO="/home/pi/WebService/passwebservice/static/images/passes/apple/logo.png"
export APPLE_ICON="/home/pi/WebService/passwebservice/static/images/passes/apple/icon.png"
export APPLE_ICON_X2="/home/pi/WebService/passwebservice/static/images/passes/apple/icon@2x.png"
export APPLE_ICON_X3="/home/pi/WebService/passwebservice/static/images/passes/apple/icon@3x.png"
export APPLE_NFC_PUB_KEY="/home/pi/WebService/passwebservice/nfckeys/NFCPublicKey.pem"
export APPLE_TOKEN_KEY_ID="4884QQP5Y7"
export APPLE_TOKEN_AUTH_KEY_FILE="/home/pi/WebService/passwebservice/certificates/ApplePushNotificationCertificates/token/AuthKey_4884QQP5Y7.p8"
export APPLE_PASS_JSON_PATH="/home/pi/WebService/passwebservice/static/applepass"

my urls.py:

from django.contrib import admin
from django.urls import path, include
from django.conf.urls import url, include
from UserHandler.views import (
login_user
)
urlpatterns = [
url(r'^api/passes/', include('django_walletpass.urls')),
path('admin/', admin.site.urls),
path("", login_user, name="login"),
path("UserHandler/", include("UserHandler.urls")),
]

my views.py for pass building and registering for push notifications:


@login_required(login_url='login')
def dashboard_user(request):
if request.method == "POST":
builder = PassBuilder(directory = settings.APPLE_PASS_JSON_PATH)
builder.pass_data_required["serialNumber"] = secrets.token_urlsafe(24)
builder.pass_data_required["authenticationToken"] = crypto.gen_random_token() #secrets.token_hex(36)
builder.pass_data_required["webServiceURL"] = "https://senitysecuritysystems.com/api/passes/"
builder.pass_data["nfc"]["message"] = secrets.token_hex(4)
builder.pass_data["generic"]["backFields"][0]["value"] = builder.pass_data_required["webServiceURL"]
builder.pass_data["generic"]["backFields"][1]["value"] = builder.pass_data_required["serialNumber"]
builder.pass_data["generic"]["backFields"][2]["value"] = builder.pass_data_required["authenticationToken"]
pkpass_content = builder.build()
    pass_instance = builder.write_to_model()
    pass_instance.save()
    #pkpass_file = open('senity.pkpass', 'rb')
    #pkpass_file.write(pkpass_content)
    #pass_instance = builder.write_to_model()
    #pass_instance.save()
    response = HttpResponse(pkpass_content, content_type="application/vnd.apple.pkpass")
    response['Content-Disposition'] = 'attachment; filename="senity.pkpass"'
    return response

return render(request, "dashboard.html", {})
freeridre commented 10 months ago

Well, finally I solved all of the errors... 👯 What was the problem for the unauthorized error? My apache2 virtualhost configuration was not properly configured. Everytime the Apple Wallet sent request in order to register the pass for push notification, the Authorization header was empty. Why? Because my Virtual Host configuration didn't contain this: WSGIPassAuthorization On As you have already found out I use django with Apache2 therefore I have to use wsgi_mod in order to handle the django webapp with my webserver. After that some other errors appeared like: the apache2 server was not able to get the pkpass file from media/passes , because that was also missed from my Virtual Host Configuration. How could I find out what was the problem? I created the logging config for my django app, and for walletpass also, and check it with apache2 error.log file. Please, confirm that the django_walletpass_log table is only for error loggings. Only one question remained. How can I send push notification for my pass? Other thing, I noticed that the documentation doesn't properly describe the settings. The webserviceurl should https://example.com/api/passes/ not https://example.com/passes/ . I will create a PR for that.

freeridre commented 10 months ago

I have already figured out how to send push notifications for the pass. I will update the documentation of this library.