diff --git a/frontend/src/auth.js b/frontend/src/auth.js
index 27bcd76..b9667cf 100644
--- a/frontend/src/auth.js
+++ b/frontend/src/auth.js
@@ -92,10 +92,17 @@ export async function initAuth() {
return initPromise;
}
-export async function login(redirectPath = window.location.pathname + window.location.search + window.location.hash) {
+export async function login(
+ redirectPath = window.location.pathname + window.location.search + window.location.hash,
+ loginHint = "",
+) {
if (!keycloak) return;
const redirectUri = new URL(redirectPath, window.location.origin).toString();
- await keycloak.login({ redirectUri });
+ const options = { redirectUri };
+ if (typeof loginHint === "string" && loginHint.trim()) {
+ options.loginHint = loginHint.trim();
+ }
+ await keycloak.login(options);
}
export async function logout() {
diff --git a/frontend/src/views/OnboardingView.vue b/frontend/src/views/OnboardingView.vue
index 5aeb678..6ee5107 100644
--- a/frontend/src/views/OnboardingView.vue
+++ b/frontend/src/views/OnboardingView.vue
@@ -26,7 +26,10 @@
Temporary password
- Use this password to log in for the first time. Keycloak will prompt you to change it.
+ Use this password to log in for the first time. Keycloak will prompt you to change it. This password is shown
+ once — copy it now.
Password
@@ -212,6 +216,7 @@ const error = ref("");
const onboarding = ref({ required_steps: [], completed_steps: [] });
const initialPassword = ref("");
const copied = ref(false);
+const usernameCopied = ref(false);
const tasks = ref([]);
const blocked = ref(false);
@@ -304,8 +309,35 @@ async function copyInitialPassword() {
}
}
+async function copyUsername() {
+ if (!requestUsername.value) return;
+ try {
+ if (navigator?.clipboard?.writeText) {
+ await navigator.clipboard.writeText(requestUsername.value);
+ } else {
+ const textarea = document.createElement("textarea");
+ textarea.value = requestUsername.value;
+ textarea.setAttribute("readonly", "");
+ textarea.style.position = "fixed";
+ textarea.style.top = "-9999px";
+ textarea.style.left = "-9999px";
+ document.body.appendChild(textarea);
+ textarea.select();
+ textarea.setSelectionRange(0, textarea.value.length);
+ document.execCommand("copy");
+ document.body.removeChild(textarea);
+ }
+ usernameCopied.value = true;
+ setTimeout(() => (usernameCopied.value = false), 1500);
+ } catch (err) {
+ error.value = err?.message || "Failed to copy username";
+ }
+}
+
async function loginToContinue() {
- await login(`/onboarding?code=${encodeURIComponent(requestCode.value.trim())}`);
+ const trimmedCode = requestCode.value.trim();
+ const hint = requestUsername.value.trim() || trimmedCode.split("~", 1)[0] || "";
+ await login(`/onboarding?code=${encodeURIComponent(trimmedCode)}`, hint);
}
async function toggleStep(step, event) {
diff --git a/frontend/src/views/RequestAccessView.vue b/frontend/src/views/RequestAccessView.vue
index c984be2..6101fb2 100644
--- a/frontend/src/views/RequestAccessView.vue
+++ b/frontend/src/views/RequestAccessView.vue
@@ -112,10 +112,6 @@
Verifying email…
-
-
Automation
@@ -134,6 +130,13 @@
One or more automation steps failed. Fix the error above, then check again.
+
+
@@ -429,6 +432,15 @@ h1 {
margin-top: 6px;
}
+.onboarding-actions {
+ margin-top: 14px;
+}
+
+.onboarding-cta {
+ flex: 1;
+ text-align: center;
+}
+
.status-form {
display: flex;
gap: 10px;