Authentication Alpha
parent
f001840670
commit
caff9e19d5
|
@ -36,6 +36,58 @@
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h1, .h2, .h3, .h4, .h5 {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,.h1 {
|
||||||
|
font-size: 200%;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
h2,.h2 {
|
||||||
|
font-size: 150%;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
h3,.h3 {
|
||||||
|
font-size: 110%;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex.column {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 0%;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
border: 1px solid black;
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
sym {
|
sym {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
|
@ -49,6 +101,18 @@ sym.trash::before {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-border {
|
||||||
|
border: none;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-solid {
|
||||||
|
font-family: 'fa-solid';
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
div.ln-tooltip {
|
div.ln-tooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
|
@ -170,6 +234,20 @@ div.ln-select::after {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ln-identity {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 200px;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
font-size: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
.ln-navbar {
|
.ln-navbar {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -28,7 +28,10 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="frame" class="ln-view">
|
<div id="frame" class="ln-view">
|
||||||
<section id="header">
|
<section id="header">
|
||||||
<h1>ln.vue Demo Application</h1>
|
<div class="flex">
|
||||||
|
<div class="h1">ln.vue Demo Application</div>
|
||||||
|
<ln-identity></ln-identity>
|
||||||
|
</div>
|
||||||
<ln-navbar></ln-navbar>
|
<ln-navbar></ln-navbar>
|
||||||
</section>
|
</section>
|
||||||
<section id="body">
|
<section id="body">
|
||||||
|
@ -208,7 +211,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
routes: {
|
routes: {
|
||||||
'/login': `<div>Login Pane<br><ln-login-pane></ln-login-pane></div>`,
|
'/login': `<div>Login Pane<br><ln-login-pane style="border: 1px solid black;"></ln-login-pane></div>`,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -102,6 +102,14 @@ Vue.component('ln-statusbar',{
|
||||||
class="ln-statusbar"
|
class="ln-statusbar"
|
||||||
>
|
>
|
||||||
<div>{{ LNVue.$_.statusText }}</div>
|
<div>{{ LNVue.$_.statusText }}</div>
|
||||||
|
<div style="flex-grow: 0;">
|
||||||
|
<span
|
||||||
|
v-if="!LNVue.$_.identity.UniqueID">NOT LOGGED IN
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="LNVue.$_.identity.UniqueID">{{ LNVue.$_.identity.IdentityName }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div style="flex-grow: 0;">{{ LNVue.$_.socket && LNVue.$_.socket._state }}</div>
|
<div style="flex-grow: 0;">{{ LNVue.$_.socket && LNVue.$_.socket._state }}</div>
|
||||||
<div
|
<div
|
||||||
class="ln-background-tasks"
|
class="ln-background-tasks"
|
||||||
|
@ -207,6 +215,7 @@ Vue.component('ln-password',{
|
||||||
template: `<input
|
template: `<input
|
||||||
type="password"
|
type="password"
|
||||||
v-bind:value="value"
|
v-bind:value="value"
|
||||||
|
autocomplete="current-password"
|
||||||
v-on:input="$emit('input', $event.target.value)">`,
|
v-on:input="$emit('input', $event.target.value)">`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -507,6 +516,54 @@ Vue.component('ln-select',{
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Vue.component('ln-identity',{
|
||||||
|
data: function(){
|
||||||
|
return {
|
||||||
|
popupVisible: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logout(){
|
||||||
|
LNVue.$_.authenticate("",null,"","");
|
||||||
|
this.popupVisible = false;
|
||||||
|
},
|
||||||
|
popup(){
|
||||||
|
this.popupVisible = !this.popupVisible;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: `<div class="ln-identity">
|
||||||
|
<div
|
||||||
|
v-if="LNVue.$_.identity.UniqueID"
|
||||||
|
v-tooltip="LNVue.$_.identity.UniqueID"
|
||||||
|
>
|
||||||
|
{{ LNVue.$_.identity.IdentityName }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!LNVue.$_.identity.UniqueID"
|
||||||
|
@click="popup()"
|
||||||
|
>Not logged in
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="no-border fa-solid"
|
||||||
|
@click="popup();"
|
||||||
|
></button>
|
||||||
|
<div
|
||||||
|
class="popup"
|
||||||
|
v-if="popupVisible"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="LNVue.$_.identity.UniqueID"
|
||||||
|
><button
|
||||||
|
@click="logout()"
|
||||||
|
>Logout</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!LNVue.$_.identity.UniqueID"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`,
|
||||||
|
});
|
||||||
|
|
||||||
Vue.component('ln-login-pane',{
|
Vue.component('ln-login-pane',{
|
||||||
props: {
|
props: {
|
||||||
|
@ -520,29 +577,25 @@ Vue.component('ln-login-pane',{
|
||||||
.then((challenges)=>{
|
.then((challenges)=>{
|
||||||
challenges = challenges.message.Challenges;
|
challenges = challenges.message.Challenges;
|
||||||
if (challenges.length > 0){
|
if (challenges.length > 0){
|
||||||
LNVue.$_.identity.identityName = this.identityName;
|
LNVue.$_.identity.IdentityName = this.identityName;
|
||||||
LNVue.$_.identity.challenges = challenges;
|
LNVue.$_.identity.challenges = challenges;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
authenticate(challenge){
|
logout(){
|
||||||
console.log("authenticate()",challenge);
|
LNVue.$_.authenticate("",null,"","");
|
||||||
},
|
},
|
||||||
authenticateSeededPassword(challenge){
|
authenticateSeededPassword(challenge){
|
||||||
let encoder = new TextEncoder();
|
let encoder = new TextEncoder();
|
||||||
let seed = LNVue.decodeHex(challenge.AuthenticationParameters);
|
let seed = LNVue.decodeHex(challenge.AuthenticationParameters);
|
||||||
let password = encoder.encode(challenge.prove);
|
let password = encoder.encode(challenge.prove);
|
||||||
|
|
||||||
console.log("password", LNVue.encodeHex(password),password);
|
|
||||||
console.log("seed", LNVue.encodeHex(seed),seed);
|
|
||||||
|
|
||||||
let secretSource = ArrayBuffer.combine(seed, password, seed);
|
let secretSource = ArrayBuffer.combine(seed, password, seed);
|
||||||
|
|
||||||
crypto.subtle.digest("SHA-256",secretSource)
|
crypto.subtle.digest("SHA-256",secretSource)
|
||||||
.then((secret)=>{
|
.then((secret)=>{
|
||||||
let challengebytes = LNVue.decodeB64(challenge.Challenge);
|
let challengebytes = LNVue.decodeB64(challenge.Challenge);
|
||||||
secret = new Uint8Array(secret);
|
secret = new Uint8Array(secret);
|
||||||
console.log("secret", LNVue.encodeHex(secret),secret);
|
|
||||||
|
|
||||||
let proveSource = ArrayBuffer.combine(challengebytes, secret, challengebytes);
|
let proveSource = ArrayBuffer.combine(challengebytes, secret, challengebytes);
|
||||||
|
|
||||||
|
@ -563,28 +616,36 @@ Vue.component('ln-login-pane',{
|
||||||
},
|
},
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<div
|
<div
|
||||||
v-if="LNVue.$_.identity.uniqueID">
|
v-if="LNVue.$_.identity.UniqueID"
|
||||||
IdentityName: {{ LNVue.$_.identity.uniqueID }} / {{ LNVue.$_.identity.identityName }}
|
>
|
||||||
</div>
|
<div>Logged in as</div>
|
||||||
|
{{ LNVue.$_.identity.IdentityName }}
|
||||||
|
<button @click="logout()">Logout</button>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!LNVue.$_.identity.uniqueID">
|
v-if="!LNVue.$_.identity.UniqueID">
|
||||||
<div
|
<div
|
||||||
v-if="LNVue.$_.identity.identityName == ''"
|
v-if="LNVue.$_.identity.IdentityName == ''"
|
||||||
><label for="LNIdentityName">Username</label>
|
><form @submit.prevent="stage1();">
|
||||||
|
<label for="username">Username</label>
|
||||||
<ln-textfield
|
<ln-textfield
|
||||||
name="LNIdentityName"
|
name="username"
|
||||||
|
autocomplete="username"
|
||||||
v-model="identityName"></ln-textfield>
|
v-model="identityName"></ln-textfield>
|
||||||
<button
|
<button
|
||||||
:disabled="identityName == ''"
|
:disabled="identityName == ''"
|
||||||
@click="stage1();"
|
type="submit"
|
||||||
>continue</button>
|
>continue</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="LNVue.$_.identity.identityName != ''"
|
v-if="LNVue.$_.identity.IdentityName != ''"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="challenge in LNVue.$_.identity.challenges"
|
v-for="challenge in LNVue.$_.identity.challenges"
|
||||||
>
|
>
|
||||||
|
<form @submit.prevent="authenticateSeededPassword(challenge); return false;">
|
||||||
|
<input style="display: none;" type="text" name="username" autocomplete="username" :value="LNVue.$_.identity.IdentityName">
|
||||||
<div
|
<div
|
||||||
v-if="challenge.SecureAttributeTypeName == 'SeededPassword'"
|
v-if="challenge.SecureAttributeTypeName == 'SeededPassword'"
|
||||||
>{{ challenge.SecureAttributeLabel }}
|
>{{ challenge.SecureAttributeLabel }}
|
||||||
|
@ -592,10 +653,12 @@ Vue.component('ln-login-pane',{
|
||||||
v-model="challenge.prove"
|
v-model="challenge.prove"
|
||||||
></ln-password>
|
></ln-password>
|
||||||
<button
|
<button
|
||||||
@click="authenticateSeededPassword(challenge)"
|
type="submit"
|
||||||
|
:disabled="challenge.prove==''"
|
||||||
>login</button>
|
>login</button>
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`,
|
</div>`,
|
||||||
});
|
});
|
||||||
|
|
13
js/ln.vue.js
13
js/ln.vue.js
|
@ -299,8 +299,8 @@
|
||||||
|
|
||||||
this.navigation = {};
|
this.navigation = {};
|
||||||
this.identity = {
|
this.identity = {
|
||||||
uniqueID: null,
|
UniqueID: null,
|
||||||
identityName: "",
|
IdentityName: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
Promise
|
Promise
|
||||||
|
@ -402,7 +402,6 @@
|
||||||
SecureAttributeTypeName: secureAttributeTypeName,
|
SecureAttributeTypeName: secureAttributeTypeName,
|
||||||
})
|
})
|
||||||
.then((challenges)=>{
|
.then((challenges)=>{
|
||||||
console.log("rx challenges",challenges);
|
|
||||||
resolve(challenges);
|
resolve(challenges);
|
||||||
},
|
},
|
||||||
(error)=>{
|
(error)=>{
|
||||||
|
@ -419,12 +418,16 @@
|
||||||
Challenge: challenge,
|
Challenge: challenge,
|
||||||
Prove: prove,
|
Prove: prove,
|
||||||
};
|
};
|
||||||
console.log("authenticate", authenticationProve);
|
console.log(authenticationProve);
|
||||||
this.socket.request("AuthenticationProve", authenticationProve)
|
this.socket.request("AuthenticationProve", authenticationProve)
|
||||||
.then((identity)=>{
|
.then((identity)=>{
|
||||||
console.log("auth",identity);
|
this.identity = identity.message;
|
||||||
},
|
},
|
||||||
(error)=>{
|
(error)=>{
|
||||||
|
this.identity = {
|
||||||
|
IdentityName: "",
|
||||||
|
UniqueID: null,
|
||||||
|
}
|
||||||
console.log("auth error",error);
|
console.log("auth error",error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue