parent
d1be8b43f6
commit
234294d564
|
@ -414,7 +414,7 @@ passwordMatched: "一致しました"
|
||||||
passwordNotMatched: "一致していません"
|
passwordNotMatched: "一致していません"
|
||||||
signinWith: "{x}でログイン"
|
signinWith: "{x}でログイン"
|
||||||
signinFailed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
|
signinFailed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
|
||||||
tapSecurityKey: "セキュリティーキーにタッチ"
|
tapSecurityKey: "セキュリティキーにタッチ"
|
||||||
or: "もしくは"
|
or: "もしくは"
|
||||||
uiLanguage: "UIの表示言語"
|
uiLanguage: "UIの表示言語"
|
||||||
groupInvited: "グループに招待されました"
|
groupInvited: "グループに招待されました"
|
||||||
|
|
|
@ -51,7 +51,7 @@ import MkButton from './ui/button.vue';
|
||||||
import MkInput from './ui/input.vue';
|
import MkInput from './ui/input.vue';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import { apiUrl, host } from '../config';
|
import { apiUrl, host } from '../config';
|
||||||
import { hexifyAB } from '../scripts/2fa';
|
import { byteify, hexify } from '../scripts/2fa';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n,
|
i18n,
|
||||||
|
@ -121,14 +121,9 @@ export default Vue.extend({
|
||||||
this.queryingKey = true;
|
this.queryingKey = true;
|
||||||
return navigator.credentials.get({
|
return navigator.credentials.get({
|
||||||
publicKey: {
|
publicKey: {
|
||||||
challenge: Buffer.from(
|
challenge: byteify(this.challengeData.challenge, 'base64'),
|
||||||
this.challengeData.challenge
|
|
||||||
.replace(/\-/g, '+')
|
|
||||||
.replace(/_/g, '/'),
|
|
||||||
'base64'
|
|
||||||
),
|
|
||||||
allowCredentials: this.challengeData.securityKeys.map(key => ({
|
allowCredentials: this.challengeData.securityKeys.map(key => ({
|
||||||
id: Buffer.from(key.id, 'hex'),
|
id: byteify(key.id, 'hex'),
|
||||||
type: 'public-key',
|
type: 'public-key',
|
||||||
transports: ['usb', 'nfc', 'ble', 'internal']
|
transports: ['usb', 'nfc', 'ble', 'internal']
|
||||||
})),
|
})),
|
||||||
|
@ -143,9 +138,9 @@ export default Vue.extend({
|
||||||
return this.$root.api('signin', {
|
return this.$root.api('signin', {
|
||||||
username: this.username,
|
username: this.username,
|
||||||
password: this.password,
|
password: this.password,
|
||||||
signature: hexifyAB(credential.response.signature),
|
signature: hexify(credential.response.signature),
|
||||||
authenticatorData: hexifyAB(credential.response.authenticatorData),
|
authenticatorData: hexify(credential.response.authenticatorData),
|
||||||
clientDataJSON: hexifyAB(credential.response.clientDataJSON),
|
clientDataJSON: hexify(credential.response.clientDataJSON),
|
||||||
credentialId: credential.id,
|
credentialId: credential.id,
|
||||||
challengeId: this.challengeData.challengeId
|
challengeId: this.challengeData.challengeId
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,12 +22,12 @@
|
||||||
|
|
||||||
<mk-switch v-model="usePasswordLessLogin" @change="updatePasswordLessLogin" v-if="$store.state.i.securityKeysList.length > 0">{{ $t('passwordLessLogin') }}</mk-switch>
|
<mk-switch v-model="usePasswordLessLogin" @change="updatePasswordLessLogin" v-if="$store.state.i.securityKeysList.length > 0">{{ $t('passwordLessLogin') }}</mk-switch>
|
||||||
|
|
||||||
<mk-info warn v-if="registration && registration.error">{{ $t('something-went-wrong') }} {{ registration.error }}</mk-info>
|
<mk-info warn v-if="registration && registration.error">{{ $t('error') }} {{ registration.error }}</mk-info>
|
||||||
<mk-button v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('_2fa.registerKey') }}</mk-button>
|
<mk-button v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('_2fa.registerKey') }}</mk-button>
|
||||||
|
|
||||||
<ol v-if="registration && !registration.error">
|
<ol v-if="registration && !registration.error">
|
||||||
<li v-if="registration.stage >= 0">
|
<li v-if="registration.stage >= 0">
|
||||||
{{ $t('activate-key') }}
|
{{ $t('tapSecurityKey') }}
|
||||||
<fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 0" />
|
<fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 0" />
|
||||||
</li>
|
</li>
|
||||||
<li v-if="registration.stage >= 1">
|
<li v-if="registration.stage >= 1">
|
||||||
|
@ -67,16 +67,12 @@ import Vue from 'vue';
|
||||||
import { faLock } from '@fortawesome/free-solid-svg-icons';
|
import { faLock } from '@fortawesome/free-solid-svg-icons';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import { hostname } from '../../config';
|
import { hostname } from '../../config';
|
||||||
import { hexifyAB } from '../../scripts/2fa';
|
import { byteify, hexify, stringify } from '../../scripts/2fa';
|
||||||
import MkButton from '../../components/ui/button.vue';
|
import MkButton from '../../components/ui/button.vue';
|
||||||
import MkInfo from '../../components/ui/info.vue';
|
import MkInfo from '../../components/ui/info.vue';
|
||||||
import MkInput from '../../components/ui/input.vue';
|
import MkInput from '../../components/ui/input.vue';
|
||||||
import MkSwitch from '../../components/ui/switch.vue';
|
import MkSwitch from '../../components/ui/switch.vue';
|
||||||
|
|
||||||
function stringifyAB(buffer) {
|
|
||||||
return String.fromCharCode.apply(null, new Uint8Array(buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n,
|
i18n,
|
||||||
components: {
|
components: {
|
||||||
|
@ -157,8 +153,8 @@ export default Vue.extend({
|
||||||
name: this.keyName,
|
name: this.keyName,
|
||||||
challengeId: this.registration.challengeId,
|
challengeId: this.registration.challengeId,
|
||||||
// we convert each 16 bits to a string to serialise
|
// we convert each 16 bits to a string to serialise
|
||||||
clientDataJSON: stringifyAB(this.registration.credential.response.clientDataJSON),
|
clientDataJSON: stringify(this.registration.credential.response.clientDataJSON),
|
||||||
attestationObject: hexifyAB(this.registration.credential.response.attestationObject)
|
attestationObject: hexify(this.registration.credential.response.attestationObject)
|
||||||
}).then(key => {
|
}).then(key => {
|
||||||
this.registration = null;
|
this.registration = null;
|
||||||
key.lastUsed = new Date();
|
key.lastUsed = new Date();
|
||||||
|
@ -208,18 +204,13 @@ export default Vue.extend({
|
||||||
challengeId: registration.challengeId,
|
challengeId: registration.challengeId,
|
||||||
stage: 0,
|
stage: 0,
|
||||||
publicKeyOptions: {
|
publicKeyOptions: {
|
||||||
challenge: Uint8Array.from(
|
challenge: byteify(registration.challenge, 'base64'),
|
||||||
atob(registration.challenge
|
|
||||||
.replace(/\-/g, '+')
|
|
||||||
.replace(/_/g, '/')),
|
|
||||||
x => x.charCodeAt(0),
|
|
||||||
),
|
|
||||||
rp: {
|
rp: {
|
||||||
id: hostname,
|
id: hostname,
|
||||||
name: 'Misskey'
|
name: 'Misskey'
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
id: Uint8Array.from(this.$store.state.i.id, c => c.charCodeAt(0)),
|
id: byteify(this.$store.state.i.id, 'ascii'),
|
||||||
name: this.$store.state.i.username,
|
name: this.$store.state.i.username,
|
||||||
displayName: this.$store.state.i.name,
|
displayName: this.$store.state.i.name,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,33 @@
|
||||||
export function hexifyAB(buffer) {
|
export function byteify(data: string, encoding: 'ascii' | 'base64' | 'hex') {
|
||||||
return Array.from(new Uint8Array(buffer))
|
switch (encoding) {
|
||||||
.map(item => item.toString(16).padStart(2, '0'))
|
case 'ascii':
|
||||||
.join('');
|
return Uint8Array.from(data, c => c.charCodeAt(0));
|
||||||
|
case 'base64':
|
||||||
|
return Uint8Array.from(
|
||||||
|
atob(
|
||||||
|
data
|
||||||
|
.replace(/-/g, '+')
|
||||||
|
.replace(/_/g, '/')
|
||||||
|
),
|
||||||
|
c => c.charCodeAt(0)
|
||||||
|
);
|
||||||
|
case 'hex':
|
||||||
|
return new Uint8Array(
|
||||||
|
data
|
||||||
|
.match(/.{1,2}/g)
|
||||||
|
.map(byte => parseInt(byte, 16))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hexify(buffer: ArrayBuffer) {
|
||||||
|
return Array.from(new Uint8Array(buffer))
|
||||||
|
.reduce(
|
||||||
|
(str, byte) => str + byte.toString(16).padStart(2, '0'),
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stringify(buffer: ArrayBuffer) {
|
||||||
|
return String.fromCharCode(... new Uint8Array(buffer));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue