mirror of
https://github.com/alexgo-io/stacks.js.git
synced 2026-01-12 22:52:34 +08:00
rehaul auth system
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -32,6 +32,6 @@ unused
|
||||
|
||||
# Folder to ignore for development with es6
|
||||
lib
|
||||
docs/tokenfiles/*.json
|
||||
docs/token-files/*.json
|
||||
|
||||
src/testing/browser/blockstack-proofs.js
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
[
|
||||
{
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiI2ZDJmYzM1My0yNDE5LTQ0MjktYmVmMi03NDAzNTNmYTA1MjUiLCJpYXQiOiIyMDE3LTAyLTI2VDIwOjE3OjI4LjUwNloiLCJleHAiOiIyMDE4LTAyLTI2VDIwOjE3OjI4LjUwNloiLCJzdWJqZWN0Ijp7InB1YmxpY0tleSI6IjAzODVmMzNmYTk0MjVhOTQwZjg1YmE0NjNmN2I2ZjIyYjE1NjcxNjNkMmZlNjAzNGNlMDkzY2NiOGUyOTgwZWRhYSJ9LCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDM4NWYzM2ZhOTQyNWE5NDBmODViYTQ2M2Y3YjZmMjJiMTU2NzE2M2QyZmU2MDM0Y2UwOTNjY2I4ZTI5ODBlZGFhIn0sImNsYWltIjp7IkBjb250ZXh0IjoiaHR0cDovL3NjaGVtYS5vcmcvIiwiQHR5cGUiOiJDcmVhdGl2ZVdvcmsiLCJuYW1lIjoiQmFsbG9vbiBEb2ciLCJjcmVhdG9yIjpbeyJAdHlwZSI6IlBlcnNvbiIsIkBpZCI6InRoZXJlYWxqZWZma29vbnMuaWQiLCJuYW1lIjoiSmVmZiBLb29ucyJ9XSwiZGF0ZUNyZWF0ZWQiOiIxOTk0LTA1LTA5VDAwOjAwOjAwLTA0MDAiLCJkYXRlUHVibGlzaGVkIjoiMjAxNS0xMi0xMFQxNDo0NDoyNi0wNTAwIn19.vfd6vt3ARA38UJMN34EtF0Nl6buH9HMfYpHgpzVxWhIcO1dKpM4nSQvZTGKcUki0k7vh9CHpjv33K53rjes8gA",
|
||||
"decodedToken": {
|
||||
"header": {
|
||||
"typ": "JWT",
|
||||
"alg": "ES256K"
|
||||
},
|
||||
"payload": {
|
||||
"jti": "6d2fc353-2419-4429-bef2-740353fa0525",
|
||||
"iat": "2017-02-26T20:17:28.506Z",
|
||||
"exp": "2018-02-26T20:17:28.506Z",
|
||||
"subject": {
|
||||
"publicKey": "0385f33fa9425a940f85ba463f7b6f22b1567163d2fe6034ce093ccb8e2980edaa"
|
||||
},
|
||||
"issuer": {
|
||||
"publicKey": "0385f33fa9425a940f85ba463f7b6f22b1567163d2fe6034ce093ccb8e2980edaa"
|
||||
},
|
||||
"claim": {
|
||||
"@context": "http://schema.org/",
|
||||
"@type": "CreativeWork",
|
||||
"name": "Balloon Dog",
|
||||
"creator": [
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "therealjeffkoons.id",
|
||||
"name": "Jeff Koons"
|
||||
}
|
||||
],
|
||||
"dateCreated": "1994-05-09T00:00:00-0400",
|
||||
"datePublished": "2015-12-10T14:44:26-0500"
|
||||
}
|
||||
},
|
||||
"signature": "vfd6vt3ARA38UJMN34EtF0Nl6buH9HMfYpHgpzVxWhIcO1dKpM4nSQvZTGKcUki0k7vh9CHpjv33K53rjes8gA"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,60 +0,0 @@
|
||||
[
|
||||
{
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiJjZjY2MmNkNi0xNDI1LTQ3YzktOWE1ZS1kMWFlYzExNTQwNzAiLCJpYXQiOiIyMDE3LTAyLTI2VDIwOjE3OjI4LjQzOFoiLCJleHAiOiIyMDE4LTAyLTI2VDIwOjE3OjI4LjQzOFoiLCJzdWJqZWN0Ijp7InB1YmxpY0tleSI6IjAzZmQyYzkxMTM1NjAxNTg1YTUxMGMzMTE0Zjg5OTk2NTQxMDcwNWEwNDc0NjU3MjEwOTdiYTU4NTg2ZTQ5NDEzNiJ9LCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDNmZDJjOTExMzU2MDE1ODVhNTEwYzMxMTRmODk5OTY1NDEwNzA1YTA0NzQ2NTcyMTA5N2JhNTg1ODZlNDk0MTM2In0sImNsYWltIjp7IkBjb250ZXh0IjoiaHR0cDovL3NjaGVtYS5vcmcvIiwiQHR5cGUiOiJPcmdhbml6YXRpb24iLCJuYW1lIjoiR29vZ2xlIiwibGVnYWxOYW1lIjoiR29vZ2xlIEluYy4iLCJlbWFpbCI6ImhlbGxvQGdvb2dsZS5vcmciLCJhZGRyZXNzIjp7IkB0eXBlIjoiUG9zdGFsQWRkcmVzcyIsImFkZHJlc3NMb2NhbGl0eSI6Ik1vdW50YWluIFZpZXcsIENBIiwicG9zdGFsQ29kZSI6Ijk0MDQzIiwic3RyZWV0QWRkcmVzcyI6IjE2MDAgQW1waGl0aGVhdHJlIFBhcmt3YXkifSwiZW1wbG95ZWUiOlt7IkB0eXBlIjoiUGVyc29uIiwiQGlkIjoibGFycnlwYWdlLmlkIiwibmFtZSI6IkxhcnJ5IFBhZ2UifSx7IkB0eXBlIjoiUGVyc29uIiwiQGlkIjoic2VyZ2V5YnJpbi5pZCIsIm5hbWUiOiJTZXJnZXkgQnJpbiJ9XSwiaW1hZ2UiOlt7IkB0eXBlIjoiSW1hZ2VPYmplY3QiLCJuYW1lIjoibG9nbyIsImNvbnRlbnRVcmwiOiJodHRwczovL3d3dy5nb29nbGUuY29tL2ltYWdlcy9icmFuZGluZy9nb29nbGVsb2dvLzJ4L2dvb2dsZWxvZ29fY29sb3JfMjcyeDkyZHAucG5nIn1dLCJwYXJlbnRPcmdhbml6YXRpb24iOnsiQHR5cGUiOiJPcmdhbml6YXRpb24iLCJAaWQiOiJhbHBoYWJldC5pZCIsIm5hbWUiOiJBbHBoYWJldCBJbmMuIn19fQ.hrF-bxIIAQ3zw2KjddH88cjv_ao0ua5XQPddBrCnT5xnwwQ0lvsrAM3uRq9ib98Bldd0N6YBBzL668g2Ao09Fw",
|
||||
"decodedToken": {
|
||||
"header": {
|
||||
"typ": "JWT",
|
||||
"alg": "ES256K"
|
||||
},
|
||||
"payload": {
|
||||
"jti": "cf662cd6-1425-47c9-9a5e-d1aec1154070",
|
||||
"iat": "2017-02-26T20:17:28.438Z",
|
||||
"exp": "2018-02-26T20:17:28.438Z",
|
||||
"subject": {
|
||||
"publicKey": "03fd2c91135601585a510c3114f899965410705a047465721097ba58586e494136"
|
||||
},
|
||||
"issuer": {
|
||||
"publicKey": "03fd2c91135601585a510c3114f899965410705a047465721097ba58586e494136"
|
||||
},
|
||||
"claim": {
|
||||
"@context": "http://schema.org/",
|
||||
"@type": "Organization",
|
||||
"name": "Google",
|
||||
"legalName": "Google Inc.",
|
||||
"email": "hello@google.org",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"addressLocality": "Mountain View, CA",
|
||||
"postalCode": "94043",
|
||||
"streetAddress": "1600 Amphitheatre Parkway"
|
||||
},
|
||||
"employee": [
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "larrypage.id",
|
||||
"name": "Larry Page"
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "sergeybrin.id",
|
||||
"name": "Sergey Brin"
|
||||
}
|
||||
],
|
||||
"image": [
|
||||
{
|
||||
"@type": "ImageObject",
|
||||
"name": "logo",
|
||||
"contentUrl": "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"
|
||||
}
|
||||
],
|
||||
"parentOrganization": {
|
||||
"@type": "Organization",
|
||||
"@id": "alphabet.id",
|
||||
"name": "Alphabet Inc."
|
||||
}
|
||||
}
|
||||
},
|
||||
"signature": "hrF-bxIIAQ3zw2KjddH88cjv_ao0ua5XQPddBrCnT5xnwwQ0lvsrAM3uRq9ib98Bldd0N6YBBzL668g2Ao09Fw"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,106 +0,0 @@
|
||||
[
|
||||
{
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiIyNThkMTI4NS0yZDhjLTRhOTEtYjQ2OC0zMDRjODVkYTUxYzgiLCJpYXQiOiIyMDE3LTAyLTI2VDIwOjE3OjI4LjU5NFoiLCJleHAiOiIyMDE4LTAyLTI2VDIwOjE3OjI4LjU5NFoiLCJzdWJqZWN0Ijp7InB1YmxpY0tleSI6IjAyZDY2Y2UwOWI4ZWNkZTYyYjQ2NzcxMTEyMTYyZGY5YzlmNzU3MDJmYjdlZTU0NmZkMjYxZGQxYmZjZTU2NTk2MyJ9LCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDJkNjZjZTA5YjhlY2RlNjJiNDY3NzExMTIxNjJkZjljOWY3NTcwMmZiN2VlNTQ2ZmQyNjFkZDFiZmNlNTY1OTYzIn0sImNsYWltIjp7IkB0eXBlIjoiUGVyc29uIiwiQGNvbnRleHQiOiJodHRwOi8vc2NoZW1hLm9yZy8iLCJuYW1lIjoiTmF2YWwgUmF2aWthbnQiLCJnaXZlbk5hbWUiOiJOYXZhbCIsImZhbWlseU5hbWUiOiJSYXZpa2FudCIsImRlc2NyaXB0aW9uIjoiQ28tZm91bmRlciBvZiBBbmdlbExpc3QiLCJpbWFnZSI6W3siQHR5cGUiOiJJbWFnZU9iamVjdCIsIm5hbWUiOiJhdmF0YXIiLCJjb250ZW50VXJsIjoiaHR0cHM6Ly9wYnMudHdpbWcuY29tL3Byb2ZpbGVfaW1hZ2VzLzM2OTY2MTczMjgvNjY3ODc0YzU5MzY3NjRkOTNkNTZjY2M3NmEyYmNjMTMuanBlZyJ9LHsiQHR5cGUiOiJJbWFnZU9iamVjdCIsIm5hbWUiOiJiYWNrZ3JvdW5kIiwiY29udGVudFVybCI6Imh0dHBzOi8vcGJzLnR3aW1nLmNvbS9wcm9maWxlX2Jhbm5lcnMvNzQ1MjczLzEzNTU3MDU3Nzcvd2ViX3JldGluYSJ9XSwid2Vic2l0ZSI6W3siQHR5cGUiOiJXZWJTaXRlIiwidXJsIjoiYW5nZWwuY28ifV0sImFjY291bnQiOlt7IkB0eXBlIjoiQWNjb3VudCIsInNlcnZpY2UiOiJmYWNlYm9vayIsImlkZW50aWZpZXIiOiJuYXZhbHIiLCJwcm9vZlR5cGUiOiJodHRwIiwicHJvb2ZVcmwiOiJodHRwczovL2ZhY2Vib29rLmNvbS9uYXZhbHIvcG9zdHMvMTAxNTIxOTA3MzQwNzcyNjEifSx7IkB0eXBlIjoiQWNjb3VudCIsInNlcnZpY2UiOiJ0d2l0dGVyIiwiaWRlbnRpZmllciI6Im5hdmFsIiwicHJvb2ZUeXBlIjoiaHR0cCIsInByb29mVXJsIjoiaHR0cHM6Ly90d2l0dGVyLmNvbS9uYXZhbC9zdGF0dXMvNDg2NjA5MjY2MjEyNDk5NDU2In0seyJAdHlwZSI6IkFjY291bnQiLCJzZXJ2aWNlIjoiZ2l0aHViIiwiaWRlbnRpZmllciI6Im5hdmFsciIsInByb29mVHlwZSI6Imh0dHAiLCJwcm9vZlVybCI6Imh0dHBzOi8vZ2lzdC5naXRodWIuY29tL25hdmFsci9mMzFhNzQwNTRmODU5ZWMwYWM2YSJ9LHsiQHR5cGUiOiJBY2NvdW50Iiwic2VydmljZSI6ImJpdGNvaW4iLCJyb2xlIjoicGF5bWVudCIsImlkZW50aWZpZXIiOiIxOTE5VXJoWXloczQ3MXBzOENGY0ozRFJwV1NkYThxdFNrIiwicHJvb2ZUeXBlIjoic2lnbmF0dXJlIiwicHJvb2ZNZXNzYWdlIjoiVmVyaWZ5aW5nIHRoYXQgK25hdmFsIGlzIG15IGJsb2NrY2hhaW4gSUQuIiwicHJvb2ZTaWduYXR1cmUiOiJJQ3VSQStEcTVEbjhBaVk5UCttY0x6R3lpYlBnRzBlYzlDcGh0TWs1MTJ1UGRCNWVBbmNEU0hoUVpZLzdreWN2bDZQTEZFdVIrajNPTS9LMlZleTErRVU9In1dLCJ3b3Jrc0ZvciI6W3siQHR5cGUiOiJPcmdhbml6YXRpb24iLCJAaWQiOiJhbmdlbGxpc3QuaWQifV0sImtub3dzIjpbeyJAdHlwZSI6IlBlcnNvbiIsIkBpZCI6Im11bmVlYi5pZCJ9LHsiQHR5cGUiOiJQZXJzb24iLCJAaWQiOiJyeWFuLmlkIn1dLCJiaXJ0aERhdGUiOiIxOTczLTAxLTAxIiwidGF4SUQiOiIwMDAtMDAtMDAwMCIsImFkZHJlc3MiOnsiQHR5cGUiOiJQb3N0YWxBZGRyZXNzIiwic3RyZWV0QWRkcmVzcyI6IjE2IE1haWRlbiBMbiIsImFkZHJlc3NMb2NhbGl0eSI6IlNhbiBGcmFuY2lzY28sIENBIiwicG9zdGFsQ29kZSI6Ijk0MTA4IiwiYWRkcmVzc0NvdW50cnkiOiJVbml0ZWQgU3RhdGVzIn19fQ.YLlbAJW9dWKMxTQcNFa1wOWgrOTUjJbCMlVnMd2teNa1GHZsC_CdwKzOHl_QZkCdBJ3MT0slgxr2OE6m6PlC_w",
|
||||
"decodedToken": {
|
||||
"header": {
|
||||
"typ": "JWT",
|
||||
"alg": "ES256K"
|
||||
},
|
||||
"payload": {
|
||||
"jti": "258d1285-2d8c-4a91-b468-304c85da51c8",
|
||||
"iat": "2017-02-26T20:17:28.594Z",
|
||||
"exp": "2018-02-26T20:17:28.594Z",
|
||||
"subject": {
|
||||
"publicKey": "02d66ce09b8ecde62b46771112162df9c9f75702fb7ee546fd261dd1bfce565963"
|
||||
},
|
||||
"issuer": {
|
||||
"publicKey": "02d66ce09b8ecde62b46771112162df9c9f75702fb7ee546fd261dd1bfce565963"
|
||||
},
|
||||
"claim": {
|
||||
"@type": "Person",
|
||||
"@context": "http://schema.org/",
|
||||
"name": "Naval Ravikant",
|
||||
"givenName": "Naval",
|
||||
"familyName": "Ravikant",
|
||||
"description": "Co-founder of AngelList",
|
||||
"image": [
|
||||
{
|
||||
"@type": "ImageObject",
|
||||
"name": "avatar",
|
||||
"contentUrl": "https://pbs.twimg.com/profile_images/3696617328/667874c5936764d93d56ccc76a2bcc13.jpeg"
|
||||
},
|
||||
{
|
||||
"@type": "ImageObject",
|
||||
"name": "background",
|
||||
"contentUrl": "https://pbs.twimg.com/profile_banners/745273/1355705777/web_retina"
|
||||
}
|
||||
],
|
||||
"website": [
|
||||
{
|
||||
"@type": "WebSite",
|
||||
"url": "angel.co"
|
||||
}
|
||||
],
|
||||
"account": [
|
||||
{
|
||||
"@type": "Account",
|
||||
"service": "facebook",
|
||||
"identifier": "navalr",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://facebook.com/navalr/posts/10152190734077261"
|
||||
},
|
||||
{
|
||||
"@type": "Account",
|
||||
"service": "twitter",
|
||||
"identifier": "naval",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://twitter.com/naval/status/486609266212499456"
|
||||
},
|
||||
{
|
||||
"@type": "Account",
|
||||
"service": "github",
|
||||
"identifier": "navalr",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://gist.github.com/navalr/f31a74054f859ec0ac6a"
|
||||
},
|
||||
{
|
||||
"@type": "Account",
|
||||
"service": "bitcoin",
|
||||
"role": "payment",
|
||||
"identifier": "1919UrhYyhs471ps8CFcJ3DRpWSda8qtSk",
|
||||
"proofType": "signature",
|
||||
"proofMessage": "Verifying that +naval is my blockchain ID.",
|
||||
"proofSignature": "ICuRA+Dq5Dn8AiY9P+mcLzGyibPgG0ec9CphtMk512uPdB5eAncDSHhQZY/7kycvl6PLFEuR+j3OM/K2Vey1+EU="
|
||||
}
|
||||
],
|
||||
"worksFor": [
|
||||
{
|
||||
"@type": "Organization",
|
||||
"@id": "angellist.id"
|
||||
}
|
||||
],
|
||||
"knows": [
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "muneeb.id"
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "ryan.id"
|
||||
}
|
||||
],
|
||||
"birthDate": "1973-01-01",
|
||||
"taxID": "000-00-0000",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "16 Maiden Ln",
|
||||
"addressLocality": "San Francisco, CA",
|
||||
"postalCode": "94108",
|
||||
"addressCountry": "United States"
|
||||
}
|
||||
}
|
||||
},
|
||||
"signature": "YLlbAJW9dWKMxTQcNFa1wOWgrOTUjJbCMlVnMd2teNa1GHZsC_CdwKzOHl_QZkCdBJ3MT0slgxr2OE6m6PlC_w"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,106 +0,0 @@
|
||||
[
|
||||
{
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiJmMjY2NzI2MS1jOTZlLTRjOGQtYmIwNy0xMTI2MzdmYWY5M2QiLCJpYXQiOiIyMDE3LTAyLTI2VDIwOjE3OjI4LjM2OVoiLCJleHAiOiIyMDE4LTAyLTI2VDIwOjE3OjI4LjM2OVoiLCJzdWJqZWN0Ijp7InB1YmxpY0tleSI6IjAzZDRiNzIzNWQ4OWYxNmM0ZmY3NGFmYzRhOGY2ZTI1ZmFkNjI1ZjY3MWQxNDk0YjI4NDc0YTM0ZDFmZWI2NmYzMyJ9LCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDNkNGI3MjM1ZDg5ZjE2YzRmZjc0YWZjNGE4ZjZlMjVmYWQ2MjVmNjcxZDE0OTRiMjg0NzRhMzRkMWZlYjY2ZjMzIn0sImNsYWltIjp7IkBjb250ZXh0IjoiaHR0cDovL3NjaGVtYS5vcmcvIiwiQHR5cGUiOiJQZXJzb24iLCJuYW1lIjoiTmF2YWwgUmF2aWthbnQiLCJnaXZlbk5hbWUiOiJOYXZhbCIsImZhbWlseU5hbWUiOiJSYXZpa2FudCIsImRlc2NyaXB0aW9uIjoiQ28tZm91bmRlciBvZiBBbmdlbExpc3QiLCJpbWFnZSI6W3siQHR5cGUiOiJJbWFnZU9iamVjdCIsIm5hbWUiOiJhdmF0YXIiLCJjb250ZW50VXJsIjoiaHR0cHM6Ly9wYnMudHdpbWcuY29tL3Byb2ZpbGVfaW1hZ2VzLzM2OTY2MTczMjgvNjY3ODc0YzU5MzY3NjRkOTNkNTZjY2M3NmEyYmNjMTMuanBlZyJ9LHsiQHR5cGUiOiJJbWFnZU9iamVjdCIsIm5hbWUiOiJiYWNrZ3JvdW5kIiwiY29udGVudFVybCI6Imh0dHBzOi8vcGJzLnR3aW1nLmNvbS9wcm9maWxlX2Jhbm5lcnMvNzQ1MjczLzEzNTU3MDU3Nzcvd2ViX3JldGluYSJ9XSwid2Vic2l0ZSI6W3siQHR5cGUiOiJXZWJTaXRlIiwidXJsIjoiYW5nZWwuY28ifV0sImFjY291bnQiOlt7IkB0eXBlIjoiQWNjb3VudCIsInNlcnZpY2UiOiJmYWNlYm9vayIsImlkZW50aWZpZXIiOiJuYXZhbHIiLCJwcm9vZlR5cGUiOiJodHRwIiwicHJvb2ZVcmwiOiJodHRwczovL2ZhY2Vib29rLmNvbS9uYXZhbHIvcG9zdHMvMTAxNTIxOTA3MzQwNzcyNjEifSx7IkB0eXBlIjoiQWNjb3VudCIsInNlcnZpY2UiOiJ0d2l0dGVyIiwiaWRlbnRpZmllciI6Im5hdmFsIiwicHJvb2ZUeXBlIjoiaHR0cCIsInByb29mVXJsIjoiaHR0cHM6Ly90d2l0dGVyLmNvbS9uYXZhbC9zdGF0dXMvNDg2NjA5MjY2MjEyNDk5NDU2In0seyJAdHlwZSI6IkFjY291bnQiLCJzZXJ2aWNlIjoiZ2l0aHViIiwiaWRlbnRpZmllciI6Im5hdmFsciIsInByb29mVHlwZSI6Imh0dHAiLCJwcm9vZlVybCI6Imh0dHBzOi8vZ2lzdC5naXRodWIuY29tL25hdmFsci9mMzFhNzQwNTRmODU5ZWMwYWM2YSJ9LHsiQHR5cGUiOiJBY2NvdW50Iiwic2VydmljZSI6ImJpdGNvaW4iLCJyb2xlIjoicGF5bWVudCIsImlkZW50aWZpZXIiOiIxOTE5VXJoWXloczQ3MXBzOENGY0ozRFJwV1NkYThxdFNrIiwicHJvb2ZUeXBlIjoic2lnbmF0dXJlIiwicHJvb2ZNZXNzYWdlIjoiVmVyaWZ5aW5nIHRoYXQgK25hdmFsIGlzIG15IGJsb2NrY2hhaW4gSUQuIiwicHJvb2ZTaWduYXR1cmUiOiJJQ3VSQStEcTVEbjhBaVk5UCttY0x6R3lpYlBnRzBlYzlDcGh0TWs1MTJ1UGRCNWVBbmNEU0hoUVpZLzdreWN2bDZQTEZFdVIrajNPTS9LMlZleTErRVU9In1dLCJ3b3Jrc0ZvciI6W3siQHR5cGUiOiJPcmdhbml6YXRpb24iLCJAaWQiOiJhbmdlbGxpc3QuaWQifV0sImtub3dzIjpbeyJAdHlwZSI6IlBlcnNvbiIsIkBpZCI6Im11bmVlYi5pZCJ9LHsiQHR5cGUiOiJQZXJzb24iLCJAaWQiOiJyeWFuLmlkIn1dLCJiaXJ0aERhdGUiOiIxOTczLTAxLTAxIiwidGF4SUQiOiIwMDAtMDAtMDAwMCIsImFkZHJlc3MiOnsiQHR5cGUiOiJQb3N0YWxBZGRyZXNzIiwic3RyZWV0QWRkcmVzcyI6IjE2IE1haWRlbiBMbiIsImFkZHJlc3NMb2NhbGl0eSI6IlNhbiBGcmFuY2lzY28sIENBIiwicG9zdGFsQ29kZSI6Ijk0MTA4IiwiYWRkcmVzc0NvdW50cnkiOiJVbml0ZWQgU3RhdGVzIn19fQ.yVrQR9sNuyjHcknnbBa5BwILYc4caT8fod6BO6nTnn_cfk8qRINHd4FkV4WC2GssE52OtRElM3DByR5BQi7bbQ",
|
||||
"decodedToken": {
|
||||
"header": {
|
||||
"typ": "JWT",
|
||||
"alg": "ES256K"
|
||||
},
|
||||
"payload": {
|
||||
"jti": "f2667261-c96e-4c8d-bb07-112637faf93d",
|
||||
"iat": "2017-02-26T20:17:28.369Z",
|
||||
"exp": "2018-02-26T20:17:28.369Z",
|
||||
"subject": {
|
||||
"publicKey": "03d4b7235d89f16c4ff74afc4a8f6e25fad625f671d1494b28474a34d1feb66f33"
|
||||
},
|
||||
"issuer": {
|
||||
"publicKey": "03d4b7235d89f16c4ff74afc4a8f6e25fad625f671d1494b28474a34d1feb66f33"
|
||||
},
|
||||
"claim": {
|
||||
"@context": "http://schema.org/",
|
||||
"@type": "Person",
|
||||
"name": "Naval Ravikant",
|
||||
"givenName": "Naval",
|
||||
"familyName": "Ravikant",
|
||||
"description": "Co-founder of AngelList",
|
||||
"image": [
|
||||
{
|
||||
"@type": "ImageObject",
|
||||
"name": "avatar",
|
||||
"contentUrl": "https://pbs.twimg.com/profile_images/3696617328/667874c5936764d93d56ccc76a2bcc13.jpeg"
|
||||
},
|
||||
{
|
||||
"@type": "ImageObject",
|
||||
"name": "background",
|
||||
"contentUrl": "https://pbs.twimg.com/profile_banners/745273/1355705777/web_retina"
|
||||
}
|
||||
],
|
||||
"website": [
|
||||
{
|
||||
"@type": "WebSite",
|
||||
"url": "angel.co"
|
||||
}
|
||||
],
|
||||
"account": [
|
||||
{
|
||||
"@type": "Account",
|
||||
"service": "facebook",
|
||||
"identifier": "navalr",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://facebook.com/navalr/posts/10152190734077261"
|
||||
},
|
||||
{
|
||||
"@type": "Account",
|
||||
"service": "twitter",
|
||||
"identifier": "naval",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://twitter.com/naval/status/486609266212499456"
|
||||
},
|
||||
{
|
||||
"@type": "Account",
|
||||
"service": "github",
|
||||
"identifier": "navalr",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://gist.github.com/navalr/f31a74054f859ec0ac6a"
|
||||
},
|
||||
{
|
||||
"@type": "Account",
|
||||
"service": "bitcoin",
|
||||
"role": "payment",
|
||||
"identifier": "1919UrhYyhs471ps8CFcJ3DRpWSda8qtSk",
|
||||
"proofType": "signature",
|
||||
"proofMessage": "Verifying that +naval is my blockchain ID.",
|
||||
"proofSignature": "ICuRA+Dq5Dn8AiY9P+mcLzGyibPgG0ec9CphtMk512uPdB5eAncDSHhQZY/7kycvl6PLFEuR+j3OM/K2Vey1+EU="
|
||||
}
|
||||
],
|
||||
"worksFor": [
|
||||
{
|
||||
"@type": "Organization",
|
||||
"@id": "angellist.id"
|
||||
}
|
||||
],
|
||||
"knows": [
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "muneeb.id"
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "ryan.id"
|
||||
}
|
||||
],
|
||||
"birthDate": "1973-01-01",
|
||||
"taxID": "000-00-0000",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "16 Maiden Ln",
|
||||
"addressLocality": "San Francisco, CA",
|
||||
"postalCode": "94108",
|
||||
"addressCountry": "United States"
|
||||
}
|
||||
}
|
||||
},
|
||||
"signature": "yVrQR9sNuyjHcknnbBa5BwILYc4caT8fod6BO6nTnn_cfk8qRINHd4FkV4WC2GssE52OtRElM3DByR5BQi7bbQ"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,93 +0,0 @@
|
||||
[
|
||||
{
|
||||
"decodedToken": {
|
||||
"header": {
|
||||
"alg": "ES256K",
|
||||
"typ": "JWT"
|
||||
},
|
||||
"payload": {
|
||||
"issuedAt": "2016-12-21T02:20:24.575047",
|
||||
"claim": {
|
||||
"image": [
|
||||
{
|
||||
"contentUrl": "https://s3.amazonaws.com/kd4/ryan",
|
||||
"name": "avatar",
|
||||
"@type": "ImageObject"
|
||||
},
|
||||
{
|
||||
"contentUrl": "https://s3.amazonaws.com/dx3/ryan",
|
||||
"name": "cover",
|
||||
"@type": "ImageObject"
|
||||
}
|
||||
],
|
||||
"@type": "Person",
|
||||
"website": [
|
||||
{
|
||||
"url": "http://shea.io",
|
||||
"@type": "WebSite"
|
||||
}
|
||||
],
|
||||
"description": "Co-founder of Blockstack Inc.",
|
||||
"address": {
|
||||
"addressLocality": "New York",
|
||||
"@type": "PostalAddress"
|
||||
},
|
||||
"account": [
|
||||
{
|
||||
"identifier": "1LFS37yRSibwbf8CnXeCn5t1GKeTEZMmu9",
|
||||
"role": "payment",
|
||||
"@type": "Account",
|
||||
"service": "bitcoin"
|
||||
},
|
||||
{
|
||||
"contentUrl": "https://s3.amazonaws.com/pk9/ryan",
|
||||
"identifier": "1E4329E6634C75730D4D88C0638F2769D55B9837",
|
||||
"@type": "Account",
|
||||
"service": "pgp"
|
||||
},
|
||||
{
|
||||
"identifier": "f2250123a6af138c86b30f3233b338961dc8fbc3",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://www.facebook.com/msrobot0/posts/10153644446452759",
|
||||
"service": "openbazaar",
|
||||
"@type": "Account"
|
||||
},
|
||||
{
|
||||
"identifier": "ryaneshea",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://twitter.com/ryaneshea/status/765575388735082496",
|
||||
"service": "twitter",
|
||||
"@type": "Account"
|
||||
},
|
||||
{
|
||||
"identifier": "shea256",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://gist.github.com/shea256/a6dc1f3182f28bb2285feaef07a14340",
|
||||
"service": "github",
|
||||
"@type": "Account"
|
||||
},
|
||||
{
|
||||
"identifier": "ryaneshea",
|
||||
"proofType": "http",
|
||||
"proofUrl": "https://www.facebook.com/ryaneshea/posts/10154182997407713",
|
||||
"service": "facebook",
|
||||
"@type": "Account"
|
||||
}
|
||||
],
|
||||
"name": "Ryan Shea"
|
||||
},
|
||||
"expiresAt": "2017-12-21T02:20:24.575047",
|
||||
"subject": {
|
||||
"publicKey": "0312ccf3255cb005e42c186aa3d2302083b306a52c1f0cb47b1119639f134e6695"
|
||||
},
|
||||
"issuer": {
|
||||
"publicKey": "0312ccf3255cb005e42c186aa3d2302083b306a52c1f0cb47b1119639f134e6695"
|
||||
}
|
||||
},
|
||||
"signature": "YVoNsoJCTMcXIwqa9D5kinkUrnyppsYus7Z-8cn7o9hA6_IG9zkoZGSvsIzfqqjG1mV8JNV1Nh04CZl1qrt1YQ"
|
||||
},
|
||||
"token": "eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJpc3N1ZWRBdCI6IjIwMTYtMTItMjFUMDI6MjA6MjQuNTc1MDQ3IiwiY2xhaW0iOnsiaW1hZ2UiOlt7ImNvbnRlbnRVcmwiOiJodHRwczovL3MzLmFtYXpvbmF3cy5jb20va2Q0L3J5YW4iLCJuYW1lIjoiYXZhdGFyIiwiQHR5cGUiOiJJbWFnZU9iamVjdCJ9LHsiY29udGVudFVybCI6Imh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS9keDMvcnlhbiIsIm5hbWUiOiJjb3ZlciIsIkB0eXBlIjoiSW1hZ2VPYmplY3QifV0sIkB0eXBlIjoiUGVyc29uIiwid2Vic2l0ZSI6W3sidXJsIjoiaHR0cDovL3NoZWEuaW8iLCJAdHlwZSI6IldlYlNpdGUifV0sImFjY291bnQiOlt7ImlkZW50aWZpZXIiOiIxTEZTMzd5UlNpYndiZjhDblhlQ241dDFHS2VURVpNbXU5Iiwicm9sZSI6InBheW1lbnQiLCJAdHlwZSI6IkFjY291bnQiLCJzZXJ2aWNlIjoiYml0Y29pbiJ9LHsiY29udGVudFVybCI6Imh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS9wazkvcnlhbiIsImlkZW50aWZpZXIiOiIxRTQzMjlFNjYzNEM3NTczMEQ0RDg4QzA2MzhGMjc2OUQ1NUI5ODM3IiwiQHR5cGUiOiJBY2NvdW50Iiwic2VydmljZSI6InBncCJ9LHsicHJvb2ZUeXBlIjoiaHR0cCIsImlkZW50aWZpZXIiOiJmMjI1MDEyM2E2YWYxMzhjODZiMzBmMzIzM2IzMzg5NjFkYzhmYmMzIiwicHJvb2ZVcmwiOiJodHRwczovL3d3dy5mYWNlYm9vay5jb20vbXNyb2JvdDAvcG9zdHMvMTAxNTM2NDQ0NDY0NTI3NTkiLCJzZXJ2aWNlIjoib3BlbmJhemFhciIsIkB0eXBlIjoiQWNjb3VudCJ9LHsicHJvb2ZUeXBlIjoiaHR0cCIsImlkZW50aWZpZXIiOiJyeWFuZXNoZWEiLCJwcm9vZlVybCI6Imh0dHBzOi8vdHdpdHRlci5jb20vcnlhbmVzaGVhL3N0YXR1cy83NjU1NzUzODg3MzUwODI0OTYiLCJzZXJ2aWNlIjoidHdpdHRlciIsIkB0eXBlIjoiQWNjb3VudCJ9LHsicHJvb2ZUeXBlIjoiaHR0cCIsImlkZW50aWZpZXIiOiJzaGVhMjU2IiwicHJvb2ZVcmwiOiJodHRwczovL2dpc3QuZ2l0aHViLmNvbS9zaGVhMjU2L2E2ZGMxZjMxODJmMjhiYjIyODVmZWFlZjA3YTE0MzQwIiwic2VydmljZSI6ImdpdGh1YiIsIkB0eXBlIjoiQWNjb3VudCJ9LHsicHJvb2ZUeXBlIjoiaHR0cCIsImlkZW50aWZpZXIiOiJyeWFuZXNoZWEiLCJwcm9vZlVybCI6Imh0dHBzOi8vd3d3LmZhY2Vib29rLmNvbS9yeWFuZXNoZWEvcG9zdHMvMTAxNTQxODI5OTc0MDc3MTMiLCJzZXJ2aWNlIjoiZmFjZWJvb2siLCJAdHlwZSI6IkFjY291bnQifV0sImFkZHJlc3MiOnsiYWRkcmVzc0xvY2FsaXR5IjoiTmV3IFlvcmsiLCJAdHlwZSI6IlBvc3RhbEFkZHJlc3MifSwiZGVzY3JpcHRpb24iOiJDby1mb3VuZGVyIG9mIEJsb2Nrc3RhY2sgSW5jLiIsIm5hbWUiOiJSeWFuIFNoZWEifSwiZXhwaXJlc0F0IjoiMjAxNy0xMi0yMVQwMjoyMDoyNC41NzUwNDciLCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDMxMmNjZjMyNTVjYjAwNWU0MmMxODZhYTNkMjMwMjA4M2IzMDZhNTJjMWYwY2I0N2IxMTE5NjM5ZjEzNGU2Njk1In0sInN1YmplY3QiOnsicHVibGljS2V5IjoiMDMxMmNjZjMyNTVjYjAwNWU0MmMxODZhYTNkMjMwMjA4M2IzMDZhNTJjMWYwY2I0N2IxMTE5NjM5ZjEzNGU2Njk1In19.YVoNsoJCTMcXIwqa9D5kinkUrnyppsYus7Z-8cn7o9hA6_IG9zkoZGSvsIzfqqjG1mV8JNV1Nh04CZl1qrt1YQ",
|
||||
"parentPublicKey": "0312ccf3255cb005e42c186aa3d2302083b306a52c1f0cb47b1119639f134e6695",
|
||||
"encrypted": false
|
||||
}
|
||||
]
|
||||
@@ -1,43 +0,0 @@
|
||||
[
|
||||
{
|
||||
"decodedToken": {
|
||||
"header": {
|
||||
"alg": "ES256K",
|
||||
"typ": "JWT"
|
||||
},
|
||||
"payload": {
|
||||
"issuedAt": "2016-04-20T12:25:14.453734",
|
||||
"claim": {
|
||||
"account": [],
|
||||
"accounts": [],
|
||||
"@type": "Person",
|
||||
"image": [
|
||||
{
|
||||
"contentUrl": "https://s3.amazonaws.com/97p/rv1.jpeg",
|
||||
"@type": "ImageObject",
|
||||
"name": "cover"
|
||||
},
|
||||
{
|
||||
"contentUrl": "https://s3.amazonaws.com/kd4/ryan_apr20",
|
||||
"@type": "ImageObject",
|
||||
"name": "avatar"
|
||||
}
|
||||
],
|
||||
"name": "Ryan Shea"
|
||||
},
|
||||
"expiresAt": "2017-04-20T12:25:14.453734",
|
||||
"subject": {
|
||||
"publicKey": "02413d7c51118104cfe1b41e540b6c2acaaf91f1e2e22316df7448fb6070d582ec"
|
||||
},
|
||||
"issuer": {
|
||||
"publicKey": "02413d7c51118104cfe1b41e540b6c2acaaf91f1e2e22316df7448fb6070d582ec"
|
||||
}
|
||||
},
|
||||
"signature": "Xj3z975ccW6oxbrlm_YsdGNreuzERRxPoj0DyyJ9vygMYfUjsTQGcxsejmkSPYafTFd6TNIbNBTquutOKZvmBA"
|
||||
},
|
||||
"token": "eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJpc3N1ZWRBdCI6IjIwMTYtMDQtMjBUMTI6MjU6MTQuNDUzNzM0IiwiY2xhaW0iOnsiYWNjb3VudCI6W10sImFjY291bnRzIjpbXSwiQHR5cGUiOiJQZXJzb24iLCJpbWFnZSI6W3siY29udGVudFVybCI6Imh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS85N3AvcnYxLmpwZWciLCJAdHlwZSI6IkltYWdlT2JqZWN0IiwibmFtZSI6ImNvdmVyIn0seyJjb250ZW50VXJsIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL2tkNC9yeWFuX2FwcjIwIiwiQHR5cGUiOiJJbWFnZU9iamVjdCIsIm5hbWUiOiJhdmF0YXIifV0sIm5hbWUiOiJSeWFuIFNoZWEifSwiZXhwaXJlc0F0IjoiMjAxNy0wNC0yMFQxMjoyNToxNC40NTM3MzQiLCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDI0MTNkN2M1MTExODEwNGNmZTFiNDFlNTQwYjZjMmFjYWFmOTFmMWUyZTIyMzE2ZGY3NDQ4ZmI2MDcwZDU4MmVjIn0sInN1YmplY3QiOnsicHVibGljS2V5IjoiMDI0MTNkN2M1MTExODEwNGNmZTFiNDFlNTQwYjZjMmFjYWFmOTFmMWUyZTIyMzE2ZGY3NDQ4ZmI2MDcwZDU4MmVjIn19.Xj3z975ccW6oxbrlm_YsdGNreuzERRxPoj0DyyJ9vygMYfUjsTQGcxsejmkSPYafTFd6TNIbNBTquutOKZvmBA",
|
||||
"parentPublicKey": "02413d7c51118104cfe1b41e540b6c2acaaf91f1e2e22316df7448fb6070d582ec",
|
||||
"publicKey": "02413d7c51118104cfe1b41e540b6c2acaaf91f1e2e22316df7448fb6070d582ec",
|
||||
"encrypted": false
|
||||
}
|
||||
]
|
||||
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"name": "blockstack-auth",
|
||||
"version": "0.2.6",
|
||||
"description": "Blockstack Auth Library",
|
||||
"main": "lib/index",
|
||||
"scripts": {
|
||||
"compile": "babel --presets es2015 src -d lib",
|
||||
"test": "npm run compile; node lib/test/unitTests.js",
|
||||
"prepublish": "npm run compile",
|
||||
"browserify-test": "npm run compile; node lib/test/browserifyTests.js",
|
||||
"browserify-app": "npm run compile; browserify lib/test/browserifyApp.js -o lib/test/bundle.js",
|
||||
"release": "npm version patch && npm publish"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/blockstack/blockstack-auth-js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"id",
|
||||
"auth",
|
||||
"authentication",
|
||||
"bitcoin",
|
||||
"blockchain auth",
|
||||
"blockchain authentication",
|
||||
"blockchainid",
|
||||
"blockchain id",
|
||||
"bitcoin auth",
|
||||
"bitcoin authentication",
|
||||
"bitcoin login",
|
||||
"blockchain login",
|
||||
"authorization",
|
||||
"login",
|
||||
"signin",
|
||||
"sso",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"token",
|
||||
"blockstack",
|
||||
"blockstack auth"
|
||||
],
|
||||
"author": {
|
||||
"name": "Blockstack Inc.",
|
||||
"email": "admin@blockstack.com",
|
||||
"url": "https://blockstack.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/blockstack/blockstack-auth-js/issues"
|
||||
},
|
||||
"homepage": "https://github.com/blockstack/blockstack-auth-js#readme",
|
||||
"dependencies": {
|
||||
"base64url": "^1.0.4",
|
||||
"elliptic": "^5.1.0",
|
||||
"elliptic-curve": "^0.1.0",
|
||||
"hasprop": "0.0.3",
|
||||
"jsontokens": "^0.6.5",
|
||||
"key-encoder": "^1.1.3",
|
||||
"keychain-manager": "^1.1.2",
|
||||
"node-uuid": "^1.4.3",
|
||||
"promise": "^7.0.4",
|
||||
"query-string": "^4.2.3",
|
||||
"request": "^2.79.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-preset-es2015": "^6.14.0",
|
||||
"browserify": "^13.1.1",
|
||||
"onename-api": "^1.0.1",
|
||||
"phantomjs-prebuilt": "^2.1.13",
|
||||
"tape": "^4.2.0",
|
||||
"tape-run": "^2.1.4"
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@
|
||||
"elliptic-curve": "^0.1.0",
|
||||
"hasprop": "0.0.4",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jsontokens": "^0.6.2",
|
||||
"jsontokens": "^0.7.3",
|
||||
"key-encoder": "^1.1.6",
|
||||
"promise": "^7.1.1",
|
||||
"query-string": "^4.3.2",
|
||||
|
||||
142
src/auth.js
Normal file
142
src/auth.js
Normal file
@@ -0,0 +1,142 @@
|
||||
'use strict'
|
||||
|
||||
import queryString from 'query-string'
|
||||
import base64url from 'base64url'
|
||||
import request from 'request'
|
||||
import { TokenSigner, decodeToken, createUnsecuredToken } from 'jsontokens'
|
||||
import { secp256k1 } from 'elliptic-curve'
|
||||
import { makeUUID4, nextMonth, nextHour } from './utils'
|
||||
import { makeDIDFromPublicKey, makeDIDFromAddress } from './decentralizedIDs'
|
||||
|
||||
export function makeAuthRequest(privateKey,
|
||||
appManifest,
|
||||
scopes=[],
|
||||
expiresAt=nextHour()) {
|
||||
let token = null
|
||||
|
||||
/* Create the payload */
|
||||
let payload = {
|
||||
jti: makeUUID4(),
|
||||
iat: new Date().getTime(),
|
||||
exp: nextHour(),
|
||||
iss: null,
|
||||
manifest: appManifest,
|
||||
scopes: scopes
|
||||
}
|
||||
|
||||
if (privateKey === null) {
|
||||
/* Create an unsecured token and return it */
|
||||
token = createUnsecuredToken(payload)
|
||||
} else {
|
||||
/* Convert the private key to a public key to an issuer */
|
||||
const publicKey = secp256k1.getPublicKey(privateKey)
|
||||
const issuer = makeDIDFromPublicKey(publicKey)
|
||||
payload.iss = issuer
|
||||
/* Sign and return the token */
|
||||
const tokenSigner = new TokenSigner('ES256k', privateKey)
|
||||
token = tokenSigner.sign(payload)
|
||||
}
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
export function verifyAuthRequest(token) {
|
||||
const decodedToken = decodeToken(token)
|
||||
const payload = decodedToken.payload
|
||||
|
||||
const publicKey = payload.iss
|
||||
const tokenVerifier = new TokenVerifier('ES256k', publicKey)
|
||||
const verified = tokenVerifier.verify(token)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function makeAuthResponse(privateKey,
|
||||
profile={},
|
||||
username=null,
|
||||
expiresAt=nextMonth()) {
|
||||
/* Convert the private key to a public key to an issuer */
|
||||
const publicKey = secp256k1.getPublicKey(privateKey)
|
||||
const issuer = makeDIDFromPublicKey(publicKey)
|
||||
/* Create the payload */
|
||||
const payload = {
|
||||
jti: makeUUID4(),
|
||||
iat: new Date().getTime(),
|
||||
exp: nextMonth(),
|
||||
iss: issuer,
|
||||
profile: profile,
|
||||
username: username
|
||||
}
|
||||
/* Sign and return the token */
|
||||
const tokenSigner = new TokenSigner('ES256k', privateKey)
|
||||
return tokenSigner.sign(payload)
|
||||
}
|
||||
|
||||
const BLOCKSTACK_HANDLER = "web+blockstack"
|
||||
|
||||
export class Authenticator {
|
||||
constructor(currentHostURL=window.location.origin) {
|
||||
this.storageLabel = 'blockstack'
|
||||
this.currentHost = currentHostURL
|
||||
this.signingKey = null
|
||||
|
||||
this.identityProviderURL = "http://localhost:8888/auth"
|
||||
}
|
||||
|
||||
isUserLoggedIn() {
|
||||
return window.localStorage.getItem(this.storageLabel) ? true : false
|
||||
}
|
||||
|
||||
requestLogin() {
|
||||
const authRequest = makeAuthRequest(this.signingKey, this.currentHost)
|
||||
|
||||
setTimeout(function() {
|
||||
window.location = this.identityProviderURL + "?authRequest=" + authRequest
|
||||
}, 200)
|
||||
|
||||
window.location = BLOCKSTACK_HANDLER + ":" + authRequest
|
||||
}
|
||||
|
||||
getAuthResponseToken() {
|
||||
const queryDict = queryString.parse(location.search)
|
||||
return queryDict.authResponse
|
||||
}
|
||||
|
||||
isLoginPending() {
|
||||
return this.getAuthResponseToken() ? true : false
|
||||
}
|
||||
|
||||
completeLogin(callbackFunction) {
|
||||
const authResponseToken = this.getAuthResponseToken()
|
||||
const decodedToken = decodeToken(authResponseToken)
|
||||
const username = decodedToken.payload.username
|
||||
const profile = decodedToken.payload.profile
|
||||
|
||||
const session = {
|
||||
username: username,
|
||||
profile: profile,
|
||||
authResponseToken: authResponseToken
|
||||
}
|
||||
window.localStorage.setItem(this.storageLabel, JSON.stringify(session))
|
||||
callbackFunction(session)
|
||||
}
|
||||
|
||||
loadSession(callbackFunction) {
|
||||
const session = JSON.parse(localStorage.getItem(this.storageLabel))
|
||||
callbackFunction(session)
|
||||
}
|
||||
|
||||
logout() {
|
||||
window.localStorage.removeItem(this.storageLabel)
|
||||
window.location = this.currentHost
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
request(requestURL, (error, response, body) => {
|
||||
if (!error && response.statusCode == 200) {
|
||||
const profile = JSON.parse(body)[username].profile
|
||||
|
||||
}
|
||||
})
|
||||
*/
|
||||
@@ -1,77 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
import queryString from 'query-string'
|
||||
import base64url from 'base64url'
|
||||
import request from 'request'
|
||||
import { decodeToken } from 'jsontokens'
|
||||
|
||||
export class AuthAgent {
|
||||
constructor(identityProviderURL, nameResolverURL,
|
||||
currentHostURL=window.location.origin) {
|
||||
this.storageLabel = 'blockstack'
|
||||
this.identityProviderURL = identityProviderURL
|
||||
this.nameResolverURL = nameResolverURL
|
||||
this.currentHost = currentHostURL
|
||||
}
|
||||
|
||||
static getUsernameFromToken(authResponseToken) {
|
||||
var decodedToken = decodeToken(authResponseToken)
|
||||
var decodedBlockstackID = decodedToken.payload.issuer.username
|
||||
return decodedBlockstackID.split('.')[0]
|
||||
}
|
||||
|
||||
isUserLoggedIn() {
|
||||
return window.localStorage.getItem(this.storageLabel) ? true : false
|
||||
}
|
||||
|
||||
requestLogin() {
|
||||
const payload = {
|
||||
appURI: this.currentHost,
|
||||
issuedAt: new Date().getTime()
|
||||
}
|
||||
const authRequest = base64url.encode(JSON.stringify(payload))
|
||||
|
||||
setTimeout(function() {
|
||||
window.location = this.identityProviderURL + "?authRequest=" + authRequest
|
||||
}, 200)
|
||||
|
||||
window.location = "web+blockstack:" + authRequest
|
||||
}
|
||||
|
||||
getAuthResponseToken() {
|
||||
const queryDict = queryString.parse(location.search)
|
||||
return queryDict.authResponse
|
||||
}
|
||||
|
||||
isLoginPending() {
|
||||
return this.getAuthResponseToken() ? true : false
|
||||
}
|
||||
|
||||
completeLogin(callbackFunction) {
|
||||
const authResponseToken = this.getAuthResponseToken()
|
||||
const username = AuthAgent.getUsernameFromToken(authResponseToken)
|
||||
const requestURL = this.nameResolverURL + username
|
||||
request(requestURL, (error, response, body) => {
|
||||
if (!error && response.statusCode == 200) {
|
||||
const profile = JSON.parse(body)[username].profile
|
||||
const session = {
|
||||
username: username,
|
||||
profile: profile,
|
||||
authResponseToken: authResponseToken
|
||||
}
|
||||
window.localStorage.setItem(this.storageLabel, JSON.stringify(session))
|
||||
callbackFunction(session)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
loadSession(callbackFunction) {
|
||||
const session = JSON.parse(localStorage.getItem(this.storageLabel))
|
||||
callbackFunction(session)
|
||||
}
|
||||
|
||||
logout() {
|
||||
window.localStorage.removeItem(this.storageLabel)
|
||||
window.location = this.currentHost
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
import KeyEncoder from 'key-encoder'
|
||||
import { TokenSigner, decodeToken, createUnsignedToken } from 'jsontokens'
|
||||
import { secp256k1 } from 'elliptic-curve'
|
||||
import base64url from 'base64url'
|
||||
import { generateUUID4 } from './utils'
|
||||
|
||||
export function createRequestPayload(issuer, provisions=null) {
|
||||
let unsignedRequest = {
|
||||
issuer: issuer,
|
||||
issuedAt: new Date().getTime()
|
||||
}
|
||||
if (provisions) {
|
||||
unsignedRequest.provisions = provisions
|
||||
}
|
||||
return unsignedRequest
|
||||
}
|
||||
|
||||
export function createUnsignedRequest(issuer) {
|
||||
const header = { typ: 'JWT' }
|
||||
const payload = createRequestPayload(issuer)
|
||||
const unsignedToken = createUnsignedToken(header, payload) + '.0'
|
||||
return unsignedToken
|
||||
}
|
||||
|
||||
export class AuthRequest {
|
||||
constructor(privateKey) {
|
||||
this.privateKey = privateKey
|
||||
this.keyEncoder = new KeyEncoder('secp256k1')
|
||||
this.publicKey = secp256k1.getPublicKey(privateKey)
|
||||
this.tokenSigner = new TokenSigner('ES256k', privateKey)
|
||||
this.issuer = { publicKey: this.publicKey }
|
||||
this.provisions = [
|
||||
{ action: 'sign', data: generateUUID4() },
|
||||
{ action: 'disclose', scope: 'username' }
|
||||
]
|
||||
}
|
||||
|
||||
setIssuer(issuer) {
|
||||
const newIssuer = this.issuer
|
||||
for (let attrname in issuer) {
|
||||
newIssuer[attrname] = issuer[attrname]
|
||||
}
|
||||
this.issuer = newIssuer
|
||||
}
|
||||
|
||||
setProvisions(provisions) {
|
||||
this.provisions = provisions
|
||||
}
|
||||
|
||||
payload() {
|
||||
return {
|
||||
issuer: this.issuer,
|
||||
issuedAt: new Date().getTime(),
|
||||
provisions: this.provisions
|
||||
}
|
||||
}
|
||||
|
||||
sign() {
|
||||
return this.tokenSigner.sign(this.payload())
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
import KeyEncoder from 'key-encoder'
|
||||
import { TokenSigner, decodeToken } from 'jsontokens'
|
||||
import { secp256k1 } from 'elliptic-curve'
|
||||
|
||||
export class AuthResponse {
|
||||
constructor(privateKey) {
|
||||
this.privateKey = privateKey
|
||||
this.keyEncoder = new KeyEncoder('secp256k1')
|
||||
this.publicKey = secp256k1.getPublicKey(privateKey)
|
||||
this.tokenSigner = new TokenSigner('ES256k', privateKey)
|
||||
this.issuer = { publicKey: this.publicKey }
|
||||
}
|
||||
|
||||
satisfyProvisions(provisions, username, privateData) {
|
||||
provisions.forEach((provision) => {
|
||||
switch(provision.action) {
|
||||
case 'disclose':
|
||||
if (provision.scope === 'username' && username) {
|
||||
provision.data = username
|
||||
}
|
||||
break;
|
||||
case 'sign':
|
||||
if (provision.data) {
|
||||
const signature = secp256k1.signMessage(
|
||||
provision.data, this.privateKey)
|
||||
provision.signature = signature
|
||||
}
|
||||
break;
|
||||
case 'write':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
this.provisions = provisions
|
||||
}
|
||||
|
||||
setIssuer(username, publicKeychain, chainPath) {
|
||||
if (username && publicKeychain && chainPath) {
|
||||
this.issuer = {
|
||||
publicKey: this.publicKey,
|
||||
username: username,
|
||||
publicKeychain: publicKeychain,
|
||||
chainPath: chainPath
|
||||
}
|
||||
} else if (username) {
|
||||
this.issuer = {
|
||||
publicKey: this.publicKey,
|
||||
username: username
|
||||
}
|
||||
} else if (username || publicKeychain || chainPath) {
|
||||
throw 'Either all or none of the following must be provided: username, publicKeychain, chainPath'
|
||||
} else {
|
||||
throw 'Cannot set issuer without the following: username, publicKeychain, chainPath'
|
||||
}
|
||||
}
|
||||
|
||||
payload() {
|
||||
return {
|
||||
issuer: this.issuer,
|
||||
issuedAt: new Date().getTime(),
|
||||
provisions: this.provisions
|
||||
}
|
||||
}
|
||||
|
||||
sign() {
|
||||
return this.tokenSigner.sign(this.payload())
|
||||
}
|
||||
}
|
||||
29
src/decentralizedIDs.js
Normal file
29
src/decentralizedIDs.js
Normal file
@@ -0,0 +1,29 @@
|
||||
'use strict'
|
||||
|
||||
export function makeDIDFromPublicKey(publicKey) {
|
||||
return `did:ecdsa-pub:${publicKey}`
|
||||
}
|
||||
|
||||
export function makeDIDFromAddress(address) {
|
||||
return `did:btc-addr:${address}`
|
||||
}
|
||||
|
||||
export function getPublicKeyOrAddressFromDID(did) {
|
||||
const didParts = did.split(':')
|
||||
|
||||
if (didParts.length !== 3) {
|
||||
throw new InvalidDIDError('Decentralized IDs must have 3 parts')
|
||||
}
|
||||
|
||||
if (didParts[0].toLowerCase() !== 'did') {
|
||||
throw new InvalidDIDError('Decentralized IDs must start with "did"')
|
||||
}
|
||||
|
||||
if (didParts[1].toLowerCase() === 'ecdsa-pub') {
|
||||
return didParts[2]
|
||||
} else if (didParts[1].toLowerCase() === 'btc-addr') {
|
||||
return didParts[2]
|
||||
} else {
|
||||
throw new InvalidDIDError('Decentralized ID format not supported')
|
||||
}
|
||||
}
|
||||
9
src/errors.js
Normal file
9
src/errors.js
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
export class InvalidDIDError extends Error {
|
||||
constructor(message) {
|
||||
super()
|
||||
this.name = 'InvalidDIDError'
|
||||
this.message = (message || '')
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,37 @@
|
||||
'use strict'
|
||||
|
||||
export {
|
||||
makeAuthRequest,
|
||||
makeAuthResponse,
|
||||
Authenticator
|
||||
} from './auth'
|
||||
|
||||
export {
|
||||
makeDIDFromPublicKey,
|
||||
makeDIDFromAddress,
|
||||
getPublicKeyOrAddressFromDID
|
||||
} from './decentralizedIDs'
|
||||
|
||||
export {
|
||||
privateKeyToPublicKey
|
||||
} from './keyUtils'
|
||||
|
||||
export {
|
||||
Profile
|
||||
} from './profile'
|
||||
|
||||
export {
|
||||
validateProofs
|
||||
} from './profileProofs'
|
||||
|
||||
export {
|
||||
Person,
|
||||
Organization,
|
||||
CreativeWork,
|
||||
getPersonFromLegacyFormat,
|
||||
resolveZoneFileToPerson
|
||||
} from './profileSchemas'
|
||||
|
||||
export {
|
||||
signProfileToken,
|
||||
wrapProfileToken,
|
||||
@@ -12,20 +40,16 @@ export {
|
||||
} from './profileTokens'
|
||||
|
||||
export {
|
||||
Person,
|
||||
Organization,
|
||||
CreativeWork,
|
||||
resolveZoneFileToPerson
|
||||
} from './profileSchemas'
|
||||
|
||||
export {
|
||||
validateProofs,
|
||||
services,
|
||||
containsValidProofStatement
|
||||
} from './profileProofs'
|
||||
} from './services'
|
||||
|
||||
export {
|
||||
nextYear,
|
||||
nextMonth,
|
||||
nextHour,
|
||||
getEntropy,
|
||||
privateKeyToPublicKey
|
||||
makeUUID4
|
||||
} from './utils'
|
||||
|
||||
export {
|
||||
@@ -33,12 +57,3 @@ export {
|
||||
getTokenFileUrlFromZoneFile
|
||||
} from './zoneFiles'
|
||||
|
||||
export {
|
||||
AuthAgent
|
||||
} from './authAgent'
|
||||
export {
|
||||
AuthRequest
|
||||
} from './authRequest'
|
||||
export {
|
||||
AuthResponse
|
||||
} from './authResponse'
|
||||
|
||||
12
src/keyUtils.js
Normal file
12
src/keyUtils.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
import BigInteger from 'bigi'
|
||||
import { ECPair as ECKeyPair } from 'bitcoinjs-lib'
|
||||
|
||||
export function privateKeyToPublicKey(privateKey) {
|
||||
const privateKeyBuffer = new Buffer(privateKey, 'hex')
|
||||
const privateKeyBigInteger = BigInteger.fromBuffer(privateKeyBuffer)
|
||||
const keyPair = new ECKeyPair(privateKeyBigInteger, null, {})
|
||||
const publicKey = keyPair.getPublicKeyBuffer().toString('hex')
|
||||
return publicKey
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import ecurve from 'ecurve'
|
||||
import { ECPair as ECKeyPair } from 'bitcoinjs-lib'
|
||||
import { decodeToken, TokenSigner, TokenVerifier } from 'jsontokens'
|
||||
import BigInteger from 'bigi'
|
||||
import { nextYear, privateKeyToPublicKey, generateUUID4 } from './utils'
|
||||
import { nextYear, makeUUID4 } from './utils'
|
||||
import { privateKeyToPublicKey } from './keyUtils'
|
||||
|
||||
const secp256k1 = ecurve.getCurveByName('secp256k1')
|
||||
|
||||
@@ -38,7 +39,7 @@ export function signProfileToken(profile, privateKey, subject=null, issuer=null,
|
||||
const tokenSigner = new TokenSigner(signingAlgorithm, privateKey)
|
||||
|
||||
const payload = {
|
||||
jti: generateUUID4(),
|
||||
jti: makeUUID4(),
|
||||
iat: issuedAt.toISOString(),
|
||||
exp: expiresAt.toISOString(),
|
||||
subject: subject,
|
||||
@@ -70,18 +71,25 @@ export function verifyProfileToken(token, publicKeyOrAddress) {
|
||||
const decodedToken = decodeToken(token)
|
||||
const payload = decodedToken.payload
|
||||
|
||||
if (!payload.hasOwnProperty('subject')) {
|
||||
// Inspect and verify the subject
|
||||
if (payload.hasOwnProperty('subject')) {
|
||||
if (!payload.subject.hasOwnProperty('publicKey')) {
|
||||
throw new Error("Token doesn't have a subject public key")
|
||||
}
|
||||
} else {
|
||||
throw new Error("Token doesn't have a subject")
|
||||
}
|
||||
if (!payload.subject.hasOwnProperty('publicKey')) {
|
||||
throw new Error("Token doesn't have a subject public key")
|
||||
}
|
||||
if (!payload.hasOwnProperty('issuer')) {
|
||||
|
||||
// Inspect and verify the issuer
|
||||
if (payload.hasOwnProperty('issuer')) {
|
||||
if (!payload.issuer.hasOwnProperty('publicKey')) {
|
||||
throw new Error("Token doesn't have an issuer public key")
|
||||
}
|
||||
} else {
|
||||
throw new Error("Token doesn't have an issuer")
|
||||
}
|
||||
if (!payload.issuer.hasOwnProperty('publicKey')) {
|
||||
throw new Error("Token doesn't have an issuer public key")
|
||||
}
|
||||
|
||||
// Inspect and verify the claim
|
||||
if (!payload.hasOwnProperty('claim')) {
|
||||
throw new Error("Token doesn't have a claim")
|
||||
}
|
||||
|
||||
@@ -1,234 +1,68 @@
|
||||
'use strict'
|
||||
|
||||
import test from 'tape'
|
||||
import { decodeToken } from 'jsontokens'
|
||||
|
||||
import {
|
||||
AuthMessage, AuthRequest, AuthResponse, createUnsignedRequest,
|
||||
verifyAuthMessage, decodeToken, AuthAgent
|
||||
makeAuthRequest, makeAuthResponse, makeDIDFromPublicKey
|
||||
} from '../index'
|
||||
import { OnenameClient } from 'onename-api'
|
||||
|
||||
let onenameResolver = new OnenameClient(
|
||||
process.env.ONENAME_APP_ID, process.env.ONENAME_APP_SECRET)
|
||||
|
||||
function testBlockstackResolver(blockstackIDs, resolve, reject) {
|
||||
if (blockstackIDs[0] === 'todo.app') {
|
||||
resolve({
|
||||
"todo.app": {
|
||||
"profile": {
|
||||
"auth": [
|
||||
{
|
||||
"publicKey": "027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (blockstackIDs[0] === 'ryan.id') {
|
||||
resolve({
|
||||
"ryan.id": {
|
||||
"profile": {
|
||||
"auth": [
|
||||
{
|
||||
"publicKeychain": "xpub661MyMwAqRbcFQVrQr4Q4kPjaP4JjWaf39fBVKjPdK6oGBayE46GAmKzo5UDPQdLSM9DufZiP8eauy56XNuHicBySvZp7J5wsyQVpi2axzZ"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
}
|
||||
|
||||
function testAuthRequest() {
|
||||
const privateKey = 'a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229'
|
||||
const publicKey = '027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69'
|
||||
|
||||
const sampleToken = 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3N1ZWRBdCI6IjE0NDA3MTM0MTQuMTkiLCJjaGFsbGVuZ2UiOiIxZDc4NTBkNy01YmNmLTQ3ZDAtYTgxYy1jMDA4NTc5NzY1NDQiLCJwZXJtaXNzaW9ucyI6WyJibG9ja2NoYWluaWQiXSwiaXNzdWVyIjp7InB1YmxpY0tleSI6IjAzODI3YjZhMzRjZWJlZTZkYjEwZDEzNzg3ODQ2ZGVlYWMxMDIzYWNiODNhN2I4NjZlMTkyZmEzNmI5MTkwNjNlNCIsImRvbWFpbiI6Im9uZW5hbWUuY29tIn19.96Q_O_4DX8uPy1enosEwS2sIcyVelWhxvfj2F8rOvHldhqt9YRYilauepb95DVnmpqpCXxJb7jurT8auNCbptw'
|
||||
const sampleTokenPayload = {"issuedAt": "1440713414.19", "challenge": "1d7850d7-5bcf-47d0-a81c-c00857976544", "permissions": ["blockchainid"], "issuer": {"publicKey": "03827b6a34cebee6db10d13787846deeac1023acb83a7b866e192fa36b919063e4", "domain": "onename.com"}}
|
||||
|
||||
test('basicRequest', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const issuingBlockchainID = 'todo.app'
|
||||
|
||||
let authRequest = new AuthRequest(privateKey)
|
||||
|
||||
authRequest.setIssuer({
|
||||
username: issuingBlockchainID
|
||||
})
|
||||
|
||||
const authRequestToken = authRequest.sign()
|
||||
const decodedAuthRequestToken = decodeToken(authRequestToken)
|
||||
|
||||
t.ok(authRequest instanceof AuthRequest, 'authRequest should be a valid AuthMessage object')
|
||||
t.equal(typeof authRequestToken, 'string', 'token should be a string')
|
||||
t.equal(decodedAuthRequestToken.payload.issuer.username, issuingBlockchainID, 'token blockchain id should match the reference')
|
||||
|
||||
verifyAuthMessage(authRequestToken, testBlockstackResolver, (verified) => {
|
||||
t.equal(verified, true, 'token should be verified')
|
||||
}, function(err) {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
test('unsignedRequest', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const unsignedRequestToken = createUnsignedRequest({ 'app': 'unknown' })
|
||||
t.ok(unsignedRequestToken)
|
||||
console.log(unsignedRequestToken)
|
||||
|
||||
const decodedRequestToken = decodeToken(unsignedRequestToken)
|
||||
t.ok(decodedRequestToken)
|
||||
console.log(decodedRequestToken)
|
||||
})
|
||||
|
||||
test('advancedRequest', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const issuingBlockchainID = 'todo.app'
|
||||
|
||||
let authRequest = new AuthRequest(privateKey)
|
||||
|
||||
authRequest.setIssuer({
|
||||
username: issuingBlockchainID,
|
||||
appName: 'Todo App',
|
||||
appDomain: 'todo.app'
|
||||
})
|
||||
authRequest.setProvisions([
|
||||
{ action: 'disclose', scope: 'username' },
|
||||
{ action: 'write', data: { uuid: '34e57db64ce7435ab0f759oca31386527c670bd1' } }
|
||||
])
|
||||
|
||||
const authRequestToken = authRequest.sign()
|
||||
const decodedAuthRequestToken = decodeToken(authRequestToken)
|
||||
|
||||
console.log(authRequestToken)
|
||||
console.log(JSON.stringify(decodedAuthRequestToken, null, 2))
|
||||
|
||||
t.ok(authRequest instanceof AuthRequest, 'authRequest should be a valid AuthMessage object')
|
||||
t.equal(typeof authRequestToken, 'string', 'token should be a string')
|
||||
t.equal(decodedAuthRequestToken.payload.issuer.username, issuingBlockchainID, 'token blockchain id should match the reference')
|
||||
|
||||
verifyAuthMessage(authRequestToken, testBlockstackResolver, function(verified) {
|
||||
t.equal(verified, true, 'token should be verified')
|
||||
}, function(err) {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
test('requestDecoding', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const decodedSampleToken = decodeToken(sampleToken)
|
||||
t.equal(JSON.stringify(decodedSampleToken.payload), JSON.stringify(sampleTokenPayload), 'token payload should match the reference payload')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function testAuthResponse() {
|
||||
let privateKeyHex = '278a5de700e29faae8e40e366ec5012b5ec63d36ec77e8a2417154cc1d25383f',
|
||||
publicKeyHex = '03fdd57adec3d438ea237fe46b33ee1e016eda6b585c3e27ea66686c2ea5358479',
|
||||
publicKeychain = 'xpub661MyMwAqRbcFQVrQr4Q4kPjaP4JjWaf39fBVKjPdK6oGBayE46GAmKzo5UDPQdLSM9DufZiP8eauy56XNuHicBySvZp7J5wsyQVpi2axzZ',
|
||||
privateKeychain = 'xprv9s21ZrQH143K2vRPJpXPhcT12MDpL3rofvjagwKn4yZpPPFpgWn1cy1Wwp3pk78wfHSLcdyZhmEBQsZ29ZwFyTQhhkVVa9QgdTC7hGMB1br',
|
||||
chainPath = 'bd62885ec3f0e3838043115f4ce25eedd22cc86711803fb0c19601eeef185e39',
|
||||
provisions = [
|
||||
{ action: 'sign', data: '7cd9ed5e-bb0e-49ea-a323-f28bde3a0549' },
|
||||
{ action: 'disclose', scope: 'username' }
|
||||
],
|
||||
challengeSignature = "3045022100963da1185591472e1bc867319ddd372593e17758724aaae49a2a30284b399bf0022037022ba279cf20421b96e9cac2a16ab6e415878cda488bc429fbc325f04315af",
|
||||
username = 'ryan.id',
|
||||
sampleToken = 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3N1ZWRBdCI6IjE0NDA3MTM0MTQuODUiLCJjaGFsbGVuZ2UiOiI3Y2Q5ZWQ1ZS1iYjBlLTQ5ZWEtYTMyMy1mMjhiZGUzYTA1NDkiLCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDNmZGQ1N2FkZWMzZDQzOGVhMjM3ZmU0NmIzM2VlMWUwMTZlZGE2YjU4NWMzZTI3ZWE2NjY4NmMyZWE1MzU4NDc5IiwiY2hhaW5QYXRoIjoiYmQ2Mjg4NWVjM2YwZTM4MzgwNDMxMTVmNGNlMjVlZWRkMjJjYzg2NzExODAzZmIwYzE5NjAxZWVlZjE4NWUzOSIsInB1YmxpY0tleWNoYWluIjoieHB1YjY2MU15TXdBcVJiY0ZRVnJRcjRRNGtQamFQNEpqV2FmMzlmQlZLalBkSzZvR0JheUU0NkdBbUt6bzVVRFBRZExTTTlEdWZaaVA4ZWF1eTU2WE51SGljQnlTdlpwN0o1d3N5UVZwaTJheHpaIiwiYmxvY2tjaGFpbmlkIjoicnlhbiJ9fQ.oO7ROPKq3T3X0azAXzHsf6ub6CYy5nUUFDoy8MS22B3TlYisqsBrRtzWIQcSYiFXLytrXwAdt6vjehj3OFioDQ',
|
||||
sampleTokenPayload = {"issuedAt": "1440713414.85", "challenge": "7cd9ed5e-bb0e-49ea-a323-f28bde3a0549", "issuer": {"publicKey": "03fdd57adec3d438ea237fe46b33ee1e016eda6b585c3e27ea66686c2ea5358479", "chainPath": "bd62885ec3f0e3838043115f4ce25eedd22cc86711803fb0c19601eeef185e39", "publicKeychain": "xpub661MyMwAqRbcFQVrQr4Q4kPjaP4JjWaf39fBVKjPdK6oGBayE46GAmKzo5UDPQdLSM9DufZiP8eauy56XNuHicBySvZp7J5wsyQVpi2axzZ", "blockchainid": "ryan"}},
|
||||
partiallyIdentifiedToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDNmZGQ1N2FkZWMzZDQzOGVhMjM3ZmU0NmIzM2VlMWUwMTZlZGE2YjU4NWMzZTI3ZWE2NjY4NmMyZWE1MzU4NDc5IiwiYmxvY2tjaGFpbmlkIjoicnlhbiIsInB1YmxpY0tleWNoYWluIjoieHB1YjY2MU15TXdBcVJiY0ZRVnJRcjRRNGtQamFQNEpqV2FmMzlmQlZLalBkSzZvR0JheUU0NkdBbUt6bzVVRFBRZExTTTlEdWZaaVA4ZWF1eTU2WE51SGljQnlTdlpwN0o1d3N5UVZwaTJheHpaIn0sImlzc3VlZEF0IjoxNDQxNzU1NjE5MDk2LCJjaGFsbGVuZ2UiOiI3Y2Q5ZWQ1ZS1iYjBlLTQ5ZWEtYTMyMy1mMjhiZGUzYTA1NDkiLCJpYXQiOjE0NDE3NTU2MTl9.1LxW_yg2z40Qd84x0kep0-7TWiDdTEoJbdYFUJ3qt297zxbwo8OOvYW43W6TMT5cloxur5wifq0WoOTdXw4C_Q',
|
||||
invalidUsername = 'ryanshea.id',
|
||||
privateData = {}
|
||||
|
||||
test('basicResponse', function(t) {
|
||||
t.plan(5)
|
||||
|
||||
let authResponse = new AuthResponse(privateKeyHex)
|
||||
|
||||
authResponse.satisfyProvisions(provisions, username, privateData)
|
||||
authResponse.setIssuer(username, publicKeychain, chainPath)
|
||||
|
||||
const authResponseToken = authResponse.sign()
|
||||
const decodedAuthResponseToken = decodeToken(authResponseToken)
|
||||
|
||||
console.log(JSON.stringify(decodedAuthResponseToken, null, 2))
|
||||
|
||||
t.ok(authResponse instanceof AuthResponse, 'authRequest should be a valid AuthResponse object')
|
||||
t.equal(typeof authResponseToken, 'string', 'token should be a string')
|
||||
t.equal(decodedAuthResponseToken.payload.issuer.publicKey, publicKeyHex, 'token public key hex should match the reference value')
|
||||
t.equal(decodedAuthResponseToken.payload.provisions[0].signature, challengeSignature, 'challenge signature should match the reference value')
|
||||
|
||||
verifyAuthMessage(authResponseToken, testBlockstackResolver, function(verified) {
|
||||
t.equal(verified, true, 'token should be verified')
|
||||
}, function(err) {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
test('partiallyIdentifiedResponse', function(t) {
|
||||
t.plan(1)
|
||||
|
||||
verifyAuthMessage(partiallyIdentifiedToken, testBlockstackResolver, function(verified) {
|
||||
t.equal(verified, false, 'token should be invalid')
|
||||
}, function(err) {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
test('responseWithIncorrectBlockchainID', function(t) {
|
||||
t.plan(2)
|
||||
|
||||
let authResponse = new AuthResponse(privateKeyHex, publicKeyHex)
|
||||
|
||||
authResponse.satisfyProvisions(provisions, invalidUsername, privateData)
|
||||
authResponse.setIssuer(invalidUsername, publicKeychain, chainPath)
|
||||
|
||||
const authResponseToken = authResponse.sign()
|
||||
t.ok(authResponseToken, 'token should have been created')
|
||||
|
||||
verifyAuthMessage(authResponseToken, testBlockstackResolver, function(verified) {
|
||||
t.equal(verified, false, 'token should be invalid')
|
||||
}, function(err) {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
test('anonymousResponse', function(t) {
|
||||
t.plan(4)
|
||||
|
||||
let authResponse = new AuthResponse(privateKeyHex)
|
||||
|
||||
authResponse.satisfyProvisions(provisions)
|
||||
|
||||
const authResponseToken = authResponse.sign()
|
||||
const decodedAuthResponseToken = decodeToken(authResponseToken)
|
||||
|
||||
t.ok(authResponse instanceof AuthResponse, 'authRequest should be a valid AuthResponse object')
|
||||
t.equal(typeof authResponseToken, 'string', 'token should be a string')
|
||||
t.equal(decodedAuthResponseToken.payload.issuer.publicKey, publicKeyHex, 'token public key hex should match the reference value')
|
||||
|
||||
verifyAuthMessage(authResponseToken, testBlockstackResolver, function(verified) {
|
||||
t.equal(verified, true, 'token should be verified')
|
||||
}, function(err) {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
test('responseDecoding', function(t) {
|
||||
t.plan(1)
|
||||
|
||||
const decodedSampleToken = decodeToken(sampleToken)
|
||||
t.equal(JSON.stringify(decodedSampleToken.payload), JSON.stringify(sampleTokenPayload), 'token payload should match the reference payload')
|
||||
})
|
||||
}
|
||||
|
||||
export function runAuthTests() {
|
||||
testAuthRequest()
|
||||
testAuthResponse()
|
||||
const privateKey = 'a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229'
|
||||
const publicKey = '027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69'
|
||||
const appManifest = {
|
||||
name: "Hello, Blockstack",
|
||||
short_name: "Hello, Blockstack",
|
||||
start_url: "https://helloblockstack.com",
|
||||
display: "standalone",
|
||||
background_color: "#fff",
|
||||
description: "A simple app demonstrating how to log in with Blockstack.",
|
||||
icons: [
|
||||
{
|
||||
src: "https://raw.githubusercontent.com/blockstack/blockstack-portal/master/app/images/app-hello-blockstack.png",
|
||||
sizes: "192x19x",
|
||||
type: "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
const profile = {
|
||||
"@type": "Person",
|
||||
name: "Ryan Shea",
|
||||
description: "Co-founder of Blockstack Inc."
|
||||
}
|
||||
|
||||
//const sampleToken = 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3N1ZWRBdCI6IjE0NDA3MTM0MTQuMTkiLCJjaGFsbGVuZ2UiOiIxZDc4NTBkNy01YmNmLTQ3ZDAtYTgxYy1jMDA4NTc5NzY1NDQiLCJwZXJtaXNzaW9ucyI6WyJibG9ja2NoYWluaWQiXSwiaXNzdWVyIjp7InB1YmxpY0tleSI6IjAzODI3YjZhMzRjZWJlZTZkYjEwZDEzNzg3ODQ2ZGVlYWMxMDIzYWNiODNhN2I4NjZlMTkyZmEzNmI5MTkwNjNlNCIsImRvbWFpbiI6Im9uZW5hbWUuY29tIn19.96Q_O_4DX8uPy1enosEwS2sIcyVelWhxvfj2F8rOvHldhqt9YRYilauepb95DVnmpqpCXxJb7jurT8auNCbptw'
|
||||
//const sampleTokenPayload = {"issuedAt": "1440713414.19", "challenge": "1d7850d7-5bcf-47d0-a81c-c00857976544", "permissions": ["blockchainid"], "issuer": {"publicKey": "03827b6a34cebee6db10d13787846deeac1023acb83a7b866e192fa36b919063e4", "domain": "onename.com"}}
|
||||
|
||||
test('makeAuthRequest', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const authRequest = makeAuthRequest(privateKey, appManifest)
|
||||
t.ok(authRequest, 'auth request should have been created')
|
||||
|
||||
const decodedToken = decodeToken(authRequest)
|
||||
t.ok(decodedToken, 'auth request token should have been decoded')
|
||||
|
||||
const referenceDID = makeDIDFromPublicKey(publicKey)
|
||||
t.equal(decodedToken.payload.iss, referenceDID, 'auth request issuer should include the public key')
|
||||
|
||||
t.equal(JSON.stringify(decodedToken.payload.scopes), "[]", 'auth request scopes should be an empty list')
|
||||
})
|
||||
|
||||
test('makeAuthResponse', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const authResponse = makeAuthResponse(privateKey, profile)
|
||||
t.ok(authResponse, 'auth response should have been created')
|
||||
|
||||
const decodedToken = decodeToken(authResponse)
|
||||
t.ok(decodedToken, 'auth response should have been decoded')
|
||||
|
||||
const referenceDID = makeDIDFromPublicKey(publicKey)
|
||||
t.equal(decodedToken.payload.iss, referenceDID, 'auth response issuer should include the public key')
|
||||
|
||||
t.equal(JSON.stringify(decodedToken.payload.profile), JSON.stringify(profile), 'auth response profile should equal the reference value')
|
||||
|
||||
t.equal(decodedToken.payload.username, null, 'auth response username should be null')
|
||||
})
|
||||
}
|
||||
@@ -2,15 +2,15 @@ import { runProofsUnitTests } from './proofsUnitTests'
|
||||
import { runUtilsUnitTests } from './utilsUnitTests'
|
||||
import { runServicesUnitTests } from './servicesUnitTests'
|
||||
import { runProfilesUnitTests } from './profilesUnitTests'
|
||||
//import { runAuthTests } from './authUnitTests'
|
||||
import { runAuthTests } from './authUnitTests'
|
||||
|
||||
/* Profiles Tests */
|
||||
// Auth Tests
|
||||
runAuthTests()
|
||||
|
||||
// Profiles Tests
|
||||
runProfilesUnitTests()
|
||||
|
||||
/* Proofs Tests */
|
||||
// Proofs Tests
|
||||
runUtilsUnitTests()
|
||||
runServicesUnitTests()
|
||||
runProofsUnitTests()
|
||||
|
||||
/* Auth Tests */
|
||||
//runAuthTests()
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
resolveZoneFileToPerson
|
||||
} from '../index'
|
||||
|
||||
import { sampleProfiles, sampleProofs, sampleVerifications, sampleTokenFiles } from './samples'
|
||||
import { sampleProfiles, sampleProofs, sampleVerifications, sampleTokenFiles } from './sampleData'
|
||||
|
||||
function testTokening(filename, profile) {
|
||||
const keyPair = new ECPair.makeRandom({ rng: getEntropy })
|
||||
|
||||
@@ -2,7 +2,7 @@ import test from 'blue-tape'
|
||||
import fs from 'fs'
|
||||
import FetchMock from 'fetch-mock'
|
||||
import { validateProofs } from '../index'
|
||||
import { sampleProfiles, sampleProofs, sampleVerifications } from './samples'
|
||||
import { sampleProfiles, sampleProofs, sampleVerifications } from './sampleData'
|
||||
|
||||
function testProofs(profile, username, totalProofs) {
|
||||
mockRequests()
|
||||
|
||||
@@ -4,7 +4,7 @@ import test from 'tape'
|
||||
import { services } from '../services/index'
|
||||
import { Service } from '../services/service'
|
||||
import { Facebook } from '../services/facebook'
|
||||
import { sampleProofs } from './samples'
|
||||
import { sampleProofs } from './sampleData'
|
||||
|
||||
export function runServicesUnitTests() {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test from 'tape'
|
||||
import fs from 'fs'
|
||||
import { containsValidProofStatement } from '../services'
|
||||
import { sampleVerifications } from './samples'
|
||||
import { sampleVerifications } from './sampleData'
|
||||
|
||||
export function runUtilsUnitTests() {
|
||||
test('containsValidProofStatement', (t) => {
|
||||
|
||||
@@ -5,7 +5,27 @@
|
||||
*/
|
||||
|
||||
export function nextYear() {
|
||||
return new Date(new Date().setFullYear(new Date().getFullYear() + 1))
|
||||
return new Date(
|
||||
new Date().setFullYear(
|
||||
new Date().getFullYear() + 1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export function nextMonth() {
|
||||
return new Date(
|
||||
new Date().setMonth(
|
||||
new Date().getMonth() + 1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export function nextHour() {
|
||||
return new Date(
|
||||
new Date().setHours(
|
||||
new Date().getHours() + 1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,33 +41,18 @@ export function getEntropy(numberOfBytes) {
|
||||
return randomBytes(numberOfBytes)
|
||||
}
|
||||
|
||||
/**
|
||||
* Elliptic Curve Keys
|
||||
*/
|
||||
|
||||
import BigInteger from 'bigi'
|
||||
import { ECPair as ECKeyPair } from 'bitcoinjs-lib'
|
||||
|
||||
export function privateKeyToPublicKey(privateKey) {
|
||||
const privateKeyBuffer = new Buffer(privateKey, 'hex')
|
||||
const privateKeyBigInteger = BigInteger.fromBuffer(privateKeyBuffer)
|
||||
const keyPair = new ECKeyPair(privateKeyBigInteger, null, {})
|
||||
const publicKey = keyPair.getPublicKeyBuffer().toString('hex')
|
||||
return publicKey
|
||||
}
|
||||
|
||||
/**
|
||||
* UUIDs
|
||||
*/
|
||||
|
||||
export function generateUUID4 () {
|
||||
let d = new Date().getTime();
|
||||
if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
|
||||
d += performance.now(); //use high-precision timer if available
|
||||
}
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
}
|
||||
export function makeUUID4() {
|
||||
let d = new Date().getTime()
|
||||
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
||||
d += performance.now(); //use high-precision timer if available
|
||||
}
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = (d + Math.random() * 16) % 16 | 0
|
||||
d = Math.floor(d / 16)
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user