forked from jrconlin/vapid
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #31 from web-push-libs/feat/3.4
Feat: 3.4 support and JSON dump
- Loading branch information
Showing
9 changed files
with
214 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
language: python | ||
python: | ||
- "2.7" | ||
- "3.5" | ||
install: | ||
- cd python | ||
- pip install -r requirements.txt | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,50 @@ This minimal library contains the minimal set of functions you need to | |
generate a VAPID key set and get the headers you'll need to sign a | ||
WebPush subscription update. | ||
|
||
This can either be installed as a library or used as a stand along | ||
app. | ||
VAPID is a voluntary standard for WebPush subscription providers | ||
(sites that send WebPush updates to remote customers) to self-identify | ||
to Push Servers (the servers that convey the push notifications). | ||
|
||
The VAPID "claims" are a set of JSON keys and values. There are two | ||
required fields, one semi-optional and several optional additional | ||
fields. | ||
|
||
At a minimum a VAPID claim set should look like: | ||
``` | ||
{"sub":"mailto:[email protected]","aud":"https://PushServerURL","exp":"ExpirationTimestamp"} | ||
``` | ||
A few notes: | ||
|
||
***sub*** is the email address you wish to have on record for this | ||
request, prefixed with "`mailto:`". If things go wrong, this is the | ||
email that will be used to contact you (for instance). This can be a | ||
general delivery address like "`mailto:[email protected]`" or a | ||
specific address like "`mailto:[email protected]`". | ||
|
||
***aud*** is the audience for the VAPID. This it the host path you use to | ||
send subscription endpoints and generally coincides with the | ||
`endpoint` specified in the Subscription Info block. | ||
|
||
As example, if a WebPush subscription info contains: | ||
`{"endpoint": "https://push.example.com:8012/v1/push/...", ...}` | ||
|
||
then the `aud` would be "`https://push.example.com:8012/`" | ||
|
||
While some Push Services consider this an optional field, others may | ||
be stricter. | ||
|
||
***exp*** This is the UTC timestamp for when this VAPID request will | ||
expire. The maximum period is 24 hours. Setting a shorter period can | ||
prevent "replay" attacks. Setting a longer period allows you to reuse | ||
headers for multiple sends (e.g. if you're sending hundreds of updates | ||
within an hour or so.) If no `exp` is included, one that will expire | ||
in 24 hours will be auto-generated for you. | ||
|
||
Claims should be stored in a JSON compatible file. In the examples | ||
below, we've stored the claims into a file named `claims.json`. | ||
|
||
py_vapid can either be installed as a library or used as a stand along | ||
app, `bin/vapid`. | ||
|
||
## App Installation | ||
|
||
|
@@ -15,18 +57,24 @@ Then run | |
``` | ||
bin/pip install -r requirements.txt | ||
bin/python setup.py`install | ||
bin/python setup.py install | ||
``` | ||
## App Usage | ||
|
||
Run by itself, `bin/vapid` will check and optionally create the | ||
public_key.pem and private_key.pem files. | ||
|
||
`bin/vapid --sign _claims.json_` will generate a set of HTTP headers | ||
`bin/vapid -gen` can be used to generate a new set of public and | ||
private key PEM files. These will overwrite the contents of | ||
`private_key.pem` and `public_key.pem`. | ||
|
||
`bin/vapid --sign claims.json` will generate a set of HTTP headers | ||
from a JSON formatted claims file. A sample `claims.json` is included | ||
with this distribution. | ||
|
||
`bin/vapid --validate _token_` will generate a token response for the | ||
Mozilla WebPush dashboard. | ||
`bin/vapid --sign claims.json --json` will output the headers in | ||
JSON format, which may be useful for other programs. | ||
|
||
See `bin/vapid -h` for all options and commands. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
Easy VAPID generation | ||
===================== | ||
|
||
This minimal library contains the minimal set of functions you need to | ||
generate a VAPID key set and get the headers you'll need to sign a | ||
WebPush subscription update. | ||
|
||
VAPID is a voluntary standard for WebPush subscription providers (sites | ||
that send WebPush updates to remote customers) to self-identify to Push | ||
Servers (the servers that convey the push notifications). | ||
|
||
The VAPID "claims" are a set of JSON keys and values. There are two | ||
required fields, one semi-optional and several optional additional | ||
fields. | ||
|
||
At a minimum a VAPID claim set should look like: | ||
|
||
:: | ||
|
||
{"sub":"mailto:[email protected]","aud":"https://PushServerURL","exp":"ExpirationTimestamp"} | ||
|
||
A few notes: | ||
|
||
***sub*** is the email address you wish to have on record for this | ||
request, prefixed with "``mailto:``". If things go wrong, this is the | ||
email that will be used to contact you (for instance). This can be a | ||
general delivery address like "``mailto:[email protected]``" | ||
or a specific address like "``mailto:[email protected]``". | ||
|
||
***aud*** is the audience for the VAPID. This it the host path you use | ||
to send subscription endpoints and generally coincides with the | ||
``endpoint`` specified in the Subscription Info block. | ||
|
||
As example, if a WebPush subscription info contains: | ||
``{"endpoint": "https://push.example.com:8012/v1/push/...", ...}`` | ||
|
||
then the ``aud`` would be "``https://push.example.com:8012/``" | ||
|
||
While some Push Services consider this an optional field, others may be | ||
stricter. | ||
|
||
***exp*** This is the UTC timestamp for when this VAPID request will | ||
expire. The maximum period is 24 hours. Setting a shorter period can | ||
prevent "replay" attacks. Setting a longer period allows you to reuse | ||
headers for multiple sends (e.g. if you're sending hundreds of updates | ||
within an hour or so.) If no ``exp`` is included, one that will expire | ||
in 24 hours will be auto-generated for you. | ||
|
||
Claims should be stored in a JSON compatible file. In the examples | ||
below, we've stored the claims into a file named ``claims.json``. | ||
|
||
py\_vapid can either be installed as a library or used as a stand along | ||
app, ``bin/vapid``. | ||
|
||
App Installation | ||
---------------- | ||
|
||
You'll need ``python virtualenv`` Run that in the current directory. | ||
|
||
Then run | ||
|
||
:: | ||
|
||
bin/pip install -r requirements.txt | ||
|
||
bin/python setup.py install | ||
|
||
App Usage | ||
--------- | ||
|
||
Run by itself, ``bin/vapid`` will check and optionally create the | ||
public\_key.pem and private\_key.pem files. | ||
|
||
``bin/vapid -gen`` can be used to generate a new set of public and | ||
private key PEM files. These will overwrite the contents of | ||
``private_key.pem`` and ``public_key.pem``. | ||
|
||
``bin/vapid --sign claims.json`` will generate a set of HTTP headers | ||
from a JSON formatted claims file. A sample ``claims.json`` is included | ||
with this distribution. | ||
|
||
``bin/vapid --sign claims.json --json`` will output the headers in JSON | ||
format, which may be useful for other programs. | ||
|
||
See ``bin/vapid -h`` for all options and commands. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,46 +12,54 @@ | |
def main(): | ||
parser = argparse.ArgumentParser(description="VAPID tool") | ||
parser.add_argument('--sign', '-s', help='claims file to sign') | ||
parser.add_argument('--gen', '-g', help='generate new key pairs', | ||
default=False, action="store_true") | ||
parser.add_argument('--validate', '-v', help='dashboard token to validate') | ||
parser.add_argument('--version2', '-2', help="use VAPID spec Draft-02", | ||
default=False, action="store_true") | ||
parser.add_argument('--version1', '-1', help="use VAPID spec Draft-01", | ||
default=True, action="store_true") | ||
parser.add_argument('--json', help="dump as json", | ||
default=False, action="store_true") | ||
args = parser.parse_args() | ||
Vapid = Vapid01 | ||
if args.version2: | ||
Vapid = Vapid02 | ||
if not os.path.exists('private_key.pem'): | ||
print "No private_key.pem file found." | ||
answer = None | ||
while answer not in ['y', 'n']: | ||
answer = raw_input("Do you want me to create one for you? (Y/n)") | ||
if not answer: | ||
answer = 'y' | ||
answer = answer.lower()[0] | ||
if answer == 'n': | ||
print "Sorry, can't do much for you then." | ||
exit | ||
if answer == 'y': | ||
break | ||
if args.gen or not os.path.exists('private_key.pem'): | ||
if not args.gen: | ||
print("No private_key.pem file found.") | ||
answer = None | ||
while answer not in ['y', 'n']: | ||
answer = input("Do you want me to create one for you? (Y/n)") | ||
if not answer: | ||
answer = 'y' | ||
answer = answer.lower()[0] | ||
if answer == 'n': | ||
print ("Sorry, can't do much for you then.") | ||
exit | ||
print("Generating private_key.pem") | ||
Vapid().save_key('private_key.pem') | ||
vapid = Vapid('private_key.pem') | ||
if not os.path.exists('public_key.pem'): | ||
print "No public_key.pem file found. You'll need this to access " | ||
print "the developer dashboard." | ||
answer = None | ||
while answer not in ['y', 'n']: | ||
answer = raw_input("Do you want me to create one for you? (Y/n)") | ||
if not answer: | ||
answer = 'y' | ||
answer = answer.lower()[0] | ||
if answer == 'y': | ||
vapid.save_public_key('public_key.pem') | ||
if args.gen or not os.path.exists('public_key.pem'): | ||
if not args.gen: | ||
print("No public_key.pem file found. You'll need this to access " | ||
"the developer dashboard.") | ||
answer = None | ||
while answer not in ['y', 'n']: | ||
answer = input("Do you want me to create one for you? (Y/n)") | ||
if not answer: | ||
answer = 'y' | ||
answer = answer.lower()[0] | ||
if answer == 'n': | ||
print ("Exiting...") | ||
exit | ||
print("Generating public_key.pem") | ||
vapid.save_public_key('public_key.pem') | ||
claim_file = args.sign | ||
if claim_file: | ||
if not os.path.exists(claim_file): | ||
print "No %s file found." % claim_file | ||
print """ | ||
print("No {} file found.".format(claim_file)) | ||
print(""" | ||
The claims file should be a JSON formatted file that holds the | ||
information that describes you. There are three elements in the claims | ||
file you'll need: | ||
|
@@ -70,25 +78,27 @@ def main(): | |
For example, a claims.json file could contain: | ||
{"sub": "mailto:[email protected]"} | ||
""" | ||
""") | ||
exit | ||
try: | ||
claims = json.loads(open(claim_file).read()) | ||
result = vapid.sign(claims) | ||
except Exception, exc: | ||
print "Crap, something went wrong: %s", repr(exc) | ||
except Exception as exc: | ||
print("Crap, something went wrong: {}".format(repr(exc))) | ||
raise exc | ||
|
||
print "Include the following headers in your request:\n" | ||
if args.json: | ||
print(json.dumps(result)) | ||
return | ||
print("Include the following headers in your request:\n") | ||
for key, value in result.items(): | ||
print "%s: %s" % (key, value) | ||
print "\n" | ||
print("{}: {}\n".format(key, value)) | ||
print("\n") | ||
|
||
token = args.validate | ||
if token: | ||
print "signed token for dashboard validation:\n" | ||
print vapid.validate(token) | ||
print "\n" | ||
print("signed token for dashboard validation:\n") | ||
print(vapid.validate(token)) | ||
print("\n") | ||
|
||
|
||
if __name__ == '__main__': | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,8 +24,12 @@ | |
-----END PUBLIC KEY----- | ||
""" | ||
|
||
T_PUBLIC_RAW = """EJwJZq_GN8jJbo1GGpyU70hmP2hbWAUpQFKDBy\ | ||
KB81yldJ9GTklBM5xqEwuPM7VuQcyiLDhvovthPIXx-gsQRQ==""".strip('=') | ||
# this is a DER RAW key ('\x04' + 2 32 octet digits) | ||
# Remember, this should have any padding stripped. | ||
T_PUBLIC_RAW = ( | ||
"BBCcCWavxjfIyW6NRhqclO9IZj9oW1gFKUBSgwcigfNc" | ||
"pXSfRk5JQTOcahMLjzO1bkHMoiw4b6L7YTyF8foLEEU" | ||
).strip('=') | ||
|
||
|
||
def setUp(self): | ||
|
@@ -87,7 +91,7 @@ def test_save_key(self): | |
v.save_key("/tmp/p2") | ||
os.unlink("/tmp/p2") | ||
|
||
def test_save_public_key(self): | ||
def test_same_public_key(self): | ||
v = Vapid01() | ||
v.generate_keys() | ||
v.save_public_key("/tmp/p2") | ||
|
@@ -108,8 +112,7 @@ def test_sign_01(self): | |
claims = {"aud": "example.com", "sub": "[email protected]"} | ||
result = v.sign(claims, "id=previous") | ||
eq_(result['Crypto-Key'], | ||
'id=previous,' | ||
'p256ecdsa=' + T_PUBLIC_RAW) | ||
'id=previous;p256ecdsa=' + T_PUBLIC_RAW) | ||
items = jws.verify(result['Authorization'].split(' ')[1], | ||
v.public_key, | ||
algorithms=["ES256"]) | ||
|
Oops, something went wrong.