The face from the provider, and whose business a password is
The password tab was hidden for accounts with no password, which is not the same as accounts that sign in through the provider — an account linked to one kept its password and therefore kept the tab. That is precisely the account the person setting this up is using. It goes by "signs in over there" now; the password it kept is the emergency door, set by an administrator. And the picture. The provider sends one, the server fetches it at each sign-in and stores it encrypted in a table beside the users rather than in them, and serves it back through the API — so the page never talks to the provider, no third-party cookie is needed for a face to appear, and it survives the provider being down like everything else here. Fetched once per account into an object URL, since an img tag cannot carry a bearer token. The suite's provider now serves a picture too, and the check asks the browser whether it actually decoded rather than whether the element exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
40bdf81aa2
commit
a2b3f91e00
12 files changed
+325
-22
No files matched your search
@@ -296,9 +296,41 @@ async function completeLogin({ code, state }) {
|
||||
username: claims.preferred_username || null,
|
||||
email: claims.email || null,
|
||||
displayName: claims.name || claims.given_name || null,
|
||||
picture: typeof claims.picture === "string" ? claims.picture : null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The profile picture, fetched by the server rather than by the browser.
|
||||
*
|
||||
* Three reasons it happens here. The address is often behind the provider's own
|
||||
* session, which this app's visitors may not have in a third-party context; the
|
||||
* browser would have to talk to the provider on every page, which is a request
|
||||
* to somewhere else on every load; and an image that lives here keeps working
|
||||
* when the provider does not, like everything else in this app.
|
||||
*
|
||||
* Best effort in the strict sense: a slow, missing, oversized or non-image
|
||||
* answer gives null and the sign-in carries on. A picture is never a reason to
|
||||
* fail to let somebody in.
|
||||
*/
|
||||
async function fetchPicture(url, { maxBytes = 512 * 1024 } = {}) {
|
||||
if (!url || !/^https?:\/\//i.test(url)) return null;
|
||||
try {
|
||||
const res = await fetch(url, { signal: AbortSignal.timeout(4000), redirect: "follow" });
|
||||
if (!res.ok) return null;
|
||||
const type = String(res.headers.get("content-type") || "").split(";")[0].trim().toLowerCase();
|
||||
if (!/^image\/(png|jpeg|webp|gif)$/.test(type)) return null;
|
||||
const declared = Number(res.headers.get("content-length"));
|
||||
if (Number.isFinite(declared) && declared > maxBytes) return null;
|
||||
const bytes = Buffer.from(await res.arrayBuffer());
|
||||
// The header may have lied or been absent; the bytes are the truth.
|
||||
if (!bytes.length || bytes.length > maxBytes) return null;
|
||||
return { bytes, type };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── The ticket the browser carries home ─── */
|
||||
|
||||
function issueTicket(payload) {
|
||||
@@ -325,6 +357,7 @@ module.exports = {
|
||||
describeConfiguration,
|
||||
beginLogin,
|
||||
completeLogin,
|
||||
fetchPicture,
|
||||
issueTicket,
|
||||
redeemTicket,
|
||||
};
|
||||
Reference in new issue
Block a user