@passwordless-id/webauthn
Advanced tools
Comparing version 0.0.4 to 0.0.5
{ | ||
"name": "@passwordless-id/webauthn", | ||
"version": "0.0.4", | ||
"description": "A small wrapper around the webauthn protocol to make one's life easier.", | ||
"main": "src/passwordless.ts", | ||
"version": "0.0.5", | ||
"description": "A small wrapper around the webauthn protocol to make one's life easier.", | ||
"main": "src/webauthn.ts", | ||
"scripts": { | ||
"build": "esbuild src/passwordless.ts --platform=neutral --bundle --sourcemap --minify --target=es2022 --outfile=dist/passwordless.min.js" | ||
"build": "esbuild src/webauthn.ts --platform=neutral --bundle --sourcemap --minify --target=es2022 --outfile=dist/webauthn.min.js", | ||
"dev": "http-server" | ||
}, | ||
@@ -17,4 +16,5 @@ "repository": { | ||
"authentication", | ||
"passwordless", | ||
"webauthn" | ||
"webauthn", | ||
"passkeys", | ||
"passwordless" | ||
], | ||
@@ -29,4 +29,5 @@ "author": "Arnaud Dagnelies", | ||
"esbuild": "^0.15.8", | ||
"http-server": "^14.1.1", | ||
"typescript": "^4.8.3" | ||
} | ||
} |
@@ -6,4 +6,11 @@ Passwordless.ID / webauthn | ||
Check out the demo at https://passwordless.id/playground.html | ||
<img src="demos/img/banner-biometric-auth.svg" /> | ||
Check out the demos: | ||
- [Basic Demo](demos/basic.html) | ||
- [Minimal Example (CDN)](demos/example-cdn.html) | ||
- [Minimal Example (repository)](demos/example-raw.html) | ||
- [Testing Playground](demos/playground.html) | ||
Installation / Usage | ||
@@ -19,3 +26,3 @@ -------------------- | ||
```js | ||
import * as passwordless from '@passwordless-id/webauthn' | ||
import * as webauthn from '@passwordless-id/webauthn' | ||
``` | ||
@@ -27,3 +34,3 @@ | ||
<script type="module"> | ||
import * as passwordless from 'https://unpkg.com/@passwordless-id/webauthn@latest/dist/passwordless.min.js' | ||
import * as webauthn from 'https://unpkg.com/@passwordless-id/webauthn@latest/dist/passwordless.min.js' | ||
</script> | ||
@@ -33,9 +40,10 @@ ``` | ||
Registration | ||
------------ | ||
Example: | ||
Example call: | ||
```js | ||
passwordless.register("Arnaud", "random-server-challenge", { | ||
webauthn.register("Arnaud", "random-server-challenge", { | ||
"authenticatorType": "auto", | ||
@@ -48,2 +56,23 @@ "userVerification": "required", | ||
Example response: | ||
```json | ||
{ | ||
"username": "Arnaud", | ||
"challenge": "random-server-challenge", | ||
"credential": { | ||
"id": "RufE-HKYK2...", | ||
"publicKey": "MIIBIjANBg...", | ||
"algorithm": "RS256" | ||
}, | ||
"authenticator": { | ||
"isLocal": true, | ||
"aaguid": "08987058-cadc-4b81-b6e1-30de50dcbe96", | ||
"name": "Windows Hello Hardware Authenticator", | ||
"attestation": "o2NmbXRjdH...", | ||
"clientData": "eyJ0eXBlIj..." | ||
} | ||
} | ||
``` | ||
Parameters: | ||
@@ -55,9 +84,11 @@ | ||
Authentication | ||
-------------- | ||
Example: | ||
Example call: | ||
```js | ||
passwordless.login(["credentialIdBase64encoded"], "random-server-challenge", { | ||
webauthn.login(["credentialIdBase64encoded"], "random-server-challenge", { | ||
"authenticatorType": "auto", | ||
@@ -69,6 +100,36 @@ "userVerification": "required", | ||
Example response: | ||
```json | ||
{ | ||
"credentialId": "c8VC7q_TY0NvKIhcS_rafPLEvdw8GwePABH81QRNt4Y", | ||
"userHash": "awopRTWFXAQrBPRAbEPFg3WUd4forBvMho7Ie4sxabE=", | ||
"clientJson": { | ||
"type": "webauthn.get", | ||
"challenge": "ZTEyNGE0ZTAtNjg4NS00YzhlLWFhODktNTZkMjJhZDUxNGYz", | ||
"origin": "http://localhost:8080", | ||
"crossOrigin": false | ||
}, | ||
"clientData": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiWlRFeU5HRTBaVEF0TmpnNE5TMDBZemhsTFdGaE9Ea3ROVFprTWpKaFpEVXhOR1l6Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ==", | ||
"signature": "J8PbSE9ZgC2JME9r2SYGY7WMDUKVDFby8WPXxSpXYfLpmfjimGed8oEqvUtD4UhvshjKV9FOlS0Dc8N8ILvIDL77gmUPeY6oZbTqrw9+2NgeXONM9hNDnxIjOUxekC8a3LY1HFq7aWy4v9I/gu1vD5NGSouvlzxJXPHcC30Bxu70EMcTwtz3EnRmQ3UGuZXjYO2xd2l2BsUgyI87c/wpquaCThrOPEf1PlzS4Larv5lE/Lfh4gQ2O/1TvmBcjtT/oSFkkb6hAgJp51/QbrUbnzdAtTtbGnSTOukM/HZ6yFY5i4oy3l+cJbwAGxEqFUU7yAdPrmTJdLeLmzimve58RA==", | ||
"authenticatorJson": { | ||
"rpIdHash": "SZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2M=", | ||
"flags": { | ||
"userPresent": true, | ||
"userVerified": true, | ||
"backupEligibility": false, | ||
"backupState": false, | ||
"attestedData": false, | ||
"extensionsIncluded": false | ||
}, | ||
"counter": 1 | ||
}, | ||
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2MFAAAAAQ==" | ||
} | ||
``` | ||
Parameters: | ||
- `credentialIds`: The list of credential IDs that can be used for signing. | ||
- `challenge`: A server-side randomly generated string, the base64 encoded version will be signed. | ||
- `challenge`: A server-side randomly generated string, the base64url encoded version will be signed. | ||
- `options`: See below | ||
@@ -91,6 +152,4 @@ | ||
Unlike the [webauthn protocol](), there are some significant differences. | ||
Unlike the [webauthn protocol](), some defaults are different: | ||
First, some defaults are different: | ||
- The `timeout` is one minute by default. | ||
@@ -100,8 +159,1 @@ - If the device can act as authenticator itself, it is preffered instead of asking which authenticator type to use. | ||
Then, the response is also different, as plain JSON with values encoded in **Base64**. It does not use *Base64url* that the webauthn protocol favors. This was chosen for two reasons: | ||
- These values should never appear in URLs anyway for both privacy and security reasons, so let's not encourage/suggest it. | ||
- Many server framework have middleware to decode base64 values out-of-the box as byte arrays while base64url frequently requires some form of pre-processing. | ||
Please also note that swapping the encoding from *base64* to *base64url* is the matter of replacing two characters in the string. |
import * as authenticatorMetadata from './authenticatorMetadata.json' | ||
import * as utils from './utils' | ||
console.debug(authenticatorMetadata) | ||
//console.debug(authenticatorMetadata) | ||
export function parseAuthData(authData :ArrayBuffer) { | ||
@@ -14,3 +13,3 @@ console.debug(authData) | ||
let parsed :any = { | ||
rpIdHash: utils.toBase64(authData.slice(0,32)), | ||
rpIdHash: utils.toBase64url(authData.slice(0,32)), | ||
flags: { | ||
@@ -35,4 +34,4 @@ userPresent: !!(flags & 1), | ||
aaguid: extractAaguid(authData), | ||
credentialId: utils.toBase64(authData.slice(55, 55+credentialLength)), | ||
publicKey: utils.toBase64(authData.slice(55+credentialLength, authData.byteLength)) // probably breaks if extensions are invoked | ||
credentialId: utils.toBase64url(authData.slice(55, 55+credentialLength)), | ||
publicKey: utils.toBase64url(authData.slice(55+credentialLength, authData.byteLength)) // probably breaks if extensions are invoked | ||
} | ||
@@ -39,0 +38,0 @@ } |
@@ -14,12 +14,14 @@ /******************************** | ||
export function toBase64(buffer :ArrayBuffer) :string { | ||
return btoa(parseBuffer(buffer)) | ||
export function isBase64url(txt :string) :boolean { | ||
return txt.match(/^[a-zA-Z0-9\-_]+=*$/) !== null | ||
} | ||
export function parseBase64(txt :string) :ArrayBuffer { | ||
return toBuffer(atob(txt)) | ||
export function toBase64url(buffer :ArrayBuffer) :string { | ||
const txt = btoa(parseBuffer(buffer)) // base64 | ||
return txt.replaceAll('+', '-').replaceAll('/', '_') | ||
} | ||
export function parseBase64url(txt :string) :ArrayBuffer { | ||
return parseBase64(txt.replace(/-/g, '+').replace(/_/g, '/')) | ||
txt = txt.replaceAll('-', '+').replaceAll('_', '/') // base64url -> base64 | ||
return toBuffer(atob(txt)) | ||
} | ||
@@ -26,0 +28,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
380045
24
674
151
3
1