feat/Update_Controllers_to_Allow_for_Premium_Membership
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { Box, Button, Chip, Paper, Typography } from "@mui/material";
|
||||
import { Box, Button, Chip, Paper, TextField, Typography } from "@mui/material";
|
||||
import { PublicClientApplication } from "@azure/msal-browser";
|
||||
|
||||
import { api, getApiErrorMessage } from "../api";
|
||||
@@ -24,9 +24,10 @@ type MeResponse = {
|
||||
};
|
||||
|
||||
let msalInstance: PublicClientApplication | null = null;
|
||||
function getMsalInstance(clientId: string): PublicClientApplication {
|
||||
export function getMicrosoftMsalInstance(clientId: string): PublicClientApplication {
|
||||
const tenant = (process.env.NEXT_PUBLIC_MICROSOFT_TENANT || "common").trim() || "common";
|
||||
msalInstance ??= new PublicClientApplication({
|
||||
auth: { clientId, authority: "https://login.microsoftonline.com/common", redirectUri: window.location.origin },
|
||||
auth: { clientId, authority: `https://login.microsoftonline.com/${tenant}`, redirectUri: window.location.origin },
|
||||
});
|
||||
return msalInstance;
|
||||
}
|
||||
@@ -37,6 +38,7 @@ export default function MicrosoftAuthCard({ onSignedIn }: { onSignedIn?: () => v
|
||||
const [me, setMe] = useState<MeResponse | null>(null);
|
||||
const [working, setWorking] = useState(false);
|
||||
const [pendingToken, setPendingToken] = useState<string | null>(null);
|
||||
const [currentPassword, setCurrentPassword] = useState("");
|
||||
|
||||
const clientId = (process.env.NEXT_PUBLIC_MICROSOFT_CLIENT_ID || "").trim();
|
||||
const signedIn = Boolean(me?.provider);
|
||||
@@ -69,18 +71,25 @@ export default function MicrosoftAuthCard({ onSignedIn }: { onSignedIn?: () => v
|
||||
if (!clientId) return;
|
||||
setWorking(true);
|
||||
try {
|
||||
const msal = getMsalInstance(clientId);
|
||||
const msal = getMicrosoftMsalInstance(clientId);
|
||||
await msal.initialize();
|
||||
const result = await msal.loginPopup({ scopes: ["openid", "profile", "email"] });
|
||||
const idToken = result.idToken;
|
||||
if (!idToken) throw new Error(t("microsoftAuthFailed"));
|
||||
|
||||
if (me?.provider === "local") {
|
||||
const res = await api.post<{ linked: boolean; email?: string | null }>("/auth/microsoft/link", { token: idToken, rememberMe: getAuthPersistencePreference() === "local" });
|
||||
const res = await api.post<{ linked: boolean; email?: string | null }>("/auth/microsoft/link", { token: idToken, currentPassword });
|
||||
toast(res.data?.email ? t("microsoftLinkedSuccessWithEmail", { email: res.data.email }) : t("microsoftLinkedSuccess"), "success");
|
||||
await refreshMe();
|
||||
clearAuthClientState();
|
||||
setMe(null);
|
||||
setCurrentPassword("");
|
||||
toast(t("microsoftSecurityChangeSignInAgain"), "info");
|
||||
} else {
|
||||
const res = await api.post<{ requiresTwoFactor?: boolean; pendingToken?: string }>("/auth/microsoft/exchange", { token: idToken, rememberMe: getAuthPersistencePreference() === "local" });
|
||||
const res = await api.post<{ requiresTwoFactor?: boolean; pendingToken?: string; legacyRelinkRequired?: boolean }>("/auth/microsoft/exchange", { token: idToken, rememberMe: getAuthPersistencePreference() === "local" });
|
||||
if (res.data?.legacyRelinkRequired) {
|
||||
toast(t("microsoftLegacyRelinkEmailSent"), "info");
|
||||
return;
|
||||
}
|
||||
if (res.data?.requiresTwoFactor && res.data.pendingToken) {
|
||||
setPendingToken(res.data.pendingToken);
|
||||
} else {
|
||||
@@ -150,12 +159,22 @@ export default function MicrosoftAuthCard({ onSignedIn }: { onSignedIn?: () => v
|
||||
<Typography variant="caption" sx={{ color: "text.secondary", fontWeight: 700, letterSpacing: 0.4, textTransform: "uppercase" }}>
|
||||
{actionLabel}
|
||||
</Typography>
|
||||
<Button variant="outlined" disabled={working} onClick={() => void handleSignIn()}>
|
||||
<Button variant="outlined" disabled={working || (me?.provider === "local" && !currentPassword)} onClick={() => void handleSignIn()}>
|
||||
{actionLabel}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2, flexWrap: "wrap" }}>
|
||||
{me?.provider === "local" ? (
|
||||
<TextField
|
||||
size="small"
|
||||
label={t("profileCurrentPassword")}
|
||||
type="password"
|
||||
value={currentPassword}
|
||||
onChange={(event) => setCurrentPassword(event.target.value)}
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
) : null}
|
||||
{signedIn ? (
|
||||
<Button
|
||||
variant="outlined"
|
||||
@@ -175,12 +194,15 @@ export default function MicrosoftAuthCard({ onSignedIn }: { onSignedIn?: () => v
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="warning"
|
||||
disabled={working}
|
||||
disabled={working || !currentPassword}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await api.delete("/auth/microsoft/link");
|
||||
await api.delete("/auth/microsoft/link", { data: { currentPassword } });
|
||||
clearAuthClientState();
|
||||
setMe(null);
|
||||
setCurrentPassword("");
|
||||
toast(t("microsoftUnlinked"), "info");
|
||||
await refreshMe();
|
||||
toast(t("microsoftSecurityChangeSignInAgain"), "info");
|
||||
} catch (e: any) {
|
||||
const msg = e?.response?.data || e?.message || t("microsoftUnlinkFailed");
|
||||
toast(String(msg), "error");
|
||||
|
||||
Reference in New Issue
Block a user