You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Login.vue 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <!--
  2. - @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
  3. -
  4. - @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
  5. -
  6. - @license GNU AGPL version 3 or any later version
  7. -
  8. - This program is free software: you can redistribute it and/or modify
  9. - it under the terms of the GNU Affero General Public License as
  10. - published by the Free Software Foundation, either version 3 of the
  11. - License, or (at your option) any later version.
  12. -
  13. - This program is distributed in the hope that it will be useful,
  14. - but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. - GNU Affero General Public License for more details.
  17. -
  18. - You should have received a copy of the GNU Affero General Public License
  19. - along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. -->
  21. <template>
  22. <div v-if="!hideLoginForm || directLogin">
  23. <transition name="fade" mode="out-in">
  24. <div v-if="!passwordlessLogin && !resetPassword && resetPasswordTarget === ''"
  25. key="login">
  26. <LoginForm
  27. :username.sync="user"
  28. :redirect-url="redirectUrl"
  29. :direct-login="directLogin"
  30. :messages="messages"
  31. :errors="errors"
  32. :throttle-delay="throttleDelay"
  33. :inverted-colors="invertedColors"
  34. :auto-complete-allowed="autoCompleteAllowed"
  35. @submit="loading = true" />
  36. <a v-if="canResetPassword && resetPasswordLink !== ''"
  37. id="lost-password"
  38. :href="resetPasswordLink">
  39. {{ t('core', 'Forgot password?') }}
  40. </a>
  41. <a v-else-if="canResetPassword && !resetPassword"
  42. id="lost-password"
  43. :href="resetPasswordLink"
  44. @click.prevent="resetPassword = true">
  45. {{ t('core', 'Forgot password?') }}
  46. </a>
  47. <br>
  48. <template v-if="hasPasswordless">
  49. <div v-if="countAlternativeLogins"
  50. class="alternative-logins">
  51. <a v-if="hasPasswordless"
  52. class="button"
  53. :class="{ 'single-alt-login-option': countAlternativeLogins }"
  54. href="#"
  55. @click.prevent="passwordlessLogin = true">
  56. {{ t('core', 'Log in with a device') }}
  57. </a>
  58. </div>
  59. <a v-else
  60. href="#"
  61. @click.prevent="passwordlessLogin = true">
  62. {{ t('core', 'Log in with a device') }}
  63. </a>
  64. </template>
  65. </div>
  66. <div v-else-if="!loading && passwordlessLogin"
  67. key="reset"
  68. class="login-additional">
  69. <PasswordLessLoginForm
  70. :username.sync="user"
  71. :redirect-url="redirectUrl"
  72. :inverted-colors="invertedColors"
  73. :auto-complete-allowed="autoCompleteAllowed"
  74. :is-https="isHttps"
  75. :is-localhost="isLocalhost"
  76. :has-public-key-credential="hasPublicKeyCredential"
  77. @submit="loading = true" />
  78. <a href="#" @click.prevent="passwordlessLogin = false">
  79. {{ t('core', 'Back') }}
  80. </a>
  81. </div>
  82. <div v-else-if="!loading && canResetPassword"
  83. key="reset"
  84. class="login-additional">
  85. <div class="lost-password-container">
  86. <ResetPassword v-if="resetPassword"
  87. :username.sync="user"
  88. :reset-password-link="resetPasswordLink"
  89. :inverted-colors="invertedColors"
  90. @abort="resetPassword = false" />
  91. </div>
  92. </div>
  93. <div v-else-if="resetPasswordTarget !== ''">
  94. <UpdatePassword :username.sync="user"
  95. :reset-password-target="resetPasswordTarget"
  96. :inverted-colors="invertedColors"
  97. @done="passwordResetFinished" />
  98. </div>
  99. </transition>
  100. </div>
  101. <div v-else>
  102. <transition name="fade" mode="out-in">
  103. <div class="warning">
  104. {{ t('core', 'Login form is disabled.') }}<br>
  105. <small>{{ t('core', 'Please contact your administrator.') }}
  106. </small>
  107. </div>
  108. </transition>
  109. </div>
  110. </template>
  111. <script>
  112. import { loadState } from '@nextcloud/initial-state'
  113. import queryString from 'query-string'
  114. import LoginForm from '../components/login/LoginForm.vue'
  115. import PasswordLessLoginForm from '../components/login/PasswordLessLoginForm.vue'
  116. import ResetPassword from '../components/login/ResetPassword.vue'
  117. import UpdatePassword from '../components/login/UpdatePassword.vue'
  118. const query = queryString.parse(location.search)
  119. if (query.clear === '1') {
  120. try {
  121. window.localStorage.clear()
  122. window.sessionStorage.clear()
  123. console.debug('Browser storage cleared')
  124. } catch (e) {
  125. console.error('Could not clear browser storage', e)
  126. }
  127. }
  128. export default {
  129. name: 'Login',
  130. components: {
  131. LoginForm,
  132. PasswordLessLoginForm,
  133. ResetPassword,
  134. UpdatePassword,
  135. },
  136. data() {
  137. return {
  138. loading: false,
  139. user: this.username,
  140. passwordlessLogin: false,
  141. resetPassword: false,
  142. // Initial data
  143. errors: loadState('core', 'loginErrors', []),
  144. messages: loadState('core', 'loginMessages', []),
  145. redirectUrl: loadState('core', 'loginRedirectUrl', undefined),
  146. username: loadState('core', 'loginUsername', ''),
  147. throttleDelay: loadState('core', 'loginThrottleDelay', 0),
  148. invertedColors: OCA.Theming && OCA.Theming.inverted,
  149. canResetPassword: loadState('core', 'loginCanResetPassword', false),
  150. resetPasswordLink: loadState('core', 'loginResetPasswordLink', ''),
  151. autoCompleteAllowed: loadState('core', 'loginAutocomplete', true),
  152. resetPasswordTarget: loadState('core', 'resetPasswordTarget', ''),
  153. resetPasswordUser: loadState('core', 'resetPasswordUser', ''),
  154. directLogin: query.direct === '1',
  155. hasPasswordless: loadState('core', 'webauthn-available', false),
  156. countAlternativeLogins: loadState('core', 'countAlternativeLogins', false),
  157. isHttps: window.location.protocol === 'https:',
  158. isLocalhost: window.location.hostname === 'localhost',
  159. hasPublicKeyCredential: typeof (window.PublicKeyCredential) !== 'undefined',
  160. hideLoginForm: loadState('core', 'hideLoginForm', false),
  161. }
  162. },
  163. methods: {
  164. passwordResetFinished() {
  165. this.resetPasswordTarget = ''
  166. this.directLogin = true
  167. },
  168. },
  169. }
  170. </script>
  171. <style>
  172. .fade-enter-active, .fade-leave-active {
  173. transition: opacity .3s;
  174. }
  175. .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  176. opacity: 0;
  177. }
  178. </style>