-
-
Notifications
You must be signed in to change notification settings - Fork 296
Integrate slack bot with google OAuth #2089
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate slack bot with google OAuth #2089
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Summary by CodeRabbit
WalkthroughAdds Google OAuth integration for Slack: new GoogleAccountAuthorization model with KMS-encrypted token fields, OAuth flow and callback view, Slack command/action and UI blocks, KMS client and encrypted model field, settings/env vars and dependencies, migrations, tests, and manifest/template updates. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
🧹 Nitpick comments (22)
backend/tests/apps/common/clients_test.py (3)
14-22: Stabilize boto3.client patching and target the correct import pathUsing
with patch("boto3.client")inside the fixture only patches during fixture execution. Calls outside (e.g.,get_kms_client()creating a newKmsClient) will hit the real boto3. Prefer patching the import path used by the module and keeping it active for the test.Apply this refactor to keep the patch active and targeted:
-@pytest.fixture(autouse=True) -def setup(self): - """Set up mock KMS client.""" - with patch("boto3.client") as mock_boto3_client: - self.mock_boto3_client = mock_boto3_client - self.mock_kms_client = Mock() - self.mock_boto3_client.return_value = self.mock_kms_client - self.kms_client = KmsClient() +@pytest.fixture(autouse=True) +def setup(self, monkeypatch): + """Set up mock KMS client.""" + self.mock_kms_client = Mock() + # Patch the exact import path used by the code under test. + monkeypatch.setattr("apps.common.clients.boto3.client", lambda *_, **__: self.mock_kms_client) + self.kms_client = KmsClient()
42-49: Avoid cross-test leakage from the KMS singleton
get_kms_client()uses a module-level singleton (_kms_client). Once set, it persists across tests. Reset it to avoid hidden coupling.Add this module-level autouse fixture to reset the singleton before each test:
# Place near the top of this file, after imports. @pytest.fixture(autouse=True) def reset_kms_singleton(monkeypatch): import apps.common.clients as clients_mod monkeypatch.setattr(clients_mod, "_kms_client", None, raising=False) yield
51-74: Make the Google client test independent of default settingsThe test asserts default
auth_uriandtoken_urivalues without overriding them, creating hidden coupling to base settings. Set them explicitly inoverride_settingsto keep the test robust.Apply this diff:
@override_settings( IS_GOOGLE_AUTH_ENABLED=True, GOOGLE_AUTH_CLIENT_ID="test_client_id", GOOGLE_AUTH_CLIENT_SECRET="test_client_secret", # noqa: S106 GOOGLE_AUTH_REDIRECT_URI="test_redirect_uri", + GOOGLE_AUTH_AUTH_URI="https://accounts.google.com/o/oauth2/auth", + GOOGLE_AUTH_TOKEN_URI="https://oauth2.googleapis.com/token", )backend/settings/urls.py (1)
19-19: Import google_auth_callback lazily and name the routeFor consistency with
slack_request_handlerand to avoid importing Slack/Google code when Slack isn’t enabled, importgoogle_auth_callbackinside the Slack block. Also consider naming the URL for easier reversing.Apply this diff:
-from apps.slack.views import google_auth_callback @@ if SlackConfig.app: - from apps.slack.views import slack_request_handler + from apps.slack.views import google_auth_callback, slack_request_handler @@ - path("integrations/slack/oauth2/callback/", google_auth_callback), + path( + "integrations/slack/oauth2/callback/", + google_auth_callback, + name="slack_google_oauth2_callback", + ),Also applies to: 41-41
backend/apps/slack/blocks.py (1)
88-97: Sign-in button added — consider centralizing action_id constant usageThe block is valid. For consistency and to avoid drift, consider referencing the shared SIGN_IN_WITH_GOOGLE_ACTION constant (and, longer-term, migrate other action_ids here to constants too).
Example change (requires adding an import at the top of this file):
+from apps.slack.constants import SIGN_IN_WITH_GOOGLE_ACTION @@ - "action_id": "sign_in_with_google_action", + "action_id": SIGN_IN_WITH_GOOGLE_ACTION,backend/apps/slack/migrations/0021_alter_googleauth_access_token_and_more.py (3)
12-16: Re-addblank=Trueto token field to preserve form/admin behaviorThis alter drops
blank=True(present in 0019/0020), making the field “required” in forms/admin despitenull=True. Tokens are programmatically set; forms shouldn’t require them.Apply this diff within this migration (or amend before merge to avoid another migration):
- field=models.BinaryField(null=True, verbose_name="Access Token"), + field=models.BinaryField(null=True, blank=True, verbose_name="Access Token"),
17-21:expires_atshould also keepblank=TrueYou’re removing
blank=Truethat existed in 0019. Without it, admin/forms will treat it as required even though DB allows NULL.- field=models.DateTimeField(null=True, verbose_name="Token Expiry"), + field=models.DateTimeField(null=True, blank=True, verbose_name="Token Expiry"),
22-26: Re-addblank=Trueforrefresh_tokenfor consistencySame reasoning as
access_token: keep forms/admin non-required; ensure consistency across migrations.- field=models.BinaryField(null=True, verbose_name="Refresh Token"), + field=models.BinaryField(null=True, blank=True, verbose_name="Refresh Token"),backend/tests/apps/common/model_fileds_test.py (3)
1-1: Typo in filename:model_fileds_test.py→model_fields_test.pyMinor but worthwhile for discoverability and consistency.
Would you like me to generate a Git move patch for this rename?
56-61: Add a “happy path” encrypt/decrypt test with KMS enabled (monkeypatched)Current tests cover disabled KMS and invalid types, but not successful round-trip. Add a test that:
- monkeypatches
apps.common.clients.get_kms_client()to a fake that returns deterministic ciphertext (e.g., prefix) and decrypts accordingly,- asserts
get_prep_value("plain")produces bytes,- asserts
from_db_value(ciphertext, ...)returns the original string.This guards against regressions in the encryption plumbing.
I can draft this test using pytest’s
monkeypatchfixture; say the word and I’ll include the code.
17-28: Intentional hard-fail behavior when KMS is disabled—double-check it’s desired globallyWhen
IS_AWS_KMS_ENABLED=False,get_prep_value,to_python, andfrom_db_valueraise. This is secure-by-default, but it can be a footgun for local dev/admin reads.If you want a softer local experience, consider:
- Allow read-as-None when disabled, while still forbidding writes; or
- Gating model/feature usage behind
IS_GOOGLE_AUTH_ENABLEDso the field isn’t read at all.If the hard-fail is intentional (as tests encode), ignore this.
backend/apps/slack/migrations/0019_googleauth.py (1)
22-27: Avoid any window where plaintext tokens could be persistedSince 0019 stores tokens as TextField, ensure environments won’t persist plaintext before 0023 lands, or add a feature gate preventing writes until encryption is in place. If any data may already exist, plan a data migration to encrypt/backfill.
I can provide a targeted backfill migration tailored to your deployment plan if needed.
backend/.env.example (2)
15-19: Use Google's v2 OAuth endpoint for new appsThe v1 auth URI is deprecated in most docs. Prefer the v2 endpoint to avoid surprises.
-DJANGO_GOOGLE_AUTH_AUTH_URI=https://accounts.google.com/o/oauth2/auth +DJANGO_GOOGLE_AUTH_AUTH_URI=https://accounts.google.com/o/oauth2/v2/auth
6-7: Clarify KMS env usage and defaultsMinor: consider commenting that these are only required when KMS is enabled so devs aren’t blocked locally.
Example comment to append next to these lines:
- "Only required if IS_AWS_KMS_ENABLED=True"
Also applies to: 20-20
backend/apps/common/clients.py (3)
8-21: Hardenget_google_auth_client: add guard and configurable scopes
- Add a defensive guard to prevent constructing a Flow when Google auth is disabled.
- Allow scopes to be configured via settings, falling back to the default.
-def get_google_auth_client(): - """Get a Google OAuth client.""" - return Flow.from_client_config( +def get_google_auth_client() -> Flow: + """Get a Google OAuth client.""" + if not getattr(settings, "IS_GOOGLE_AUTH_ENABLED", False): + raise ValueError("Google OAuth client ID is not configured or Google auth is disabled.") + return Flow.from_client_config( client_config={ "web": { "client_id": settings.GOOGLE_AUTH_CLIENT_ID, "client_secret": settings.GOOGLE_AUTH_CLIENT_SECRET, "redirect_uris": [settings.GOOGLE_AUTH_REDIRECT_URI], "auth_uri": settings.GOOGLE_AUTH_AUTH_URI, "token_uri": settings.GOOGLE_AUTH_TOKEN_URI, } }, - scopes=["https://www.googleapis.com/auth/calendar.readonly"], + scopes=getattr( + settings, + "GOOGLE_AUTH_SCOPES", + ["https://www.googleapis.com/auth/calendar.readonly"], + ), )If you want me to add GOOGLE_AUTH_SCOPES to settings with a sane default, I can draft that as well.
27-35: Fail fast when AWS region is missing for KMSIf KMS is enabled but region is unset, boto3 will raise later with a less clear error. Validate upfront for a better DX.
def __init__(self): """Initialize the KMS client.""" - self.client = boto3.client( + if settings.AWS_REGION in (None, "None", ""): + raise ValueError("AWS_REGION is not configured but required when using KMS.") + self.client = boto3.client( "kms", aws_access_key_id=settings.AWS_ACCESS_KEY_ID, aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, region_name=settings.AWS_REGION, )
47-55: Thread-safety of the singleton KMS clientModule-level lazy init is not thread-safe. Rare in Django, but easy to make robust with a lock if desired.
Happy to draft a lock-based initialization if you prefer.
backend/apps/common/model_fields.py (1)
8-11: Error message mentions only bytes/bytearray but you also accept memoryviewKeep the message accurate to reduce confusion when debugging.
-BYTES_ERROR_MESSAGE = "Value must be bytes or bytearray to decrypt." +BYTES_ERROR_MESSAGE = "Value must be bytes, bytearray, or memoryview to decrypt."backend/tests/apps/slack/models/google_auth_test.py (1)
202-245: Refresh flow coverage looks good; minor nit: token types in testThe mock assigns byte strings to
token/refresh_token. Google credentials typically yield strings. Not harmful here, but consider using str for parity.backend/apps/slack/models/google_auth.py (3)
14-18: Clarify misconfiguration error messaging.AUTH_ERROR_MESSAGE implies config validation but code only checks the enable flag. Either update the message to reflect that or add explicit config checks (see get_flow refactor below).
Apply this diff to make the message accurate:
-AUTH_ERROR_MESSAGE = ( - "Google OAuth client ID, secret, and redirect URI must be set in environment variables." -) +AUTH_ERROR_MESSAGE = ( + "Google OAuth is disabled or misconfigured. Ensure client ID, client secret, auth/token URIs, and redirect URI are set." +)
40-48: Avoid mixed return types fromauthenticatefor API clarity.Returning either a model instance or a tuple complicates call sites. Consider returning a small result object (e.g., a dataclass with
auth,authorization_url,state) for clarity.If desired, I can provide a minimal
AuthResultdataclass and update usage sites.
33-38: Unit tests: cover refresh-token preservation and error paths.
- Add a test ensuring
refresh_access_tokendoes not overwrite an existing refresh_token when Google returns None.- Add tests for refresh failure raising ValidationError.
- Add tests for
authenticatefallback when refresh fails.I can draft these tests aligned with your existing test style (
backend/tests/apps/slack/models/google_auth_test.py).Also applies to: 111-136
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
backend/poetry.lockis excluded by!**/*.lock
📒 Files selected for processing (21)
backend/.env.example(1 hunks)backend/apps/common/clients.py(1 hunks)backend/apps/common/model_fields.py(1 hunks)backend/apps/slack/actions/home.py(4 hunks)backend/apps/slack/blocks.py(1 hunks)backend/apps/slack/constants.py(1 hunks)backend/apps/slack/migrations/0019_googleauth.py(1 hunks)backend/apps/slack/migrations/0020_alter_googleauth_access_token_and_more.py(1 hunks)backend/apps/slack/migrations/0021_alter_googleauth_access_token_and_more.py(1 hunks)backend/apps/slack/migrations/0022_alter_googleauth_options_alter_googleauth_table.py(1 hunks)backend/apps/slack/migrations/0023_alter_googleauth_access_token_and_more.py(1 hunks)backend/apps/slack/models/__init__.py(1 hunks)backend/apps/slack/models/google_auth.py(1 hunks)backend/apps/slack/views.py(2 hunks)backend/pyproject.toml(1 hunks)backend/settings/base.py(1 hunks)backend/settings/urls.py(2 hunks)backend/tests/apps/common/clients_test.py(1 hunks)backend/tests/apps/common/model_fileds_test.py(1 hunks)backend/tests/apps/slack/models/google_auth_test.py(1 hunks)cspell/custom-dict.txt(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (10)
backend/tests/apps/slack/models/google_auth_test.py (2)
backend/apps/slack/models/google_auth.py (6)
GoogleAuth(20-139)is_token_expired(105-109)get_flow(98-102)authenticate(41-66)refresh_access_token(112-135)authenticate_callback(69-95)backend/apps/slack/models/member.py (1)
Member(10-78)
backend/settings/urls.py (1)
backend/apps/slack/views.py (1)
google_auth_callback(32-53)
backend/tests/apps/common/clients_test.py (1)
backend/apps/common/clients.py (5)
KmsClient(24-44)get_google_auth_client(8-21)get_kms_client(50-55)encrypt(40-44)decrypt(36-38)
backend/tests/apps/common/model_fileds_test.py (1)
backend/apps/common/model_fields.py (4)
KmsEncryptedField(13-47)get_prep_value(18-27)to_python(45-47)from_db_value(41-43)
backend/apps/slack/models/__init__.py (1)
backend/apps/slack/models/google_auth.py (1)
GoogleAuth(20-139)
backend/apps/slack/migrations/0023_alter_googleauth_access_token_and_more.py (1)
backend/apps/common/model_fields.py (1)
KmsEncryptedField(13-47)
backend/apps/slack/views.py (2)
backend/apps/slack/apps.py (1)
SlackConfig(13-26)backend/apps/slack/models/google_auth.py (2)
GoogleAuth(20-139)authenticate_callback(69-95)
backend/apps/slack/models/google_auth.py (3)
backend/apps/common/clients.py (1)
get_google_auth_client(8-21)backend/apps/common/model_fields.py (1)
KmsEncryptedField(13-47)backend/apps/slack/models/member.py (1)
Member(10-78)
backend/apps/common/model_fields.py (1)
backend/apps/common/clients.py (3)
get_kms_client(50-55)encrypt(40-44)decrypt(36-38)
backend/apps/slack/actions/home.py (3)
backend/apps/slack/models/google_auth.py (1)
GoogleAuth(20-139)backend/apps/slack/models/member.py (1)
Member(10-78)backend/apps/slack/blocks.py (1)
markdown(21-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run frontend e2e tests
- GitHub Check: Run backend tests
- GitHub Check: Run frontend unit tests
- GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (18)
cspell/custom-dict.txt (1)
21-21: LGTM: Add “PLW” to cspell dictionaryPrevents false positives for ruff codes like PLW0603. Placement after PLR keeps the list ordered.
backend/apps/slack/constants.py (1)
27-27: LGTM: Introduces dedicated action ID for Google sign-inConstant name and value are clear and align with the action_id used in blocks and handlers.
backend/settings/urls.py (1)
34-42: Confirm OAuth “state” handling defends against CSRF and tamperingThe callback route is wired; ensure
google_auth_callbackvalidates the OAuthstatesecurely (e.g., signed/nonce-bound server-side, not just a raw member ID).Would you confirm that:
- A cryptographically random state value is generated per login attempt and bound server-side (e.g., cache/DB), or is HMAC-signed to embed member_id safely.
- The callback verifies the state value before exchanging the code.
If not, I can propose a minimal HMAC-based state scheme for Slack flows.
backend/apps/slack/models/__init__.py (1)
3-3: LGTM: Re-exporting GoogleAuth from models package is appropriatePublicly exposing GoogleAuth via the models package aligns with how other models are surfaced and eases imports in views/actions/tests.
backend/apps/slack/migrations/0022_alter_googleauth_options_alter_googleauth_table.py (1)
1-20: LGTM: Migration updates model options and table nameThe migration correctly updates verbose_name_plural and db_table for GoogleAuth and chains to 0021 as expected.
backend/apps/slack/actions/home.py (3)
13-13: LGTM: Constant import for Google sign-in actionUsing SIGN_IN_WITH_GOOGLE_ACTION keeps action registration consistent with the rest of the actions.
33-35: LGTM: Lazy imports to avoid init-time/cyclic issuesImporting GoogleAuth and Member inside the handler is a reasonable trade-off to avoid app init or circular import issues in Slack runtime.
127-128: LGTM: Action registration updatedNew SIGN_IN_WITH_GOOGLE_ACTION registered with the app alongside others; matches the header button’s action_id.
backend/apps/slack/views.py (1)
3-11: LGTM on imports; consider adding signing for state verificationThe new imports are fine. For secure OAuth, you’ll need django signing to validate the state (see callback comment).
backend/apps/slack/migrations/0020_alter_googleauth_access_token_and_more.py (1)
11-21: Check DB casting safety: TextField → BinaryField may not auto-cast cleanlyOn PostgreSQL, altering
text→byteacan fail or produce unexpected values without an explicit USING clause. Auto-generated migrations sometimes work, but it’s DB-version sensitive. Since this is transitional (0021/0023 follow quickly), please verify applying this on a database with potential rows.Suggested verification steps before merge:
- Apply the full migration chain on a snapshot with sample plaintext token values and confirm no errors and that values remain readable (post-0023) when KMS is enabled.
- If risk is identified, replace this AlterField with a two-step: add new binary columns, backfill, then drop old ones; or inject a USING clause via
RunSQL.backend/apps/slack/migrations/0019_googleauth.py (1)
12-38: Baseline model creation looks correctOne-to-one to Slack member, token fields and expiry shape are sensible for OAuth state. Given later migrations move to encrypted storage, this is a reasonable starting point.
backend/.env.example (1)
18-18: Redirect URI Matches Registered Route
Confirmed:backend/settings/urls.pydefinespath("integrations/slack/oauth2/callback/", google_auth_callback),which exactly matches the
.env.examplevalue (including trailing slash). No changes needed.backend/apps/common/model_fields.py (1)
29-40: Decryption path is consistent and conciseGood handling of empty values, type validation, and timezone-agnostic bytes handling.
backend/tests/apps/slack/models/google_auth_test.py (2)
91-101: Solid coverage for the "existing valid token" pathGood use of get_or_create and ensuring no unnecessary save occurs.
128-145: Nice: expired token path refreshes via static method and preserves instanceClear expectations without touching the DB.
backend/settings/base.py (1)
26-31: Good separation of Google OAuth configExplicitly enumerating URIs and secrets keeps usage clear across the codebase.
backend/apps/slack/models/google_auth.py (2)
33-38: Good use of KMS-encrypted fields for token storage.Storing access/refresh tokens in KmsEncryptedField is the right call, and handling timezone-awareness on expiry is solid.
97-103: Validate required Google OAuth settings before building the flow.This makes AUTH_ERROR_MESSAGE actionable and avoids late failures.
Apply this diff:
def get_flow(): """Create a Google OAuth flow instance.""" if not settings.IS_GOOGLE_AUTH_ENABLED: raise ValueError(AUTH_ERROR_MESSAGE) - return get_google_auth_client() + required = ( + "GOOGLE_AUTH_CLIENT_ID", + "GOOGLE_AUTH_CLIENT_SECRET", + "GOOGLE_AUTH_REDIRECT_URI", + "GOOGLE_AUTH_AUTH_URI", + "GOOGLE_AUTH_TOKEN_URI", + ) + missing = [name for name in required if not getattr(settings, name, None)] + if missing: + raise ValueError(f"{AUTH_ERROR_MESSAGE} Missing: {', '.join(missing)}") + return get_google_auth_client()⛔ Skipped due to learnings
Learnt from: ahmedxgouda PR: OWASP/Nest#2021 File: backend/apps/slack/models/google_auth.py:39-52 Timestamp: 2025-08-12T17:38:56.165Z Learning: In the OWASP Nest project, IS_GOOGLE_AUTH_ENABLED in backend/settings/base.py already validates that all three Google OAuth settings (CLIENT_ID, CLIENT_SECRET, REDIRECT_URI) are present and not None/"None"/"", so additional validation in GoogleAuth.get_flow() is redundant.
backend/apps/slack/migrations/0023_alter_googleauth_access_token_and_more.py
Outdated
Show resolved
Hide resolved
…st.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (4)
backend/tests/apps/nest/models/google_account_authorization_test.py (3)
50-56: Don't assert reverse OneToOne accessors via ORM instances; assert via model metadata instead.Accessing
self.member.google_account_authorizationwithout saving triggers ORM access and couples the test to KMS/DB. Assert the accessor and relationship using model metadata.- def test_one_to_one_relationship(self): - """Test one-to-one relationship with Member.""" - auth = GoogleAccountAuthorization(member=self.member, access_token=self.valid_token) - - assert self.member.google_account_authorization == auth - assert auth.member == self.member + def test_one_to_one_relationship(self): + """Test OneToOne relationship metadata.""" + field = GoogleAccountAuthorization._meta.get_field("member") + assert field.related_model is Member + assert field.remote_field.get_accessor_name() == "google_account_authorization"
24-26: Use str, not bytes, for tokens in tests (KmsEncryptedField expects str).Tests currently set tokens as byte strings and assert against bytes. This will diverge from production behavior and can mask issues (e.g., field prep expecting str). Convert all token literals and related assertions to plain str.
Apply the following diff hunks:
@@ - self.valid_token = b"valid_token" - self.valid_refresh_token = b"valid_refresh_token" + self.valid_token = "valid_token" + self.valid_refresh_token = "valid_refresh_token" @@ - mock_credentials_instance = Mock() - mock_credentials_instance.token = b"token" # NOSONAR - mock_credentials_instance.refresh_token = b"refresh_token" + mock_credentials_instance = Mock() + mock_credentials_instance.token = "token" # NOSONAR + mock_credentials_instance.refresh_token = "refresh_token" @@ - assert auth.access_token == b"token" - assert auth.refresh_token == b"refresh_token" + assert auth.access_token == "token" + assert auth.refresh_token == "refresh_token" @@ - mock_credentials = Mock() - mock_credentials.token = b"token" # NOSONAR - mock_credentials.refresh_token = b"refresh_token" + mock_credentials = Mock() + mock_credentials.token = "token" # NOSONAR + mock_credentials.refresh_token = "refresh_token" @@ - assert result.access_token == b"token" - assert result.refresh_token == b"refresh_token" + assert result.access_token == "token" + assert result.refresh_token == "refresh_token" @@ - mock_get_or_create.assert_called_once_with( + mock_get_or_create.assert_called_once_with( member=self.member, defaults={ - "access_token": b"token", - "refresh_token": b"refresh_token", + "access_token": "token", + "refresh_token": "refresh_token", "expires_at": self.future_time, "scope": ["https://www.googleapis.com/auth/userinfo.profile"], }, )If helpful, I can push an automated commit updating all remaining byte-token literals across tests.
#!/usr/bin/env bash # Find lingering byte-string tokens in test files rg -nP "b\"(token|refresh_token|valid_token)\"" backend/tests --type py -C1Also applies to: 268-276, 276-281, 361-378, 379-387
93-98: Fix incorrect parameter type passed to authorize().
GoogleAccountAuthorization.authorize()expects aslack_user_id: str, but the test passes a Member instance.- with pytest.raises(ValueError, match="AWS KMS is not enabled"): - GoogleAccountAuthorization.authorize(self.member) + with pytest.raises(ValueError, match="AWS KMS is not enabled"): + GoogleAccountAuthorization.authorize(self.member.slack_user_id)backend/apps/nest/models/google_account_authorization.py (1)
118-141: Harden token exchange; wrap fetch_token and avoid logging PII.Network/OAuth errors will currently bubble and 500 the view. Wrap
fetch_tokenand surface a user-friendlyValidationError. Also avoid logging the raw state/token values.- flow = GoogleAccountAuthorization.get_flow() - flow.redirect_uri = settings.GOOGLE_AUTH_REDIRECT_URI - flow.fetch_token(authorization_response=auth_response) + flow = GoogleAccountAuthorization.get_flow() + flow.redirect_uri = settings.GOOGLE_AUTH_REDIRECT_URI + try: + flow.fetch_token(authorization_response=auth_response) + except Exception as e: # google/auth/requests exceptions + raise ValidationError("Failed to retrieve Google OAuth tokens.") from e @@ - auth, _ = GoogleAccountAuthorization.objects.get_or_create( + auth, _ = GoogleAccountAuthorization.objects.get_or_create( member=member, defaults={ "access_token": flow.credentials.token, "refresh_token": flow.credentials.refresh_token, "expires_at": expires_at, "scope": settings.GOOGLE_AUTH_SCOPES, }, )
🧹 Nitpick comments (6)
backend/tests/apps/nest/models/google_account_authorization_test.py (2)
204-223: Optional: Assert the returned (url, state) tuple from authorize() for first-time auth.You're validating
authorization_urlwas called with the expected args, but not verifying thatauthorize()actually returns the(url, state)tuple. Add an assertion for the return value to increase coverage.Example:
url_state = GoogleAccountAuthorization.authorize(self.member.slack_user_id) assert isinstance(url_state, tuple) and len(url_state) == 2
350-359: Optional: Use a realistic URL string for auth_response.Passing
{}asauth_responserelies on mocks tolerating non-string inputs. Provide a minimal realistic callback URL (e.g., "https://example.com/callback?state=...&code=...") to keep the test closer to runtime behavior.- result = GoogleAccountAuthorization.authorize_callback({}) + result = GoogleAccountAuthorization.authorize_callback( + "https://example.com/callback?state=test_state&code=abc" + )backend/apps/nest/models/google_account_authorization.py (4)
98-106: Handle Google OAuth error callbacks early (access_denied, etc.).If the user cancels or an error occurs, Google includes
error/error_descriptionin the callback. Handle this before state verification for a cleaner UX.- parsed_url = urlparse(auth_response) - state = parse_qs(parsed_url.query).get("state", [None])[0] + parsed_url = urlparse(auth_response) + query = parse_qs(parsed_url.query) + error = query.get("error", [None])[0] + if error: + description = query.get("error_description", [error])[0] + raise ValidationError(description) + state = query.get("state", [None])[0]
64-86: Optional: Centralize feature/config checks in a helper and raise ImproperlyConfigured.Checks for
IS_GOOGLE_AUTH_ENABLED/IS_AWS_KMS_ENABLEDare duplicated across methods; messages also imply missing client credentials. Consider a single_ensure_config()guard that validates flags and required settings, raisingImproperlyConfigured. This reduces duplication and clarifies intent.Example:
+from django.core.exceptions import ImproperlyConfigured @@ +REQUIRED_GOOGLE_SETTINGS = ( + "GOOGLE_AUTH_CLIENT_ID", + "GOOGLE_AUTH_CLIENT_SECRET", + "GOOGLE_AUTH_REDIRECT_URI", + "GOOGLE_AUTH_AUTH_URI", + "GOOGLE_AUTH_TOKEN_URI", +) + class GoogleAccountAuthorization(models.Model): @@ + @staticmethod + def _ensure_config(): + if not settings.IS_GOOGLE_AUTH_ENABLED: + raise ImproperlyConfigured("Google OAuth is disabled. Set IS_GOOGLE_AUTH_ENABLED=True.") + missing = [s for s in REQUIRED_GOOGLE_SETTINGS if not getattr(settings, s, None)] + if missing: + raise ImproperlyConfigured(f"Missing Google OAuth settings: {', '.join(missing)}") + if not settings.IS_AWS_KMS_ENABLED: + raise ImproperlyConfigured(KMS_ERROR_MESSAGE)Then call
_ensure_config()at the start ofauthorize,authorize_callback,get_flow, andrefresh_access_token.
35-45: Model design: member is nullable; consider uniqueness and identity.With
member=null=Trueand no alternative unique identifier (e.g., Google account ID or email), the table may accumulate duplicate or orphaned records. If Slack Member can be absent during bootstrap, consider:
- Storing
google_subject(sub) or email and adding a unique constraint.- Making
membernon-nullable once linked, or enforcing a partial unique index.If you want, I can propose a migration strategy with a nullable
google_subjectinitially, then enforce uniqueness.
174-188: Optional: Avoid logging-and-raise in refresh path; message is sufficient.Inside the
except RefreshErrorblock, logging the error and re-raising leads to duplicated logs upstream. Prefer raisingValidationErrorwith a clear message and let the caller log.- except RefreshError as e: - error_message = "Error refreshing Google OAuth token" - logger.exception(error_message) - raise ValidationError(error_message) from e + except RefreshError as e: + raise ValidationError("Error refreshing Google OAuth token") from e
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
backend/apps/nest/models/google_account_authorization.py(1 hunks)backend/tests/apps/nest/models/google_account_authorization_test.py(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-12T17:38:56.165Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#2021
File: backend/apps/slack/models/google_auth.py:39-52
Timestamp: 2025-08-12T17:38:56.165Z
Learning: In the OWASP Nest project, IS_GOOGLE_AUTH_ENABLED in backend/settings/base.py already validates that all three Google OAuth settings (CLIENT_ID, CLIENT_SECRET, REDIRECT_URI) are present and not None/"None"/"", so additional validation in GoogleAuth.get_flow() is redundant.
Applied to files:
backend/apps/nest/models/google_account_authorization.py
📚 Learning: 2025-08-22T06:36:42.582Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#2089
File: backend/apps/nest/models/google_account_authorization.py:61-62
Timestamp: 2025-08-22T06:36:42.582Z
Learning: In the OWASP/Nest project, ruff linting rules discourage using logger.error() immediately before raising exceptions as it creates redundant logging. The preferred approach is to remove the logging call and let the caller handle logging the exception appropriately.
Applied to files:
backend/apps/nest/models/google_account_authorization.py
🔇 Additional comments (1)
backend/apps/nest/models/google_account_authorization.py (1)
151-155: Bug: timezone.timedelta does not exist; import timedelta from datetime.
django.utils.timezonehas notimedelta. This will raise at runtime. Usefrom datetime import timedeltaand referencetimedelta(...).@@ -from django.utils import timezone +from django.utils import timezone +from datetime import timedelta @@ - return self.expires_at is None or self.expires_at <= timezone.now() + timezone.timedelta( - seconds=60 - ) + return ( + self.expires_at is None + or self.expires_at <= timezone.now() + timedelta(seconds=60) + )⛔ Skipped due to learnings
Learnt from: ahmedxgouda PR: OWASP/Nest#2021 File: backend/apps/slack/models/google_auth.py:77-81 Timestamp: 2025-08-12T17:45:36.105Z Learning: django.utils.timezone.timedelta exists and is an alias for datetime.timedelta. It can be used directly after importing timezone from django.utils.
| if not settings.IS_GOOGLE_AUTH_ENABLED: | ||
| logger.exception(AUTH_ERROR_MESSAGE) | ||
| raise ValueError(AUTH_ERROR_MESSAGE) | ||
|
|
||
| if not settings.IS_AWS_KMS_ENABLED: | ||
| logger.exception(KMS_ERROR_MESSAGE) | ||
| raise ValueError(KMS_ERROR_MESSAGE) | ||
| parsed_url = urlparse(auth_response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove pre-raise logging; raise directly.
Logging immediately before raising is discouraged in this codebase (ruff + local convention) and adds noise. Raise directly.
- if not settings.IS_GOOGLE_AUTH_ENABLED:
- logger.exception(AUTH_ERROR_MESSAGE)
- raise ValueError(AUTH_ERROR_MESSAGE)
-
- if not settings.IS_AWS_KMS_ENABLED:
- logger.exception(KMS_ERROR_MESSAGE)
- raise ValueError(KMS_ERROR_MESSAGE)
+ if not settings.IS_GOOGLE_AUTH_ENABLED:
+ raise ValueError(AUTH_ERROR_MESSAGE)
+ if not settings.IS_AWS_KMS_ENABLED:
+ raise ValueError(KMS_ERROR_MESSAGE)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if not settings.IS_GOOGLE_AUTH_ENABLED: | |
| logger.exception(AUTH_ERROR_MESSAGE) | |
| raise ValueError(AUTH_ERROR_MESSAGE) | |
| if not settings.IS_AWS_KMS_ENABLED: | |
| logger.exception(KMS_ERROR_MESSAGE) | |
| raise ValueError(KMS_ERROR_MESSAGE) | |
| parsed_url = urlparse(auth_response) | |
| if not settings.IS_GOOGLE_AUTH_ENABLED: | |
| raise ValueError(AUTH_ERROR_MESSAGE) | |
| if not settings.IS_AWS_KMS_ENABLED: | |
| raise ValueError(KMS_ERROR_MESSAGE) | |
| parsed_url = urlparse(auth_response) |
🤖 Prompt for AI Agents
In backend/apps/nest/models/google_account_authorization.py around lines 91 to
98, remove the pre-raise logger.exception calls and raise the ValueError
directly; replace "logger.exception(AUTH_ERROR_MESSAGE); raise
ValueError(AUTH_ERROR_MESSAGE)" with just "raise ValueError(AUTH_ERROR_MESSAGE)"
and similarly replace the KMS logging+raise with just "raise
ValueError(KMS_ERROR_MESSAGE)" so no logging occurs immediately before the
raises.
…google-slack-integration
…google-slack-integration
backend/apps/nest/clients/kms.py
Outdated
| _kms_client = None | ||
|
|
||
|
|
||
| def get_kms_client() -> KmsClient: | ||
| """Get the AWS KMS client.""" | ||
| global _kms_client # noqa: PLW0603 | ||
| if _kms_client is None: | ||
| _kms_client = KmsClient() | ||
| return _kms_client |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not quite. It may be look like one but it's a global variable technically. And its discouraged to use them -- see the PLW0603 you suppressed (I suggest paying more attention to what automated checks suggest)
| @property | ||
| def is_token_expired(self): | ||
| """Check if the access token is expired.""" | ||
| return self.expires_at is None or self.expires_at <= timezone.now() + timezone.timedelta( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 60 seconds timedelta?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rabbit suggested that, there is a case that the user may use the token near the expiration date.
|
arkid15r
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it's still not a singleton -- just a regular class. Nevermind, let's fix that later.
76cf178
into
OWASP:feature/nestbot-calendar-events
* Add google libs * Add model * Update logic and add tests * Update tests, apply make-check and suggestions * Skip sonar and apply rabbit suggestion * Update poetry lock * Add migrations * Change user to member * Update env.example * Update poetry.lock * Update tokens to binary field * Improve auth logic * Apply migrations * Apply rabbit's suggestions * Separate google client from slack.GoogleAuth model * Convert singleton to factory * Update auth_uri * Apply suggestions * Add meta class * Update refresh logic and tests * Make google auth credentials secret * Update code * Apply suggestions * Update tests and member related name * Clean up migrations * Add kms key id to env * Add AWS to settings * Add KMS client, encryption and decryption * Separate kms client from GoogleAuth model * Update kms client * Update tests * Update refresh and tests * Apply rabbit suggestion * Apply check-spelling * Apply rabbit suggestion and add google client test * Add custom field for kms * Update tests * Apply suggestions * Update tests * Guard authenticate method with KMS * Update tests * Add migrations * Update settings * Clean up migrations * Add google callback * Add google sign in button * Apply suggestions * Apply suggestion * Update tests * Change name * Add migrations * Refactor google and kms clients * Update tests * Add sign in command * Update tests * Add handler test * Clean up migrations * Apply suggestions * Update backend/tests/apps/nest/models/google_account_authorization_test.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Apply rabbit's suggestion * Update poetry lock * Update code * Apply suggestions --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Add google libs * Add model * Update logic and add tests * Update tests, apply make-check and suggestions * Skip sonar and apply rabbit suggestion * Update poetry lock * Add migrations * Change user to member * Update env.example * Update poetry.lock * Update tokens to binary field * Improve auth logic * Apply migrations * Apply rabbit's suggestions * Separate google client from slack.GoogleAuth model * Convert singleton to factory * Update auth_uri * Apply suggestions * Add meta class * Update refresh logic and tests * Make google auth credentials secret * Update code * Apply suggestions * Update tests and member related name * Clean up migrations * Add kms key id to env * Add AWS to settings * Add KMS client, encryption and decryption * Separate kms client from GoogleAuth model * Update kms client * Update tests * Update refresh and tests * Apply rabbit suggestion * Apply check-spelling * Apply rabbit suggestion and add google client test * Add custom field for kms * Update tests * Apply suggestions * Update tests * Guard authenticate method with KMS * Update tests * Add migrations * Update settings * Clean up migrations * Add google callback * Add google sign in button * Apply suggestions * Apply suggestion * Update tests * Change name * Add migrations * Refactor google and kms clients * Update tests * Add sign in command * Update tests * Add handler test * Clean up migrations * Apply suggestions * Update backend/tests/apps/nest/models/google_account_authorization_test.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Apply rabbit's suggestion * Update poetry lock * Update code * Apply suggestions --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Add google libs * Add model * Update logic and add tests * Update tests, apply make-check and suggestions * Skip sonar and apply rabbit suggestion * Update poetry lock * Add migrations * Change user to member * Update env.example * Update poetry.lock * Update tokens to binary field * Improve auth logic * Apply migrations * Apply rabbit's suggestions * Separate google client from slack.GoogleAuth model * Convert singleton to factory * Update auth_uri * Apply suggestions * Add meta class * Update refresh logic and tests * Make google auth credentials secret * Update code * Apply suggestions * Update tests and member related name * Clean up migrations * Add kms key id to env * Add AWS to settings * Add KMS client, encryption and decryption * Separate kms client from GoogleAuth model * Update kms client * Update tests * Update refresh and tests * Apply rabbit suggestion * Apply check-spelling * Apply rabbit suggestion and add google client test * Add custom field for kms * Update tests * Apply suggestions * Update tests * Guard authenticate method with KMS * Update tests * Add migrations * Update settings * Clean up migrations * Add google callback * Add google sign in button * Apply suggestions * Apply suggestion * Update tests * Change name * Add migrations * Refactor google and kms clients * Update tests * Add sign in command * Update tests * Add handler test * Clean up migrations * Apply suggestions * Update backend/tests/apps/nest/models/google_account_authorization_test.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Apply rabbit's suggestion * Update poetry lock * Update code * Apply suggestions --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Add google libs * Add model * Update logic and add tests * Update tests, apply make-check and suggestions * Skip sonar and apply rabbit suggestion * Update poetry lock * Add migrations * Change user to member * Update env.example * Update poetry.lock * Update tokens to binary field * Improve auth logic * Apply migrations * Apply rabbit's suggestions * Separate google client from slack.GoogleAuth model * Convert singleton to factory * Update auth_uri * Apply suggestions * Add meta class * Update refresh logic and tests * Make google auth credentials secret * Update code * Apply suggestions * Update tests and member related name * Clean up migrations * Add kms key id to env * Add AWS to settings * Add KMS client, encryption and decryption * Separate kms client from GoogleAuth model * Update kms client * Update tests * Update refresh and tests * Apply rabbit suggestion * Apply check-spelling * Apply rabbit suggestion and add google client test * Add custom field for kms * Update tests * Apply suggestions * Update tests * Guard authenticate method with KMS * Update tests * Add migrations * Update settings * Clean up migrations * Add google callback * Add google sign in button * Apply suggestions * Apply suggestion * Update tests * Change name * Add migrations * Refactor google and kms clients * Update tests * Add sign in command * Update tests * Add handler test * Clean up migrations * Apply suggestions * Update backend/tests/apps/nest/models/google_account_authorization_test.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Apply rabbit's suggestion * Update poetry lock * Update code * Apply suggestions --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Add google libs * Add model * Update logic and add tests * Update tests, apply make-check and suggestions * Skip sonar and apply rabbit suggestion * Update poetry lock * Add migrations * Change user to member * Update env.example * Update poetry.lock * Update tokens to binary field * Improve auth logic * Apply migrations * Apply rabbit's suggestions * Separate google client from slack.GoogleAuth model * Convert singleton to factory * Update auth_uri * Apply suggestions * Add meta class * Update refresh logic and tests * Make google auth credentials secret * Update code * Apply suggestions * Update tests and member related name * Clean up migrations * Add kms key id to env * Add AWS to settings * Add KMS client, encryption and decryption * Separate kms client from GoogleAuth model * Update kms client * Update tests * Update refresh and tests * Apply rabbit suggestion * Apply check-spelling * Apply rabbit suggestion and add google client test * Add custom field for kms * Update tests * Apply suggestions * Update tests * Guard authenticate method with KMS * Update tests * Add migrations * Update settings * Clean up migrations * Add google callback * Add google sign in button * Apply suggestions * Apply suggestion * Update tests * Change name * Add migrations * Refactor google and kms clients * Update tests * Add sign in command * Update tests * Add handler test * Clean up migrations * Apply suggestions * Update backend/tests/apps/nest/models/google_account_authorization_test.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Apply rabbit's suggestion * Update poetry lock * Update code * Apply suggestions --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Add google libs * Add model * Update logic and add tests * Update tests, apply make-check and suggestions * Skip sonar and apply rabbit suggestion * Update poetry lock * Add migrations * Change user to member * Update env.example * Update poetry.lock * Update tokens to binary field * Improve auth logic * Apply migrations * Apply rabbit's suggestions * Separate google client from slack.GoogleAuth model * Convert singleton to factory * Update auth_uri * Apply suggestions * Add meta class * Update refresh logic and tests * Make google auth credentials secret * Update code * Apply suggestions * Update tests and member related name * Clean up migrations * Add kms key id to env * Add AWS to settings * Add KMS client, encryption and decryption * Separate kms client from GoogleAuth model * Update kms client * Update tests * Update refresh and tests * Apply rabbit suggestion * Apply check-spelling * Apply rabbit suggestion and add google client test * Add custom field for kms * Update tests * Apply suggestions * Update tests * Guard authenticate method with KMS * Update tests * Add migrations * Update settings * Clean up migrations * Add google callback * Add google sign in button * Apply suggestions * Apply suggestion * Update tests * Change name * Add migrations * Refactor google and kms clients * Update tests * Add sign in command * Update tests * Add handler test * Clean up migrations * Apply suggestions * Update backend/tests/apps/nest/models/google_account_authorization_test.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Apply rabbit's suggestion * Update poetry lock * Update code * Apply suggestions --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Add google libs * Add model * Update logic and add tests * Update tests, apply make-check and suggestions * Skip sonar and apply rabbit suggestion * Update poetry lock * Add migrations * Change user to member * Update env.example * Update poetry.lock * Update tokens to binary field * Improve auth logic * Apply migrations * Apply rabbit's suggestions * Separate google client from slack.GoogleAuth model * Convert singleton to factory * Update auth_uri * Apply suggestions * Add meta class * Update refresh logic and tests * Make google auth credentials secret * Update code * Apply suggestions * Update tests and member related name * Clean up migrations * Add kms key id to env * Add AWS to settings * Add KMS client, encryption and decryption * Separate kms client from GoogleAuth model * Update kms client * Update tests * Update refresh and tests * Apply rabbit suggestion * Apply check-spelling * Apply rabbit suggestion and add google client test * Add custom field for kms * Update tests * Apply suggestions * Update tests * Guard authenticate method with KMS * Update tests * Add migrations * Update settings * Clean up migrations * Add google callback * Add google sign in button * Apply suggestions * Apply suggestion * Update tests * Change name * Add migrations * Refactor google and kms clients * Update tests * Add sign in command * Update tests * Add handler test * Clean up migrations * Apply suggestions * Update backend/tests/apps/nest/models/google_account_authorization_test.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Apply rabbit's suggestion * Update poetry lock * Update code * Apply suggestions --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Add google libs * Add model * Update logic and add tests * Update tests, apply make-check and suggestions * Skip sonar and apply rabbit suggestion * Update poetry lock * Add migrations * Change user to member * Update env.example * Update poetry.lock * Update tokens to binary field * Improve auth logic * Apply migrations * Apply rabbit's suggestions * Separate google client from slack.GoogleAuth model * Convert singleton to factory * Update auth_uri * Apply suggestions * Add meta class * Update refresh logic and tests * Make google auth credentials secret * Update code * Apply suggestions * Update tests and member related name * Clean up migrations * Add kms key id to env * Add AWS to settings * Add KMS client, encryption and decryption * Separate kms client from GoogleAuth model * Update kms client * Update tests * Update refresh and tests * Apply rabbit suggestion * Apply check-spelling * Apply rabbit suggestion and add google client test * Add custom field for kms * Update tests * Apply suggestions * Update tests * Guard authenticate method with KMS * Update tests * Add migrations * Update settings * Clean up migrations * Add google callback * Add google sign in button * Apply suggestions * Apply suggestion * Update tests * Change name * Add migrations * Refactor google and kms clients * Update tests * Add sign in command * Update tests * Add handler test * Clean up migrations * Apply suggestions * Update backend/tests/apps/nest/models/google_account_authorization_test.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Apply rabbit's suggestion * Update poetry lock * Update code * Apply suggestions --------- Co-authored-by: Arkadii Yakovets <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>



Proposed change
Resolves #2044
Add the PR description here.
Checklist
make check-testlocally; all checks and tests passed.