mirror of
https://github.com/semaphore-protocol/semaphore.git
synced 2026-01-10 07:08:17 -05:00
feat(heyauthn): create heyauthn package
re #284
Former-commit-id: f67c1f07d7
This commit is contained in:
21
packages/heyauthn/LICENSE
Normal file
21
packages/heyauthn/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Vivek Bhupatiraju
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
111
packages/heyauthn/README.md
Normal file
111
packages/heyauthn/README.md
Normal file
@@ -0,0 +1,111 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
HeyAuthn
|
||||
</h1>
|
||||
<p align="center">A library to allow developers to create and manage Semaphore identities using WebAuthn.</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@semaphore-protocol/heyauthn">
|
||||
<img alt="NPM version" src="https://img.shields.io/npm/v/@semaphore-protocol/heyauthn?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/heyauthn">
|
||||
<img alt="Downloads" src="https://img.shields.io/npm/dm/@semaphore-protocol/heyauthn.svg?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint" />
|
||||
</a>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
| This library allows developers to create and manage Semaphore identities using [WebAuthn](https://webauthn.io/) as a cross-device biometric authentication in a way that is more convenient, smoother and secure than localStorage, Chrome extensions, or password manager based solutions. |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
## 🛠 Install
|
||||
|
||||
### npm or yarn
|
||||
|
||||
Install the `@semaphore-protocol/heyauthn` package with npm:
|
||||
|
||||
```bash
|
||||
npm i @semaphore-protocol/heyauthn
|
||||
```
|
||||
|
||||
or yarn:
|
||||
|
||||
```bash
|
||||
yarn add @semaphore-protocol/heyauthn
|
||||
```
|
||||
|
||||
## 📜 Usage
|
||||
|
||||
```typescript
|
||||
import { HeyAuthn } from "@semaphore-protocol/heyauthn"
|
||||
|
||||
// STEP 1: Configure WebAuthn options.
|
||||
|
||||
const options = {
|
||||
rpName: "my-app",
|
||||
rpID: window.location.hostname,
|
||||
userID: "my-id",
|
||||
userName: "my-name"
|
||||
}
|
||||
|
||||
// STEP 2: Register a new WebAuthn credential and get its Semaphore identity.
|
||||
|
||||
const { identity } = await HeyAuthn.fromRegister(options)
|
||||
|
||||
// Now you could also save the identity commitment in your DB (pseudocode).
|
||||
fetch("/api/register" /* Replace this with your endpoint */, {
|
||||
identity.commmitment
|
||||
// ...
|
||||
})
|
||||
|
||||
// STEP 3: Authenticate existing WebAuthn credential and signal.
|
||||
|
||||
const { identity } = await HeyAuthn.fromRegister(options)
|
||||
|
||||
// Get existing group and signal anonymously (pseudocode).
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { utils } from "ethers"
|
||||
|
||||
const group = new Group("42")
|
||||
|
||||
group.addMembers(memberList)
|
||||
|
||||
const signal = utils.formatBytes32String("Hey anon!")
|
||||
|
||||
generateProof(identity, group, group.id, "42", {
|
||||
zkeyFilePath: "./semaphore.zkey",
|
||||
wasmFilePath: "./semaphore.wasm"
|
||||
})
|
||||
```
|
||||
8
packages/heyauthn/build.tsconfig.json
Normal file
8
packages/heyauthn/build.tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"declarationDir": "dist/types"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
42
packages/heyauthn/package.json
Normal file
42
packages/heyauthn/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/heyauthn",
|
||||
"version": "3.2.3",
|
||||
"description": "A library to allow developers to create and manage Semaphore identities using WebAuthn",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.node.js",
|
||||
"exports": {
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.node.js"
|
||||
},
|
||||
"types": "dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist/",
|
||||
"src/",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"repository": "https://github.com/semaphore-protocol/semaphore",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/heyauthn",
|
||||
"bugs": {
|
||||
"url": "https://github.com/semaphore-protocol/semaphore.git/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
|
||||
"build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
|
||||
"prepublishOnly": "yarn build",
|
||||
"docs": "typedoc src/index.ts --out ../../docs/heyauthn"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rollup-plugin-cleanup": "^3.2.1",
|
||||
"rollup-plugin-typescript2": "^0.31.2",
|
||||
"typedoc": "^0.22.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/identity": "3.2.3",
|
||||
"@simplewebauthn/browser": "7.2.0",
|
||||
"@simplewebauthn/server": "7.2.0"
|
||||
}
|
||||
}
|
||||
29
packages/heyauthn/rollup.config.ts
Normal file
29
packages/heyauthn/rollup.config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import typescript from "rollup-plugin-typescript2"
|
||||
import * as fs from "fs"
|
||||
import cleanup from "rollup-plugin-cleanup"
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
|
||||
const banner = `/**
|
||||
* @module ${pkg.name}
|
||||
* @version ${pkg.version}
|
||||
* @file ${pkg.description}
|
||||
* @copyright Vivek Bhupatiraju 2023
|
||||
* @license ${pkg.license}
|
||||
* @see [Github]{@link ${pkg.homepage}}
|
||||
*/`
|
||||
|
||||
export default {
|
||||
input: "src/index.ts",
|
||||
output: [
|
||||
{ file: pkg.exports.require, format: "cjs", banner, exports: "auto" },
|
||||
{ file: pkg.exports.import, format: "es", banner }
|
||||
],
|
||||
external: Object.keys(pkg.dependencies),
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfig: "./build.tsconfig.json",
|
||||
useTsconfigDeclarationDir: true
|
||||
}),
|
||||
cleanup({ comments: "jsdoc" })
|
||||
]
|
||||
}
|
||||
75
packages/heyauthn/src/heyAuthn.test.ts
Normal file
75
packages/heyauthn/src/heyAuthn.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import {
|
||||
GenerateRegistrationOptionsOpts as RegistrationOptions,
|
||||
GenerateAuthenticationOptionsOpts as AuthenticationOptions
|
||||
} from "@simplewebauthn/server"
|
||||
|
||||
import HeyAuthn from "./heyAuthn"
|
||||
|
||||
jest.mock("@simplewebauthn/browser", () => ({
|
||||
startRegistration: async () => ({
|
||||
id: "my-new-credential",
|
||||
rawId: "my-new-credential",
|
||||
response: {
|
||||
clientDataJSON: "",
|
||||
attestationObject: ""
|
||||
},
|
||||
clientExtensionResults: {},
|
||||
type: "public-key"
|
||||
}),
|
||||
startAuthentication: async () => ({
|
||||
id: "my-existing-credential",
|
||||
rawId: "my-existing-credential",
|
||||
response: {
|
||||
clientDataJSON: "",
|
||||
attestationObject: ""
|
||||
},
|
||||
clientExtensionResults: {},
|
||||
type: "public-key"
|
||||
})
|
||||
}))
|
||||
|
||||
describe("HeyAuthn", () => {
|
||||
describe("# getIdentity", () => {
|
||||
it("Should get the identity of the HeyAuthn instance", async () => {
|
||||
const expectedIdentity = new Identity()
|
||||
const heyAuthn = new HeyAuthn(expectedIdentity)
|
||||
const identity = heyAuthn.getIdentity()
|
||||
|
||||
expect(expectedIdentity.toString()).toEqual(identity.toString())
|
||||
})
|
||||
})
|
||||
|
||||
describe("# fromRegister", () => {
|
||||
const options: RegistrationOptions = {
|
||||
rpName: "my-app",
|
||||
rpID: "hostname",
|
||||
userID: "my-id",
|
||||
userName: "my-name"
|
||||
}
|
||||
|
||||
it("Should create an identity identical to the one created registering credential", async () => {
|
||||
const { identity } = await HeyAuthn.fromRegister(options)
|
||||
const expectedIdentity = new Identity("my-new-credential")
|
||||
|
||||
expect(expectedIdentity.trapdoor).toEqual(identity.trapdoor)
|
||||
expect(expectedIdentity.nullifier).toEqual(identity.nullifier)
|
||||
expect(expectedIdentity.commitment).toEqual(identity.commitment)
|
||||
})
|
||||
})
|
||||
|
||||
describe("# fromAuthenticate", () => {
|
||||
const options: AuthenticationOptions = {
|
||||
rpID: "hostname"
|
||||
}
|
||||
|
||||
it("Should create an identity identical to the one created authenticating credential", async () => {
|
||||
const { identity } = await HeyAuthn.fromAuthenticate(options)
|
||||
const expectedIdentity = new Identity("my-existing-credential")
|
||||
|
||||
expect(expectedIdentity.trapdoor).toEqual(identity.trapdoor)
|
||||
expect(expectedIdentity.nullifier).toEqual(identity.nullifier)
|
||||
expect(expectedIdentity.commitment).toEqual(identity.commitment)
|
||||
})
|
||||
})
|
||||
})
|
||||
62
packages/heyauthn/src/heyAuthn.ts
Normal file
62
packages/heyauthn/src/heyAuthn.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
generateAuthenticationOptions,
|
||||
generateRegistrationOptions,
|
||||
GenerateRegistrationOptionsOpts as RegistrationOptions,
|
||||
GenerateAuthenticationOptionsOpts as AuthenticationOptions
|
||||
} from "@simplewebauthn/server"
|
||||
import { startAuthentication, startRegistration } from "@simplewebauthn/browser"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
|
||||
export default class HeyAuthn {
|
||||
private _identity: Identity
|
||||
|
||||
constructor(identity: Identity) {
|
||||
this._identity = identity
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new WebAuthn credential and returns its HeyAuthn instance.
|
||||
*
|
||||
* @param {GenerateRegistrationOptionsOpts} options - WebAuthn options for registering a new credential.
|
||||
* @returns A HeyAuthn instance with the newly registered credential.
|
||||
*/
|
||||
public static async fromRegister(options: RegistrationOptions) {
|
||||
const registrationOptions = generateRegistrationOptions(options)
|
||||
const { id } = await startRegistration(registrationOptions)
|
||||
|
||||
const identity = new Identity(id)
|
||||
|
||||
return new HeyAuthn(identity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates an existing WebAuthn credential and returns its HeyAuthn instance.
|
||||
*
|
||||
* @param {GenerateAuthenticationOptionsOpts} options - WebAuthn options for authenticating an existing credential.
|
||||
* @returns A HeyAuthn instance with the existing credential.
|
||||
*/
|
||||
public static async fromAuthenticate(options: AuthenticationOptions) {
|
||||
const authenticationOptions = generateAuthenticationOptions(options)
|
||||
const { id } = await startAuthentication(authenticationOptions)
|
||||
|
||||
const identity = new Identity(id)
|
||||
|
||||
return new HeyAuthn(identity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Semaphore identity instance.
|
||||
* @returns The Semaphore identity.
|
||||
*/
|
||||
public get identity(): Identity {
|
||||
return this._identity
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Semaphore identity instance.
|
||||
* @returns The Semaphore identity.
|
||||
*/
|
||||
public getIdentity(): Identity {
|
||||
return this._identity
|
||||
}
|
||||
}
|
||||
10
packages/heyauthn/src/index.ts
Normal file
10
packages/heyauthn/src/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { GenerateAuthenticationOptionsOpts, GenerateRegistrationOptionsOpts } from "@simplewebauthn/server"
|
||||
import HeyAuthn from "./heyAuthn"
|
||||
|
||||
export {
|
||||
HeyAuthn,
|
||||
GenerateRegistrationOptionsOpts as RegistrationOptions,
|
||||
GenerateAuthenticationOptionsOpts as AuthenticationOptions,
|
||||
Identity
|
||||
}
|
||||
4
packages/heyauthn/tsconfig.json
Normal file
4
packages/heyauthn/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src", "rollup.config.ts"]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
5d109b5b737dadc4046717430e8fd16c727749b9
|
||||
79ba77b3433cbc87caa22bad54df813dc8cdd90d
|
||||
Reference in New Issue
Block a user