Use esbuild
This commit is contained in:
parent
540bd17792
commit
97f3848b66
13 changed files with 494 additions and 17 deletions
Binary file not shown.
Binary file not shown.
|
@ -1,15 +1,2 @@
|
|||
import { Auth } from './auth.js';
|
||||
|
||||
let auth = new Auth();
|
||||
|
||||
document.querySelectorAll('.add-passkey').forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
auth.register();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.login-passkey').forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
auth.login();
|
||||
});
|
||||
});
|
||||
(()=>{var i=class{constructor(){}async register(){let n=await this.getCreateOptions(),e={challenge:this.base64URLStringToBuffer(n.challenge),rp:{id:n.rp.id,name:n.rp.name},user:{id:new TextEncoder().encode(window.atob(n.user.id)),name:n.user.name,displayName:n.user.displayName},pubKeyCredParams:n.pubKeyCredParams,excludeCredentials:[],authenticatorSelection:n.authenticatorSelection,timeout:6e4},t=await navigator.credentials.create({publicKey:e});if(!t)throw new Error("Error generating a passkey");let r={id:t.id?t.id:null,type:t.type?t.type:null,rawId:t.rawId?this.bufferToBase64URLString(t.rawId):null,response:{attestationObject:t.response.attestationObject?this.bufferToBase64URLString(t.response.attestationObject):null,clientDataJSON:t.response.clientDataJSON?this.bufferToBase64URLString(t.response.clientDataJSON):null}};if(!(await window.fetch("/admin/passkeys/register",{method:"POST",body:JSON.stringify(r),cache:"no-cache",headers:{"Content-Type":"application/json","X-CSRF-TOKEN":document.querySelector('meta[name="csrf-token"]').getAttribute("content")}})).ok)throw new Error("Error saving the passkey");window.location.reload()}async getCreateOptions(){return await(await fetch("/admin/passkeys/register",{method:"GET"})).json()}async login(){let n=await this.getLoginData(),e=await navigator.credentials.get({publicKey:{challenge:this.base64URLStringToBuffer(n.challenge),userVerification:n.userVerification,timeout:6e4}});if(!e)throw new Error("Authentication failed");let t={id:e.id?e.id:"",type:e.type?e.type:"",rawId:e.rawId?this.bufferToBase64URLString(e.rawId):"",response:{authenticatorData:e.response.authenticatorData?this.bufferToBase64URLString(e.response.authenticatorData):"",clientDataJSON:e.response.clientDataJSON?this.bufferToBase64URLString(e.response.clientDataJSON):"",signature:e.response.signature?this.bufferToBase64URLString(e.response.signature):"",userHandle:e.response.userHandle?this.bufferToBase64URLString(e.response.userHandle):""}};if(!(await window.fetch("/login/passkey",{method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json","X-CSRF-TOKEN":document.querySelector('meta[name="csrf-token"]').getAttribute("content")}})).ok)throw new Error("Login failed");window.location.assign("/admin")}async getLoginData(){return await(await fetch("/login/passkey",{method:"GET"})).json()}base64URLStringToBuffer(n){let e=n.replace(/-/g,"+").replace(/_/g,"/"),t=(4-e.length%4)%4,r=e.padEnd(e.length+t,"="),a=window.atob(r),c=new ArrayBuffer(a.length),d=new Uint8Array(c);for(let o=0;o<a.length;o++)d[o]=a.charCodeAt(o);return c}bufferToBase64URLString(n){let e=new Uint8Array(n),t="";for(let a of e)t+=String.fromCharCode(a);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}};var l=new i;document.querySelectorAll(".add-passkey").forEach(s=>{s.addEventListener("click",()=>{l.register()})});document.querySelectorAll(".login-passkey").forEach(s=>{s.addEventListener("click",()=>{l.login()})});})();
|
||||
//# sourceMappingURL=app.js.map
|
||||
|
|
Binary file not shown.
7
public/assets/js/app.js.map
Normal file
7
public/assets/js/app.js.map
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -1,167 +0,0 @@
|
|||
class Auth {
|
||||
constructor() {}
|
||||
|
||||
async register() {
|
||||
const createOptions = await this.getCreateOptions();
|
||||
|
||||
const publicKeyCredentialCreationOptions = {
|
||||
challenge: this.base64URLStringToBuffer(createOptions.challenge),
|
||||
rp: {
|
||||
id: createOptions.rp.id,
|
||||
name: createOptions.rp.name,
|
||||
},
|
||||
user: {
|
||||
id: new TextEncoder().encode(window.atob(createOptions.user.id)),
|
||||
name: createOptions.user.name,
|
||||
displayName: createOptions.user.displayName,
|
||||
},
|
||||
pubKeyCredParams: createOptions.pubKeyCredParams,
|
||||
excludeCredentials: [],
|
||||
authenticatorSelection: createOptions.authenticatorSelection,
|
||||
timeout: 60000,
|
||||
};
|
||||
|
||||
const credential = await navigator.credentials.create({
|
||||
publicKey: publicKeyCredentialCreationOptions
|
||||
});
|
||||
if (!credential) {
|
||||
throw new Error('Error generating a passkey');
|
||||
}
|
||||
|
||||
const authenticatorAttestationResponse = {
|
||||
id: credential.id ? credential.id : null,
|
||||
type: credential.type ? credential.type : null,
|
||||
rawId: credential.rawId ? this.bufferToBase64URLString(credential.rawId) : null,
|
||||
response: {
|
||||
attestationObject: credential.response.attestationObject ? this.bufferToBase64URLString(credential.response.attestationObject) : null,
|
||||
clientDataJSON: credential.response.clientDataJSON ? this.bufferToBase64URLString(credential.response.clientDataJSON) : null,
|
||||
}
|
||||
};
|
||||
|
||||
const registerCredential = await window.fetch('/admin/passkeys/register', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(authenticatorAttestationResponse),
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
},
|
||||
});
|
||||
|
||||
if (!registerCredential.ok) {
|
||||
throw new Error('Error saving the passkey');
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
async getCreateOptions() {
|
||||
const response = await fetch('/admin/passkeys/register', {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async login() {
|
||||
const loginData = await this.getLoginData();
|
||||
|
||||
const publicKeyCredential = await navigator.credentials.get({
|
||||
publicKey: {
|
||||
challenge: this.base64URLStringToBuffer(loginData.challenge),
|
||||
userVerification: loginData.userVerification,
|
||||
timeout: 60000,
|
||||
}
|
||||
});
|
||||
|
||||
if (!publicKeyCredential) {
|
||||
throw new Error('Authentication failed');
|
||||
}
|
||||
|
||||
const authenticatorAttestationResponse = {
|
||||
id: publicKeyCredential.id ? publicKeyCredential.id : '',
|
||||
type: publicKeyCredential.type ? publicKeyCredential.type : '',
|
||||
rawId: publicKeyCredential.rawId ? this.bufferToBase64URLString(publicKeyCredential.rawId) : '',
|
||||
response: {
|
||||
authenticatorData: publicKeyCredential.response.authenticatorData ? this.bufferToBase64URLString(publicKeyCredential.response.authenticatorData) : '',
|
||||
clientDataJSON: publicKeyCredential.response.clientDataJSON ? this.bufferToBase64URLString(publicKeyCredential.response.clientDataJSON) : '',
|
||||
signature: publicKeyCredential.response.signature ? this.bufferToBase64URLString(publicKeyCredential.response.signature) : '',
|
||||
userHandle: publicKeyCredential.response.userHandle ? this.bufferToBase64URLString(publicKeyCredential.response.userHandle) : '',
|
||||
},
|
||||
};
|
||||
|
||||
const loginAttempt = await window.fetch('/login/passkey', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(authenticatorAttestationResponse),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
},
|
||||
});
|
||||
|
||||
if (!loginAttempt.ok) {
|
||||
throw new Error('Login failed');
|
||||
}
|
||||
|
||||
window.location.assign('/admin');
|
||||
}
|
||||
|
||||
async getLoginData() {
|
||||
const response = await fetch('/login/passkey', {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a base64 URL string to a buffer.
|
||||
*
|
||||
* Sourced from https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/browser/src/helpers/base64URLStringToBuffer.ts#L8
|
||||
*
|
||||
* @param {string} base64URLString
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
base64URLStringToBuffer(base64URLString) {
|
||||
// Convert from Base64URL to Base64
|
||||
const base64 = base64URLString.replace(/-/g, '+').replace(/_/g, '/');
|
||||
/**
|
||||
* Pad with '=' until it's a multiple of four
|
||||
* (4 - (85 % 4 = 1) = 3) % 4 = 3 padding
|
||||
* (4 - (86 % 4 = 2) = 2) % 4 = 2 padding
|
||||
* (4 - (87 % 4 = 3) = 1) % 4 = 1 padding
|
||||
* (4 - (88 % 4 = 0) = 4) % 4 = 0 padding
|
||||
*/
|
||||
const padLength = (4 - (base64.length % 4)) % 4;
|
||||
const padded = base64.padEnd(base64.length + padLength, '=');
|
||||
// Convert to a binary string
|
||||
const binary = window.atob(padded);
|
||||
// Convert binary string to buffer
|
||||
const buffer = new ArrayBuffer(binary.length);
|
||||
const bytes = new Uint8Array(buffer);
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a buffer to a base64 URL string.
|
||||
*
|
||||
* Sourced from https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/browser/src/helpers/bufferToBase64URLString.ts#L7
|
||||
*
|
||||
* @param {ArrayBuffer} buffer
|
||||
* @returns {string}
|
||||
*/
|
||||
bufferToBase64URLString(buffer) {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let str = '';
|
||||
for (const charCode of bytes) {
|
||||
str += String.fromCharCode(charCode);
|
||||
}
|
||||
const base64String = btoa(str);
|
||||
return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
export { Auth };
|
Binary file not shown.
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue