-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add support for auth (via email) #19
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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,11 +1,13 @@ | ||
# Frames | ||
|
||
This repository contains code for the recovery and export components of Turnkey. These components can be embedded as iframes by users to support end-users in recovery and export. | ||
This repository contains code for the auth (which includes recovery) and export components of Turnkey. These components can be embedded as iframes by users to support end-users. | ||
|
||
## Email Recovery | ||
This self-contained HTML page is meant to be used as a standalone document to help first-party Turnkey root users. It's also going to be embedded as an iframe to help with sub-org root recovery. | ||
## Auth | ||
This self-contained HTML page is meant to be used for the following use cases: | ||
- As a standalone document to enable first-party Turnkey root users to perform recovery and auth | ||
- Embedded as an iframe for sub-org root recovery and auth | ||
|
||
This page is hosted at https://recovery.turnkey.com/ | ||
This page is hosted at https://auth.turnkey.com/, but we will retain https://recovery.turnkey.com/for compatibility. | ||
|
||
## Key and Wallet Export | ||
This self-contained HTML page is meant to be used as either a standalone document or to be embedded as an iframe. | ||
|
@@ -28,15 +30,15 @@ nvm use | |
|
||
Install dependencies: | ||
```sh | ||
cd recovery && npm install | ||
cd auth && npm install | ||
cd export && npm install | ||
``` | ||
|
||
# Unit Testing | ||
|
||
The export and recovery pages have tests. They run on CI automatically. If you want to run them locally: | ||
The auth and recovery pages each have tests. They run on CI automatically. If you want to run them locally: | ||
```sh | ||
cd recovery && npm test | ||
cd auth && npm test | ||
cd export && npm test | ||
``` | ||
|
||
|
@@ -51,19 +53,35 @@ Clone the `sdk` repo. | |
git clone [email protected]:tkhq/sdk.git | ||
``` | ||
|
||
Follow the README.md for the `key-export` example. Set the `NEXT_PUBLIC_EXPORT_IFRAME_URL="http://localhost:3000/export"` in the example's environment variables configuration. The `wallet-export` example embeds this page as an iframe. | ||
Follow the README.md for the `wallet-export` [example](https://github.com/tkhq/sdk/tree/main/examples/wallet-export). Set the `NEXT_PUBLIC_EXPORT_IFRAME_URL="http://localhost:3000/"` in the example's environment variables configuration. The `wallet-export` example embeds this page as an iframe. | ||
```sh | ||
cd sdk/examples/wallet-export | ||
``` | ||
|
||
# Running Local Auth | ||
Start the server. This command will run a simple static server on port 8080. | ||
```sh | ||
npm start | ||
``` | ||
|
||
Clone the `sdk` repo. | ||
```sh | ||
git clone [email protected]:tkhq/sdk.git | ||
``` | ||
|
||
Follow the README.md for the `email-auth` [example](https://github.com/tkhq/sdk/tree/main/examples/email-auth). Set the `NEXT_PUBLIC_AUTH_IFRAME_URL="http://localhost:3000/"` in the example's environment variables configuration. The `email-auth` example embeds this page as an iframe. | ||
```sh | ||
cd sdk/examples/email-auth | ||
``` | ||
|
||
# Building and running in Docker | ||
|
||
To build: | ||
``` | ||
docker build . -t frames | ||
``` | ||
|
||
To run (mapping 8080 and 8081 to 18080/18081 because they're often busy): | ||
To run (mapping `[8080, 8081]` to `[18080, 18081]` because they're often busy): | ||
``` | ||
docker run -p18080:8080 -p18081:8081 -t frames | ||
``` | ||
|
File renamed without changes.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 |
---|---|---|
|
@@ -2,8 +2,9 @@ | |
<html class="no-js" lang=""> | ||
|
||
<head> | ||
<link rel="icon" type="image/svg+xml" href="./favicon.svg" /> | ||
<meta charset="utf-8"> | ||
<title>Turnkey Recovery</title> | ||
<title>Turnkey Recovery and Auth</title> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
<style> | ||
body { | ||
|
@@ -75,8 +76,8 @@ | |
</head> | ||
|
||
<body> | ||
<h2>Init Recovery</h2> | ||
<p><em>This public key will be sent along with your email inside of a new <code>INIT_USER_EMAIL_RECOVERY</code> activity</em></p> | ||
<h2>Init Recovery or Auth</h2> | ||
<p><em>This public key will be sent along with your email inside of a new <code>INIT_USER_EMAIL_RECOVERY</code> or <code>EMAIL_AUTH</code> activity</em></p> | ||
<form> | ||
<label>Embedded key</label> | ||
<input type="text" name="embedded-key" id="embedded-key" disabled/> | ||
|
@@ -85,18 +86,18 @@ <h2>Init Recovery</h2> | |
<br> | ||
<br> | ||
<br> | ||
<h2>Inject Recovery Bundle</h2> | ||
<p><em>The recovery bundle comes from your email. We can also simulate this locally: see instructions <a href="https://github.com/tkhq/recovery#running-a-fake-recovery" target="_blank">here</a>. A recovery bundle is composed of a public key and an encrypted payload. The payload is encrypted to this document's embedded key (stored in local storage and displayed above). The scheme relies on <a target="_blank" href="https://datatracker.ietf.org/doc/rfc9180/">HPKE (RFC 9180)</a></em>.</p> | ||
<h2>Inject Credential Bundle</h2> | ||
<p><em>The credential bundle will come from your email. This bundle can then be used for email recovery or auth. We can simulate this locally: see instructions <a href="https://github.com/tkhq/frames#running-local-auth" target="_blank">here</a>. A credential bundle is composed of a public key and an encrypted payload. The payload is encrypted to this document's embedded key (stored in local storage and displayed above). The scheme relies on <a target="_blank" href="https://datatracker.ietf.org/doc/rfc9180/">HPKE (RFC 9180)</a></em>.</p> | ||
<form> | ||
<label>Bundle</label> | ||
<input type="text" name="recovery-bundle" id="recovery-bundle"/> | ||
<input type="text" name="credential-bundle" id="credential-bundle"/> | ||
<button id="inject">Inject Bundle</button> | ||
</form> | ||
<br> | ||
<br> | ||
<br> | ||
<h2>Stamp</h2> | ||
<p><em>Once you've injected the recovery bundle, the recovery credential is ready to sign. A new <code>RECOVER</code> activity for example. This iframe doesn't know anything about Turnkey activity however, it's a simple stamper!</em></p> | ||
<p><em>Once you've injected the credential bundle, the credential is ready to sign. A new <code>RECOVER</code> activity for example. This iframe doesn't know anything about Turnkey activity however, it's a simple stamper!</em></p> | ||
<form> | ||
<label>Payload</label> | ||
<input type="text" name="payload" id="payload"/> | ||
|
@@ -464,7 +465,7 @@ <h2>Message log</h2> | |
* when performing `crypto.subtle.importKey` operations. | ||
* @param {Uint8Array} privateKeyBytes | ||
*/ | ||
var importRecoveryCredential = async function(privateKeyBytes) { | ||
var importCredential = async function(privateKeyBytes) { | ||
var privateKeyHexString = uint8arrayToHexString(privateKeyBytes); | ||
var privateKey = BigInt('0x' + privateKeyHexString); | ||
var publicKeyPoint = P256Generator.multiply(privateKey); | ||
|
@@ -537,7 +538,7 @@ <h2>Message log</h2> | |
} | ||
|
||
/** | ||
* Accepts a public key array buffer, and returns a buffer with the uncomrpessed version of the public key | ||
* Accepts a public key array buffer, and returns a buffer with the uncompressed version of the public key | ||
* @param {Uint8Array} rawPublicKey | ||
* @return {Uint8Array} the uncompressed bytes | ||
*/ | ||
|
@@ -629,7 +630,7 @@ <h2>Message log</h2> | |
* ---- | ||
* IMPORTANT NOTE: below we implement basic field arithmetic for P256 | ||
* This is only used to compute public point from a secret key inside of | ||
* `importRecoveryCredential` above. If something goes wrong with the code below | ||
* `importCredential` above. If something goes wrong with the code below | ||
* the web crypto API will simply refuse to import the key. | ||
* None of the functions below are returned from the closure to minimize the risk of misuse. | ||
*********************************************************************************************/ | ||
|
@@ -821,7 +822,7 @@ <h2>Message log</h2> | |
getEmbeddedKey, | ||
setEmbeddedKey, | ||
onResetEmbeddedKey, | ||
importRecoveryCredential, | ||
importCredential, | ||
compressRawPublicKey, | ||
uncompressRawPublicKey, | ||
p256JWKPrivateToPublic, | ||
|
@@ -849,8 +850,8 @@ <h2>Message log</h2> | |
// TODO: this should be bundled at build time or replaced with code written by Turnkey entirely. | ||
import * as hpke from "https://esm.sh/@hpke/core"; | ||
|
||
// In memory spot for the recovery credential to live. We do NOT persist it to localStorage. | ||
var RECOVERY_CREDENTIAL_BYTES = null; | ||
// In memory spot for the credential to live. We do NOT persist it to localStorage. | ||
var CREDENTIAL_BYTES = null; | ||
|
||
document.addEventListener("DOMContentLoaded", async function () { | ||
await TKHQ.initEmbeddedKey(); | ||
|
@@ -863,7 +864,7 @@ <h2>Message log</h2> | |
// TODO: find a way to filter messages and ensure they're coming from the parent window? | ||
// We do not want to arbitrarily receive messages from all origins. | ||
window.addEventListener("message", async function(event) { | ||
if (event.data && event.data["type"] == "INJECT_RECOVERY_BUNDLE") { | ||
if (event.data && (event.data["type"] == "INJECT_CREDENTIAL_BUNDLE" || event.data["type"] == "INJECT_RECOVERY_BUNDLE")) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👏 |
||
TKHQ.logMessage(`⬇️ Received message ${event.data["type"]}: ${event.data["value"]}`); | ||
try { | ||
await onInjectBundle(event.data["value"]) | ||
|
@@ -890,15 +891,15 @@ <h2>Message log</h2> | |
}, false); | ||
|
||
/** | ||
* Event handlers to power the recovery flow in standalone mode | ||
* Event handlers to power the recovery and auth flows in standalone mode | ||
* Instead of receiving events from the parent page, forms trigger them. | ||
* This is useful for debugging as well. | ||
*/ | ||
document.getElementById("inject").addEventListener("click", async function(e) { | ||
e.preventDefault(); | ||
window.postMessage({ | ||
"type": "INJECT_RECOVERY_BUNDLE", | ||
"value": document.getElementById("recovery-bundle").value, | ||
"type": "INJECT_CREDENTIAL_BUNDLE", | ||
"value": document.getElementById("credential-bundle").value, | ||
}) | ||
}, false); | ||
document.getElementById("stamp").addEventListener("click", async function(e) { | ||
|
@@ -915,7 +916,7 @@ <h2>Message log</h2> | |
}, false); | ||
|
||
/** | ||
* Function triggered when INJECT_RECOVERY_BUNDLE event is received. | ||
* Function triggered when INJECT_CREDENTIAL_BUNDLE event is received. | ||
* The `bundle` param is the concatenation of a public key and an encrypted payload, and then base64 encoded | ||
* Example: A6ZPGAlxBRZhjKWky4RpXnHVceGzJjTuBrzKvMGnIgZ3r6JD4D1iiSg_m-y_u0BgJKI397Xjn0wgu17w9wuRooEp-F38m4ql57FgQ7sX9nQA | ||
* @param {string} bundle | ||
|
@@ -952,31 +953,31 @@ <h2>Message log</h2> | |
// Decompress the compressed key | ||
var encappedKeyBuf = TKHQ.uncompressRawPublicKey(compressedEncappedKeyBuf); | ||
|
||
var recoveryCredentialBytes = await HpkeDecrypt( | ||
var credentialBytes = await HpkeDecrypt( | ||
{ | ||
ciphertextBuf, | ||
encappedKeyBuf, | ||
receiverPrivJwk: embeddedKeyJwk, | ||
}); | ||
|
||
RECOVERY_CREDENTIAL_BYTES = new Uint8Array(recoveryCredentialBytes); | ||
CREDENTIAL_BYTES = new Uint8Array(credentialBytes); | ||
TKHQ.sendMessageUp("BUNDLE_INJECTED", true) | ||
} | ||
/** | ||
* Function triggered when STAMP_REQUEST event is received. | ||
* @param {string} payload to sign | ||
*/ | ||
var onStampRequest = async function(payload) { | ||
if (RECOVERY_CREDENTIAL_BYTES === null) { | ||
if (CREDENTIAL_BYTES === null) { | ||
throw new Error("cannot sign payload without credential. Credential bytes are null"); | ||
} | ||
var recoveryKey = await TKHQ.importRecoveryCredential(RECOVERY_CREDENTIAL_BYTES) | ||
var key = await TKHQ.importCredential(CREDENTIAL_BYTES) | ||
var signatureIeee1363 = await window.crypto.subtle.sign( | ||
{ | ||
name: "ECDSA", | ||
hash: {name: "SHA-256"}, | ||
}, | ||
recoveryKey, | ||
key, | ||
new TextEncoder().encode(payload) | ||
); | ||
|
||
|
@@ -988,7 +989,7 @@ <h2>Message log</h2> | |
// - Then imported without the private "d" component, and exported to get the public key | ||
// ^^ (that's what `p256JWKPrivateToPublic` does) | ||
// - Finally, compress the public key. | ||
var jwkKey = await crypto.subtle.exportKey("jwk", recoveryKey); | ||
var jwkKey = await crypto.subtle.exportKey("jwk", key); | ||
var publicKey = await TKHQ.p256JWKPrivateToPublic(jwkKey); | ||
var compressedPublicKey = TKHQ.compressRawPublicKey(publicKey); | ||
|
||
|
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
File renamed without changes.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,12 +1,12 @@ | ||
{ | ||
"name": "recovery-tests", | ||
"name": "auth-tests", | ||
"version": "1.0.0", | ||
"main": "index.test.js", | ||
"scripts": { | ||
"start": "serve", | ||
"test": "jest" | ||
}, | ||
"repository": "[email protected]:tkhq/recovery.git", | ||
"repository": "[email protected]:tkhq/frames.git", | ||
"author": "Turnkey <[email protected]>", | ||
"description": "This package is only here to help us test the main html file", | ||
"license": "MIT", | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this will fail on the cloudflare side, I'll need to edit the project to be named "auth".done!