Skip to content

Commit 2c000a2

Browse files
authored
feat(auth): surface is_new_user in OAuth login responses (#8011)
1 parent d53e0d7 commit 2c000a2

4 files changed

Lines changed: 35 additions & 15 deletions

File tree

api/custom_auth/oauth/serializers.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
UserModel = get_user_model()
2323

2424

25+
class OAuthTokenSerializer(serializers.Serializer): # type: ignore[type-arg]
26+
key = serializers.CharField(read_only=True)
27+
is_new_user = serializers.BooleanField(read_only=True)
28+
29+
2530
class OAuthLoginSerializer(InviteLinkValidationMixin, serializers.Serializer): # type: ignore[type-arg]
2631
access_token = serializers.CharField(
2732
required=True,
@@ -42,6 +47,7 @@ class OAuthLoginSerializer(InviteLinkValidationMixin, serializers.Serializer):
4247
utm_data = UTMDataSerializer(required=False, allow_null=True)
4348
auth_type: AuthType | None = None
4449
user_model_id_attribute: str = "id"
50+
is_new_user: bool = False
4551

4652
class Meta:
4753
abstract = True
@@ -98,6 +104,7 @@ def _get_user(self, user_data: dict): # type: ignore[type-arg,no-untyped-def]
98104
user = FFAdminUser.objects.create(
99105
**user_data, email=email.lower(), sign_up_type=sign_up_type
100106
)
107+
self.is_new_user = True
101108

102109
# On first OAuth signup, we register the hubspot cookies and utms before creating the hubspot contact
103110
if request := self.context.get("request"):

api/custom_auth/oauth/views.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
from custom_auth.oauth.serializers import (
1515
GithubLoginSerializer,
1616
GoogleLoginSerializer,
17+
OAuthTokenSerializer,
1718
)
18-
from custom_auth.serializers import CustomTokenSerializer
1919

2020
logger = logging.getLogger(__name__)
2121

@@ -26,7 +26,7 @@
2626

2727
@extend_schema(
2828
request=GoogleLoginSerializer,
29-
responses={200: CustomTokenSerializer, 502: ErrorSerializer},
29+
responses={200: OAuthTokenSerializer, 502: ErrorSerializer},
3030
)
3131
@api_view(["POST"])
3232
@permission_classes([AllowAny])
@@ -39,7 +39,11 @@ def login_with_google(request): # type: ignore[no-untyped-def]
3939
token = serializer.save()
4040
if settings.COOKIE_AUTH_ENABLED:
4141
return authorise_response(token.user, Response(status=HTTP_204_NO_CONTENT))
42-
return Response(data=CustomTokenSerializer(instance=token).data)
42+
return Response(
43+
data=OAuthTokenSerializer(
44+
{"key": token.key, "is_new_user": serializer.is_new_user}
45+
).data
46+
)
4347
except GoogleError as e:
4448
logger.warning("%s: %s" % (GOOGLE_AUTH_ERROR_MESSAGE, str(e)))
4549
return Response(
@@ -50,7 +54,7 @@ def login_with_google(request): # type: ignore[no-untyped-def]
5054

5155
@extend_schema(
5256
request=GithubLoginSerializer,
53-
responses={200: CustomTokenSerializer, 502: ErrorSerializer},
57+
responses={200: OAuthTokenSerializer, 502: ErrorSerializer},
5458
)
5559
@api_view(["POST"])
5660
@permission_classes([AllowAny])
@@ -63,7 +67,11 @@ def login_with_github(request): # type: ignore[no-untyped-def]
6367
token = serializer.save()
6468
if settings.COOKIE_AUTH_ENABLED:
6569
return authorise_response(token.user, Response(status=HTTP_204_NO_CONTENT))
66-
return Response(data=CustomTokenSerializer(instance=token).data)
70+
return Response(
71+
data=OAuthTokenSerializer(
72+
{"key": token.key, "is_new_user": serializer.is_new_user}
73+
).data
74+
)
6775
except GithubError as e:
6876
logger.warning("%s: %s" % (GITHUB_AUTH_ERROR_MESSAGE, str(e)))
6977
return Response(

api/tests/unit/custom_auth/oauth/test_unit_oauth_views.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def test_google_oauth_login__invite_exists_and_registration_disabled__returns_20
7878

7979
# Then
8080
assert response.status_code == status.HTTP_200_OK
81+
assert response.json()["is_new_user"] is True
8182

8283

8384
@mock.patch("custom_auth.oauth.serializers.GithubUser")
@@ -107,6 +108,7 @@ def test_github_oauth_login__invite_exists_and_registration_disabled__returns_20
107108

108109
# Then
109110
assert response.status_code == status.HTTP_200_OK
111+
assert response.json()["is_new_user"] is True
110112

111113

112114
@mock.patch("custom_auth.oauth.serializers.get_user_info")
@@ -133,6 +135,7 @@ def test_google_oauth_login__existing_user_and_registration_disabled__returns_20
133135
# Then
134136
assert response.status_code == status.HTTP_200_OK
135137
assert "key" in response.json()
138+
assert response.json()["is_new_user"] is False
136139

137140

138141
@mock.patch("custom_auth.oauth.serializers.GithubUser")
@@ -161,6 +164,7 @@ def test_github_oauth_login__existing_user_and_registration_disabled__returns_20
161164
# Then
162165
assert response.status_code == status.HTTP_200_OK
163166
assert "key" in response.json()
167+
assert response.json()["is_new_user"] is False
164168

165169

166170
def test_google_oauth_login__case_insensitive_email__updates_existing_user(

openapi.yaml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ paths:
497497
content:
498498
application/json:
499499
schema:
500-
$ref: '#/components/schemas/CustomToken'
500+
$ref: '#/components/schemas/OAuthToken'
501501
'502':
502502
description: ''
503503
content:
@@ -531,7 +531,7 @@ paths:
531531
content:
532532
application/json:
533533
schema:
534-
$ref: '#/components/schemas/CustomToken'
534+
$ref: '#/components/schemas/OAuthToken'
535535
'502':
536536
description: ''
537537
content:
@@ -19140,14 +19140,6 @@ components:
1914019140
$ref: '#/components/schemas/MultivariateFeatureStateValue'
1914119141
required:
1914219142
- feature_state_value
19143-
CustomToken:
19144-
type: object
19145-
properties:
19146-
key:
19147-
type: string
19148-
maxLength: 40
19149-
required:
19150-
- key
1915119143
CustomUserCreate:
1915219144
type: object
1915319145
properties:
@@ -22049,6 +22041,15 @@ components:
2204922041
- app_id
2205022042
NullEnum:
2205122043
type: 'null'
22044+
OAuthToken:
22045+
type: object
22046+
properties:
22047+
key:
22048+
type: string
22049+
readOnly: true
22050+
is_new_user:
22051+
type: boolean
22052+
readOnly: true
2205222053
OrganisationAPIUsageNotification:
2205322054
type: object
2205422055
properties:

0 commit comments

Comments
 (0)