bazis-authing 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ try:
16
+ from importlib.metadata import PackageNotFoundError, version
17
+ __version__ = version('bazis-authing')
18
+ except PackageNotFoundError:
19
+ __version__ = 'dev'
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '2.2.0'
32
+ __version_tuple__ = version_tuple = (2, 2, 0)
33
+
34
+ __commit_id__ = commit_id = None
@@ -0,0 +1,22 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from django.utils.translation import gettext_lazy as _
16
+
17
+ from bazis.core.utils.apps import BaseConfig
18
+
19
+
20
+ class AuthingConfig(BaseConfig):
21
+ name = 'bazis.contrib.authing'
22
+ verbose_name = _('Authing')
@@ -0,0 +1,38 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from django.utils.translation import gettext_lazy as _
16
+
17
+ from pydantic import Field
18
+
19
+ from bazis.core.utils.schemas import BazisSettings
20
+
21
+
22
+ class Settings(BazisSettings):
23
+ BAZIS_AUTH_COOKIE_LIFETIME: int = Field(600, title=_('Authorization cookie lifetime'))
24
+ BAZIS_AUTH_KINDS: list[str] = Field(
25
+ [
26
+ 'bazis.contrib.authing.services.password',
27
+ ],
28
+ title=_('Authorization services'),
29
+ )
30
+ AUTHENTICATION_BACKENDS: list[str] = Field(
31
+ [
32
+ 'django.contrib.auth.backends.ModelBackend',
33
+ ],
34
+ title=_('Authentication backends'),
35
+ )
36
+
37
+
38
+ settings = Settings()
@@ -0,0 +1,58 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PACKAGE VERSION\n"
10
+ "Report-Msgid-Bugs-To: \n"
11
+ "POT-Creation-Date: 2024-07-04 07:02+0000\n"
12
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "Language: \n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=UTF-8\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
20
+ #: apps.py:7
21
+ msgid "Authing"
22
+ msgstr ""
23
+
24
+ #: conf.py:7
25
+ msgid "Name of the authorization cookie"
26
+ msgstr ""
27
+
28
+ #: conf.py:8
29
+ msgid "Authorization cookie lifetime"
30
+ msgstr ""
31
+
32
+ #: conf.py:12
33
+ msgid "Authorization services"
34
+ msgstr ""
35
+
36
+ #: conf.py:15
37
+ msgid "Authentication backends"
38
+ msgstr ""
39
+
40
+ #: services/password/routes.py:16
41
+ msgid "Authentication"
42
+ msgstr ""
43
+
44
+ #: routes.py:58 routes.py:61
45
+ msgid "User is not authorized."
46
+ msgstr ""
47
+
48
+ #: routes.py:76
49
+ msgid "Next action"
50
+ msgstr ""
51
+
52
+ #: routes.py:88
53
+ msgid "Authentication error"
54
+ msgstr ""
55
+
56
+ #: services/password/__init__.py:11
57
+ msgid "Login/Password"
58
+ msgstr ""
@@ -0,0 +1,78 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PACKAGE VERSION\n"
10
+ "Report-Msgid-Bugs-To: \n"
11
+ "POT-Creation-Date: 2025-03-29 00:27+0300\n"
12
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "Language: \n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=UTF-8\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+ "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
20
+ "n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || "
21
+ "(n%100>=11 && n%100<=14)? 2 : 3);\n"
22
+
23
+ #: apps.py:7
24
+ msgid "Authing"
25
+ msgstr "Авторизация"
26
+
27
+ #: conf.py:7
28
+ msgid "Authorization cookie lifetime"
29
+ msgstr "Время жизни куки авторизации"
30
+
31
+ #: conf.py:13
32
+ msgid "Authorization services"
33
+ msgstr "Сервисы авторизации"
34
+
35
+ #: conf.py:19
36
+ msgid "Authentication backends"
37
+ msgstr "Бекенды аутентификации"
38
+
39
+ #: services/password/routes.py:15
40
+ msgid "Authentication"
41
+ msgstr "Аутентификация"
42
+
43
+ #: routes.py:58 routes.py:61
44
+ msgid "User is not authorized."
45
+ msgstr "Пользователь не авторизован"
46
+
47
+ #: routes.py:77
48
+ msgid "Next action"
49
+ msgstr "Следующее действие"
50
+
51
+ #: routes.py:91
52
+ msgid "Authentication error"
53
+ msgstr "Ошибка аутентификации"
54
+
55
+ #: services/google/__init__.py:12 services/google/routes.py:24
56
+ #, fuzzy
57
+ #| msgid "Authentication"
58
+ msgid "Google Authentication"
59
+ msgstr "Аутентификация"
60
+
61
+ #: services/google/conf.py:7
62
+ msgid "Google client ID"
63
+ msgstr ""
64
+
65
+ #: services/google/conf.py:8
66
+ msgid "Google client secret"
67
+ msgstr ""
68
+
69
+ #: services/google/conf.py:9
70
+ msgid "Google redirect URI"
71
+ msgstr ""
72
+
73
+ #: services/password/__init__.py:12
74
+ msgid "Login/Password"
75
+ msgstr "Логин/пароль"
76
+
77
+ #~ msgid "Name of the authorization cookie"
78
+ #~ msgstr "Наименование авторизационной куки"
@@ -0,0 +1,15 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from bazis.contrib.authing.routes import router # noqa: F401
@@ -0,0 +1,113 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from django.conf import settings
16
+ from django.utils.translation import gettext as _
17
+
18
+ from fastapi import Depends
19
+
20
+ from starlette.status import HTTP_100_CONTINUE, HTTP_401_UNAUTHORIZED, HTTP_422_UNPROCESSABLE_ENTITY
21
+
22
+ from bazis.contrib.users import get_user_model
23
+ from bazis.contrib.users.service import get_token_data, get_user_optional
24
+ from bazis.core.errors import JsonApiBazisError, JsonApiBazisException
25
+ from bazis.core.routing import BazisRouter
26
+ from bazis.core.utils.imp import import_module
27
+
28
+ from .schemas import AuthResponse
29
+ from .service import AuthStore
30
+
31
+
32
+ User = get_user_model()
33
+
34
+ router = BazisRouter(tags=[_('Authentication')])
35
+
36
+
37
+ @router.get('/auth/', response_model=AuthResponse)
38
+ def auth(
39
+ auth_store: AuthStore = Depends(),
40
+ user: User = Depends(get_user_optional),
41
+ token_data: dict = Depends(get_token_data),
42
+ ):
43
+ if user.is_anonymous:
44
+ if user_id := auth_store.user_id:
45
+ user = User.objects.filter(id=user_id).first()
46
+
47
+ services = []
48
+ for service_path in settings.BAZIS_AUTH_KINDS:
49
+ try:
50
+ services.append(import_module(service_path))
51
+ except ImportError:
52
+ pass
53
+
54
+ if user and not user.is_anonymous:
55
+ auth_type = auth_store.auth_type or token_data.get('auth_type')
56
+
57
+ return AuthResponse(
58
+ user_id=user.id,
59
+ username=user.username,
60
+ first_name=user.first_name,
61
+ last_name=user.last_name,
62
+ email=user.email,
63
+ token=user.jwt_build(auth_type=auth_type),
64
+ logout_actions=(
65
+ [
66
+ s.get_logout_actions()
67
+ for s in services
68
+ if s.AUTH_CODE == auth_type and hasattr(s, 'get_logout_actions')
69
+ ]
70
+ + [None]
71
+ )[0],
72
+ )
73
+
74
+ errors = [
75
+ JsonApiBazisError(
76
+ _('User is not authorized.'),
77
+ status=HTTP_401_UNAUTHORIZED,
78
+ code='UNAUTHORIZED',
79
+ title=_('User is not authorized.'),
80
+ meta_data={
81
+ 'actions': [
82
+ s.get_login_action() for s in services if hasattr(s, 'get_login_action')
83
+ ],
84
+ 'token': auth_store.token.value,
85
+ },
86
+ )
87
+ ]
88
+
89
+ if auth_store.actions:
90
+ errors.append(
91
+ JsonApiBazisError(
92
+ auth_store.actions.name,
93
+ status=HTTP_100_CONTINUE,
94
+ code=auth_store.actions.code,
95
+ title=_('Next action'),
96
+ meta_data={
97
+ 'actions': auth_store.actions.values,
98
+ },
99
+ )
100
+ )
101
+
102
+ if auth_store.errors:
103
+ for err in auth_store.errors:
104
+ errors.append(
105
+ JsonApiBazisError(
106
+ err.detail,
107
+ status=HTTP_422_UNPROCESSABLE_ENTITY,
108
+ code=err.code,
109
+ title=_('Authentication error'),
110
+ )
111
+ )
112
+
113
+ raise JsonApiBazisException(errors, cookies=[auth_store.cookie])
@@ -0,0 +1,27 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from uuid import UUID
16
+
17
+ from pydantic import BaseModel
18
+
19
+
20
+ class AuthResponse(BaseModel):
21
+ user_id: UUID | int
22
+ username: str
23
+ first_name: str | None = None
24
+ last_name: str | None = None
25
+ email: str | None = None
26
+ token: str
27
+ logout_actions: list[dict] | None = None
@@ -0,0 +1,212 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import logging
16
+ from collections import UserDict
17
+ from dataclasses import asdict, dataclass
18
+ from secrets import token_hex
19
+
20
+ from django.conf import settings
21
+ from django.contrib.auth.signals import user_logged_in
22
+ from django.core.cache import cache
23
+ from django.utils.functional import cached_property
24
+
25
+ from fastapi import Cookie, Depends, Header, Query
26
+ from fastapi.security.utils import get_authorization_scheme_param
27
+
28
+ from starlette.requests import Request
29
+
30
+ from jose import jwt
31
+
32
+ from bazis.contrib.users.service import get_anonymous_user_model, get_user_model
33
+ from bazis.core.errors import JsonApi401Exception
34
+ from bazis.core.utils.functools import CtxToggle
35
+
36
+
37
+ LOG = logging.getLogger()
38
+ User = get_user_model()
39
+ AnonymousUser = get_anonymous_user_model()
40
+
41
+
42
+ @dataclass
43
+ class AuthToken:
44
+ key: str = None
45
+
46
+ @classmethod
47
+ def parse(cls, data, required=False):
48
+ if not data:
49
+ if required:
50
+ raise JsonApi401Exception(detail='Token required')
51
+ return None
52
+
53
+ try:
54
+ token_data = jwt.decode(
55
+ data, settings.SECRET_KEY, algorithms=settings.BAZIS_JWT_SESSION_ALG
56
+ )
57
+ except Exception:
58
+ raise JsonApi401Exception(detail='Token is invalid') from None
59
+
60
+ return cls(
61
+ key=token_data['sub'],
62
+ )
63
+
64
+ @classmethod
65
+ def new(cls):
66
+ return cls(
67
+ key=token_hex(),
68
+ )
69
+
70
+ @cached_property
71
+ def value(self):
72
+ return jwt.encode(
73
+ {
74
+ 'sub': self.key,
75
+ },
76
+ settings.SECRET_KEY,
77
+ algorithm=settings.BAZIS_JWT_SESSION_ALG,
78
+ )
79
+
80
+
81
+ @dataclass
82
+ class AuthActions:
83
+ values: list
84
+ code: str
85
+ name: str
86
+
87
+
88
+ @dataclass
89
+ class AuthError:
90
+ code: str
91
+ detail: str
92
+
93
+
94
+ def get_token_header(authorization: str | None = Header(default=None, alias='Authorization')):
95
+ scheme, param = get_authorization_scheme_param(authorization)
96
+ if authorization and scheme.lower() == 'bearer':
97
+ return param
98
+
99
+
100
+ class AuthStore(UserDict):
101
+ token = None
102
+ cache_prefix = 'auth_store_'
103
+ token_required = False
104
+
105
+ def __init__(
106
+ self,
107
+ token_param: str | None = Query(default=None, alias=settings.BAZIS_AUTH_COOKIE_NAME),
108
+ token_cookie: str | None = Cookie(default=None, alias=settings.BAZIS_AUTH_COOKIE_NAME),
109
+ token_header: str | None = Depends(get_token_header),
110
+ ):
111
+ super().__init__()
112
+ self.push_toggle = CtxToggle()
113
+ self.token = AuthToken.parse(
114
+ token_param or token_header or token_cookie, required=self.token_required
115
+ )
116
+
117
+ if self.token:
118
+ self.data = cache.get(f'{self.cache_prefix}{self.token.key}')
119
+ if self.data is None:
120
+ if self.token_required:
121
+ raise JsonApi401Exception(detail='Token has been expired')
122
+ else:
123
+ self.token = None
124
+
125
+ if not self.token:
126
+ self.token = AuthToken.new()
127
+ self.data_reset()
128
+
129
+ @cached_property
130
+ def cookie(self):
131
+ # the authorization cookie is set indefinitely
132
+ return settings.BAZIS_AUTH_COOKIE_NAME, self.token.value, 31536000
133
+
134
+ @cached_property
135
+ def as_param(self):
136
+ return f'{settings.BAZIS_AUTH_COOKIE_NAME}={self.token.value}'
137
+
138
+ def response_set_cookie(self, response):
139
+ key, value, max_age = self.cookie
140
+ # set the cookie
141
+ response.set_cookie(key=key, value=value, max_age=max_age)
142
+ return response
143
+
144
+ def _push_data(self):
145
+ if self.push_toggle.allow:
146
+ cache.set(
147
+ f'{self.cache_prefix}{self.token.key}',
148
+ self.data,
149
+ settings.BAZIS_AUTH_COOKIE_LIFETIME,
150
+ )
151
+
152
+ def data_reset(self):
153
+ self.data = {}
154
+ self._push_data()
155
+
156
+ def __setitem__(self, k, v) -> None:
157
+ super().__setitem__(k, v)
158
+ self._push_data()
159
+
160
+ def update(self, d, **kwargs) -> None:
161
+ with self.push_toggle:
162
+ super().update(d, **kwargs)
163
+ self._push_data()
164
+
165
+ def __delitem__(self, v) -> None:
166
+ super().__delitem__(v)
167
+ self._push_data()
168
+
169
+ def clear(self) -> None:
170
+ with self.push_toggle:
171
+ super().clear()
172
+ self._push_data()
173
+
174
+ def login(self, user, request: Request, auth_type: str):
175
+ user_logged_in.send(sender=user.__class__, request=request, user=user)
176
+ self.data['_user_id'] = user.id
177
+ self.data['_auth_type'] = auth_type
178
+ self._push_data()
179
+
180
+ @property
181
+ def user_id(self):
182
+ return self.data.get('_user_id')
183
+
184
+ @property
185
+ def auth_type(self):
186
+ return self.data.get('_auth_type')
187
+
188
+ @property
189
+ def errors(self) -> list[AuthError]:
190
+ if errors := self.data.get('_errors'):
191
+ return [AuthError(**err) for err in errors]
192
+
193
+ def set_error(self, code, detail=None):
194
+ LOG.info('AuthStore set_error: %s, %s', code, detail)
195
+ err = AuthError(code, detail)
196
+ errors = self.data.get('errors', [])
197
+ errors.append(asdict(err))
198
+ self.data['_errors'] = errors
199
+ self._push_data()
200
+
201
+ @property
202
+ def actions(self) -> AuthActions:
203
+ if actions := self.data.get('_actions'):
204
+ return AuthActions(**actions)
205
+
206
+ def set_actions(self, values: list, code: str, name: str = None):
207
+ self.data['_actions'] = asdict(AuthActions(values, code, name))
208
+ self._push_data()
209
+
210
+
211
+ class AuthStoreTokenRequired(AuthStore):
212
+ token_required = True
@@ -0,0 +1,14 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
@@ -0,0 +1,29 @@
1
+ # Copyright 2026 EcoFuture Technology Services LLC and contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from django.utils.translation import gettext_lazy as _
16
+
17
+
18
+ AUTH_CODE = 'google'
19
+
20
+
21
+ def get_login_action():
22
+ from bazis.core.app import app
23
+
24
+ return {
25
+ 'code': AUTH_CODE,
26
+ 'name': _('Google Authentication'),
27
+ 'url': app.router.url_path_for('google_auth_init'),
28
+ 'method': 'GET',
29
+ }