From 6d629011e4c15bbd8692eeb020ea0f214b87753e Mon Sep 17 00:00:00 2001 From: Maya Date: Thu, 20 Oct 2022 11:09:10 +0200 Subject: [PATCH 01/24] Add social accounts authentication && improve email confirmation --- cvat-ui/src/components/cvat-app.tsx | 2 + .../email-verification-is-required.tsx | 32 ++ .../email-confirmation-page/styles.scss | 3 +- .../src/components/login-page/login-form.tsx | 15 +- cvat/apps/engine/views.py | 4 +- cvat/apps/iam/adapters.py | 47 +++ cvat/apps/iam/serializers.py | 20 ++ .../email/email_confirmation_message.html | 303 +++++++++++++++++ .../email_confirmation_signup_message.html | 316 +++++++++++++++++- .../email/email_confirmation_subject.txt | 4 + cvat/apps/iam/urls.py | 25 +- cvat/apps/iam/views.py | 39 ++- cvat/settings/base.py | 52 +++ cvat/settings/development.py | 1 + 14 files changed, 840 insertions(+), 23 deletions(-) create mode 100644 cvat-ui/src/components/email-confirmation-page/email-verification-is-required.tsx create mode 100644 cvat/apps/iam/adapters.py create mode 100644 cvat/apps/iam/templates/account/email/email_confirmation_message.html create mode 100644 cvat/apps/iam/templates/account/email/email_confirmation_subject.txt diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index f38a0b01b9e5..43db9a1c9dd8 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -64,6 +64,7 @@ import showPlatformNotification, { } from 'utils/platform-checker'; import '../styles.scss'; import EmailConfirmationPage from './email-confirmation-page/email-confirmed'; +import EmailVerificationIsRequiredPage from './email-confirmation-page/email-verification-is-required'; interface CVATAppProps { loadFormats: () => void; @@ -427,6 +428,7 @@ class CVATApplication extends React.PureComponent + + + + +

Please, confirm your email

+ + +
+
+ + ); +} diff --git a/cvat-ui/src/components/email-confirmation-page/styles.scss b/cvat-ui/src/components/email-confirmation-page/styles.scss index ee645cbba63f..893ee7b4ef0e 100644 --- a/cvat-ui/src/components/email-confirmation-page/styles.scss +++ b/cvat-ui/src/components/email-confirmation-page/styles.scss @@ -2,7 +2,8 @@ // // SPDX-License-Identifier: MIT -#email-confirmation-page-container { +#email-confirmation-page-container, +#email-verification-is-required-page-container { height: 100%; text-align: center; } diff --git a/cvat-ui/src/components/login-page/login-form.tsx b/cvat-ui/src/components/login-page/login-form.tsx index fb7e622ea28c..35c4f6ec0a1d 100644 --- a/cvat-ui/src/components/login-page/login-form.tsx +++ b/cvat-ui/src/components/login-page/login-form.tsx @@ -7,7 +7,10 @@ import React from 'react'; import Form from 'antd/lib/form'; import Button from 'antd/lib/button'; import Input from 'antd/lib/input'; -import { UserOutlined, LockOutlined } from '@ant-design/icons'; +import { + UserOutlined, LockOutlined, GithubOutlined, GooglePlusOutlined, +} from '@ant-design/icons'; +import Space from 'antd/lib/space'; export interface LoginData { credential: string; @@ -57,6 +60,16 @@ function LoginFormComponent(props: Props): JSX.Element { type='password' /> + + + + + + - {% endcomment %} - Confirm @@ -277,15 +264,6 @@

-

To stop receiving these emails, you can unsubscribe at any time.

-

Paste 1234 S. Broadway St. City, State 12345

- - {% endcomment %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - -
- - Logo - -
- -
- - - - - -
-

- Confirm Your Email Address -

-
- -
- - - - - - - - - - - - - - - - - - - - - -
- {% blocktrans with site_name=current_site.name site_domain=current_site.domain %} -

- Thank you for signing up for CVAT! -

-

- To complete registration and start annotating, simply tap the button below and confirm your email address. - If you didn't create an account with {{ site_name }}, - you can safely delete this email. -

- {% endblocktrans %} -
- - - - -
- - - - -
- - {% comment %}
- {% csrf_token %} - {{ form.as_p }} - -
{% endcomment %} - - - Confirm - -
-
-
-
- {% blocktrans with site_name=current_site.name site_domain=current_site.domain %} -

- {{ site_domain }} -

- {% endblocktrans %} {% endautoescape %} -
- -
- - - - - - - - - - - {% comment %} - - {% endcomment %} - - -
-

If you didn't request this, please ignore this email.

-
-

To stop receiving these emails, you can unsubscribe at any time.

-

Paste 1234 S. Broadway St. City, State 12345

-
- -
- - - - +{% include "account/email/email_confirmation_message.html" %} From 60e053524d293aa8e8a0143fb3a3163e5349f486 Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 21 Oct 2022 01:19:46 +0200 Subject: [PATCH 05/24] Small refactoring --- cvat-ui/src/components/cvat-app.tsx | 6 ++-- .../email-confirmed.tsx | 0 .../email-verification-sent.tsx} | 6 ++-- .../styles.scss | 2 +- .../src/components/login-page/login-form.tsx | 15 +--------- .../src/components/login-page/login-page.tsx | 30 +++++++++++++++++++ cvat/apps/iam/adapters.py | 2 +- cvat/settings/base.py | 2 +- cvat/settings/development.py | 2 +- 9 files changed, 41 insertions(+), 24 deletions(-) rename cvat-ui/src/components/{email-confirmation-page => email-confirmation-pages}/email-confirmed.tsx (100%) rename cvat-ui/src/components/{email-confirmation-page/email-verification-is-required.tsx => email-confirmation-pages/email-verification-sent.tsx} (82%) rename cvat-ui/src/components/{email-confirmation-page => email-confirmation-pages}/styles.scss (76%) diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index 43db9a1c9dd8..e820e2931139 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -63,8 +63,8 @@ import showPlatformNotification, { showUnsupportedNotification, } from 'utils/platform-checker'; import '../styles.scss'; -import EmailConfirmationPage from './email-confirmation-page/email-confirmed'; -import EmailVerificationIsRequiredPage from './email-confirmation-page/email-verification-is-required'; +import EmailConfirmationPage from './email-confirmation-pages/email-confirmed'; +import EmailVerificationSentPage from './email-confirmation-pages/email-verification-sent'; interface CVATAppProps { loadFormats: () => void; @@ -428,7 +428,7 @@ class CVATApplication extends React.PureComponent - + - +

Please, confirm your email

- - - + + + + +
diff --git a/cvat/apps/iam/adapters.py b/cvat/apps/iam/adapters.py index e232dab34b50..f64892b25e58 100644 --- a/cvat/apps/iam/adapters.py +++ b/cvat/apps/iam/adapters.py @@ -17,7 +17,7 @@ class DefaultAccountAdapterEx(DefaultAccountAdapter): def respond_email_verification_sent(self, request, user): - return HttpResponseRedirect(settings.ACCOUNT_EMAIL_VERIFICATION_IS_REQUIRED_REDIRECT_URL) + return HttpResponseRedirect(settings.ACCOUNT_EMAIL_VERIFICATION_SENT_REDIRECT_URL) class SocialAccountAdapterEx(DefaultSocialAccountAdapter): def pre_social_login(self, request, sociallogin): diff --git a/cvat/settings/base.py b/cvat/settings/base.py index a155b9e1cfa8..3e5748da72ed 100644 --- a/cvat/settings/base.py +++ b/cvat/settings/base.py @@ -265,7 +265,7 @@ def add_ssh_keys(): # set UI url to redirect after a successful e-mail confirmation #changed from '/auth/login' to '/auth/email-confirmation' for email confirmation message ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = '/auth/email-confirmation' -ACCOUNT_EMAIL_VERIFICATION_IS_REQUIRED_REDIRECT_URL = '/auth/account-email-verification-sent' +ACCOUNT_EMAIL_VERIFICATION_SENT_REDIRECT_URL = '/auth/email-verification-sent' OLD_PASSWORD_FIELD_ENABLED = True diff --git a/cvat/settings/development.py b/cvat/settings/development.py index a84dd6c80a45..4eea1af26ee0 100644 --- a/cvat/settings/development.py +++ b/cvat/settings/development.py @@ -39,7 +39,7 @@ UI_URL += ':{}'.format(UI_PORT) # set UI url to redirect to after successful e-mail confirmation ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = '{}/auth/email-confirmation'.format(UI_URL) -ACCOUNT_EMAIL_VERIFICATION_IS_REQUIRED_REDIRECT_URL = '{}/auth/account-email-verification-sent'.format(UI_URL) +ACCOUNT_EMAIL_VERIFICATION_SENT_REDIRECT_URL = '{}/auth/email-verification-sent'.format(UI_URL) CORS_ORIGIN_WHITELIST = [UI_URL] CORS_REPLACE_HTTPS_REFERER = True From b9808f2ec1f2c6b389b6eb118ca2e21bc42d64fc Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 21 Oct 2022 16:45:33 +0200 Subject: [PATCH 06/24] Send email verification && redirect to /auth/email-verification-sent --- cvat-ui/src/actions/auth-actions.ts | 7 ++- .../src/components/login-page/login-page.tsx | 12 ++++- .../src/containers/login-page/login-page.tsx | 2 + cvat-ui/src/reducers/auth-reducer.ts | 7 ++- cvat-ui/src/reducers/index.ts | 1 + cvat/apps/iam/urls.py | 5 +- cvat/apps/iam/views.py | 49 +++++++++++++------ 7 files changed, 61 insertions(+), 22 deletions(-) diff --git a/cvat-ui/src/actions/auth-actions.ts b/cvat-ui/src/actions/auth-actions.ts index 143a8be16f84..fcaca6ab1ed2 100644 --- a/cvat-ui/src/actions/auth-actions.ts +++ b/cvat-ui/src/actions/auth-actions.ts @@ -42,7 +42,9 @@ export const authActions = { authorizeFailed: (error: any) => createAction(AuthActionTypes.AUTHORIZED_FAILED, { error }), login: () => createAction(AuthActionTypes.LOGIN), loginSuccess: (user: any) => createAction(AuthActionTypes.LOGIN_SUCCESS, { user }), - loginFailed: (error: any) => createAction(AuthActionTypes.LOGIN_FAILED, { error }), + loginFailed: (error: any, hasEmailVerificationBeenSent = false) => ( + createAction(AuthActionTypes.LOGIN_FAILED, { error, hasEmailVerificationBeenSent }) + ), register: () => createAction(AuthActionTypes.REGISTER), registerSuccess: (user: any) => createAction(AuthActionTypes.REGISTER_SUCCESS, { user }), registerFailed: (error: any) => createAction(AuthActionTypes.REGISTER_FAILED, { error }), @@ -109,7 +111,8 @@ export const loginAsync = (credential: string, password: string): ThunkAction => const users = await cvat.users.get({ self: true }); dispatch(authActions.loginSuccess(users[0])); } catch (error) { - dispatch(authActions.loginFailed(error)); + const hasEmailVerificationBeenSent = error.message.includes('Unverified email'); + dispatch(authActions.loginFailed(error, hasEmailVerificationBeenSent)); } }; diff --git a/cvat-ui/src/components/login-page/login-page.tsx b/cvat-ui/src/components/login-page/login-page.tsx index c278dc9548a1..746f9b6250fb 100644 --- a/cvat-ui/src/components/login-page/login-page.tsx +++ b/cvat-ui/src/components/login-page/login-page.tsx @@ -4,7 +4,7 @@ // SPDX-License-Identifier: MIT import React from 'react'; -import { RouteComponentProps } from 'react-router'; +import { RouteComponentProps, useHistory } from 'react-router'; import { Link, withRouter } from 'react-router-dom'; import Button from 'antd/lib/button'; import Title from 'antd/lib/typography/Title'; @@ -22,10 +22,12 @@ const cvat = getCore(); interface LoginPageComponentProps { fetching: boolean; renderResetPassword: boolean; + hasEmailVerificationBeenSent: boolean; onLogin: (credential: string, password: string) => void; } function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps): JSX.Element { + const history = useHistory(); const { backendAPI } = cvat.config; const sizes = { style: { @@ -35,7 +37,13 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps const { Content } = Layout; - const { fetching, onLogin, renderResetPassword } = props; + const { + fetching, onLogin, renderResetPassword, hasEmailVerificationBeenSent, + } = props; + + if (hasEmailVerificationBeenSent) { + history.push('/auth/email-verification-sent'); + } return ( diff --git a/cvat-ui/src/containers/login-page/login-page.tsx b/cvat-ui/src/containers/login-page/login-page.tsx index 3f6e4d205959..71c39939f952 100644 --- a/cvat-ui/src/containers/login-page/login-page.tsx +++ b/cvat-ui/src/containers/login-page/login-page.tsx @@ -10,6 +10,7 @@ import { loginAsync } from 'actions/auth-actions'; interface StateToProps { fetching: boolean; renderResetPassword: boolean; + hasEmailVerificationBeenSent: boolean; } interface DispatchToProps { @@ -20,6 +21,7 @@ function mapStateToProps(state: CombinedState): StateToProps { return { fetching: state.auth.fetching, renderResetPassword: state.auth.allowResetPassword, + hasEmailVerificationBeenSent: state.auth.hasEmailVerificationBeenSent, }; } diff --git a/cvat-ui/src/reducers/auth-reducer.ts b/cvat-ui/src/reducers/auth-reducer.ts index 362130977498..c62e17e1eecb 100644 --- a/cvat-ui/src/reducers/auth-reducer.ts +++ b/cvat-ui/src/reducers/auth-reducer.ts @@ -15,6 +15,7 @@ const defaultState: AuthState = { allowChangePassword: false, showChangePasswordDialog: false, allowResetPassword: false, + hasEmailVerificationBeenSent: false, }; export default function (state = defaultState, action: AuthActions | BoundariesActions): AuthState { @@ -40,12 +41,16 @@ export default function (state = defaultState, action: AuthActions | BoundariesA ...state, fetching: false, user: action.payload.user, + hasEmailVerificationBeenSent: false, }; - case AuthActionTypes.LOGIN_FAILED: + case AuthActionTypes.LOGIN_FAILED: { + const { hasEmailVerificationBeenSent } = action.payload; return { ...state, fetching: false, + hasEmailVerificationBeenSent, }; + } case AuthActionTypes.LOGOUT: return { ...state, diff --git a/cvat-ui/src/reducers/index.ts b/cvat-ui/src/reducers/index.ts index 5926941d8b1e..382b2e246626 100644 --- a/cvat-ui/src/reducers/index.ts +++ b/cvat-ui/src/reducers/index.ts @@ -23,6 +23,7 @@ export interface AuthState { showChangePasswordDialog: boolean; allowChangePassword: boolean; allowResetPassword: boolean; + hasEmailVerificationBeenSent: boolean; } export interface ProjectsQuery { diff --git a/cvat/apps/iam/urls.py b/cvat/apps/iam/urls.py index 56edf7ec816f..7ea0b1af1ca7 100644 --- a/cvat/apps/iam/urls.py +++ b/cvat/apps/iam/urls.py @@ -18,11 +18,12 @@ github_oauth2_login as github_login, github_oauth2_callback as github_callback, google_oauth2_login as google_login, - google_oauth2_callback as google_callback + google_oauth2_callback as google_callback, + LoginViewEx, ) urlpatterns = [ - path('login', LoginView.as_view(), name='rest_login'), + path('login', LoginViewEx.as_view(), name='rest_login'), path('logout', LogoutView.as_view(), name='rest_logout'), path('signing', SigningView.as_view(), name='signing') ] diff --git a/cvat/apps/iam/views.py b/cvat/apps/iam/views.py index 1a5e63647bab..697874b9681b 100644 --- a/cvat/apps/iam/views.py +++ b/cvat/apps/iam/views.py @@ -10,13 +10,11 @@ from rest_framework.exceptions import ValidationError from django.conf import settings from rest_framework.response import Response -from rest_framework import status -from dj_rest_auth.registration.views import RegisterView, SocialLoginView +from dj_rest_auth.registration.views import RegisterView +from dj_rest_auth.views import LoginView from allauth.account import app_settings as allauth_settings from allauth.account.views import ConfirmEmailView -from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter -from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter -from allauth.socialaccount.providers.oauth2.client import OAuth2Client +from allauth.account.utils import has_verified_email, send_email_confirmation from allauth.socialaccount.providers.oauth2.views import OAuth2CallbackView, OAuth2LoginView from furl import furl @@ -112,6 +110,37 @@ def post(self, request): url = furl(url).add({Signer.QUERY_PARAM: sign}).url return Response(url) +class LoginViewEx(LoginView): + def post(self, request, *args, **kwargs): + self.request = request + self.serializer = self.get_serializer(data=self.request.data) + try: + self.serializer.is_valid(raise_exception=True) + except ValidationError as ex: + print(ex) + + user = self.serializer.get_auth_user( + self.serializer.data.get('username'), + self.serializer.data.get('email'), + self.serializer.data.get('password') + ) + if not user: + raise + + # Check that user's email is verified. + # If not, send a verification email. + if not has_verified_email(user): + send_email_confirmation(request, user) + # we cannot use redirect to ACCOUNT_EMAIL_VERIFICATION_SENT_REDIRECT_URL here + # because redirect will make a POST request and we'll get a 404 code + # (although in the browser request method will be displayed like GET) + return HttpResponseBadRequest('Unverified email') + + except Exception as ex: + print(ex) + + self.login() + return self.get_response() class RegisterViewEx(RegisterView): def get_response_data(self, user): @@ -124,16 +153,6 @@ def get_response_data(self, user): data['key'] = user.auth_token.key return data -# class GithubLogin(SocialLoginView): -# adapter_class = GitHubOAuth2Adapter -# callback_url = settings.GITHUB_CALLBACK_URL -# client_class = OAuth2Client - -# class GoogleLogin(SocialLoginView): -# adapter_class = GoogleOAuth2Adapter -# callback_url = settings.GOOGLE_CALLBACK_URL -# client_class = OAuth2Client - github_oauth2_login = OAuth2LoginView.adapter_view(GitHubAdapter) github_oauth2_callback = OAuth2CallbackView.adapter_view(GitHubAdapter) From fa6d06a58663fa929332844736ac332b58560ca4 Mon Sep 17 00:00:00 2001 From: Maya Date: Tue, 25 Oct 2022 16:28:58 +0200 Subject: [PATCH 07/24] Fix helm chart --- .../email/email_confirmation_message.html | 9 +++- helm-chart/templates/cvat-server-secret.yml | 10 +++-- .../cvat_backend/server/deployment.yml | 44 +++++++++---------- helm-chart/values.yaml | 4 +- 4 files changed, 39 insertions(+), 28 deletions(-) diff --git a/cvat/apps/iam/templates/account/email/email_confirmation_message.html b/cvat/apps/iam/templates/account/email/email_confirmation_message.html index 1523a1a1765a..8cd8294636d3 100644 --- a/cvat/apps/iam/templates/account/email/email_confirmation_message.html +++ b/cvat/apps/iam/templates/account/email/email_confirmation_message.html @@ -210,7 +210,14 @@

- + Confirm diff --git a/helm-chart/templates/cvat-server-secret.yml b/helm-chart/templates/cvat-server-secret.yml index 4885d74ad60d..137bc743c741 100644 --- a/helm-chart/templates/cvat-server-secret.yml +++ b/helm-chart/templates/cvat-server-secret.yml @@ -1,9 +1,13 @@ +{{- if .Values.cvat.backend.server.secret.create }} apiVersion: v1 kind: Secret metadata: - name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}-auth" -data: + name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}" + namespace: {{ .Release.Namespace }} +type: generic +stringData: googleClientId: {{ .Values.cvat.backend.server.secret.socialAccountAuthentication.googleClientId | b64enc }} googleClientSecret: {{ .Values.cvat.backend.server.secret.socialAccountAuthentication.googleClientSecret | b64enc }} githubClientId: {{ .Values.cvat.backend.server.secret.socialAccountAuthentication.githubClientId | b64enc }} - githubClientSecret: {{ .Values.cvat.backend.server.secret.socialAccountAuthentication.githubClientSecret | b64enc }} \ No newline at end of file + githubClientSecret: {{ .Values.cvat.backend.server.secret.socialAccountAuthentication.githubClientSecret | b64enc }} +{{- end }} \ No newline at end of file diff --git a/helm-chart/templates/cvat_backend/server/deployment.yml b/helm-chart/templates/cvat_backend/server/deployment.yml index 8ffe3c1b9af1..0bd2c2aa07ad 100644 --- a/helm-chart/templates/cvat_backend/server/deployment.yml +++ b/helm-chart/templates/cvat_backend/server/deployment.yml @@ -58,28 +58,28 @@ spec: value: {{ .Values.cvat.backend.server.envs.DJANGO_MODWSGI_EXTRA_ARGS}} - name: USE_ALLAUTH_SOCIAL_ACCOUNTS value: {{ .Values.cvat.backend.server.envs.USE_ALLAUTH_SOCIAL_ACCOUNTS }} - {{- if .Values.cvat.backend.server.envs.USE_ALLAUTH_SOCIAL_ACCOUNTS }} - - name: SOCIAL_AUTH_GOOGLE_CLIENT_ID - valueFrom: - secretKeyRef: - key: googleClientId - name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}-auth" - - name: SOCIAL_AUTH_GOOGLE_CLIENT_SECRET - valueFrom: - secretKeyRef: - key: googleClientSecret - name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}-auth" - - name: SOCIAL_AUTH_GITHUB_CLIENT_ID - valueFrom: - secretKeyRef: - key: githubClientId - name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}-auth" - - name: SOCIAL_AUTH_GITHUB_CLIENT_SECRET - valueFrom: - secretKeyRef: - key: googleClientSecret - name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}-auth" - {{- end }} + {{- if .Values.cvat.backend.server.envs.USE_ALLAUTH_SOCIAL_ACCOUNTS }} + - name: SOCIAL_AUTH_GOOGLE_CLIENT_ID + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}" + key: googleClientId + - name: SOCIAL_AUTH_GOOGLE_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}" + key: googleClientSecret + - name: SOCIAL_AUTH_GITHUB_CLIENT_ID + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}" + key: githubClientId + - name: SOCIAL_AUTH_GITHUB_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-{{ .Values.cvat.backend.server.secret.name }}" + key: googleClientSecret + {{- end }} {{- if .Values.redis.enabled }} - name: CVAT_REDIS_HOST value: "{{ .Release.Name }}-redis-master" diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index 1cfbd03b9985..964d38bc4ea6 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -19,9 +19,9 @@ cvat: envs: ALLOWED_HOSTS: "*" DJANGO_MODWSGI_EXTRA_ARGS: "" - USE_ALLAUTH_SOCIAL_ACCOUNTS: "False" + USE_ALLAUTH_SOCIAL_ACCOUNTS: "" secret: - # create: true + create: true name: cvat-server-secret socialAccountAuthentication: googleClientId: "" From 606ee5a947b34f6e8a12a9a27d36c0d7ddfaac15 Mon Sep 17 00:00:00 2001 From: Maya Date: Tue, 25 Oct 2022 16:30:16 +0200 Subject: [PATCH 08/24] Fix typo --- .../en/docs/administration/advanced/k8s_deployment_with_helm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/en/docs/administration/advanced/k8s_deployment_with_helm.md b/site/content/en/docs/administration/advanced/k8s_deployment_with_helm.md index 5b7773118ebb..4ebfe1bf5d2b 100644 --- a/site/content/en/docs/administration/advanced/k8s_deployment_with_helm.md +++ b/site/content/en/docs/administration/advanced/k8s_deployment_with_helm.md @@ -56,7 +56,7 @@ helm dependency update traefik: service: externalIPs: - - "your minikube IP (can be obtained with `minicube ip` command)" + - "your minikube IP (can be obtained with `minikube ip` command)" ``` - Also ensure that your CVAT ingress appears on your hosts file (/etc/hosts). You can do this by running this command: From fa5e5db2f7ad543fc05f481e207a3e259ddb6ca6 Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 26 Oct 2022 00:34:01 +0200 Subject: [PATCH 09/24] Pass enabled advanced auth methods to client --- cvat-core/src/api-implementation.ts | 5 ++ cvat-core/src/api.ts | 12 ++++ cvat-core/src/server-proxy.ts | 13 ++++ cvat-ui/src/actions/auth-actions.ts | 21 ++++++ .../src/components/login-page/login-page.tsx | 70 ++++++++++++------- .../src/containers/login-page/login-page.tsx | 8 ++- cvat-ui/src/reducers/auth-reducer.ts | 29 ++++++++ cvat-ui/src/reducers/index.ts | 12 ++++ cvat/apps/engine/views.py | 27 ++++++- cvat/apps/iam/permissions.py | 3 +- cvat/apps/iam/rules/server.csv | 2 +- cvat/apps/iam/urls.py | 2 +- 12 files changed, 172 insertions(+), 32 deletions(-) diff --git a/cvat-core/src/api-implementation.ts b/cvat-core/src/api-implementation.ts index 22ea2ae1ff97..ce6a510fc3da 100644 --- a/cvat-core/src/api-implementation.ts +++ b/cvat-core/src/api-implementation.ts @@ -88,6 +88,11 @@ const config = require('./config'); await serverProxy.server.logout(); }; + cvat.server.advancedAuthentication.implementation = async () => { + const result = await serverProxy.server.advancedAuthentication(); + return result; + }; + cvat.server.changePassword.implementation = async (oldPassword, newPassword1, newPassword2) => { await serverProxy.server.changePassword(oldPassword, newPassword1, newPassword2); }; diff --git a/cvat-core/src/api.ts b/cvat-core/src/api.ts index 2225d95b9705..07a1dda37021 100644 --- a/cvat-core/src/api.ts +++ b/cvat-core/src/api.ts @@ -173,6 +173,18 @@ function build() { const result = await PluginRegistry.apiWrapper(cvat.server.logout); return result; }, + /** + * Method returns enabled advanced authentication methods + * @method advancedAuthentication + * @async + * @memberof module:API.cvat.server + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async advancedAuthentication() { + const result = await PluginRegistry.apiWrapper(cvat.server.advancedAuthentication); + return result; + }, /** * Method allows to change user password * @method changePassword diff --git a/cvat-core/src/server-proxy.ts b/cvat-core/src/server-proxy.ts index d2e337af2d68..30227aa17bce 100644 --- a/cvat-core/src/server-proxy.ts +++ b/cvat-core/src/server-proxy.ts @@ -2184,6 +2184,18 @@ class ServerProxy { } } + async function advancedAuthentication(): Promise { + const { backendAPI } = config; + try { + const response = await Axios.get(`${backendAPI}/server/advanced-auth`, { + proxy: config.proxy, + }); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } + } + Object.defineProperties( this, Object.freeze({ @@ -2195,6 +2207,7 @@ class ServerProxy { exception, login, logout, + advancedAuthentication, changePassword, requestPasswordReset, resetPassword, diff --git a/cvat-ui/src/actions/auth-actions.ts b/cvat-ui/src/actions/auth-actions.ts index fcaca6ab1ed2..aafce8dd26ad 100644 --- a/cvat-ui/src/actions/auth-actions.ts +++ b/cvat-ui/src/actions/auth-actions.ts @@ -7,6 +7,7 @@ import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; import { UserConfirmation } from 'components/register-page/register-form'; import { getCore } from 'cvat-core-wrapper'; import isReachable from 'utils/url-checker'; +import { AdvancedAuthMethodsList } from '../reducers'; const cvat = getCore(); @@ -35,6 +36,9 @@ export enum AuthActionTypes { LOAD_AUTH_ACTIONS = 'LOAD_AUTH_ACTIONS', LOAD_AUTH_ACTIONS_SUCCESS = 'LOAD_AUTH_ACTIONS_SUCCESS', LOAD_AUTH_ACTIONS_FAILED = 'LOAD_AUTH_ACTIONS_FAILED', + LOAD_ADVANCED_AUTHENTICATION = 'LOAD_ADVANCED_AUTHENTICATION', + LOAD_ADVANCED_AUTHENTICATION_SUCCESS = 'LOAD_ADVANCED_AUTHENTICATION_SUCCESS', + LOAD_ADVANCED_AUTHENTICATION_FAILED = 'LOAD_ADVANCED_AUTHENTICATION_FAILED', } export const authActions = { @@ -71,6 +75,13 @@ export const authActions = { }) ), loadServerAuthActionsFailed: (error: any) => createAction(AuthActionTypes.LOAD_AUTH_ACTIONS_FAILED, { error }), + loadAdvancedAuth: () => createAction(AuthActionTypes.LOAD_ADVANCED_AUTHENTICATION), + loadAdvancedAuthSuccess: (list: AdvancedAuthMethodsList) => ( + createAction(AuthActionTypes.LOAD_ADVANCED_AUTHENTICATION_SUCCESS, { list }) + ), + loadAdvancedAuthFailed: (error: any) => ( + createAction(AuthActionTypes.LOAD_ADVANCED_AUTHENTICATION_FAILED, { error }) + ), }; export type AuthActions = ActionUnion; @@ -200,3 +211,13 @@ export const loadAuthActionsAsync = (): ThunkAction => async (dispatch) => { dispatch(authActions.loadServerAuthActionsFailed(error)); } }; + +export const loadAdvancedAuthAsync = (): ThunkAction => async (dispatch): Promise => { + dispatch(authActions.loadAdvancedAuth()); + try { + const list: AdvancedAuthMethodsList = await cvat.server.advancedAuthentication(); + dispatch(authActions.loadAdvancedAuthSuccess(list)); + } catch (error) { + dispatch(authActions.loadAdvancedAuthFailed(error)); + } +}; diff --git a/cvat-ui/src/components/login-page/login-page.tsx b/cvat-ui/src/components/login-page/login-page.tsx index 746f9b6250fb..ee15d4557456 100644 --- a/cvat-ui/src/components/login-page/login-page.tsx +++ b/cvat-ui/src/components/login-page/login-page.tsx @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: MIT -import React from 'react'; +import React, { useEffect } from 'react'; import { RouteComponentProps, useHistory } from 'react-router'; import { Link, withRouter } from 'react-router-dom'; import Button from 'antd/lib/button'; @@ -23,7 +23,10 @@ interface LoginPageComponentProps { fetching: boolean; renderResetPassword: boolean; hasEmailVerificationBeenSent: boolean; + googleAuthentication: boolean; + githubAuthentication: boolean; onLogin: (credential: string, password: string) => void; + loadAdvancedAuthenticationMethods: () => void; } function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps): JSX.Element { @@ -38,13 +41,18 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps const { Content } = Layout; const { - fetching, onLogin, renderResetPassword, hasEmailVerificationBeenSent, + fetching, renderResetPassword, hasEmailVerificationBeenSent, + googleAuthentication, githubAuthentication, onLogin, loadAdvancedAuthenticationMethods, } = props; if (hasEmailVerificationBeenSent) { history.push('/auth/email-verification-sent'); } + useEffect(() => { + loadAdvancedAuthenticationMethods(); + }, []); + return ( @@ -58,29 +66,41 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps onLogin(loginData.credential, loginData.password); }} /> - - - or - - - - - - - - - - + {(googleAuthentication || githubAuthentication) && + ( + <> + + + or + + + + {googleAuthentication && ( + + + + )} + {githubAuthentication && ( + + + + )} + + + )} diff --git a/cvat-ui/src/containers/login-page/login-page.tsx b/cvat-ui/src/containers/login-page/login-page.tsx index 71c39939f952..be23a08f84f4 100644 --- a/cvat-ui/src/containers/login-page/login-page.tsx +++ b/cvat-ui/src/containers/login-page/login-page.tsx @@ -5,16 +5,19 @@ import { connect } from 'react-redux'; import LoginPageComponent from 'components/login-page/login-page'; import { CombinedState } from 'reducers'; -import { loginAsync } from 'actions/auth-actions'; +import { loginAsync, loadAdvancedAuthAsync } from 'actions/auth-actions'; interface StateToProps { fetching: boolean; renderResetPassword: boolean; hasEmailVerificationBeenSent: boolean; + googleAuthentication: boolean; + githubAuthentication: boolean; } interface DispatchToProps { onLogin: typeof loginAsync; + loadAdvancedAuthenticationMethods: typeof loadAdvancedAuthAsync; } function mapStateToProps(state: CombinedState): StateToProps { @@ -22,11 +25,14 @@ function mapStateToProps(state: CombinedState): StateToProps { fetching: state.auth.fetching, renderResetPassword: state.auth.allowResetPassword, hasEmailVerificationBeenSent: state.auth.hasEmailVerificationBeenSent, + googleAuthentication: state.auth.advancedAuthList.GOOGLE_ACCOUNT_AUTHENTICATION, + githubAuthentication: state.auth.advancedAuthList.GITHUB_ACCOUNT_AUTHENTICATION, }; } const mapDispatchToProps: DispatchToProps = { onLogin: loginAsync, + loadAdvancedAuthenticationMethods: loadAdvancedAuthAsync, }; export default connect(mapStateToProps, mapDispatchToProps)(LoginPageComponent); diff --git a/cvat-ui/src/reducers/auth-reducer.ts b/cvat-ui/src/reducers/auth-reducer.ts index c62e17e1eecb..a7687bd4bb43 100644 --- a/cvat-ui/src/reducers/auth-reducer.ts +++ b/cvat-ui/src/reducers/auth-reducer.ts @@ -16,6 +16,12 @@ const defaultState: AuthState = { showChangePasswordDialog: false, allowResetPassword: false, hasEmailVerificationBeenSent: false, + advancedAuthFetching: false, + advancedAuthInitialized: false, + advancedAuthList: { + GOOGLE_ACCOUNT_AUTHENTICATION: false, + GITHUB_ACCOUNT_AUTHENTICATION: false, + }, }; export default function (state = defaultState, action: AuthActions | BoundariesActions): AuthState { @@ -154,6 +160,29 @@ export default function (state = defaultState, action: AuthActions | BoundariesA allowChangePassword: false, allowResetPassword: false, }; + case AuthActionTypes.LOAD_ADVANCED_AUTHENTICATION: { + return { + ...state, + advancedAuthFetching: true, + advancedAuthInitialized: false, + }; + } + case AuthActionTypes.LOAD_ADVANCED_AUTHENTICATION_SUCCESS: { + const { list } = action.payload; + return { + ...state, + advancedAuthFetching: false, + advancedAuthInitialized: true, + advancedAuthList: list, + }; + } + case AuthActionTypes.LOAD_ADVANCED_AUTHENTICATION_FAILED: { + return { + ...state, + advancedAuthFetching: false, + advancedAuthInitialized: true, + }; + } case BoundariesActionTypes.RESET_AFTER_ERROR: { return { ...defaultState }; } diff --git a/cvat-ui/src/reducers/index.ts b/cvat-ui/src/reducers/index.ts index 382b2e246626..409adaf0f3ea 100644 --- a/cvat-ui/src/reducers/index.ts +++ b/cvat-ui/src/reducers/index.ts @@ -14,6 +14,15 @@ export type StringObject = { [index: string]: string; }; +enum AdvancedAuthMethods { + GOOGLE_ACCOUNT_AUTHENTICATION = 'GOOGLE_ACCOUNT_AUTHENTICATION', + GITHUB_ACCOUNT_AUTHENTICATION = 'GITHUB_ACCOUNT_AUTHENTICATION', +} + +export type AdvancedAuthMethodsList = { + [name in AdvancedAuthMethods]: boolean; +}; + export interface AuthState { initialized: boolean; fetching: boolean; @@ -24,6 +33,9 @@ export interface AuthState { allowChangePassword: boolean; allowResetPassword: boolean; hasEmailVerificationBeenSent: boolean; + advancedAuthFetching: boolean; + advancedAuthInitialized: boolean; + advancedAuthList: AdvancedAuthMethodsList; } export interface ProjectsQuery { diff --git a/cvat/apps/engine/views.py b/cvat/apps/engine/views.py index 319cd2be6637..bf3a5793a873 100644 --- a/cvat/apps/engine/views.py +++ b/cvat/apps/engine/views.py @@ -28,7 +28,7 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import ( OpenApiParameter, OpenApiResponse, PolymorphicProxySerializer, - extend_schema_view, extend_schema + extend_schema_view, extend_schema, inline_serializer ) from drf_spectacular.plumbing import build_array_type, build_basic_type @@ -235,8 +235,29 @@ def plugins(request): 'ANALYTICS': strtobool(os.environ.get("CVAT_ANALYTICS", '0')), 'MODELS': strtobool(os.environ.get("CVAT_SERVERLESS", '0')), 'PREDICT': False, # FIXME: it is unused anymore (for UI only) - 'SOCIAL_PROVIDERS': [] if not settings.USE_ALLAUTH_SOCIAL_ACCOUNTS else \ - settings.SOCIALACCOUNT_PROVIDERS.keys() + } + return Response(response) + + @staticmethod + @extend_schema( + summary='Method provides a list with advanced integrated authentication methods (e.g. social accounts)', + responses={ + '200': OpenApiResponse(response=inline_serializer( + name='AdvancedAuthentication', + fields={ + 'GOOGLE_ACCOUNT_AUTHENTICATION': serializers.BooleanField(), + 'GITHUB_ACCOUNT_AUTHENTICATION': serializers.BooleanField(), + } + )), + } + ) + @action(detail=False, methods=['GET'], url_path='advanced-auth', permission_classes=[]) + def advanced_authentication(request): + use_social_auth = settings.USE_ALLAUTH_SOCIAL_ACCOUNTS + integrated_auth_providers = settings.SOCIALACCOUNT_PROVIDERS.keys() if use_social_auth else [] + response = { + 'GOOGLE_ACCOUNT_AUTHENTICATION': use_social_auth and 'google' in integrated_auth_providers, + 'GITHUB_ACCOUNT_AUTHENTICATION': use_social_auth and 'github' in integrated_auth_providers, } return Response(response) diff --git a/cvat/apps/iam/permissions.py b/cvat/apps/iam/permissions.py index 4774ea378b59..0d25c6d0abf9 100644 --- a/cvat/apps/iam/permissions.py +++ b/cvat/apps/iam/permissions.py @@ -275,7 +275,8 @@ def get_scopes(request, view, obj): 'plugins': 'view', 'exception': 'send:exception', 'logs': 'send:logs', - 'share': 'list:content' + 'share': 'list:content', + 'advanced_authentication': 'view', }.get(view.action, None)] def get_resource(self): diff --git a/cvat/apps/iam/rules/server.csv b/cvat/apps/iam/rules/server.csv index 7291fb6c0878..03aa75b78755 100644 --- a/cvat/apps/iam/rules/server.csv +++ b/cvat/apps/iam/rules/server.csv @@ -1,5 +1,5 @@ Scope,Resource,Context,Ownership,Limit,Method,URL,Privilege,Membership -view,N/A,N/A,N/A,,GET,"/server/about, /server/annotation/formats, /server/plugins",None,N/A +view,N/A,N/A,N/A,,GET,"/server/about, /server/annotation/formats, /server/plugins, /server/advanced-auth",None,N/A send:exception,N/A,N/A,N/A,,POST,/server/exception,None,N/A send:logs,N/A,N/A,N/A,,POST,/server/logs,None,N/A list:content,N/A,N/A,N/A,,GET,/server/share,Worker,N/A \ No newline at end of file diff --git a/cvat/apps/iam/urls.py b/cvat/apps/iam/urls.py index 7ea0b1af1ca7..09f9e7f390ce 100644 --- a/cvat/apps/iam/urls.py +++ b/cvat/apps/iam/urls.py @@ -7,7 +7,7 @@ from django.conf import settings from django.urls.conf import include from dj_rest_auth.views import ( - LoginView, LogoutView, PasswordChangeView, + LogoutView, PasswordChangeView, PasswordResetView, PasswordResetConfirmView) from allauth.account import app_settings as allauth_settings From 83ffc622da5c1e2e690667d78e1b89e93e7b27f3 Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 26 Oct 2022 00:38:35 +0200 Subject: [PATCH 10/24] Rename class --- cvat/apps/iam/tests/test_rest_api.py | 5 +++-- cvat/apps/iam/urls.py | 4 ++-- cvat/apps/iam/views.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cvat/apps/iam/tests/test_rest_api.py b/cvat/apps/iam/tests/test_rest_api.py index b02850a8d45e..5d539e8a53ea 100644 --- a/cvat/apps/iam/tests/test_rest_api.py +++ b/cvat/apps/iam/tests/test_rest_api.py @@ -8,12 +8,13 @@ from rest_framework.authtoken.models import Token from django.test import override_settings from cvat.apps.iam.urls import urlpatterns as iam_url_patterns +from cvat.apps.iam.views import ConfirmEmailViewEx from django.urls import path, re_path -from allauth.account.views import ConfirmEmailView, EmailVerificationSentView +from allauth.account.views import EmailVerificationSentView urlpatterns = iam_url_patterns + [ - re_path(r'^account-confirm-email/(?P[-:\w]+)/$', ConfirmEmailView.as_view(), + re_path(r'^account-confirm-email/(?P[-:\w]+)/$', ConfirmEmailViewEx.as_view(), name='account_confirm_email'), path('register/account-email-verification-sent', EmailVerificationSentView.as_view(), name='account_email_verification_sent'), diff --git a/cvat/apps/iam/urls.py b/cvat/apps/iam/urls.py index bf71f92908d2..959fa9b93873 100644 --- a/cvat/apps/iam/urls.py +++ b/cvat/apps/iam/urls.py @@ -12,7 +12,7 @@ from allauth.account import app_settings as allauth_settings from cvat.apps.iam.views import ( - SigningView, RegisterViewEx, RulesView, CustomConfirmEmailView, + SigningView, RegisterViewEx, RulesView, ConfirmEmailViewEx, ) from cvat.apps.iam.views import ( github_oauth2_login as github_login, @@ -44,7 +44,7 @@ allauth_settings.EmailVerificationMethod.NONE: # emails urlpatterns += [ - re_path(r'^account-confirm-email/(?P[-:\w]+)/$', CustomConfirmEmailView.as_view(), + re_path(r'^account-confirm-email/(?P[-:\w]+)/$', ConfirmEmailViewEx.as_view(), name='account_confirm_email'), ] if settings.USE_ALLAUTH_SOCIAL_ACCOUNTS: diff --git a/cvat/apps/iam/views.py b/cvat/apps/iam/views.py index 552539b2ebc1..7d2bf2740405 100644 --- a/cvat/apps/iam/views.py +++ b/cvat/apps/iam/views.py @@ -183,7 +183,7 @@ def get(self, request): google_oauth2_login = OAuth2LoginView.adapter_view(GoogleAdapter) google_oauth2_callback = OAuth2CallbackView.adapter_view(GoogleAdapter) -class CustomConfirmEmailView(ConfirmEmailView): +class ConfirmEmailViewEx(ConfirmEmailView): template_name = 'account/email/email_confirmation_signup_message.html' def get(self, *args, **kwargs): From 3f4b7c7cf7855a42ad396efa56d3a9161bc465d3 Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 26 Oct 2022 09:27:52 +0200 Subject: [PATCH 11/24] Fix --- cvat/settings/development.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cvat/settings/development.py b/cvat/settings/development.py index 4eea1af26ee0..aa3e4651665d 100644 --- a/cvat/settings/development.py +++ b/cvat/settings/development.py @@ -46,5 +46,5 @@ IAM_OPA_DATA_URL = 'http://localhost:8181/v1/data' if USE_ALLAUTH_SOCIAL_ACCOUNTS: - GITHUB_CALLBACK_URL = f'${UI_SCHEME}://${UI_HOST}:7000/api/auth/github/login/callback/' - GOOGLE_CALLBACK_URL = f'${UI_SCHEME}://${UI_HOST}:7000/api/auth/google/login/callback/' + GITHUB_CALLBACK_URL = f'{UI_SCHEME}://{UI_HOST}:7000/api/auth/github/login/callback/' + GOOGLE_CALLBACK_URL = f'{UI_SCHEME}://{UI_HOST}:7000/api/auth/google/login/callback/' From 6ca9a2e65750fb650b930a2a467795e2e027feea Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 26 Oct 2022 10:44:33 +0200 Subject: [PATCH 12/24] Fix helm --- helm-chart/templates/cvat_backend/server/deployment.yml | 2 +- helm-chart/values.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-chart/templates/cvat_backend/server/deployment.yml b/helm-chart/templates/cvat_backend/server/deployment.yml index 0bd2c2aa07ad..2c4dcacae39e 100644 --- a/helm-chart/templates/cvat_backend/server/deployment.yml +++ b/helm-chart/templates/cvat_backend/server/deployment.yml @@ -57,7 +57,7 @@ spec: - name: DJANGO_MODWSGI_EXTRA_ARGS value: {{ .Values.cvat.backend.server.envs.DJANGO_MODWSGI_EXTRA_ARGS}} - name: USE_ALLAUTH_SOCIAL_ACCOUNTS - value: {{ .Values.cvat.backend.server.envs.USE_ALLAUTH_SOCIAL_ACCOUNTS }} + value: {{ .Values.cvat.backend.server.envs.USE_ALLAUTH_SOCIAL_ACCOUNTS | squote }} {{- if .Values.cvat.backend.server.envs.USE_ALLAUTH_SOCIAL_ACCOUNTS }} - name: SOCIAL_AUTH_GOOGLE_CLIENT_ID valueFrom: diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index 8d7aa8c9168b..e933bdd646cb 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -19,7 +19,7 @@ cvat: envs: ALLOWED_HOSTS: "*" DJANGO_MODWSGI_EXTRA_ARGS: "" - USE_ALLAUTH_SOCIAL_ACCOUNTS: "" + USE_ALLAUTH_SOCIAL_ACCOUNTS: false secret: create: true name: cvat-server-secret From b52285f79e84246f829ba27a6c39a8445bc1e4d1 Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 26 Oct 2022 11:08:03 +0200 Subject: [PATCH 13/24] Fix github scope --- cvat/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat/settings/base.py b/cvat/settings/base.py index 3e5748da72ed..248f32b60e60 100644 --- a/cvat/settings/base.py +++ b/cvat/settings/base.py @@ -617,7 +617,7 @@ def add_ssh_keys(): 'secret': SOCIAL_AUTH_GITHUB_CLIENT_SECRET, 'key': '' }, - 'SCOPE': [ 'profile', 'email', 'openid'], + 'SCOPE': [ 'read:user', 'user:email' ], 'AUTH_PARAMS': { 'access_type': 'online', } From bcd93c529844fbc2f07012b28fefa8309aae570c Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 26 Oct 2022 15:27:41 +0200 Subject: [PATCH 14/24] Some fixes --- cvat/apps/iam/views.py | 11 +++++++++++ cvat/settings/base.py | 4 ---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cvat/apps/iam/views.py b/cvat/apps/iam/views.py index 7d2bf2740405..ad508ba98cdd 100644 --- a/cvat/apps/iam/views.py +++ b/cvat/apps/iam/views.py @@ -118,6 +118,17 @@ def post(self, request): return Response(url) class LoginViewEx(LoginView): + """ + Check the credentials and return the REST Token + if the credentials are valid and authenticated. + If email verification is enabled and the user has the unverified email, + an email with a confirmation link will be sent. + Calls Django Auth login method to register User ID + in Django session framework. + + Accept the following POST parameters: username, email, password + Return the REST Framework Token Object's key. + """ def post(self, request, *args, **kwargs): self.request = request self.serializer = self.get_serializer(data=self.request.data) diff --git a/cvat/settings/base.py b/cvat/settings/base.py index 248f32b60e60..cc802c532d60 100644 --- a/cvat/settings/base.py +++ b/cvat/settings/base.py @@ -584,7 +584,6 @@ def add_ssh_keys(): # the same in UI ACCOUNT_USERNAME_MIN_LENGTH = 5 ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True -ACCOUNT_LOGOUT_ON_GET = True if USE_ALLAUTH_SOCIAL_ACCOUNTS: SOCIALACCOUNT_ADAPTER = 'cvat.apps.iam.adapters.SocialAccountAdapterEx' @@ -618,8 +617,5 @@ def add_ssh_keys(): 'key': '' }, 'SCOPE': [ 'read:user', 'user:email' ], - 'AUTH_PARAMS': { - 'access_type': 'online', - } }, } From 36e42bd27cd76eabb8eaa59882c1dedcf689c23f Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 26 Oct 2022 16:02:30 +0200 Subject: [PATCH 15/24] Fix schema generation --- cvat/apps/iam/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cvat/apps/iam/views.py b/cvat/apps/iam/views.py index ad508ba98cdd..00c41d1d8c97 100644 --- a/cvat/apps/iam/views.py +++ b/cvat/apps/iam/views.py @@ -27,6 +27,7 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer, extend_schema_view +from drf_spectacular.contrib.rest_auth import get_token_serializer_class from cvat.apps.iam.adapters import GitHubAdapter, GoogleAdapter from .authentication import Signer @@ -129,6 +130,7 @@ class LoginViewEx(LoginView): Accept the following POST parameters: username, email, password Return the REST Framework Token Object's key. """ + @extend_schema(responses=get_token_serializer_class()) def post(self, request, *args, **kwargs): self.request = request self.serializer = self.get_serializer(data=self.request.data) From e6859e2650645da05b32edebb2a6b0b0400f222f Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 26 Oct 2022 17:01:50 +0200 Subject: [PATCH 16/24] Fixes --- cvat-core/src/server-proxy.ts | 1 - cvat/settings/base.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cvat-core/src/server-proxy.ts b/cvat-core/src/server-proxy.ts index 30227aa17bce..37a4028b406e 100644 --- a/cvat-core/src/server-proxy.ts +++ b/cvat-core/src/server-proxy.ts @@ -302,7 +302,6 @@ class ServerProxy { return []; } catch (errorData) { - throw generateError(errorData); } } diff --git a/cvat/settings/base.py b/cvat/settings/base.py index cc802c532d60..8b4aaf6bf996 100644 --- a/cvat/settings/base.py +++ b/cvat/settings/base.py @@ -577,7 +577,7 @@ def add_ssh_keys(): } # allauth configuration -USE_ALLAUTH_SOCIAL_ACCOUNTS = strtobool(os.getenv('USE_ALLAUTH_SOCIAL_ACCOUNTS', 'False')) +USE_ALLAUTH_SOCIAL_ACCOUNTS = strtobool(os.getenv('USE_ALLAUTH_SOCIAL_ACCOUNTS') or 'False') ACCOUNT_ADAPTER = 'cvat.apps.iam.adapters.DefaultAccountAdapterEx' From 25a6ddc63cc4422c597158243c9d7ac3a9b03f11 Mon Sep 17 00:00:00 2001 From: Maya Date: Thu, 27 Oct 2022 11:26:45 +0200 Subject: [PATCH 17/24] Apply comments --- cvat/apps/iam/adapters.py | 1 + cvat/apps/iam/views.py | 9 +++------ docker-compose.yml | 10 +++++----- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/cvat/apps/iam/adapters.py b/cvat/apps/iam/adapters.py index f64892b25e58..efa6ede46652 100644 --- a/cvat/apps/iam/adapters.py +++ b/cvat/apps/iam/adapters.py @@ -38,6 +38,7 @@ def pre_social_login(self, request, sociallogin): elif users: sociallogin.connect(request, users[0]) return + class GitHubAdapter(GitHubOAuth2Adapter): def get_callback_url(self, request, app): return settings.GITHUB_CALLBACK_URL diff --git a/cvat/apps/iam/views.py b/cvat/apps/iam/views.py index 00c41d1d8c97..97fed94065cf 100644 --- a/cvat/apps/iam/views.py +++ b/cvat/apps/iam/views.py @@ -136,9 +136,7 @@ def post(self, request, *args, **kwargs): self.serializer = self.get_serializer(data=self.request.data) try: self.serializer.is_valid(raise_exception=True) - except ValidationError as ex: - print(ex) - + except ValidationError: user = self.serializer.get_auth_user( self.serializer.data.get('username'), self.serializer.data.get('email'), @@ -155,9 +153,8 @@ def post(self, request, *args, **kwargs): # because redirect will make a POST request and we'll get a 404 code # (although in the browser request method will be displayed like GET) return HttpResponseBadRequest('Unverified email') - - except Exception as ex: - print(ex) + except Exception: + pass self.login() return self.get_response() diff --git a/docker-compose.yml b/docker-compose.yml index 642eec8bd90e..fbfe3f217fc7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,11 +41,11 @@ services: ADAPTIVE_AUTO_ANNOTATION: 'false' no_proxy: elasticsearch,kibana,logstash,nuclio,opa,${no_proxy} NUMPROCS: 1 - USE_ALLAUTH_SOCIAL_ACCOUNTS: ${USE_ALLAUTH_SOCIAL_ACCOUNTS} - SOCIAL_AUTH_GOOGLE_CLIENT_ID: ${SOCIAL_AUTH_GOOGLE_CLIENT_ID} - SOCIAL_AUTH_GOOGLE_CLIENT_SECRET: ${SOCIAL_AUTH_GOOGLE_CLIENT_SECRET} - SOCIAL_AUTH_GITHUB_CLIENT_ID: ${SOCIAL_AUTH_GITHUB_CLIENT_ID} - SOCIAL_AUTH_GITHUB_CLIENT_SECRET: ${SOCIAL_AUTH_GITHUB_CLIENT_SECRET} + USE_ALLAUTH_SOCIAL_ACCOUNTS: "" + SOCIAL_AUTH_GOOGLE_CLIENT_ID: "" + SOCIAL_AUTH_GOOGLE_CLIENT_SECRET: "" + SOCIAL_AUTH_GITHUB_CLIENT_ID: "" + SOCIAL_AUTH_GITHUB_CLIENT_SECRET: "" command: -c supervisord/server.conf labels: - traefik.enable=true From e8c4dd66cb50a1a7eaca5e16bdb3f96020b60d61 Mon Sep 17 00:00:00 2001 From: Maya Date: Thu, 27 Oct 2022 11:28:30 +0200 Subject: [PATCH 18/24] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41e3a2c93121..700a6f587a82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Objects sorting option in the sidebar, by z order. Additional visualization when the sorting is applied () - Added YOLOv5 serverless function NVIDIA GPU support () +- Authentication with social accounts google & github () ### Changed - `api/docs`, `api/swagger`, `api/schema`, `server/about` endpoints now allow unauthorized access (, ) From 936a25f448af8ac38f54fd261973c007c846c283 Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 28 Oct 2022 12:43:44 +0200 Subject: [PATCH 19/24] Add more user friendly page when email confirmation url is incorrect && fix login when email is private --- cvat-ui/src/components/cvat-app.tsx | 2 + .../incorrect-email-confirmation.tsx | 37 +++++++++++++++++++ .../email-confirmation-pages/styles.scss | 3 +- cvat/apps/iam/adapters.py | 6 +-- cvat/apps/iam/views.py | 6 +-- cvat/settings/base.py | 5 +++ cvat/settings/development.py | 1 + 7 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 cvat-ui/src/components/email-confirmation-pages/incorrect-email-confirmation.tsx diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index e820e2931139..7edaa6fbc67f 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -65,6 +65,7 @@ import showPlatformNotification, { import '../styles.scss'; import EmailConfirmationPage from './email-confirmation-pages/email-confirmed'; import EmailVerificationSentPage from './email-confirmation-pages/email-verification-sent'; +import IncorrectEmailConfirmationPage from './email-confirmation-pages/incorrect-email-confirmation'; interface CVATAppProps { loadFormats: () => void; @@ -429,6 +430,7 @@ class CVATApplication extends React.PureComponent + + + + +

+ This e-mail confirmation link expired or is invalid. +

+

+ Please issue a new e-mail confirmation request. +

+ + +
+
+
+ ); +} diff --git a/cvat-ui/src/components/email-confirmation-pages/styles.scss b/cvat-ui/src/components/email-confirmation-pages/styles.scss index bafd1254e357..5701e244bf30 100644 --- a/cvat-ui/src/components/email-confirmation-pages/styles.scss +++ b/cvat-ui/src/components/email-confirmation-pages/styles.scss @@ -3,7 +3,8 @@ // SPDX-License-Identifier: MIT #email-confirmation-page-container, -#email-verification-sent-page-container { +#email-verification-sent-page-container, +#incorrect-email-confirmation-page-container { height: 100%; text-align: center; } diff --git a/cvat/apps/iam/adapters.py b/cvat/apps/iam/adapters.py index efa6ede46652..e09737db2c5b 100644 --- a/cvat/apps/iam/adapters.py +++ b/cvat/apps/iam/adapters.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT from django.contrib.auth import get_user_model -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, HttpResponseBadRequest from django.conf import settings from allauth.socialaccount.adapter import DefaultSocialAccountAdapter @@ -30,11 +30,11 @@ def pre_social_login(self, request, sociallogin): return if not sociallogin.email_addresses: - raise ImmediateHttpResponse(response='No email is associated with this social account') + raise ImmediateHttpResponse(response=HttpResponseBadRequest('No email is associated with this social account')) users = filter_users_by_email(sociallogin.user.email) if len(users) > 1: - raise ImmediateHttpResponse(f'Cannot connect account with ${sociallogin.user.email} email.') + raise ImmediateHttpResponse(HttpResponseBadRequest(f'Cannot connect account with ${sociallogin.user.email} email.')) elif users: sociallogin.connect(request, users[0]) return diff --git a/cvat/apps/iam/views.py b/cvat/apps/iam/views.py index 97fed94065cf..7ea79a8cf76a 100644 --- a/cvat/apps/iam/views.py +++ b/cvat/apps/iam/views.py @@ -9,7 +9,7 @@ from django.core.exceptions import BadRequest from django.utils.functional import SimpleLazyObject -from django.http import Http404, HttpResponseBadRequest +from django.http import Http404, HttpResponseBadRequest, HttpResponseRedirect from rest_framework import views, serializers from rest_framework.exceptions import ValidationError from django.conf import settings @@ -202,5 +202,5 @@ def get(self, *args, **kwargs): return super().get(*args, **kwargs) return self.post(*args, **kwargs) except Http404: - return HttpResponseBadRequest('This e-mail confirmation link expired or is invalid.' - 'Please issue a new e-mail confirmation request') + return HttpResponseRedirect(settings.INCORRECT_EMAIL_CONFIRMATION_URL) + diff --git a/cvat/settings/base.py b/cvat/settings/base.py index 8b4aaf6bf996..b480855fecb8 100644 --- a/cvat/settings/base.py +++ b/cvat/settings/base.py @@ -266,6 +266,7 @@ def add_ssh_keys(): #changed from '/auth/login' to '/auth/email-confirmation' for email confirmation message ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = '/auth/email-confirmation' ACCOUNT_EMAIL_VERIFICATION_SENT_REDIRECT_URL = '/auth/email-verification-sent' +INCORRECT_EMAIL_CONFIRMATION_URL = '/auth/incorrect-email-confirmation' OLD_PASSWORD_FIELD_ENABLED = True @@ -588,6 +589,10 @@ def add_ssh_keys(): if USE_ALLAUTH_SOCIAL_ACCOUNTS: SOCIALACCOUNT_ADAPTER = 'cvat.apps.iam.adapters.SocialAccountAdapterEx' SOCIALACCOUNT_LOGIN_ON_GET = True + # It's required to define email in the case when a user has a private hidden email. + # (e.g in github account set keep my email addresses private) + # default = ACCOUNT_EMAIL_REQUIRED + SOCIALACCOUNT_QUERY_EMAIL = True GITHUB_CALLBACK_URL = 'http://localhost:8080/api/auth/github/login/callback/' GOOGLE_CALLBACK_URL = 'http://localhost:8080/api/auth/google/login/callback/' diff --git a/cvat/settings/development.py b/cvat/settings/development.py index aa3e4651665d..db6d0692e348 100644 --- a/cvat/settings/development.py +++ b/cvat/settings/development.py @@ -40,6 +40,7 @@ # set UI url to redirect to after successful e-mail confirmation ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = '{}/auth/email-confirmation'.format(UI_URL) ACCOUNT_EMAIL_VERIFICATION_SENT_REDIRECT_URL = '{}/auth/email-verification-sent'.format(UI_URL) +INCORRECT_EMAIL_CONFIRMATION_URL = '{}/auth/incorrect-email-confirmation'.format(UI_URL) CORS_ORIGIN_WHITELIST = [UI_URL] CORS_REPLACE_HTTPS_REFERER = True From d1cdb7e16bf9b46276385f2d42569f6da592e6d6 Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 28 Oct 2022 12:54:42 +0200 Subject: [PATCH 20/24] Fix pylint --- cvat/apps/engine/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cvat/apps/engine/views.py b/cvat/apps/engine/views.py index bf3a5793a873..f6604beba812 100644 --- a/cvat/apps/engine/views.py +++ b/cvat/apps/engine/views.py @@ -329,7 +329,7 @@ def get_queryset(self): queryset = perm.filter(queryset) return queryset - def perform_create(self, serializer): + def perform_create(self, serializer, **kwargs): super().perform_create( serializer, owner=self.request.user, @@ -836,7 +836,7 @@ def perform_update(self, serializer): if updated_instance.project: updated_instance.project.save() - def perform_create(self, serializer): + def perform_create(self, serializer, **kwargs): super().perform_create( serializer, owner=self.request.user, @@ -1755,7 +1755,7 @@ def get_serializer_class(self): else: return IssueWriteSerializer - def perform_create(self, serializer): + def perform_create(self, serializer, **kwargs): super().perform_create(serializer, owner=self.request.user) @extend_schema(summary='The action returns all comments of a specific issue', @@ -1830,7 +1830,7 @@ def get_serializer_class(self): else: return CommentWriteSerializer - def perform_create(self, serializer): + def perform_create(self, serializer, **kwargs): super().perform_create(serializer, owner=self.request.user) @extend_schema(tags=['users']) From 63bfbb1bcd276109267b27aa71c08b3b80c4a4eb Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 28 Oct 2022 22:46:36 +0200 Subject: [PATCH 21/24] Redirect to login page after cansel authorization --- cvat/apps/iam/views.py | 12 ++++++++++-- cvat/settings/base.py | 1 + cvat/settings/development.py | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cvat/apps/iam/views.py b/cvat/apps/iam/views.py index 7ea79a8cf76a..ee775948b160 100644 --- a/cvat/apps/iam/views.py +++ b/cvat/apps/iam/views.py @@ -187,11 +187,19 @@ def get(self, request): file_path = self._get_bundle_path() return sendfile(request, file_path) +class OAuth2CallbackViewEx(OAuth2CallbackView): + def dispatch(self, request, *args, **kwargs): + # Distinguish cancel from error + if (auth_error := request.GET.get('error', None)) and \ + auth_error == self.adapter.login_cancelled_error: + return HttpResponseRedirect(settings.SOCIALACCOUNT_CALLBACK_CANCELLED_URL) + return super().dispatch(request, *args, **kwargs) + github_oauth2_login = OAuth2LoginView.adapter_view(GitHubAdapter) -github_oauth2_callback = OAuth2CallbackView.adapter_view(GitHubAdapter) +github_oauth2_callback = OAuth2CallbackViewEx.adapter_view(GitHubAdapter) google_oauth2_login = OAuth2LoginView.adapter_view(GoogleAdapter) -google_oauth2_callback = OAuth2CallbackView.adapter_view(GoogleAdapter) +google_oauth2_callback = OAuth2CallbackViewEx.adapter_view(GoogleAdapter) class ConfirmEmailViewEx(ConfirmEmailView): template_name = 'account/email/email_confirmation_signup_message.html' diff --git a/cvat/settings/base.py b/cvat/settings/base.py index b480855fecb8..21bc5b9c8fc6 100644 --- a/cvat/settings/base.py +++ b/cvat/settings/base.py @@ -593,6 +593,7 @@ def add_ssh_keys(): # (e.g in github account set keep my email addresses private) # default = ACCOUNT_EMAIL_REQUIRED SOCIALACCOUNT_QUERY_EMAIL = True + SOCIALACCOUNT_CALLBACK_CANCELLED_URL = '/auth/login' GITHUB_CALLBACK_URL = 'http://localhost:8080/api/auth/github/login/callback/' GOOGLE_CALLBACK_URL = 'http://localhost:8080/api/auth/google/login/callback/' diff --git a/cvat/settings/development.py b/cvat/settings/development.py index db6d0692e348..8ec346268a71 100644 --- a/cvat/settings/development.py +++ b/cvat/settings/development.py @@ -49,3 +49,4 @@ if USE_ALLAUTH_SOCIAL_ACCOUNTS: GITHUB_CALLBACK_URL = f'{UI_SCHEME}://{UI_HOST}:7000/api/auth/github/login/callback/' GOOGLE_CALLBACK_URL = f'{UI_SCHEME}://{UI_HOST}:7000/api/auth/google/login/callback/' + SOCIALACCOUNT_CALLBACK_CANCELLED_URL = f'{UI_URL}/auth/login' From a2f010f1646f4569d3aee9a68ce7268b20a0b916 Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 28 Oct 2022 23:16:21 +0200 Subject: [PATCH 22/24] Fix bandit --- cvat/apps/iam/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat/apps/iam/views.py b/cvat/apps/iam/views.py index ee775948b160..fe4143351447 100644 --- a/cvat/apps/iam/views.py +++ b/cvat/apps/iam/views.py @@ -153,7 +153,7 @@ def post(self, request, *args, **kwargs): # because redirect will make a POST request and we'll get a 404 code # (although in the browser request method will be displayed like GET) return HttpResponseBadRequest('Unverified email') - except Exception: + except Exception: # nosec pass self.login() From c71ea8fb645e469aa47360bb1169865125bf0104 Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 4 Nov 2022 10:52:06 +0100 Subject: [PATCH 23/24] Update enabled advanced authentication methods --- cvat/apps/engine/views.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cvat/apps/engine/views.py b/cvat/apps/engine/views.py index de191402d3ed..a4dba89ba896 100644 --- a/cvat/apps/engine/views.py +++ b/cvat/apps/engine/views.py @@ -255,9 +255,19 @@ def plugins(request): def advanced_authentication(request): use_social_auth = settings.USE_ALLAUTH_SOCIAL_ACCOUNTS integrated_auth_providers = settings.SOCIALACCOUNT_PROVIDERS.keys() if use_social_auth else [] + google_auth_is_enabled = ( + 'google' in integrated_auth_providers + and settings.SOCIAL_AUTH_GOOGLE_CLIENT_ID + and settings.SOCIAL_AUTH_GOOGLE_CLIENT_SECRET + ) + github_auth_is_enabled = ( + 'github' in integrated_auth_providers + and settings.SOCIAL_AUTH_GITHUB_CLIENT_ID + and settings.SOCIAL_AUTH_GITHUB_CLIENT_SECRET + ) response = { - 'GOOGLE_ACCOUNT_AUTHENTICATION': use_social_auth and 'google' in integrated_auth_providers, - 'GITHUB_ACCOUNT_AUTHENTICATION': use_social_auth and 'github' in integrated_auth_providers, + 'GOOGLE_ACCOUNT_AUTHENTICATION': google_auth_is_enabled, + 'GITHUB_ACCOUNT_AUTHENTICATION': github_auth_is_enabled, } return Response(response) From d0f27cc9ce122574991599f0b10eeddb645cd21e Mon Sep 17 00:00:00 2001 From: Maya Date: Fri, 4 Nov 2022 10:52:35 +0100 Subject: [PATCH 24/24] Update dev callback urls --- cvat/settings/development.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cvat/settings/development.py b/cvat/settings/development.py index 8ec346268a71..26b55e0c8fab 100644 --- a/cvat/settings/development.py +++ b/cvat/settings/development.py @@ -47,6 +47,6 @@ IAM_OPA_DATA_URL = 'http://localhost:8181/v1/data' if USE_ALLAUTH_SOCIAL_ACCOUNTS: - GITHUB_CALLBACK_URL = f'{UI_SCHEME}://{UI_HOST}:7000/api/auth/github/login/callback/' - GOOGLE_CALLBACK_URL = f'{UI_SCHEME}://{UI_HOST}:7000/api/auth/google/login/callback/' + GITHUB_CALLBACK_URL = f'{UI_URL}/api/auth/github/login/callback/' + GOOGLE_CALLBACK_URL = f'{UI_URL}/api/auth/google/login/callback/' SOCIALACCOUNT_CALLBACK_CANCELLED_URL = f'{UI_URL}/auth/login'