login_spec.rb 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. # Login and logout work only via controller, so use type: request.
  4. RSpec.describe Gql::Mutations::Login, :aggregate_failures, type: :request do
  5. before do
  6. stub_const('Auth::BRUTE_FORCE_SLEEP', 0)
  7. end
  8. context 'when logging on' do
  9. let(:agent_password) { 'some_test_password' }
  10. let(:agent) { create(:agent, password: agent_password) }
  11. let(:query) do
  12. <<~QUERY
  13. mutation login($input: LoginInput!) {
  14. login(input: $input) {
  15. session {
  16. id
  17. afterAuth {
  18. type
  19. data
  20. }
  21. }
  22. twoFactorRequired {
  23. defaultTwoFactorAuthenticationMethod
  24. availableTwoFactorAuthenticationMethods
  25. recoveryCodesAvailable
  26. }
  27. errors {
  28. message
  29. field
  30. }
  31. }
  32. }
  33. QUERY
  34. end
  35. let(:password) { agent_password }
  36. let(:fingerprint) { Faker::Number.unique.number(digits: 6).to_s }
  37. let(:two_factor_authentication) { nil }
  38. let(:two_factor_recovery) { nil }
  39. let(:variables) do
  40. {
  41. input: {
  42. login: agent.login,
  43. password: password,
  44. twoFactorAuthentication: two_factor_authentication,
  45. twoFactorRecovery: two_factor_recovery
  46. }
  47. }
  48. end
  49. let(:headers) do
  50. {
  51. 'X-Browser-Fingerprint' => fingerprint,
  52. }
  53. end
  54. let(:graphql_response) do
  55. execute_graphql_query
  56. json_response
  57. end
  58. def execute_graphql_query
  59. post '/graphql', params: { query: query, variables: variables }, headers: headers, as: :json
  60. end
  61. context 'with correct credentials' do
  62. context 'without two factor authentication' do
  63. it 'returns session data' do
  64. expect(graphql_response['data']['login']['session']['id']).to be_present
  65. end
  66. it 'sets the :persistent session parameter' do
  67. expect { execute_graphql_query }.to change { request&.session&.fetch(:persistent) }.to(true)
  68. end
  69. it 'adds an activity stream entry for the user’s session' do
  70. # Create the user before the GraphQL query execution, so that we have only the activity stream
  71. # change from the login.
  72. agent
  73. expect { execute_graphql_query }.to change(ActivityStream, :count).by(1)
  74. end
  75. context 'with remember me' do
  76. let(:remember_me) { true }
  77. let(:variables) do
  78. {
  79. input: {
  80. login: agent.login,
  81. password: password,
  82. rememberMe: remember_me,
  83. }
  84. }
  85. end
  86. it 'adds an activity stream entry for the user’s session' do
  87. execute_graphql_query
  88. expect(request.env['rack.session.options'][:expire_after]).to eq(1.year)
  89. end
  90. context 'with not activated remember me' do
  91. let(:remember_me) { false }
  92. it 'adds an activity stream entry for the user’s session' do
  93. execute_graphql_query
  94. expect(request.env['rack.session.options'][:expire_after]).to be_nil
  95. end
  96. end
  97. end
  98. end
  99. context 'with after_auth information' do
  100. before do
  101. allow_any_instance_of(Auth::AfterAuth::TwoFactorConfiguration).to receive(:check).and_return(true)
  102. end
  103. it 'returns after_auth data' do
  104. expect(graphql_response['data']['login']['session']['afterAuth']).to eq({ 'type' => 'TwoFactorConfiguration', 'data' => {} })
  105. end
  106. end
  107. context 'with two factor authentication' do
  108. let!(:two_factor_pref) { create(:user_two_factor_preference, :authenticator_app, user: agent) }
  109. let(:enabled) { true }
  110. before do
  111. Setting.set('two_factor_authentication_method_authenticator_app', enabled)
  112. end
  113. context 'without token' do
  114. it 'returns two factor availability data' do
  115. expect(graphql_response['data']['login']['twoFactorRequired']).to eq(
  116. {
  117. 'defaultTwoFactorAuthenticationMethod' => 'authenticator_app',
  118. 'availableTwoFactorAuthenticationMethods' => ['authenticator_app'],
  119. 'recoveryCodesAvailable' => false
  120. }
  121. )
  122. expect(graphql_response['data']['login']['session']).not_to be_present
  123. end
  124. end
  125. context 'with wrong token' do
  126. let(:two_factor_authentication) do
  127. {
  128. twoFactorMethod: :authenticator_app,
  129. twoFactorPayload: 'wrong'
  130. }
  131. end
  132. it 'fails with error message' do
  133. expect(graphql_response['data']['login']['errors']).to eq(
  134. [{
  135. 'message' => 'Login failed. Please double-check your two-factor authentication method.',
  136. 'field' => nil
  137. }]
  138. )
  139. end
  140. context 'when default method is disabled, but another exists' do
  141. let(:enabled) { true }
  142. before do
  143. two_factor_pref
  144. create(:user_two_factor_preference, :security_keys, user: agent)
  145. agent.reload
  146. agent.preferences[:two_factor_authentication][:default] = 'security_keys'
  147. agent.save!
  148. end
  149. it 'fails with error message' do
  150. expect(graphql_response['data']['login']['errors']).to eq(
  151. [{
  152. 'message' => 'Login failed. Please double-check your two-factor authentication method.',
  153. 'field' => nil
  154. }]
  155. )
  156. end
  157. end
  158. context 'with disabled authenticator method' do
  159. let(:enabled) { false }
  160. it 'returns session data' do
  161. expect(graphql_response['data']['login']['session']['id']).to be_present
  162. end
  163. end
  164. end
  165. context 'with correct token' do
  166. let(:two_factor_authentication) do
  167. {
  168. twoFactorMethod: :authenticator_app,
  169. twoFactorPayload: two_factor_pref.configuration[:code]
  170. }
  171. end
  172. it 'returns session data' do
  173. expect(graphql_response['data']['login']['session']['id']).to be_present
  174. end
  175. context 'with disabled authenticator method' do
  176. let(:enabled) { false }
  177. it 'returns session data' do
  178. expect(graphql_response['data']['login']['session']['id']).to be_present
  179. end
  180. end
  181. end
  182. end
  183. end
  184. context 'without CSRF token', allow_forgery_protection: true do
  185. it 'fails with error message' do
  186. expect(graphql_response['errors'][0]).to include('message' => 'CSRF token verification failed!')
  187. end
  188. it 'fails with error type' do
  189. expect(graphql_response['errors'][0]['extensions']).to include({ 'type' => 'Exceptions::NotAuthorized' })
  190. end
  191. end
  192. context 'with wrong password' do
  193. let(:password) { 'wrong' }
  194. it 'fails with error message' do
  195. expect(graphql_response['data']['login']['errors']).to eq(
  196. [{
  197. 'message' => 'Login failed. Have you double-checked your credentials and completed the email verification step?',
  198. 'field' => nil
  199. }]
  200. )
  201. end
  202. end
  203. context 'without fingerprint' do
  204. let(:fingerprint) { nil }
  205. it 'fails with error message' do
  206. expect(graphql_response['errors'][0]).to include('message' => 'Need fingerprint param!')
  207. end
  208. # No error type available for GraphQL::ExecutionErrors.
  209. end
  210. context 'with fingerprint' do
  211. let(:fingerprint) { 'my_finger_print' }
  212. it 'exists in controller session' do
  213. execute_graphql_query
  214. expect(controller.session[:user_device_fingerprint]).to eq('my_finger_print')
  215. end
  216. it 'added user device log entry', :performs_jobs do
  217. perform_enqueued_jobs only: UserDeviceLogJob do
  218. execute_graphql_query
  219. end
  220. expect(UserDevice.where(user_id: agent.id).count).to eq(1)
  221. end
  222. end
  223. end
  224. end