mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-03 01:41:44 +00:00
219 lines
6.8 KiB
Templ
219 lines
6.8 KiB
Templ
package register
|
|
|
|
type RegisterPasskeyData struct {
|
|
Address string
|
|
Handle string
|
|
Name string
|
|
Challenge string
|
|
CreationBlock string
|
|
}
|
|
|
|
templ formRegisterPasskey(action, method string, data RegisterPasskeyData) {
|
|
<form action={ templ.SafeURL(action) } method={ method } id="passkey-form">
|
|
<input type="hidden" name="credential" id="credential-data" required/>
|
|
<sl-card class="card-form gap-4 max-w-lg">
|
|
<div slot="header">
|
|
<div class="w-full py-2">
|
|
@sonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock)
|
|
</div>
|
|
</div>
|
|
<sl-select
|
|
label="Accounts"
|
|
value="SNR BTC ETH"
|
|
help-text="Select Blockchains to connect with your Vault"
|
|
multiple
|
|
class="custom-tag py-2"
|
|
>
|
|
@cryptoWalletOption("SNR", "Sonr", true)
|
|
@cryptoWalletOption("BTC", "Bitcoin", true)
|
|
@cryptoWalletOption("ETH", "Ethereum", true)
|
|
@cryptoWalletOption("SOL", "Solana", false)
|
|
@cryptoWalletOption("LTC", "Litecoin", false)
|
|
@cryptoWalletOption("DOGE", "Dogecoin", false)
|
|
@cryptoWalletOption("XRP", "Ripple", false)
|
|
@cryptoWalletOption("OSMO", "Osmosis", false)
|
|
@cryptoWalletOption("ATOM", "Cosmos", false)
|
|
@cryptoWalletOption("STARZ", "Stargaze", false)
|
|
@cryptoWalletOption("AKT", "Akash", false)
|
|
@cryptoWalletOption("EVMOS", "Evmos", false)
|
|
@cryptoWalletOption("FIL", "Filecoin", false)
|
|
@cryptoWalletOption("AXL", "Axelar", false)
|
|
</sl-select>
|
|
<script type="module">
|
|
const select = document.querySelector('.custom-tag');
|
|
|
|
select.getTag = (option, index) => {
|
|
// Use the same icon used in the <sl-option>
|
|
const name = option.querySelector('sl-icon[slot="prefix"]').name;
|
|
|
|
// You can return a string, a Lit Template, or an HTMLElement here
|
|
return `
|
|
<sl-tag removable>
|
|
<sl-icon name="${name}" library="crypto" style="padding-inline-end: .5rem;"></sl-icon>
|
|
${option.getTextLabel()}
|
|
</sl-tag>
|
|
`;
|
|
};
|
|
</script>
|
|
<div slot="footer" class="space-y-2">
|
|
@passkeyDropzone(data.Address, data.Handle, data.Challenge)
|
|
<sl-button href="/" style="width: 100%;" outline>
|
|
<sl-icon slot="prefix" name="x-lg"></sl-icon>
|
|
Cancel
|
|
</sl-button>
|
|
</div>
|
|
<style>
|
|
.card-form [slot='footer'] {
|
|
justify-content: space-evenly;
|
|
align-items: center;
|
|
}
|
|
</style>
|
|
</sl-card>
|
|
</form>
|
|
}
|
|
|
|
templ passkeyDropzone(addr string, userHandle string, challenge string) {
|
|
<sl-button style="width: 100%;" onclick={ createPasskey(addr, userHandle, challenge) }>
|
|
<sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-neutral-500"></sl-icon>
|
|
Register Passkey
|
|
</sl-button>
|
|
}
|
|
|
|
script createPasskey(userId string, userHandle string, challenge string) {
|
|
const publicKey = {
|
|
challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)),
|
|
rp: {
|
|
name: "Sonr.ID",
|
|
},
|
|
user: {
|
|
// Assuming that userId is ASCII-only
|
|
id: Uint8Array.from(userId, (c) => c.charCodeAt(0)),
|
|
name: userId,
|
|
displayName: userHandle,
|
|
},
|
|
pubKeyCredParams: [
|
|
{
|
|
type: "public-key",
|
|
alg: -7, // "ES256"
|
|
},
|
|
{
|
|
type: "public-key",
|
|
alg: -257, // "RS256"
|
|
},
|
|
],
|
|
authenticatorSelection: {
|
|
userVerification: "required",
|
|
residentKey: "required",
|
|
authenticatorAttachment: "platform",
|
|
},
|
|
timeout: 60000, // 1 minute
|
|
extensions: {
|
|
payment: {
|
|
isPayment: true,
|
|
},
|
|
},
|
|
};
|
|
// Helper function to convert ArrayBuffer to Base64URL string
|
|
function arrayBufferToBase64URL(buffer) {
|
|
const bytes = new Uint8Array(buffer);
|
|
let str = '';
|
|
bytes.forEach(byte => { str += String.fromCharCode(byte) });
|
|
return btoa(str)
|
|
.replace(/\+/g, '-')
|
|
.replace(/\//g, '_')
|
|
.replace(/=/g, '');
|
|
}
|
|
|
|
navigator.credentials
|
|
.create({ publicKey })
|
|
.then((newCredentialInfo) => {
|
|
if (!(newCredentialInfo instanceof PublicKeyCredential)) {
|
|
throw new Error('Received credential is not a PublicKeyCredential');
|
|
}
|
|
|
|
const response = newCredentialInfo.response;
|
|
if (!(response instanceof AuthenticatorAttestationResponse)) {
|
|
throw new Error('Response is not an AuthenticatorAttestationResponse');
|
|
}
|
|
|
|
// Convert the credential data to a cross-platform compatible format
|
|
const credentialJSON = {
|
|
id: newCredentialInfo.id,
|
|
rawId: arrayBufferToBase64URL(newCredentialInfo.rawId),
|
|
type: newCredentialInfo.type,
|
|
authenticatorAttachment: newCredentialInfo.authenticatorAttachment || null,
|
|
transports: Array.isArray(response.getTransports) ? response.getTransports() : [],
|
|
clientExtensionResults: newCredentialInfo.getClientExtensionResults(),
|
|
response: {
|
|
attestationObject: arrayBufferToBase64URL(response.attestationObject),
|
|
clientDataJSON: arrayBufferToBase64URL(response.clientDataJSON)
|
|
}
|
|
};
|
|
|
|
// Set the form value with the stringified credential data
|
|
const credentialInput = document.getElementById('credential-data');
|
|
credentialInput.value = JSON.stringify(credentialJSON);
|
|
|
|
// Submit the form
|
|
const form = document.getElementById('passkey-form');
|
|
form.submit();
|
|
})
|
|
.catch((err) => {
|
|
console.error('Passkey creation failed:', err);
|
|
alert(`Failed to create passkey: ${err.message || 'Unknown error'}`);
|
|
});
|
|
}
|
|
|
|
templ sonrProfile(addr string, name string, handle string, creationBlock string) {
|
|
<div class="profile-card min-w-[320px]">
|
|
<div class="text-white max-w-xs my-auto mx-auto bg-gradient-to-r from-cyan-700 to-cyan-300 p-4 py-5 px-5 rounded-xl">
|
|
<div class="flex justify-between">
|
|
<div>
|
|
<h2>sonr-testnet-1</h2>
|
|
<p class="text-2xl font-bold">{ handle }</p>
|
|
</div>
|
|
<div class="flex items-center opacity-60">
|
|
<sl-icon style="font-size: 52px;" library="sonr" name="sonr-fill"></sl-icon>
|
|
</div>
|
|
</div>
|
|
<div class="mt-5 flex justify-between items-center w-52">
|
|
<span class="text-lg font-mono">{ shortenAddress(addr) }</span>
|
|
</div>
|
|
<div class="flex justify-between mt-5 w-48 ">
|
|
<div>
|
|
<h3 class="text-xs">Block Created </h3>
|
|
<p class="font-bold"><span>#</span>{ creationBlock }</p>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-xs">Issued to</h3>
|
|
<p class="font-bold">{ name }</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
templ cryptoWalletOption(ticker string, name string, isDefault bool) {
|
|
if isDefault {
|
|
<sl-option value={ ticker } selected disabled>
|
|
<sl-icon slot="prefix" name={ ticker } library="crypto"></sl-icon>
|
|
{ name }
|
|
</sl-option>
|
|
<sl-divider></sl-divider>
|
|
} else {
|
|
<sl-option value={ ticker }>
|
|
<sl-icon slot="prefix" name={ ticker } library="crypto"></sl-icon>
|
|
{ name }
|
|
</sl-option>
|
|
<sl-divider></sl-divider>
|
|
}
|
|
}
|
|
|
|
// Helper function to shorten address
|
|
func shortenAddress(address string) string {
|
|
if len(address) <= 20 {
|
|
return address
|
|
}
|
|
return address[:16] + "..." + address[len(address)-4:]
|
|
}
|