Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a27c3fcbf | ||
|
|
3197fafad1 | ||
|
|
c5cc972a74 | ||
|
|
d0a7de0b00 | ||
|
|
a7aae69229 | ||
|
|
12957b7438 | ||
|
|
363169667d | ||
|
|
236b7d4541 | ||
|
|
6d101f5ba3 | ||
|
|
c88fb147f8 | ||
|
|
ebdca08da7 | ||
|
|
8150604a54 |
@@ -1,32 +1,23 @@
|
|||||||
class Api::V1::Accounts::Google::AuthorizationsController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::Google::AuthorizationsController < Api::V1::Accounts::OauthAuthorizationController
|
||||||
include GoogleConcern
|
include GoogleConcern
|
||||||
before_action :check_authorization
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
email = params[:authorization][:email]
|
|
||||||
redirect_url = google_client.auth_code.authorize_url(
|
redirect_url = google_client.auth_code.authorize_url(
|
||||||
{
|
{
|
||||||
redirect_uri: "#{base_url}/google/callback",
|
redirect_uri: "#{base_url}/google/callback",
|
||||||
scope: 'email profile https://mail.google.com/',
|
scope: scope,
|
||||||
response_type: 'code',
|
response_type: 'code',
|
||||||
prompt: 'consent', # the oauth flow does not return a refresh token, this is supposed to fix it
|
prompt: 'consent', # the oauth flow does not return a refresh token, this is supposed to fix it
|
||||||
access_type: 'offline', # the default is 'online'
|
access_type: 'offline', # the default is 'online'
|
||||||
|
state: state,
|
||||||
client_id: GlobalConfigService.load('GOOGLE_OAUTH_CLIENT_ID', nil)
|
client_id: GlobalConfigService.load('GOOGLE_OAUTH_CLIENT_ID', nil)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if redirect_url
|
if redirect_url
|
||||||
cache_key = "google::#{email.downcase}"
|
|
||||||
::Redis::Alfred.setex(cache_key, Current.account.id, 5.minutes)
|
|
||||||
render json: { success: true, url: redirect_url }
|
render json: { success: true, url: redirect_url }
|
||||||
else
|
else
|
||||||
render json: { success: false }, status: :unprocessable_entity
|
render json: { success: false }, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def check_authorization
|
|
||||||
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
class Api::V1::Accounts::Instagram::AuthorizationsController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::Instagram::AuthorizationsController < Api::V1::Accounts::OauthAuthorizationController
|
||||||
include InstagramConcern
|
include InstagramConcern
|
||||||
include Instagram::IntegrationHelper
|
include Instagram::IntegrationHelper
|
||||||
before_action :check_authorization
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
# https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login#step-1--get-authorization
|
# https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login#step-1--get-authorization
|
||||||
@@ -21,10 +20,4 @@ class Api::V1::Accounts::Instagram::AuthorizationsController < Api::V1::Accounts
|
|||||||
render json: { success: false }, status: :unprocessable_entity
|
render json: { success: false }, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def check_authorization
|
|
||||||
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,28 +1,19 @@
|
|||||||
class Api::V1::Accounts::Microsoft::AuthorizationsController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::Microsoft::AuthorizationsController < Api::V1::Accounts::OauthAuthorizationController
|
||||||
include MicrosoftConcern
|
include MicrosoftConcern
|
||||||
before_action :check_authorization
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
email = params[:authorization][:email]
|
|
||||||
redirect_url = microsoft_client.auth_code.authorize_url(
|
redirect_url = microsoft_client.auth_code.authorize_url(
|
||||||
{
|
{
|
||||||
redirect_uri: "#{base_url}/microsoft/callback",
|
redirect_uri: "#{base_url}/microsoft/callback",
|
||||||
scope: 'offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send openid profile',
|
scope: scope,
|
||||||
|
state: state,
|
||||||
prompt: 'consent'
|
prompt: 'consent'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if redirect_url
|
if redirect_url
|
||||||
cache_key = "microsoft::#{email.downcase}"
|
|
||||||
::Redis::Alfred.setex(cache_key, Current.account.id, 5.minutes)
|
|
||||||
render json: { success: true, url: redirect_url }
|
render json: { success: true, url: redirect_url }
|
||||||
else
|
else
|
||||||
render json: { success: false }, status: :unprocessable_entity
|
render json: { success: false }, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def check_authorization
|
|
||||||
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
class Api::V1::Accounts::OauthAuthorizationController < Api::V1::Accounts::BaseController
|
||||||
|
before_action :check_authorization
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def scope
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
|
def state
|
||||||
|
Current.account.to_sgid(expires_in: 15.minutes).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_url
|
||||||
|
ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_authorization
|
||||||
|
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -14,7 +14,7 @@ module GoogleConcern
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def base_url
|
def scope
|
||||||
ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
|
'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://mail.google.com/'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ module MicrosoftConcern
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def base_url
|
def scope
|
||||||
ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
|
'offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send openid profile'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,6 +12,16 @@ class Google::CallbacksController < OauthCallbackController
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def verify_scopes
|
||||||
|
granted_scopes = parsed_body['scope']&.split || []
|
||||||
|
required_scopes = scope.split
|
||||||
|
|
||||||
|
missing_scopes = required_scopes - granted_scopes
|
||||||
|
return if missing_scopes.empty?
|
||||||
|
|
||||||
|
raise StandardError, I18n.t('errors.oauth.insufficient_scopes', scopes: missing_scopes.join(', '))
|
||||||
|
end
|
||||||
|
|
||||||
def provider_name
|
def provider_name
|
||||||
'google'
|
'google'
|
||||||
end
|
end
|
||||||
@@ -24,4 +34,13 @@ class Google::CallbacksController < OauthCallbackController
|
|||||||
# from GoogleConcern
|
# from GoogleConcern
|
||||||
google_client
|
google_client
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_error(exception)
|
||||||
|
ChatwootExceptionTracker.new(exception).capture_exception
|
||||||
|
|
||||||
|
error_message = exception.message || I18n.t('errors.oauth.authorization_failed')
|
||||||
|
redirect_url = "#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/app/accounts/#{account.id}/settings/inboxes/new/email"
|
||||||
|
|
||||||
|
redirect_to "#{redirect_url}?error=#{CGI.escape(error_message)}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ class OauthCallbackController < ApplicationController
|
|||||||
redirect_uri: "#{base_url}/#{provider_name}/callback"
|
redirect_uri: "#{base_url}/#{provider_name}/callback"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
verify_scopes
|
||||||
handle_response
|
handle_response
|
||||||
::Redis::Alfred.delete(cache_key)
|
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
ChatwootExceptionTracker.new(e).capture_exception
|
handle_error(e)
|
||||||
redirect_to '/'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -64,8 +63,17 @@ class OauthCallbackController < ApplicationController
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_key
|
def verify_scopes
|
||||||
"#{provider_name}::#{users_data['email'].downcase}"
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def failure_redirect_url
|
||||||
|
'/'
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_error(exception)
|
||||||
|
ChatwootExceptionTracker.new(exception).capture_exception
|
||||||
|
redirect_to failure_redirect_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_channel_with_inbox
|
def create_channel_with_inbox
|
||||||
@@ -85,12 +93,14 @@ class OauthCallbackController < ApplicationController
|
|||||||
decoded_token[0]
|
decoded_token[0]
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_id
|
def account_from_signed_id
|
||||||
::Redis::Alfred.get(cache_key)
|
raise 'Missing state variable' if params[:state].blank?
|
||||||
|
|
||||||
|
GlobalID::Locator.locate_signed(params[:state])
|
||||||
end
|
end
|
||||||
|
|
||||||
def account
|
def account
|
||||||
@account ||= Account.find(account_id)
|
@account ||= account_from_signed_id
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fallback name, for when name field is missing from users_data
|
# Fallback name, for when name field is missing from users_data
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import ForwardToOption from './emailChannels/ForwardToOption.vue';
|
import ForwardToOption from './emailChannels/ForwardToOption.vue';
|
||||||
import Microsoft from './emailChannels/Microsoft.vue';
|
import Microsoft from './emailChannels/Microsoft.vue';
|
||||||
import Google from './emailChannels/Google.vue';
|
import Google from './emailChannels/Google.vue';
|
||||||
import ChannelSelector from 'dashboard/components/ChannelSelector.vue';
|
import ChannelSelector from 'dashboard/components/ChannelSelector.vue';
|
||||||
import PageHeader from '../../SettingsSubPageHeader.vue';
|
import PageHeader from '../../SettingsSubPageHeader.vue';
|
||||||
|
|
||||||
|
import { useAlert } from 'dashboard/composables';
|
||||||
import { useStoreGetters } from 'dashboard/composables/store';
|
import { useStoreGetters } from 'dashboard/composables/store';
|
||||||
|
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const provider = ref('');
|
const provider = ref('');
|
||||||
|
|
||||||
const getters = useStoreGetters();
|
const getters = useStoreGetters();
|
||||||
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const globalConfig = getters['globalConfig/get'];
|
const globalConfig = getters['globalConfig/get'];
|
||||||
@@ -45,6 +49,15 @@ const emailProviderList = computed(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const checkForOAuthError = () => {
|
||||||
|
const { error } = route.query;
|
||||||
|
if (error) {
|
||||||
|
useAlert(decodeURIComponent(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(checkForOAuthError);
|
||||||
|
|
||||||
function onClick(emailProvider) {
|
function onClick(emailProvider) {
|
||||||
if (emailProvider.isEnabled) {
|
if (emailProvider.isEnabled) {
|
||||||
provider.value = emailProvider.key;
|
provider.value = emailProvider.key;
|
||||||
|
|||||||
-1
@@ -12,7 +12,6 @@ defineOptions({
|
|||||||
provider="google"
|
provider="google"
|
||||||
:title="$t('INBOX_MGMT.ADD.GOOGLE.TITLE')"
|
:title="$t('INBOX_MGMT.ADD.GOOGLE.TITLE')"
|
||||||
:description="$t('INBOX_MGMT.ADD.GOOGLE.DESCRIPTION')"
|
:description="$t('INBOX_MGMT.ADD.GOOGLE.DESCRIPTION')"
|
||||||
:input-placeholder="$t('INBOX_MGMT.ADD.GOOGLE.EMAIL_PLACEHOLDER')"
|
|
||||||
:submit-button-text="$t('INBOX_MGMT.ADD.GOOGLE.SIGN_IN')"
|
:submit-button-text="$t('INBOX_MGMT.ADD.GOOGLE.SIGN_IN')"
|
||||||
:error-message="$t('INBOX_MGMT.ADD.GOOGLE.ERROR_MESSAGE')"
|
:error-message="$t('INBOX_MGMT.ADD.GOOGLE.ERROR_MESSAGE')"
|
||||||
/>
|
/>
|
||||||
|
|||||||
-1
@@ -12,7 +12,6 @@ defineOptions({
|
|||||||
provider="microsoft"
|
provider="microsoft"
|
||||||
:title="$t('INBOX_MGMT.ADD.MICROSOFT.TITLE')"
|
:title="$t('INBOX_MGMT.ADD.MICROSOFT.TITLE')"
|
||||||
:description="$t('INBOX_MGMT.ADD.MICROSOFT.DESCRIPTION')"
|
:description="$t('INBOX_MGMT.ADD.MICROSOFT.DESCRIPTION')"
|
||||||
:input-placeholder="$t('INBOX_MGMT.ADD.MICROSOFT.EMAIL_PLACEHOLDER')"
|
|
||||||
:submit-button-text="$t('INBOX_MGMT.ADD.MICROSOFT.SIGN_IN')"
|
:submit-button-text="$t('INBOX_MGMT.ADD.MICROSOFT.SIGN_IN')"
|
||||||
:error-message="$t('INBOX_MGMT.ADD.MICROSOFT.ERROR_MESSAGE')"
|
:error-message="$t('INBOX_MGMT.ADD.MICROSOFT.ERROR_MESSAGE')"
|
||||||
/>
|
/>
|
||||||
|
|||||||
+1
-13
@@ -30,14 +30,9 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
inputPlaceholder: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const isRequestingAuthorization = ref(false);
|
const isRequestingAuthorization = ref(false);
|
||||||
const email = ref('');
|
|
||||||
|
|
||||||
const client = computed(() => {
|
const client = computed(() => {
|
||||||
if (props.provider === 'microsoft') {
|
if (props.provider === 'microsoft') {
|
||||||
@@ -50,9 +45,7 @@ const client = computed(() => {
|
|||||||
async function requestAuthorization() {
|
async function requestAuthorization() {
|
||||||
try {
|
try {
|
||||||
isRequestingAuthorization.value = true;
|
isRequestingAuthorization.value = true;
|
||||||
const response = await client.value.generateAuthorization({
|
const response = await client.value.generateAuthorization();
|
||||||
email: email.value,
|
|
||||||
});
|
|
||||||
const {
|
const {
|
||||||
data: { url },
|
data: { url },
|
||||||
} = response;
|
} = response;
|
||||||
@@ -75,11 +68,6 @@ async function requestAuthorization() {
|
|||||||
:header-content="description"
|
:header-content="description"
|
||||||
/>
|
/>
|
||||||
<form class="mt-6" @submit.prevent="requestAuthorization">
|
<form class="mt-6" @submit.prevent="requestAuthorization">
|
||||||
<woot-input
|
|
||||||
v-model="email"
|
|
||||||
type="email"
|
|
||||||
:placeholder="inputPlaceholder"
|
|
||||||
/>
|
|
||||||
<NextButton
|
<NextButton
|
||||||
:is-loading="isRequestingAuthorization"
|
:is-loading="isRequestingAuthorization"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
+2
-18
@@ -1,35 +1,19 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref } from 'vue';
|
||||||
import InboxReconnectionRequired from '../../components/InboxReconnectionRequired.vue';
|
import InboxReconnectionRequired from '../../components/InboxReconnectionRequired.vue';
|
||||||
import googleClient from 'dashboard/api/channel/googleClient';
|
import googleClient from 'dashboard/api/channel/googleClient';
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
inbox: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const isRequestingAuthorization = ref(false);
|
const isRequestingAuthorization = ref(false);
|
||||||
|
|
||||||
const inboxEmail = computed(() => {
|
|
||||||
if (props.inbox.imap_login && props.inbox.imap_enabled) {
|
|
||||||
return props.inbox.imap_login;
|
|
||||||
}
|
|
||||||
return props.inbox.email;
|
|
||||||
});
|
|
||||||
|
|
||||||
async function requestAuthorization() {
|
async function requestAuthorization() {
|
||||||
try {
|
try {
|
||||||
isRequestingAuthorization.value = true;
|
isRequestingAuthorization.value = true;
|
||||||
const response = await googleClient.generateAuthorization({
|
const response = await googleClient.generateAuthorization();
|
||||||
email: inboxEmail.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { url },
|
data: { url },
|
||||||
|
|||||||
+1
-10
@@ -6,13 +6,6 @@ import microsoftClient from 'dashboard/api/channel/microsoftClient';
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
inbox: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const isRequestingAuthorization = ref(false);
|
const isRequestingAuthorization = ref(false);
|
||||||
@@ -20,9 +13,7 @@ const isRequestingAuthorization = ref(false);
|
|||||||
async function requestAuthorization() {
|
async function requestAuthorization() {
|
||||||
try {
|
try {
|
||||||
isRequestingAuthorization.value = true;
|
isRequestingAuthorization.value = true;
|
||||||
const response = await microsoftClient.generateAuthorization({
|
const response = await microsoftClient.generateAuthorization();
|
||||||
email: props.inbox.email,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { url },
|
data: { url },
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ en:
|
|||||||
email_already_exists: 'You have already signed up for an account with %{email}'
|
email_already_exists: 'You have already signed up for an account with %{email}'
|
||||||
invalid_params: 'Invalid, please check the signup paramters and try again'
|
invalid_params: 'Invalid, please check the signup paramters and try again'
|
||||||
failed: Signup failed
|
failed: Signup failed
|
||||||
|
oauth:
|
||||||
|
insufficient_scopes: 'Insufficient permissions granted. Missing scopes: %{scopes}'
|
||||||
|
authorization_failed: 'Authorization failed'
|
||||||
data_import:
|
data_import:
|
||||||
data_type:
|
data_type:
|
||||||
invalid: Invalid data type
|
invalid: Invalid data type
|
||||||
|
|||||||
@@ -13,12 +13,17 @@ RSpec.describe 'Google::CallbacksController', type: :request do
|
|||||||
describe 'GET /google/callback' do
|
describe 'GET /google/callback' do
|
||||||
let(:response_body_success) do
|
let(:response_body_success) do
|
||||||
{ id_token: JWT.encode({ email: email, name: 'test' }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
{ id_token: JWT.encode({ email: email, name: 'test' }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||||
refresh_token: SecureRandom.hex(10) }
|
refresh_token: SecureRandom.hex(10), scope: 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://mail.google.com/' }
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:response_body_success_without_name) do
|
let(:response_body_success_without_name) do
|
||||||
{ id_token: JWT.encode({ email: email }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
{ id_token: JWT.encode({ email: email }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||||
refresh_token: SecureRandom.hex(10) }
|
refresh_token: SecureRandom.hex(10), scope: 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://mail.google.com/' }
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:response_body_insufficient_scopes) do
|
||||||
|
{ id_token: JWT.encode({ email: email, name: 'test' }, false), access_token: SecureRandom.hex(10), token_type: 'Bearer',
|
||||||
|
refresh_token: SecureRandom.hex(10), scope: 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email' }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates inboxes if authentication is successful' do
|
it 'creates inboxes if authentication is successful' do
|
||||||
@@ -73,7 +78,20 @@ RSpec.describe 'Google::CallbacksController', type: :request do
|
|||||||
expect(inbox.name).to eq email.split('@').first.parameterize.titleize
|
expect(inbox.name).to eq email.split('@').first.parameterize.titleize
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'redirects to google app in case of error' do
|
it 'redirects to custom error URL when insufficient scopes are granted' do
|
||||||
|
stub_request(:post, 'https://accounts.google.com/o/oauth2/token')
|
||||||
|
.with(body: { 'code' => code, 'grant_type' => 'authorization_code',
|
||||||
|
'redirect_uri' => "#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/google/callback" })
|
||||||
|
.to_return(status: 200, body: response_body_insufficient_scopes.to_json, headers: { 'Content-Type' => 'application/json' })
|
||||||
|
|
||||||
|
get google_callback_url, params: { code: code }
|
||||||
|
|
||||||
|
expect(response).to redirect_to(/#{Regexp.escape("#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/app/accounts/#{account.id}/settings/inboxes/new/email")}/)
|
||||||
|
expect(response.location).to include('error=')
|
||||||
|
expect(Redis::Alfred.get(cache_key).to_i).to eq account.id
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects to custom error URL in case of OAuth error' do
|
||||||
stub_request(:post, 'https://accounts.google.com/o/oauth2/token')
|
stub_request(:post, 'https://accounts.google.com/o/oauth2/token')
|
||||||
.with(body: { 'code' => code, 'grant_type' => 'authorization_code',
|
.with(body: { 'code' => code, 'grant_type' => 'authorization_code',
|
||||||
'redirect_uri' => "#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/google/callback" })
|
'redirect_uri' => "#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/google/callback" })
|
||||||
@@ -81,7 +99,8 @@ RSpec.describe 'Google::CallbacksController', type: :request do
|
|||||||
|
|
||||||
get google_callback_url, params: { code: code }
|
get google_callback_url, params: { code: code }
|
||||||
|
|
||||||
expect(response).to redirect_to '/'
|
expect(response).to redirect_to(/#{Regexp.escape("#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/app/accounts/#{account.id}/settings/inboxes/new/email")}/)
|
||||||
|
expect(response.location).to include('error=')
|
||||||
expect(Redis::Alfred.get(cache_key).to_i).to eq account.id
|
expect(Redis::Alfred.get(cache_key).to_i).to eq account.id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user