Compare commits

..

2 Commits

Author SHA1 Message Date
hyung-hwan d500c611dd updated Topbar to show the display name if not blank.
changed to use the display name as part of TOTP provisioning URL if not blank
2026-06-10 21:54:22 +09:00
hyung-hwan e6667f2e9d updated AccountPage to navigate to / upon successful TOTP verification 2026-06-10 21:28:01 +09:00
5 changed files with 14 additions and 4 deletions
+1
View File
@@ -350,6 +350,7 @@ func (api *API) oidcGetOrCreateUser(ctx context.Context, claims oidcUserClaims)
var hash string
var err error
var created models.User
username = oidcUsernameFromSub(claims.Sub)
user, hash, err = api.storeContext(ctx).GetUserByUsername(username)
_ = hash
+7 -2
View File
@@ -114,11 +114,13 @@ func (api *API) GetMyTOTP(w http.ResponseWriter, r *http.Request, _ map[string]s
func (api *API) SetupMyTOTP(w http.ResponseWriter, r *http.Request, _ map[string]string) {
var ctxUser models.User
var ok bool
var secret string
var encrypted string
var url string
var displayName string
var ok bool
var err error
ctxUser, ok = middleware.UserFromContext(r.Context())
if !ok {
w.WriteHeader(http.StatusUnauthorized)
@@ -139,7 +141,10 @@ func (api *API) SetupMyTOTP(w http.ResponseWriter, r *http.Request, _ map[string
WriteJSONWithErrorReason(w, r, http.StatusInternalServerError, err.Error())
return
}
url = auth.TOTPProvisioningURL(api.totpIssuer(r), ctxUser.Username, secret)
displayName = ctxUser.DisplayName
if displayName == "" { displayName = ctxUser.Username }
url = auth.TOTPProvisioningURL(api.totpIssuer(r), displayName, secret)
WriteJSON(w, http.StatusOK, totpSetupResponse{Secret: secret, OTPAuthURL: url})
}
+1 -1
View File
@@ -224,7 +224,7 @@ export default function Layout() {
{user ? (
<>
<Button color="inherit" onClick={openAccountMenu} endIcon={<KeyboardArrowDownIcon />}>
{user.username}
{user.display_name || user.username}
</Button>
<Menu
anchorEl={accountMenuAnchor}
+1 -1
View File
@@ -5,7 +5,7 @@ import App from './app/App'
import { routes } from './app/routes'
const router = createBrowserRouter(routes, {
future: { v7_startTransition: true, v7_relativeSplatPath: true }
future: { v7_relativeSplatPath: true }
})
ReactDOM.createRoot(document.getElementById('root')!).render(
+4
View File
@@ -4,8 +4,10 @@ import { Box, Button, Paper, TextField, Typography } from '@mui/material'
import { useEffect, useState } from 'react'
import { QRCodeSVG } from 'qrcode.react'
import { api, TOTPSetupResponse, User } from '../api'
import { useNavigate } from 'react-router-dom'
export default function AccountPage() {
const navigate = useNavigate()
const [user, setUser] = useState<User | null>(null)
const [displayName, setDisplayName] = useState('')
const [email, setEmail] = useState('')
@@ -118,12 +120,14 @@ export default function AccountPage() {
const handleVerifyTOTP = async () => {
let message: string
setTOTPBusy(true)
setTOTPError(null)
try {
await api.verifyMyTOTP(totpCode.trim())
setTOTPVerifyRequired(false)
setTOTPCode('')
navigate('/') // LoginPage.tsx also navigates to '/'.
} catch (err) {
message = err instanceof Error ? err.message : 'Failed to verify TOTP'
setTOTPError(message)