examples(authz): initial authenticator using keyring crate

This commit is contained in:
nazeh
2024-08-31 00:12:04 +03:00
parent 8e70b2b0e6
commit 52fddd807b
6 changed files with 2092 additions and 9 deletions

View File

@@ -9,7 +9,11 @@
<script type="module" src="/src/pubky-auth-widget.js"></script>
</head>
<body>
<pubky-auth-widget relay="https://demo.httprelay.io/link/"></pubky-auth-widget>
<pubky-auth-widget
relay="https://demo.httprelay.io/link/"
caps="/pub/pubky.app/:rw"
>
</pubky-auth-widget>
<main>
<h1>Third Party app!</h1>

View File

@@ -19,6 +19,10 @@ export class PubkyAuthWidget extends LitElement {
* https://demo.httprelay.io/link
*/
relay: { type: String },
/**
* Capabilities requested or this application encoded as a string.
*/
caps: { type: String },
/**
* Widget's state (open or closed)
*/
@@ -50,10 +54,10 @@ export class PubkyAuthWidget extends LitElement {
const channel = Math.random().toString(32).slice(2);
callbackUrl.pathname = callbackUrl.pathname + "/" + channel
this.authUrl = `pubky:auth?cb=${callbackUrl.toString()}`;
this.authUrl = `pubkyauth:///?cb=${callbackUrl.toString()}&caps=${this.caps}`;
fetch(callbackUrl)
.catch(console.log)
.catch(error => console.error("PubkyAuthWidget: Failed to subscribe to http relay channel", error))
.then(this._onCallback)
}
@@ -63,17 +67,22 @@ export class PubkyAuthWidget extends LitElement {
id="widget"
class=${this.open ? "open" : ""}
>
<button class="header" @click=${this._switchOpen}>Pubky Auth</button>
<button class="header" @click=${this._switchOpen}>
<svg id="pubky-icon" version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1511 1511"><path fill-rule="evenodd" d="m636.3 1066.7 14.9-59.7c50.5-27.7 90.8-71.7 113.7-124.9-47.3 51.3-115.2 83.4-190.6 83.4-51.9 0-100.1-15.1-140.5-41.2L394 1066.7H193.9L356.4 447H567l-.1.1q3.7-.1 7.4-.1c77.7 0 147.3 34 194.8 88l22-88h202.1l-47 180.9L1130 447h249l-323 332.8 224 286.9H989L872.4 912l-40.3 154.7H636.3z" style="fill:#fff"/></svg>
<span class="text">
Pubky Auth
</span>
</button>
<div class="line"></div>
<div id="widget-content">
<p>Scan or copy Pubky auth URL</p>
<div class="card">
<canvas id="qr" ${ref(this._setQr)}></canvas>
</div>
<button class="card url" href=${this.authUrl}>
<a class="card url" href=${this.authUrl}>
<p>${this.authUrl}</p>
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="10" height="12" rx="2" fill="white"></rect><rect x="3" y="3" width="10" height="12" rx="2" fill="white" stroke="#3B3B3B"></rect></svg>
</button>
</a>
</div>
</div>
`
@@ -110,7 +119,7 @@ export class PubkyAuthWidget extends LitElement {
console.log({ uint8Array })
} catch (error) {
console.error('Failed to fetch and convert the API response:', error);
console.error('PubkyAuthWidget: Failed to read incoming AuthToken', error);
}
}
@@ -124,6 +133,11 @@ export class PubkyAuthWidget extends LitElement {
--full-width: 22rem;
--full-height: 31rem;
--header-height: 3rem;
--closed-width: 3rem;
}
a {
text-decoration: none;
}
button {
@@ -157,7 +171,7 @@ export class PubkyAuthWidget extends LitElement {
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
width: 7rem;
width: var(--closed-width);
height: var(--header-height);
will-change: height,width;
@@ -172,13 +186,39 @@ export class PubkyAuthWidget extends LitElement {
}
.header {
width: 100%;
height: var(--header-height);
display: flex;
justify-content: center;
align-items: center;
}
#widget
.header .text {
display: none;
font-weight: bold;
}
#widget.open
.header .text {
display: block
}
#widget.open
.header {
width: var(--full-width);
justify-content: center;
}
#pubky-icon {
height: 100%;
width: 100%;
}
#widget.open
#pubky-icon {
width: var(--header-height);
height: 74%;
}
#widget-content{
width: var(--full-width);
padding: 0 1rem

29
examples/authz/README.md Normal file
View File

@@ -0,0 +1,29 @@
# Pubky Auth Example
This example shows 3rd party authorization in Pubky.
It consists of 2 parts:
1. [3rd party app](./3rd-party-app): A web component showing the how to implement a Pubky Auth widget.
2. [Authenticator CLI](./authenticator): A CLI showing the authenticator (key chain) asking user for consent and generating the AuthToken.
## Usage
First you need to be running a local testnet Homeserver, in the root of this repo run
```bash
cargo run --bin pubky_homeserver -- --testnet
```
Run the frontend of the 3rd party app
```bash
cd ./3rd-party-app
npm start
```
Copy the Pubky Auth URL from the frontend.
Finally run the CLI to paste the Pubky Auth in.
You should see the frontend reacting by showing the success of authorization and session details.

1906
examples/authz/authenticator/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
[package]
name = "authenticator"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.86"
keyring = { version = "3.2.0", features = ["linux-native", "apple-native", "windows-native"] }
pubky = { version = "0.1.0", path = "../../../pubky" }
pubky-common = { version = "0.1.0", path = "../../../pubky-common" }
rpassword = "7.3.1"
[workspace]

View File

@@ -0,0 +1,91 @@
use std::io;
use keyring::Entry;
use pubky_common::crypto::Keypair;
const SERVICE_NAME: &str = "pubky";
fn main() -> anyhow::Result<()> {
println!("Enter the alias for your keypair in your operating system secure storage:");
let mut name = String::new();
io::stdin().read_line(&mut name)?;
name = name.trim_end().to_lowercase();
let entry = Entry::new(SERVICE_NAME, &name)?;
let keypair = match entry.get_secret() {
Ok(secret_key) => {
let secret_key: &[u8; 32] = secret_key
.as_slice()
.try_into()
.expect("Invalid secret_key");
let keypair = Keypair::from_secret_key(&secret_key);
println!("\nFound secret_key for Pubky {}", keypair.public_key());
keypair
}
Err(error) => {
let keypair = Keypair::random();
println!(
"\n{}\nGenerated new Pubky {}",
error.to_string(),
keypair.public_key()
);
loop {
println!("\nStore the new Pubky keypair in operating system secure storage?[y/n]");
let mut choice = String::new();
io::stdin().read_line(&mut choice)?;
match choice.as_str() {
"y\n" => {
entry.set_secret(&keypair.secret_key())?;
break;
}
"n\n" => {
return Ok(());
}
_ => {}
};
}
keypair
}
};
dbg!(keypair);
// entry.set_password("topS3cr3tP4$$w0rd")?;
// let password = entry.get_password()?;
// println!("My password is '{}'", password);
// entry.delete_credential()?;
// Ok(())
Ok(())
}
// fn read_file_content<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
// let mut file = File::open(path)?;
// let mut content = Vec::new();
// file.read_to_end(&mut content)?;
// Ok(content)
// }
// fn decrypt_content(content: &[u8], password: &str) -> Result<String, &'static str> {
// // Create a key and IV (Initialization Vector) from the password
// let key = GenericArray::from_slice(password.as_bytes()); // Example key derivation
// let iv = GenericArray::from_slice(b"unique_iv_16bytes"); // Replace with proper IV derivation
//
// let cipher = Aes256::new(key, iv);
//
// // Decrypt the content using the cipher
// let mut buffer = content.to_vec();
// cipher
// .decrypt_padded_mut::<Pkcs7>(&mut buffer)
// .map_err(|_| "Decryption failed")?;
//
// // Convert decrypted bytes to string
// String::from_utf8(buffer).map_err(|_| "Failed to convert decrypted content to string")
// }