2017-09-25 03:22:33 +00:00
|
|
|
<template lang="pug">
|
|
|
|
.login(:class='{ "is-error": error }')
|
2018-01-10 01:41:53 +00:00
|
|
|
.login-container(:class='{ "is-expanded": strategies.length > 1, "is-loading": isLoading }')
|
2017-09-25 03:22:33 +00:00
|
|
|
.login-providers(v-show='strategies.length > 1')
|
2017-10-01 03:47:14 +00:00
|
|
|
button(v-for='strategy in strategies', :class='{ "is-active": strategy.key === selectedStrategy }', @click='selectStrategy(strategy.key, strategy.useForm)', :title='strategy.title')
|
|
|
|
em(v-html='strategy.icon')
|
2017-09-25 03:22:33 +00:00
|
|
|
span {{ strategy.title }}
|
2017-12-30 07:00:49 +00:00
|
|
|
.login-providers-fill
|
2018-01-10 01:41:53 +00:00
|
|
|
.login-frame(v-show='screen === "login"')
|
2017-09-25 03:22:33 +00:00
|
|
|
h1 {{ siteTitle }}
|
2018-01-10 01:41:53 +00:00
|
|
|
h2 {{ $t('auth:loginRequired') }}
|
|
|
|
input(type='text', ref='iptEmail', v-model='username', :placeholder='$t("auth:fields.emailUser")')
|
|
|
|
input(type='password', ref='iptPassword', v-model='password', :placeholder='$t("auth:fields.password")', @keyup.enter='login')
|
2017-12-31 19:40:28 +00:00
|
|
|
button.button.is-blue.is-fullwidth(@click='login')
|
2017-12-30 07:00:49 +00:00
|
|
|
span {{ $t('auth:actions.login') }}
|
2018-01-10 01:41:53 +00:00
|
|
|
.login-frame(v-show='screen === "tfa"')
|
|
|
|
.login-frame-icon
|
|
|
|
svg.icons.is-48(role='img')
|
|
|
|
title {{ $t('auth:tfa.title') }}
|
|
|
|
use(xlink:href='#nc-key')
|
|
|
|
h2 {{ $t('auth:tfa.subtitle') }}
|
|
|
|
input(type='text', ref='iptTFA', v-model='securityCode', :placeholder='$t("auth:tfa.placeholder")', @keyup.enter='verifySecurityCode')
|
|
|
|
button.button.is-blue.is-fullwidth(@click='verifySecurityCode')
|
|
|
|
span {{ $t('auth:tfa.verifyToken') }}
|
2017-09-25 03:22:33 +00:00
|
|
|
.login-copyright
|
2018-01-10 01:41:53 +00:00
|
|
|
span {{ $t('footer.poweredBy') }}
|
2017-09-25 03:22:33 +00:00
|
|
|
a(href='https://wiki.js.org', rel='external', title='Wiki.js') Wiki.js
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2017-10-30 01:36:05 +00:00
|
|
|
/* global CONSTANTS, graphQL, siteConfig */
|
|
|
|
|
2018-03-09 05:33:43 +00:00
|
|
|
import _ from 'lodash'
|
|
|
|
|
2017-09-25 03:22:33 +00:00
|
|
|
export default {
|
2018-01-10 01:41:53 +00:00
|
|
|
data () {
|
2017-09-25 03:22:33 +00:00
|
|
|
return {
|
|
|
|
error: false,
|
2017-10-01 03:47:14 +00:00
|
|
|
strategies: [],
|
2018-01-10 01:41:53 +00:00
|
|
|
selectedStrategy: 'local',
|
|
|
|
screen: 'login',
|
|
|
|
username: '',
|
|
|
|
password: '',
|
|
|
|
securityCode: '',
|
|
|
|
loginToken: '',
|
|
|
|
isLoading: false
|
2017-09-25 03:22:33 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
2018-01-10 01:41:53 +00:00
|
|
|
siteTitle () {
|
2017-09-25 03:22:33 +00:00
|
|
|
return siteConfig.title
|
|
|
|
}
|
2017-09-30 02:32:43 +00:00
|
|
|
},
|
2018-02-03 21:48:25 +00:00
|
|
|
mounted () {
|
|
|
|
this.$store.commit('navigator/subtitleStatic', 'Login')
|
|
|
|
this.refreshStrategies()
|
|
|
|
this.$refs.iptEmail.focus()
|
|
|
|
},
|
2017-09-25 03:22:33 +00:00
|
|
|
methods: {
|
2018-01-10 01:41:53 +00:00
|
|
|
selectStrategy (key, useForm) {
|
2017-10-01 03:47:14 +00:00
|
|
|
this.selectedStrategy = key
|
2018-01-10 01:41:53 +00:00
|
|
|
this.screen = 'login'
|
2017-10-01 03:47:14 +00:00
|
|
|
if (!useForm) {
|
2018-01-15 03:05:08 +00:00
|
|
|
this.isLoading = true
|
|
|
|
window.location.assign(this.$helpers.resolvePath('login/' + key))
|
2018-01-10 01:41:53 +00:00
|
|
|
} else {
|
|
|
|
this.$refs.iptEmail.focus()
|
2017-10-01 03:47:14 +00:00
|
|
|
}
|
|
|
|
},
|
2018-01-10 01:41:53 +00:00
|
|
|
refreshStrategies () {
|
|
|
|
this.isLoading = true
|
2017-10-01 03:47:14 +00:00
|
|
|
graphQL.query({
|
2018-03-09 05:33:43 +00:00
|
|
|
query: CONSTANTS.GRAPH.AUTHENTICATION.QUERY_LOGIN_PROVIDERS
|
2017-10-01 03:47:14 +00:00
|
|
|
}).then(resp => {
|
2018-03-09 05:33:43 +00:00
|
|
|
if (_.has(resp, 'data.authentication.providers')) {
|
|
|
|
this.strategies = _.get(resp, 'data.authentication.providers', [])
|
2017-10-01 03:47:14 +00:00
|
|
|
} else {
|
|
|
|
throw new Error('No authentication providers available!')
|
|
|
|
}
|
2018-01-10 01:41:53 +00:00
|
|
|
this.isLoading = false
|
2017-10-01 03:47:14 +00:00
|
|
|
}).catch(err => {
|
|
|
|
console.error(err)
|
2018-01-10 01:41:53 +00:00
|
|
|
this.$store.dispatch('alert', {
|
|
|
|
style: 'error',
|
|
|
|
icon: 'gg-warning',
|
|
|
|
msg: err.message
|
|
|
|
})
|
|
|
|
this.isLoading = false
|
2017-10-01 03:47:14 +00:00
|
|
|
})
|
2017-12-30 07:00:49 +00:00
|
|
|
},
|
2018-01-10 01:41:53 +00:00
|
|
|
login () {
|
|
|
|
if (this.username.length < 2) {
|
|
|
|
this.$store.dispatch('alert', {
|
|
|
|
style: 'error',
|
|
|
|
icon: 'gg-warning',
|
|
|
|
msg: 'Enter a valid email / username.'
|
|
|
|
})
|
|
|
|
this.$refs.iptEmail.focus()
|
|
|
|
} else if (this.password.length < 2) {
|
|
|
|
this.$store.dispatch('alert', {
|
|
|
|
style: 'error',
|
|
|
|
icon: 'gg-warning',
|
|
|
|
msg: 'Enter a valid password.'
|
|
|
|
})
|
|
|
|
this.$refs.iptPassword.focus()
|
|
|
|
} else {
|
|
|
|
this.isLoading = true
|
|
|
|
graphQL.mutate({
|
|
|
|
mutation: CONSTANTS.GRAPHQL.GQL_MUTATION_LOGIN,
|
|
|
|
variables: {
|
|
|
|
username: this.username,
|
|
|
|
password: this.password,
|
|
|
|
provider: this.selectedStrategy
|
|
|
|
}
|
|
|
|
}).then(resp => {
|
|
|
|
if (resp.data.login) {
|
|
|
|
let respObj = resp.data.login
|
|
|
|
if (respObj.succeeded === true) {
|
|
|
|
if (respObj.tfaRequired === true) {
|
|
|
|
this.screen = 'tfa'
|
|
|
|
this.securityCode = ''
|
|
|
|
this.loginToken = respObj.tfaLoginToken
|
|
|
|
this.$nextTick(() => {
|
|
|
|
this.$refs.iptTFA.focus()
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
this.$store.dispatch('alert', {
|
|
|
|
style: 'success',
|
|
|
|
icon: 'gg-check',
|
|
|
|
msg: 'Login successful!'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
this.isLoading = false
|
|
|
|
} else {
|
|
|
|
throw new Error(respObj.message)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new Error('Authentication is unavailable.')
|
|
|
|
}
|
|
|
|
}).catch(err => {
|
|
|
|
console.error(err)
|
|
|
|
this.$store.dispatch('alert', {
|
|
|
|
style: 'error',
|
|
|
|
icon: 'gg-warning',
|
|
|
|
msg: err.message
|
|
|
|
})
|
|
|
|
this.isLoading = false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
verifySecurityCode () {
|
|
|
|
if (this.securityCode.length !== 6) {
|
|
|
|
this.$store.dispatch('alert', {
|
|
|
|
style: 'error',
|
|
|
|
icon: 'gg-warning',
|
|
|
|
msg: 'Enter a valid security code.'
|
|
|
|
})
|
|
|
|
this.$refs.iptTFA.focus()
|
|
|
|
} else {
|
|
|
|
this.isLoading = true
|
|
|
|
graphQL.mutate({
|
|
|
|
mutation: CONSTANTS.GRAPHQL.GQL_MUTATION_LOGINTFA,
|
|
|
|
variables: {
|
|
|
|
loginToken: this.loginToken,
|
|
|
|
securityCode: this.securityCode
|
|
|
|
}
|
|
|
|
}).then(resp => {
|
|
|
|
if (resp.data.loginTFA) {
|
|
|
|
let respObj = resp.data.loginTFA
|
|
|
|
if (respObj.succeeded === true) {
|
|
|
|
this.$store.dispatch('alert', {
|
|
|
|
style: 'success',
|
|
|
|
icon: 'gg-check',
|
|
|
|
msg: 'Login successful!'
|
|
|
|
})
|
|
|
|
this.isLoading = false
|
|
|
|
} else {
|
|
|
|
throw new Error(respObj.message)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new Error('Authentication is unavailable.')
|
|
|
|
}
|
|
|
|
}).catch(err => {
|
|
|
|
console.error(err)
|
|
|
|
this.$store.dispatch('alert', {
|
|
|
|
style: 'error',
|
|
|
|
icon: 'gg-warning',
|
|
|
|
msg: err.message
|
|
|
|
})
|
|
|
|
this.isLoading = false
|
|
|
|
})
|
|
|
|
}
|
2017-09-25 03:22:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
2018-01-21 22:54:43 +00:00
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
.login {
|
|
|
|
background-color: mc('blue', '800');
|
2018-02-24 22:35:56 +00:00
|
|
|
background-image: url('../static/svg/login-bg-motif.svg');
|
2018-01-21 22:54:43 +00:00
|
|
|
background-repeat: repeat;
|
|
|
|
background-size: 200px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
animation: loginBgReveal 20s linear infinite;
|
|
|
|
|
|
|
|
@include keyframes(loginBgReveal) {
|
|
|
|
0% {
|
|
|
|
background-position-y: 0;
|
|
|
|
}
|
|
|
|
100% {
|
|
|
|
background-position-y: -800px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&::before {
|
|
|
|
content: '';
|
|
|
|
position: absolute;
|
2018-02-24 22:35:56 +00:00
|
|
|
background-image: url('../static/svg/login-bg.svg');
|
2018-01-21 22:54:43 +00:00
|
|
|
background-position: center bottom;
|
|
|
|
background-size: cover;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
width: 100vw;
|
|
|
|
height: 100vh;
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&::after {
|
|
|
|
content: '';
|
|
|
|
position: absolute;
|
|
|
|
background-image: linear-gradient(to bottom, rgba(mc('blue', '800'), 1) 0%, rgba(mc('blue', '800'), 0) 100%);
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
width: 100vw;
|
|
|
|
height: 25vh;
|
|
|
|
}
|
|
|
|
|
|
|
|
&-container {
|
|
|
|
position: relative;
|
|
|
|
display: flex;
|
|
|
|
width: 400px;
|
|
|
|
align-items: stretch;
|
|
|
|
box-shadow: 0 14px 28px rgba(0,0,0,0.2);
|
|
|
|
border-radius: 6px;
|
|
|
|
animation: zoomIn .5s ease;
|
|
|
|
|
|
|
|
&::after {
|
|
|
|
position: absolute;
|
|
|
|
top: 1rem;
|
|
|
|
right: 1rem;
|
|
|
|
content: " ";
|
|
|
|
@include spinner(mc('blue', '500'),0.5s,16px);
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
&.is-expanded {
|
|
|
|
width: 650px;
|
|
|
|
|
|
|
|
.login-frame {
|
|
|
|
border-radius: 0 6px 6px 0;
|
|
|
|
border-left: none;
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
border-radius: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&.is-loading::after {
|
|
|
|
display: block;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
width: 100%;
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
|
|
&.is-expanded {
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&-providers {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
width: 250px;
|
|
|
|
|
|
|
|
border-right: none;
|
|
|
|
border-radius: 6px 0 0 6px;
|
|
|
|
z-index: 1;
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
width: 50px;
|
|
|
|
border-radius: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
button {
|
|
|
|
flex: 0 1 50px;
|
|
|
|
padding: 5px 15px;
|
|
|
|
border: none;
|
|
|
|
color: #FFF;
|
|
|
|
// background: linear-gradient(to right, rgba(mc('light-blue', '800'), .7), rgba(mc('light-blue', '800'), 1));
|
|
|
|
// border-top: 1px solid rgba(mc('light-blue', '900'), .5);
|
|
|
|
background: linear-gradient(to right, rgba(0,0,0, .5), rgba(0,0,0, .7));
|
|
|
|
border-top: 1px solid rgba(0,0,0, .2);
|
|
|
|
font-family: $core-font-standard;
|
|
|
|
font-weight: 600;
|
|
|
|
text-align: left;
|
|
|
|
min-height: 40px;
|
|
|
|
display: flex;
|
|
|
|
justify-content: flex-start;
|
|
|
|
align-items: center;
|
|
|
|
transition: all .4s ease;
|
|
|
|
|
|
|
|
&:focus {
|
|
|
|
outline: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
justify-content: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
background-color: rgba(0,0,0, .4);
|
|
|
|
}
|
|
|
|
|
|
|
|
&:first-child {
|
|
|
|
border-top: none;
|
|
|
|
|
|
|
|
&.is-active {
|
|
|
|
border-top: 1px solid rgba(255,255,255, .5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&.is-active {
|
|
|
|
background-image: linear-gradient(to right, rgba(255,255,255,1) 0%,rgba(255,255,255,.77) 100%);
|
|
|
|
color: mc('grey', '800');
|
|
|
|
cursor: default;
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
background-color: transparent;
|
|
|
|
}
|
|
|
|
|
|
|
|
svg path {
|
|
|
|
fill: mc('grey', '800');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i {
|
|
|
|
margin-right: 10px;
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
margin-right: 0;
|
|
|
|
font-size: 20px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
svg {
|
|
|
|
margin-right: 10px;
|
|
|
|
width: auto;
|
|
|
|
height: 20px;
|
|
|
|
max-width: 18px;
|
|
|
|
max-height: 20px;
|
|
|
|
|
|
|
|
path {
|
|
|
|
fill: #FFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
margin-right: 0;
|
|
|
|
font-size: 20px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
span {
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&-fill {
|
|
|
|
flex: 1 1 0;
|
|
|
|
background: linear-gradient(to right, rgba(mc('light-blue', '800'), .7), rgba(mc('light-blue', '800'), 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&-frame {
|
|
|
|
background-image: radial-gradient(circle at top center, rgba(255,255,255,1) 5%,rgba(255,255,255,.6) 100%);
|
|
|
|
border: 1px solid rgba(255,255,255, .5);
|
|
|
|
border-radius: 6px;
|
|
|
|
width: 400px;
|
|
|
|
padding: 1rem;
|
|
|
|
color: mc('grey', '700');
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
flex-direction: column;
|
|
|
|
text-align: center;
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
width: 100%;
|
|
|
|
border-radius: 0;
|
|
|
|
border: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
h1 {
|
|
|
|
font-size: 2rem;
|
|
|
|
font-weight: 600;
|
|
|
|
color: mc('light-blue', '700');
|
|
|
|
text-shadow: 1px 1px 0 #FFF;
|
|
|
|
padding: 0;
|
|
|
|
margin: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
h2 {
|
|
|
|
font-size: 1.5rem;
|
|
|
|
font-weight: 300;
|
|
|
|
color: mc('grey', '700');
|
|
|
|
text-shadow: 1px 1px 0 #FFF;
|
|
|
|
padding: 0;
|
|
|
|
margin: 0 0 25px 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
form {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
}
|
|
|
|
|
|
|
|
input[type=text], input[type=password] {
|
|
|
|
width: 100%;
|
|
|
|
border: 1px solid rgba(mc('blue-grey','500'), .5);
|
|
|
|
border-radius: 3px;
|
|
|
|
background-color: rgba(255,255,255,.9);
|
|
|
|
box-shadow: inset 0 0 0 3px rgba(255,255,255, .25);
|
|
|
|
padding: 0 15px;
|
|
|
|
height: 40px;
|
|
|
|
margin: 0 0 10px 0;
|
|
|
|
color: mc('grey', '700');
|
|
|
|
font-weight: 600;
|
|
|
|
font-size: .8rem;
|
|
|
|
transition: all 0.4s ease;
|
|
|
|
text-align: center;
|
|
|
|
|
|
|
|
&:focus {
|
|
|
|
outline: none;
|
|
|
|
border-color: mc('light-blue','500');
|
|
|
|
background-color: rgba(255,255,255,1);
|
|
|
|
box-shadow: inset 0 0 8px rgba(mc('light-blue','500'), .5);
|
|
|
|
color: mc('light-blue', '800');
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.button {
|
|
|
|
background-image: linear-gradient(to bottom, mc('blue', '400') 0%, mc('blue', '600') 50%, mc('blue', '700') 100%);
|
|
|
|
background-repeat: no-repeat;
|
|
|
|
background-size: 100% 200%;
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
background-position-y: 100%;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
&-tfa {
|
|
|
|
position: relative;
|
|
|
|
display: flex;
|
|
|
|
width: 400px;
|
|
|
|
align-items: stretch;
|
|
|
|
box-shadow: 0 14px 28px rgba(0,0,0,0.2);
|
|
|
|
border-radius: 6px;
|
|
|
|
animation: zoomIn .5s ease;
|
|
|
|
}
|
|
|
|
|
|
|
|
&-copyright {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
position: absolute;
|
|
|
|
left: 0;
|
|
|
|
bottom: 10vh;
|
|
|
|
width: 100%;
|
|
|
|
z-index: 2;
|
|
|
|
color: mc('grey', '500');
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
|
|
a {
|
|
|
|
font-weight: 600;
|
|
|
|
color: mc('blue', '500');
|
|
|
|
margin-left: .25rem;
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
color: mc('blue', '200');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@include until($tablet) {
|
|
|
|
color: mc('blue', '50');
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|