Wire quick login in fallback mode

When external script.js fails and inline fallback runs, connect Naver/Kakao/Google buttons to Auth0 loginWithRedirect (by connection) and show logout/user state after authentication.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dsyoon
2026-02-07 18:28:41 +09:00
parent 812a59bc9f
commit a6e192c738

View File

@@ -22,10 +22,18 @@
// 2) Allowed Callback URLs / Allowed Logout URLs에 현재 사이트 주소를 등록하세요.
// 예: https://drive.daewoongai.com/apps/dashboard/
window.AUTH_CONFIG = {
// end-user가 설정 모달을 사용하는지 여부(기본: false)
allowEndUserConfig: false,
auth0: {
domain: "",
clientId: "",
},
// Auth0 connection 이름(선택). 예: google-oauth2 / kakao / naver
connections: {
google: "",
kakao: "",
naver: "",
},
// 관리 허용 이메일(대소문자 무시)
allowedEmails: [],
};
@@ -383,6 +391,7 @@
const STORAGE_KEY = "links_home_v1";
const THEME_KEY = "links_home_theme_v1";
const AUTH_OVERRIDE_KEY = "links_home_auth_override_v1";
const el = {
q: document.getElementById("q"),
@@ -395,6 +404,13 @@
btnImport: document.getElementById("btnImport"),
btnExport: document.getElementById("btnExport"),
btnTheme: document.getElementById("btnTheme"),
snsLogin: document.getElementById("snsLogin"),
btnNaver: document.getElementById("btnNaver"),
btnKakao: document.getElementById("btnKakao"),
btnGoogle: document.getElementById("btnGoogle"),
btnLogout: document.getElementById("btnLogout"),
user: document.getElementById("user"),
userText: document.getElementById("userText"),
toast: document.getElementById("toast"),
modal: document.getElementById("modal"),
btnClose: document.getElementById("btnClose"),
@@ -662,6 +678,138 @@
applyTheme(prefersLight ? "light" : "dark");
})();
// Auth (fallback): wire SNS quick login buttons
const auth = {
client: null,
user: null,
ready: false,
};
function loadAuthOverride() {
const raw = localStorage.getItem(AUTH_OVERRIDE_KEY);
const data = raw ? safeJsonParse(raw, null) : null;
if (!data || typeof data !== "object") return null;
const auth0 = data.auth0 && typeof data.auth0 === "object" ? data.auth0 : {};
const connections = data.connections && typeof data.connections === "object" ? data.connections : {};
const allowedEmails = Array.isArray(data.allowedEmails) ? data.allowedEmails : [];
return {
auth0: {
domain: String(auth0.domain || "").trim(),
clientId: String(auth0.clientId || "").trim(),
},
connections: {
google: String(connections.google || "").trim(),
kakao: String(connections.kakao || "").trim(),
naver: String(connections.naver || "").trim(),
},
allowedEmails: allowedEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean),
};
}
function getAuthConfig() {
const cfg = window.AUTH_CONFIG && typeof window.AUTH_CONFIG === "object" ? window.AUTH_CONFIG : {};
const auth0 = cfg.auth0 && typeof cfg.auth0 === "object" ? cfg.auth0 : {};
const connections = cfg.connections && typeof cfg.connections === "object" ? cfg.connections : {};
const allowedEmails = Array.isArray(cfg.allowedEmails) ? cfg.allowedEmails : [];
const base = {
auth0: {
domain: String(auth0.domain || "").trim(),
clientId: String(auth0.clientId || "").trim(),
},
connections: {
google: String(connections.google || "").trim(),
kakao: String(connections.kakao || "").trim(),
naver: String(connections.naver || "").trim(),
},
allowedEmails: allowedEmails.map((e) => String(e).trim().toLowerCase()).filter(Boolean),
};
const over = loadAuthOverride();
if (!over) return base;
return {
auth0: {
domain: over.auth0.domain || base.auth0.domain,
clientId: over.auth0.clientId || base.auth0.clientId,
},
connections: {
google: over.connections.google || base.connections.google,
kakao: over.connections.kakao || base.connections.kakao,
naver: over.connections.naver || base.connections.naver,
},
allowedEmails: over.allowedEmails.length ? over.allowedEmails : base.allowedEmails,
};
}
function currentUrlNoQuery() {
const u = new URL(location.href);
u.searchParams.delete("code");
u.searchParams.delete("state");
return u.toString();
}
async function ensureAuthClient() {
if (auth.client) return auth.client;
const cfg = getAuthConfig();
if (!cfg.auth0.domain || !cfg.auth0.clientId) return null;
if (typeof window.createAuth0Client !== "function") return null;
auth.client = await window.createAuth0Client({
domain: cfg.auth0.domain,
clientId: cfg.auth0.clientId,
authorizationParams: {
redirect_uri: location.origin === "null" ? location.href : location.origin + location.pathname,
},
cacheLocation: "localstorage",
useRefreshTokens: true,
});
return auth.client;
}
async function initAuth() {
auth.ready = true;
const client = await ensureAuthClient();
if (!client) {
// No config: keep buttons visible but disabled style
return;
}
const u = new URL(location.href);
const isCallback = u.searchParams.has("code") && u.searchParams.has("state");
if (isCallback) {
try {
await client.handleRedirectCallback();
} finally {
history.replaceState({}, document.title, currentUrlNoQuery());
}
}
const isAuthed = await client.isAuthenticated();
auth.user = isAuthed ? await client.getUser() : null;
if (el.btnLogout) el.btnLogout.hidden = !auth.user;
if (el.snsLogin) el.snsLogin.hidden = Boolean(auth.user);
if (el.user) el.user.hidden = !auth.user;
if (el.userText && auth.user) el.userText.textContent = auth.user.email || auth.user.name || "로그인됨";
}
async function loginWithConnection(provider) {
const cfg = getAuthConfig();
const client = await ensureAuthClient();
if (!client) {
toast("간편로그인은 관리자 설정 후 사용 가능합니다.");
return;
}
const conn = cfg.connections[provider] || "";
if (!conn) {
toast("관리자가 Auth0 connection 이름을 설정해야 합니다.");
return;
}
await client.loginWithRedirect({ authorizationParams: { connection: conn } });
}
async function logout() {
const client = await ensureAuthClient();
if (!client) return;
await client.logout({
logoutParams: { returnTo: location.origin === "null" ? location.href : location.origin + location.pathname },
});
}
// Wire events
if (el.q)
el.q.addEventListener("input", () => {
@@ -777,7 +925,13 @@
applyTheme(cur === "dark" ? "light" : "dark");
});
if (el.btnNaver) el.btnNaver.addEventListener("click", () => loginWithConnection("naver").catch(() => toast("로그인에 실패했습니다.")));
if (el.btnKakao) el.btnKakao.addEventListener("click", () => loginWithConnection("kakao").catch(() => toast("로그인에 실패했습니다.")));
if (el.btnGoogle) el.btnGoogle.addEventListener("click", () => loginWithConnection("google").catch(() => toast("로그인에 실패했습니다.")));
if (el.btnLogout) el.btnLogout.addEventListener("click", () => logout().catch(() => toast("로그아웃에 실패했습니다.")));
render();
initAuth().catch(() => {});
toast("스크립트 로딩 문제로 폴백 모드로 실행 중입니다.");
}, 200);
})();