authentication.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. import { useLocalStorage } from '@vueuse/core'
  3. import { defineStore } from 'pinia'
  4. import { ref } from 'vue'
  5. import useFingerprint from '#shared/composables/useFingerprint.ts'
  6. import { useLoginMutation } from '#shared/graphql/mutations/login.api.ts'
  7. import { useLogoutMutation } from '#shared/graphql/mutations/logout.api.ts'
  8. import {
  9. type EnumTwoFactorAuthenticationMethod,
  10. type LoginInput,
  11. } from '#shared/graphql/types.ts'
  12. import { clearApolloClientStore } from '#shared/server/apollo/client.ts'
  13. import { MutationHandler } from '#shared/server/apollo/handler/index.ts'
  14. import testFlags from '#shared/utils/testFlags.ts'
  15. import { useApplicationStore } from './application.ts'
  16. import { resetAndDisposeStores } from './index.ts'
  17. import { useSessionStore } from './session.ts'
  18. interface LoginOptions {
  19. login: string
  20. password: string
  21. rememberMe: boolean
  22. twoFactorAuthentication?: {
  23. method: EnumTwoFactorAuthenticationMethod
  24. payload: unknown
  25. }
  26. recoveryCode?: string
  27. }
  28. export const useAuthenticationStore = defineStore(
  29. 'authentication',
  30. () => {
  31. const authenticated = useLocalStorage<boolean>('authenticated', false)
  32. const externalLogout = ref(false)
  33. const { fingerprint } = useFingerprint()
  34. const clearAuthentication = async (): Promise<void> => {
  35. await clearApolloClientStore()
  36. const session = useSessionStore()
  37. session.resetCurrentSession()
  38. authenticated.value = false
  39. resetAndDisposeStores(true)
  40. // Refresh the config after logout, to have only the non authenticated version.
  41. await useApplicationStore().resetAndGetConfig()
  42. session.initialized = false
  43. }
  44. const refreshAfterAuthentication = async (): Promise<void> => {
  45. await Promise.all([
  46. useApplicationStore().getConfig(),
  47. useSessionStore().getCurrentUser(),
  48. ])
  49. }
  50. const logout = async (): Promise<void> => {
  51. const logoutMutation = new MutationHandler(
  52. useLogoutMutation({
  53. context: {
  54. batch: {
  55. active: false,
  56. },
  57. },
  58. }),
  59. )
  60. const result = await logoutMutation.send()
  61. if (result?.logout?.success) {
  62. if (result.logout.externalLogoutUrl) {
  63. externalLogout.value = true
  64. authenticated.value = false
  65. window.location.href = result.logout.externalLogoutUrl
  66. return
  67. }
  68. await clearAuthentication()
  69. testFlags.set('logout.success')
  70. }
  71. }
  72. const setAuthenticatedSessionId = async (newSessionId: string | null) => {
  73. if (!newSessionId) return false
  74. const session = useSessionStore()
  75. session.id = newSessionId
  76. authenticated.value = true
  77. await refreshAfterAuthentication()
  78. session.initialized = true
  79. return true
  80. }
  81. const login = async ({
  82. login,
  83. password,
  84. rememberMe,
  85. twoFactorAuthentication,
  86. recoveryCode,
  87. }: LoginOptions) => {
  88. const loginInput: LoginInput = {
  89. login,
  90. password,
  91. rememberMe,
  92. }
  93. if (twoFactorAuthentication) {
  94. loginInput.twoFactorAuthentication = {
  95. twoFactorMethod: twoFactorAuthentication.method,
  96. twoFactorPayload: twoFactorAuthentication.payload,
  97. }
  98. } else if (recoveryCode) {
  99. loginInput.twoFactorRecovery = {
  100. recoveryCode,
  101. }
  102. }
  103. const loginMutation = new MutationHandler(
  104. useLoginMutation({
  105. variables: {
  106. input: loginInput,
  107. },
  108. context: {
  109. headers: {
  110. 'X-Browser-Fingerprint': fingerprint.value,
  111. },
  112. batch: {
  113. active: false,
  114. },
  115. },
  116. }),
  117. )
  118. const result = await loginMutation.send()
  119. if (result?.login?.errors || !result) {
  120. return Promise.reject(result?.login?.errors)
  121. }
  122. await setAuthenticatedSessionId(result.login?.session?.id || null)
  123. return {
  124. twoFactor: result.login?.twoFactorRequired,
  125. afterAuth: result.login?.session?.afterAuth,
  126. }
  127. }
  128. return {
  129. authenticated,
  130. externalLogout,
  131. clearAuthentication,
  132. logout,
  133. login,
  134. refreshAfterAuthentication,
  135. setAuthenticatedSessionId,
  136. }
  137. },
  138. {
  139. requiresAuth: false,
  140. },
  141. )