summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2019-07-07 05:13:21 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2019-07-07 05:13:21 +0900
commit351b2360145e0322705b3085a23d4e07029eeaf6 (patch)
tree03e3f5db0fb770b0a9e9f7c580b1dd9be2f02e3a /src
parentイベント送信時に指定の変数の値を添付出来るように (diff)
parentSupport password-less login with WebAuthn (#5112) (diff)
downloadsharkey-351b2360145e0322705b3085a23d4e07029eeaf6.tar.gz
sharkey-351b2360145e0322705b3085a23d4e07029eeaf6.tar.bz2
sharkey-351b2360145e0322705b3085a23d4e07029eeaf6.zip
Merge branch 'develop' of https://github.com/syuilo/misskey into develop
Diffstat (limited to 'src')
-rw-r--r--src/client/app/common/views/components/settings/2fa.vue16
-rw-r--r--src/client/app/common/views/components/signin.vue6
-rw-r--r--src/models/entities/user-profile.ts5
-rw-r--r--src/models/repositories/user.ts2
-rw-r--r--src/server/api/endpoints/i/2fa/password-less.ts21
-rw-r--r--src/server/api/private/signin.ts36
6 files changed, 76 insertions, 10 deletions
diff --git a/src/client/app/common/views/components/settings/2fa.vue b/src/client/app/common/views/components/settings/2fa.vue
index eb645898e2..813a91b5c0 100644
--- a/src/client/app/common/views/components/settings/2fa.vue
+++ b/src/client/app/common/views/components/settings/2fa.vue
@@ -28,6 +28,10 @@
</div>
</div>
+ <ui-switch v-model="usePasswordLessLogin" @change="updatePasswordLessLogin" v-if="$store.state.i.securityKeysList.length > 0">
+ {{ $t('use-password-less-login') }}
+ </ui-switch>
+
<ui-info warn v-if="registration && registration.error">{{ $t('something-went-wrong') }} {{ registration.error }}</ui-info>
<ui-button v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('register') }}</ui-button>
@@ -80,6 +84,7 @@ export default Vue.extend({
return {
data: null,
supportsCredentials: !!navigator.credentials,
+ usePasswordLessLogin: this.$store.state.i.usePasswordLessLogin,
registration: null,
keyName: '',
token: null
@@ -113,6 +118,9 @@ export default Vue.extend({
this.$root.api('i/2fa/unregister', {
password: password
}).then(() => {
+ this.usePasswordLessLogin = false;
+ this.updatePasswordLessLogin();
+ }).then(() => {
this.$notify(this.$t('unregistered'));
this.$store.state.i.twoFactorEnabled = false;
});
@@ -158,6 +166,9 @@ export default Vue.extend({
password,
credentialId: key.id
}).then(() => {
+ this.usePasswordLessLogin = false;
+ this.updatePasswordLessLogin();
+ }).then(() => {
this.$notify(this.$t('key-unregistered'));
});
});
@@ -213,6 +224,11 @@ export default Vue.extend({
this.registration.stage = -1;
});
});
+ },
+ updatePasswordLessLogin() {
+ this.$root.api('i/2fa/password-less', {
+ value: !!this.usePasswordLessLogin
+ });
}
}
});
diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue
index 8498a1dc3e..f76f989d6d 100644
--- a/src/client/app/common/views/components/signin.vue
+++ b/src/client/app/common/views/components/signin.vue
@@ -7,7 +7,7 @@
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</ui-input>
- <ui-input v-model="password" type="password" :with-password-toggle="true" required>
+ <ui-input v-model="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
<span>{{ $t('password') }}</span>
<template #prefix><fa icon="lock"/></template>
</ui-input>
@@ -28,6 +28,10 @@
</div>
<div class="twofa-group totp-group">
<p style="margin-bottom:0;">{{ $t('enter-2fa-code') }}</p>
+ <ui-input v-model="password" type="password" :with-password-toggle="true" v-if="user && user.usePasswordLessLogin" required>
+ <span>{{ $t('password') }}</span>
+ <template #prefix><fa icon="lock"/></template>
+ </ui-input>
<ui-input v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
<span>{{ $t('@.2fa') }}</span>
<template #prefix><fa icon="gavel"/></template>
diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts
index 6f960f1b7b..4a588ebfbf 100644
--- a/src/models/entities/user-profile.ts
+++ b/src/models/entities/user-profile.ts
@@ -81,6 +81,11 @@ export class UserProfile {
})
public securityKeysAvailable: boolean;
+ @Column('boolean', {
+ default: false,
+ })
+ public usePasswordLessLogin: boolean;
+
@Column('varchar', {
length: 128, nullable: true,
comment: 'The password hash of the User. It will be null if the origin of the user is local.'
diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index cc89b674c5..06da74197f 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -156,6 +156,7 @@ export class UserRepository extends Repository<User> {
detail: true
}),
twoFactorEnabled: profile!.twoFactorEnabled,
+ usePasswordLessLogin: profile!.usePasswordLessLogin,
securityKeys: profile!.twoFactorEnabled
? UserSecurityKeys.count({
userId: user.id
@@ -208,7 +209,6 @@ export class UserRepository extends Repository<User> {
select: ['id', 'name', 'lastUsed']
})
: []
-
} : {}),
...(relation ? {
diff --git a/src/server/api/endpoints/i/2fa/password-less.ts b/src/server/api/endpoints/i/2fa/password-less.ts
new file mode 100644
index 0000000000..19e75ca1c5
--- /dev/null
+++ b/src/server/api/endpoints/i/2fa/password-less.ts
@@ -0,0 +1,21 @@
+import $ from 'cafy';
+import define from '../../../define';
+import { UserProfiles } from '../../../../../models';
+
+export const meta = {
+ requireCredential: true,
+
+ secure: true,
+
+ params: {
+ value: {
+ validator: $.boolean
+ }
+ }
+};
+
+export default define(meta, async (ps, user) => {
+ await UserProfiles.update(user.id, {
+ usePasswordLessLogin: ps.value
+ });
+});
diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts
index bc9346d088..67afed760b 100644
--- a/src/server/api/private/signin.ts
+++ b/src/server/api/private/signin.ts
@@ -72,19 +72,25 @@ export default async (ctx: Koa.BaseContext) => {
}
}
- if (!same) {
- await fail(403, {
- error: 'incorrect password'
- });
- return;
- }
-
if (!profile.twoFactorEnabled) {
- signin(ctx, user);
+ if (same) {
+ signin(ctx, user);
+ } else {
+ await fail(403, {
+ error: 'incorrect password'
+ });
+ }
return;
}
if (token) {
+ if (!same) {
+ await fail(403, {
+ error: 'incorrect password'
+ });
+ return;
+ }
+
const verified = (speakeasy as any).totp.verify({
secret: profile.twoFactorSecret,
encoding: 'base32',
@@ -101,6 +107,13 @@ export default async (ctx: Koa.BaseContext) => {
return;
}
} else if (body.credentialId) {
+ if (!same && !profile.usePasswordLessLogin) {
+ await fail(403, {
+ error: 'incorrect password'
+ });
+ return;
+ }
+
const clientDataJSON = Buffer.from(body.clientDataJSON, 'hex');
const clientData = JSON.parse(clientDataJSON.toString('utf-8'));
const challenge = await AttestationChallenges.findOne({
@@ -163,6 +176,13 @@ export default async (ctx: Koa.BaseContext) => {
return;
}
} else {
+ if (!same && !profile.usePasswordLessLogin) {
+ await fail(403, {
+ error: 'incorrect password'
+ });
+ return;
+ }
+
const keys = await UserSecurityKeys.find({
userId: user.id
});