Skip to content

Commit c967209

Browse files
committed
fix: improve SSO callback validation in useSsoAuth
- Added state validation before redirecting during SSO callback to ensure parameters are correct. - Removed redundant error handling for mismatched state to streamline the code and enhance clarity.
1 parent b84d980 commit c967209

File tree

4 files changed

+35
-27
lines changed

4 files changed

+35
-27
lines changed

api/dev/configs/api.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
"extraOrigins": [],
44
"sandbox": false,
55
"ssoSubIds": [],
6-
"plugins": []
6+
"plugins": [
7+
"unraid-api-plugin-connect"
8+
]
79
}

api/src/unraid-api/rest/rest.controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ export class RestController {
128128
fullUrl
129129
);
130130

131-
// Redirect to login page with the token
132-
const loginUrl = `/login?token=${encodeURIComponent(paddedToken)}`;
131+
// Redirect to login page with the token in hash to keep it out of server logs
132+
const loginUrl = `/login#token=${encodeURIComponent(paddedToken)}`;
133133

134134
// Manually set redirect headers for better proxy compatibility
135135
res.status(302);
@@ -148,7 +148,7 @@ export class RestController {
148148
this.logger.debug(`Error during OIDC callback: ${error.message}`);
149149
}
150150

151-
const loginUrl = `/login?error=${encodeURIComponent(errorMessage)}`;
151+
const loginUrl = `/login#error=${encodeURIComponent(errorMessage)}`;
152152

153153
res.status(302);
154154
res.header('Location', loginUrl);

web/components/sso/useSsoAuth.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ export function useSsoAuth() {
4848
};
4949

5050
const disableFormOnSubmit = () => {
51-
const { form } = getInputFields();
52-
if (form) {
53-
form.style.display = 'none';
51+
const fields = getInputFields();
52+
if (fields?.form) {
53+
fields.form.style.display = 'none';
5454
}
5555
};
5656

5757
const reEnableFormOnError = () => {
58-
const { form } = getInputFields();
59-
if (form) {
60-
form.style.display = 'block';
58+
const fields = getInputFields();
59+
if (fields?.form) {
60+
fields.form.style.display = 'block';
6161
}
6262
};
6363

@@ -76,36 +76,37 @@ export function useSsoAuth() {
7676

7777
const handleOAuthCallback = async () => {
7878
try {
79+
// First check hash parameters (for token and error - keeps them out of server logs)
80+
const hashParams = new URLSearchParams(window.location.hash.slice(1));
81+
const hashToken = hashParams.get('token');
82+
const hashError = hashParams.get('error');
83+
84+
// Then check query parameters (for OAuth code/state from provider redirects)
7985
const search = new URLSearchParams(window.location.search);
8086
const code = search.get('code') ?? '';
8187
const state = search.get('state') ?? '';
82-
const errorParam = search.get('error') ?? '';
8388
const sessionState = getStateToken();
8489

85-
// Check for error parameter
90+
// Check for error in hash (preferred) or query params (fallback)
91+
const errorParam = hashError || search.get('error') || '';
8692
if (errorParam) {
8793
currentState.value = 'error';
88-
// Use the error parameter directly from the backend
8994
error.value = errorParam;
9095

91-
// Clean up the URL
92-
const url = new URL(window.location.href);
93-
url.searchParams.delete('error');
94-
window.history.replaceState({}, document.title, url.pathname + url.search);
96+
// Clean up the URL (both hash and query params)
97+
window.history.replaceState({}, document.title, window.location.pathname);
9598
return;
9699
}
97100

98-
// Handle OAuth callback if we have a token (from OIDC redirect)
99-
const token = search.get('token');
101+
// Handle OAuth callback if we have a token in hash (from OIDC redirect)
102+
const token = hashToken || search.get('token'); // Check hash first, query as fallback
100103
if (token) {
101104
currentState.value = 'loading';
102105
disableFormOnSubmit();
103106
enterCallbackTokenIntoField(token);
104107

105-
// Clean up the URL
106-
const url = new URL(window.location.href);
107-
url.searchParams.delete('token');
108-
window.history.replaceState({}, document.title, url.pathname);
108+
// Clean up the URL (both hash and query params)
109+
window.history.replaceState({}, document.title, window.location.pathname);
109110
return;
110111
}
111112

web/pages/login.vue

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@ const cliToken = ref('');
1919
const cliOutput = ref('');
2020
const isExecutingCli = ref(false);
2121
22-
// Check for token in URL on mount
22+
// Check for token in URL hash on mount (keeps token out of server logs)
2323
const route = useRoute();
2424
onMounted(() => {
25-
const tokenFromUrl = route.query.token as string;
26-
if (tokenFromUrl) {
27-
cliToken.value = tokenFromUrl;
25+
// Check hash first (preferred), then query params (fallback)
26+
const hashParams = new URLSearchParams(window.location.hash.slice(1));
27+
const tokenFromHash = hashParams.get('token');
28+
const tokenFromQuery = route.query.token as string;
29+
30+
const token = tokenFromHash || tokenFromQuery;
31+
if (token) {
32+
cliToken.value = token;
2833
}
2934
});
3035

0 commit comments

Comments
 (0)