mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-10-20 06:37:23 +02:00
fix: save verified email addresses during GitHub registration
This commit is contained in:
1 parent
2d21448a77
commit
444e5ab248
8 files changed
+229
-10
No files matched your search
@@ -0,0 +1,41 @@
|
||||
import UserModel from "./model/users/users.model";
|
||||
import { IUserDocument } from "./model/users/users.types";
|
||||
import { normalizeEmail, notificationEmail } from "./owner-notifications";
|
||||
|
||||
function usableEmail(value: unknown): string | null {
|
||||
const email = normalizeEmail(value);
|
||||
if (!email) return null;
|
||||
const domain = email.split("@")[1].toLowerCase();
|
||||
return ["users.noreply.github.com", "noreply.github.com"].includes(domain) ? null : email;
|
||||
}
|
||||
|
||||
/** Fill a missing address after the GitHub identity has been authenticated. */
|
||||
export async function saveRegistrationEmail(user: IUserDocument, token: string): Promise<void> {
|
||||
if (notificationEmail(user.emails, user.notificationEmail)) return;
|
||||
// Compare the original fields so a concurrent settings save always wins.
|
||||
const previousEmails = user.toObject().emails;
|
||||
const previousPreference = user.notificationEmail ?? null;
|
||||
try {
|
||||
const response = await fetch("https://api.github.com/user/emails?per_page=100", {
|
||||
headers: { Accept: "application/vnd.github+json", Authorization: `Bearer ${token}`,
|
||||
"X-GitHub-Api-Version": "2022-11-28", "User-Agent": "Anonymous-GitHub" },
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
if (!response.ok) return;
|
||||
const data: unknown = await response.json();
|
||||
if (!Array.isArray(data)) return;
|
||||
const verified = data.filter((entry): entry is { email: string; primary?: boolean } =>
|
||||
!!entry && entry.verified === true && !!usableEmail(entry.email));
|
||||
const email = usableEmail((verified.find(entry => entry.primary === true) || verified[0])?.email);
|
||||
if (!email) return;
|
||||
const emails = [{ email, default: true }];
|
||||
const result = await UserModel.updateOne({ _id: user._id, notificationEmail: previousPreference,
|
||||
$or: [{ emails: previousEmails ?? [] },
|
||||
...(!previousEmails?.length ? [{ emails: { $exists: false } }] : [])],
|
||||
}, { $set: { emails } });
|
||||
if (result.modifiedCount) user.emails = emails;
|
||||
} catch {
|
||||
// Missing App email permission or an upstream outage must not prevent login.
|
||||
// The existing email form remains available when automatic collection fails.
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { resetOwnerAccessAlerts } from "../../core/owner-notifications";
|
||||
import { saveRegistrationEmail } from "../../core/registration-email";
|
||||
import { createClient } from "redis";
|
||||
import * as passport from "passport";
|
||||
import * as session from "express-session";
|
||||
@@ -100,12 +101,9 @@ export const verify = async (
|
||||
externalIDs: {
|
||||
github: profile.id,
|
||||
},
|
||||
emails: profile.emails?.map((email) => {
|
||||
return { email: email.value, default: false };
|
||||
}),
|
||||
emails: [],
|
||||
photo,
|
||||
});
|
||||
if (user.emails?.length) user.emails[0].default = true;
|
||||
await user.save();
|
||||
}
|
||||
}
|
||||
@@ -121,6 +119,7 @@ export const verify = async (
|
||||
return;
|
||||
}
|
||||
await setCredential(String(user!._id), accessToken);
|
||||
await saveRegistrationEmail(user!, accessToken);
|
||||
await resetOwnerAccessAlerts(String(user!._id));
|
||||
done(null, { username: user!.username, user });
|
||||
} catch (error) {
|
||||
@@ -208,7 +207,7 @@ router.get(
|
||||
returnTo: safeAuthReturnTo(req.query.returnTo, current ? "/connections" : "/dashboard"), expires: Date.now() + 10 * 60000 };
|
||||
next();
|
||||
},
|
||||
passport.authenticate("github", { scope: ["repo"] }), // Note the scope here
|
||||
passport.authenticate("github", { scope: ["repo", "user:email"] }),
|
||||
function (req: express.Request, res: express.Response) {
|
||||
res.redirect("/");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { notificationEmail, normalizeEmail, resetOwnerAccessAlerts } from "../../core/owner-notifications";
|
||||
import { saveRegistrationEmail } from "../../core/registration-email";
|
||||
import * as express from "express";
|
||||
import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "crypto";
|
||||
import config from "../../config";
|
||||
@@ -94,6 +95,7 @@ router.get("/app/callback", enabled, async (req, res) => {
|
||||
}
|
||||
if (isDisabledAccount(user.status)) throw appError("not_connected", 403);
|
||||
await saveAppGrant(user.id, tokens);
|
||||
await saveRegistrationEmail(user, tokens.access_token);
|
||||
await resetOwnerAccessAlerts(user.id);
|
||||
await new Promise<void>((resolve, reject) => req.login({ username: user!.username, user }, err => err ? reject(err) : resolve()));
|
||||
res.redirect(flow.install ? `/github/app/install?returnTo=${encodeURIComponent(flow.returnTo)}&repository=${encodeURIComponent(flow.repository || "")}` : flow.returnTo);
|
||||
|
||||
Reference in new issue
Block a user