Skip to content

Commit 56ac9de

Browse files
authored
Merge pull request #606 from BloomBooks/BL14503_NoEmailNoLogin
Don't report successful login more than once or if no email (BL-14503) (#606)
2 parents 0af5593 + e22c1ff commit 56ac9de

3 files changed

Lines changed: 53 additions & 12 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"private": true,
44
"type": "module",
55
"scripts": {
6-
"dev": "yarn copyInBloomPlayer && vite",
6+
"//": "default port is 5173 which is the same for BloomDesktop and can create conflicts",
7+
"dev": "yarn copyInBloomPlayer && vite --port 5174",
78
"serve": "vite preview",
89
"build": "yarn copyInBloomPlayer && vite build",
910
"build:ci": "set CI=true&& yarn build",

src/connection/ParseServerConnection.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,7 @@ function checkIfUserIsModerator() {
113113

114114
export async function connectParseServer(
115115
jwtToken: string,
116-
userId: string
117-
//,returnParseUser: (user: any) => void
116+
emailAddress: string
118117
) {
119118
return new Promise<any>((resolve, reject) => {
120119
const connection = getConnection();
@@ -130,7 +129,7 @@ export async function connectParseServer(
130129
`${connection.url}functions/bloomLink`,
131130
{
132131
token: jwtToken,
133-
id: userId,
132+
id: emailAddress,
134133
},
135134

136135
{
@@ -144,32 +143,49 @@ export async function connectParseServer(
144143
`${connection.url}users`,
145144
{
146145
authData: {
147-
bloom: { token: jwtToken, id: userId },
146+
bloom: { token: jwtToken, id: emailAddress },
148147
},
149-
username: userId,
150-
email: userId, // needed in case we are creating a new user
148+
username: emailAddress,
149+
// Parse requires an `email` field when creating a new _User.
150+
email: emailAddress,
151151
},
152152

153153
{
154154
headers: connection.headers,
155155
}
156156
)
157157
.then((usersResult) => {
158-
if (usersResult.data.sessionToken) {
158+
// We require BOTH a session token and an email.
159+
// We don't actually know why we would ever get here without either.
160+
// But we were sending posts to Bloom with a missing email value in the payload,
161+
// which caused Bloom's `/bloom/api/external/login` handler to throw a runtime exception. See BL-14503.
162+
// I don't see any reason to pretend a non-editor login was successful if email
163+
// is missing, either. And it simplifies the code to just check up front.
164+
if (
165+
usersResult.data.sessionToken &&
166+
usersResult.data.email
167+
) {
159168
LoggedInUser.current = new User(usersResult.data);
160-
//Object.assign(CurrentUser, usersResult.data);
161169
connection.headers["X-Parse-Session-Token"] =
162170
usersResult.data.sessionToken;
163171

164172
if (isForEditor()) {
165173
informEditorOfSuccessfulLogin(usersResult.data);
166174
}
167175

168-
//console.log("Got ParseServer Session ID");
169176
resolve(usersResult.data);
170-
//returnParseUser(result.data);
171177
checkIfUserIsModerator();
172-
} else failedToLoginInToParseServer();
178+
} else {
179+
failedToLoginInToParseServer();
180+
// Reject the Promise returned by `connectParseServer()`.
181+
// This lets callers stop the login flow (for example, `firebase.ts` catches this and
182+
// signs the user out of Firebase) rather than silently continuing.
183+
reject(
184+
new Error(
185+
"Missing sessionToken or email in usersResult.data"
186+
)
187+
);
188+
}
173189
})
174190
.catch((err) => {
175191
failedToLoginInToParseServer();

src/editor.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,35 @@ export function isForEditor() {
1414
return window.location.pathname.includes("login-for-editor");
1515
}
1616

17+
// Tracks a sessionToken we've already notified Bloom (editor) about.
18+
//
19+
// Why this exists:
20+
// - In theory, `connectParseServer()` can be triggered more than once during a single browser session
21+
// (e.g. auth state callbacks can fire more than once, the user can reload the tab, etc.).
22+
// - We have observed multiple posts to Bloom for a single "login-for-editor" attempt.
23+
// In BL-14503, Bloom's `/bloom/api/external/login` endpoint logged multiple
24+
// "External login successful" lines for the same attempt, and also threw when the payload
25+
// was missing `email`. Suppressing duplicate notifications on the Blorg side helps reduce
26+
// noise and was part of an attempt to bolster error handling.
27+
//
28+
// Behavior:
29+
// - Set before sending to suppress duplicates while the POST is in-flight.
30+
// - Cleared on failure. I don't think this much matter, but it seems cleaner.
31+
let notifiedSessionToken: string | undefined;
32+
1733
export function informEditorOfSuccessfulLogin(userData: any) {
34+
if (notifiedSessionToken === userData.sessionToken) {
35+
return;
36+
}
37+
1838
const postData = {
1939
sessionToken: userData.sessionToken,
2040
email: userData.email,
2141
userId: userData.objectId,
2242
};
43+
44+
notifiedSessionToken = userData.sessionToken;
45+
2346
axios
2447
.post(`${getEditorApiUrl()}login`, postData)
2548
.then(() => {
@@ -29,6 +52,7 @@ export function informEditorOfSuccessfulLogin(userData: any) {
2952
.catch((err) => {
3053
LoggedInUser.current!.informEditorResult =
3154
IInformEditorResult.Failure;
55+
notifiedSessionToken = undefined;
3256

3357
console.error("Unable to inform editor of successful login.");
3458
console.error(err);

0 commit comments

Comments
 (0)