webadmin: remove the implicit flow

we now use pkce . main advantage is that we don't see the access token
in the url anymore.

in pkce, the auth code by itself is useless. need the verifier.

fixes #844
This commit is contained in:
Girish Ramakrishnan
2026-03-14 22:06:17 +05:30
parent dc1449c7b6
commit c15e342bb8
7 changed files with 101 additions and 28 deletions
+3 -3
View File
@@ -8,7 +8,7 @@ import { onMounted, onUnmounted, ref, useTemplateRef, provide } from 'vue';
import { Notification, InputDialog, fetcher } from '@cloudron/pankow';
import { setLanguage } from './i18n.js';
import { API_ORIGIN, TOKEN_TYPES } from './constants.js';
import { redirectIfNeeded } from './utils.js';
import { redirectIfNeeded, startAuthFlow } from './utils.js';
import ProfileModel from './models/ProfileModel.js';
import ProvisionModel from './models/ProvisionModel.js';
import NotificationsModel from './models/NotificationsModel.js';
@@ -436,8 +436,8 @@ onMounted(async () => {
if (!localStorage.token) {
localStorage.setItem('redirectToHash', window.location.hash);
// start oidc flow
window.location.href = `${API_ORIGIN}/openid/auth?client_id=` + (API_ORIGIN ? TOKEN_TYPES.ID_DEVELOPMENT : TOKEN_TYPES.ID_WEBADMIN) + '&scope=openid email profile&response_type=code token&redirect_uri=' + window.location.origin + '/authcallback.html';
const clientId = API_ORIGIN ? TOKEN_TYPES.ID_DEVELOPMENT : TOKEN_TYPES.ID_WEBADMIN;
window.location.href = await startAuthFlow(clientId, API_ORIGIN);
return;
}
+2 -1
View File
@@ -5,6 +5,7 @@ import { marked } from 'marked';
import { Button, PasswordInput, FormGroup, TextInput } from '@cloudron/pankow';
import PublicPageLayout from '../components/PublicPageLayout.vue';
import ProfileModel from '../models/ProfileModel.js';
import { startAuthFlow } from '../utils.js';
const profileModel = ProfileModel.create();
@@ -89,7 +90,7 @@ async function onSubmit() {
// set token to autologin on first oidc flow
localStorage.cloudronFirstTimeToken = result.accessToken;
dashboardUrl.value = '/openid/auth?client_id=cid-webadmin&scope=openid email profile&response_type=code token&redirect_uri=' + window.location.origin + '/authcallback.html';
dashboardUrl.value = await startAuthFlow('cid-webadmin', '');
busy.value = false;
mode.value = MODE.DONE;
+33 -2
View File
@@ -660,6 +660,35 @@ function parseFullBackupPath(fullPath) {
return { prefix, remotePath };
}
function base64urlEncode(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64urlEncode(array);
}
async function computeCodeChallenge(verifier) {
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
return base64urlEncode(new Uint8Array(hash));
}
async function startAuthFlow(clientId, apiOrigin) {
const codeVerifier = generateCodeVerifier();
const codeChallenge = await computeCodeChallenge(codeVerifier);
sessionStorage.setItem('pkce_code_verifier', codeVerifier);
sessionStorage.setItem('pkce_client_id', clientId);
sessionStorage.setItem('pkce_api_origin', apiOrigin || '');
const redirectUri = window.location.origin + '/authcallback.html';
const base = apiOrigin || '';
return `${base}/openid/auth?client_id=${clientId}&scope=openid email profile&response_type=code&redirect_uri=${redirectUri}&code_challenge=${codeChallenge}&code_challenge_method=S256`;
}
// named exports
export {
renderSafeMarkdown,
@@ -679,7 +708,8 @@ export {
getColor,
prettySchedule,
parseSchedule,
parseFullBackupPath
parseFullBackupPath,
startAuthFlow
};
// default export
@@ -701,5 +731,6 @@ export default {
getColor,
prettySchedule,
parseSchedule,
parseFullBackupPath
parseFullBackupPath,
startAuthFlow
};
+2 -2
View File
@@ -3,7 +3,7 @@
import { ref, useTemplateRef, onMounted } from 'vue';
import { Button, Checkbox, FormGroup, TextInput, PasswordInput, EmailInput } from '@cloudron/pankow';
import ProvisionModel from '../models/ProvisionModel.js';
import { redirectIfNeeded } from '../utils.js';
import { redirectIfNeeded, startAuthFlow } from '../utils.js';
const provisionModel = ProvisionModel.create();
@@ -59,7 +59,7 @@ async function onOwnerSubmit() {
// set token to autologin on first oidc flow
localStorage.cloudronFirstTimeToken = result;
window.location.href = '/openid/auth?client_id=cid-webadmin&scope=openid email profile&response_type=code token&redirect_uri=' + window.location.origin + '/authcallback.html';
window.location.href = await startAuthFlow('cid-webadmin', '');
}
onMounted(async () => {