# frozen_string_literal: true require_dependency 'barong/jwt' module API::V2 module Identity class Sessions < Grape::API helpers do def get_user(email) user = User.find_by(email: email) error!({ errors: ['identity.session.invalid_params'] }, 401) unless user if user.state == 'banned' login_error!(reason: 'Your account is banned', error_code: 401, user: user.id, action: 'login', result: 'failed', error_text: 'banned') end if user.state == 'deleted' login_error!(reason: 'Your account is deleted', error_code: 401, user: user.id, action: 'login', result: 'failed', error_text: 'deleted') end # if user is not active or pending, then return 401 unless user.state.in?(%w[active pending]) login_error!(reason: 'Your account is not active', error_code: 401, user: user.id, action: 'login', result: 'failed', error_text: 'not_active') end user end end desc 'Session related routes' resource :sessions do desc 'Start a new session for every LogIn', failure: [ { code: 400, message: 'Required params are empty' }, { code: 404, message: 'Record is not found' } ] params do requires :email requires :password optional :captcha_response, types: { value: [String, Hash], message: 'identity.session.invalid_captcha_format' }, desc: 'Response from captcha widget' optional :otp_code, type: String, desc: 'Code from Google Authenticator' end post do verify_captcha!(response: params['captcha_response'], endpoint: 'session_create') declared_params = declared(params, include_missing: false) user = get_user(declared_params[:email]) error!({ errors: ['identity.session.not_active'] }, 401) unless user.state == 'active' unless user.authenticate(declared_params[:password]) publish_session_failed(user) login_error!(reason: 'Invalid Email or Password', error_code: 401, user: user.id, action: 'login', result: 'failed', error_text: 'invalid_params') end unless user.otp activity_record(user: user.id, action: 'login', result: 'succeed', topic: 'session') csrf_token = open_session(user) publish_session_create(user) present user, with: API::V2::Entities::UserWithFullInfo, csrf_token: csrf_token return status 200 end error!({ errors: ['identity.session.missing_otp'] }, 401) if declared_params[:otp_code].blank? unless TOTPService.validate?(user.uid, declared_params[:otp_code]) login_error!(reason: 'OTP code is invalid', error_code: 403, user: user.id, action: 'login::2fa', result: 'failed', error_text: 'invalid_otp') end activity_record(user: user.id, action: 'login::2fa', result: 'succeed', topic: 'session') csrf_token = open_session(user) publish_session_create(user) present user, with: API::V2::Entities::UserWithFullInfo, csrf_token: csrf_token status(200) end desc 'Destroy current session for LogOut', failure: [ { code: 400, message: 'Required params are empty' }, { code: 404, message: 'Record is not found' } ], success: { code: 200, message: 'Session was destroyed' } delete do user = User.find_by(uid: session[:uid]) error!({ errors: ['identity.session.not_found'] }, 404) unless user activity_record(user: user.id, action: 'logout', result: 'succeed', topic: 'session') session.destroy status(200) end desc 'Auth0 authentication by id_token', success: { code: 200, message: 'User authenticated' }, failure: [ { code: 400, message: 'Required params are empty' }, { code: 404, message: 'Record is not found' } ] params do requires :id_token, type: String, allow_blank: false, desc: 'ID Token' end post '/auth0' do begin # Decode ID token to get user info claims = Barong::Auth0::JWT.verify(params[:id_token]).first error!({ errors: ['identity.session.auth0.invalid_params'] }, 401) unless claims.key?('email') user = User.find_by(email: claims['email']) # If there is no user in platform and user email verified from id_token # system will create user if user.blank? && claims['email_verified'] user = User.create!(email: claims['email'], state: 'active') user.labels.create!(scope: 'private', key: 'email', value: 'verified') elsif claims['email_verified'] == false error!({ errors: ['identity.session.auth0.invalid_params'] }, 401) unless user end activity_record(user: user.id, action: 'login', result: 'succeed', topic: 'session') csrf_token = open_session(user) publish_session_create(user) present user, with: API::V2::Entities::UserWithFullInfo, csrf_token: csrf_token rescue StandardError => e report_exception(e) error!({ errors: ['identity.session.auth0.invalid_params'] }, 422) end end desc 'Resend confirmations code(authorization code)', success: { code: 201, message: 'Generated verification code' }, failure: [ { code: 400, message: 'Required params are missing' }, { code: 422, message: 'Validation errors' } ] params do requires :data, type: String, allow_blank: false, desc: 'Account email or Telephone number' requires :action, type: String, allow_blank: false, desc: 'for what need auth code' optional :channel, type: String, allow_blank: true, default: 'email', desc: 'channel that send in' optional :captcha_response, types: [String, Hash], desc: 'Response from captcha widget' end post '/resend' do current_user = User.find_by_email(params[:data]) return status 201 if current_user.nil? publish_confirmation_code(current_user, Barong::App.config.domain, params[:action]) status 201 end end end end end