Skip to content

Commit

Permalink
prevent bearer tokens or username/passwords to be stored in browser l…
Browse files Browse the repository at this point in the history
…ocal storage

* for security reasons
  • Loading branch information
thjaeckle committed Oct 4, 2024
1 parent 31f31d1 commit ed47f2e
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 63 deletions.
12 changes: 3 additions & 9 deletions documentation/src/main/resources/pages/ditto/user-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,25 +146,19 @@ export type OidcAuthSettings = {
/** Whether to automatically start SSO when the Authorize popup model loads */
autoSso: boolean,
/** The actually chosen OIDC provider (which can be changed by the user in the frontend) - must match a key in "AuthSettings.oidc" */
provider?: string,
/** The cached bearer token obtained via SSO */
bearerToken?: string
provider?: string
}

type BasicAuthSettings = {
/** Whether the Basic Auth section should be enabled in the Authorize popup */
enabled: boolean,
/** The default username and password to pre-configure */
defaultUsernamePassword: string | null,
/** The cached username and password */
usernamePassword?: string
defaultUsernamePassword: string | null
}

type BearerAuthSettings = {
/** Whether the Bearer Auth section should be enabled in the Authorize popup */
enabled: boolean,
/** The cached bearer token */
bearerToken?: string
enabled: boolean
}

type PreAuthSettings = {
Expand Down
38 changes: 26 additions & 12 deletions ui/modules/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
*/

import { EventSourcePolyfill } from 'event-source-polyfill';
import {
devopsBearerToken,
devopsOidcBearerToken,
devopsUsernamePassword,
mainBearerToken,
mainOidcBearerToken,
mainUsernamePassword
} from './environments/authorization';
import * as Environments from './environments/environments.js';
import { AuthMethod } from './environments/environments.js';
import * as Utils from './utils.js';
Expand Down Expand Up @@ -286,23 +294,26 @@ export function setAuthHeader(forDevOps: boolean) {
if (forDevOps) {
let devopsAuthMethod = environment.authSettings?.devops?.method;
if (devopsAuthMethod === AuthMethod.basic) {
if (environment.authSettings.devops.basic.usernamePassword) {
let devopsUserPass = devopsUsernamePassword();
if (devopsUserPass) {
authHeaderKey = 'Authorization';
authHeaderValue = 'Basic ' + window.btoa(environment.authSettings.devops.basic.usernamePassword);
authHeaderValue = 'Basic ' + window.btoa(devopsUserPass);
} else {
showError('DevOps Username/password missing')
}
} else if (devopsAuthMethod === AuthMethod.bearer) {
if (environment.authSettings.devops.bearer.bearerToken) {
let devopsToken = devopsBearerToken();
if (devopsToken) {
authHeaderKey = 'Authorization';
authHeaderValue = 'Bearer ' + environment.authSettings.devops.bearer.bearerToken;
authHeaderValue = 'Bearer ' + devopsToken;
} else {
showError('DevOps Bearer token missing')
}
} else if (devopsAuthMethod === AuthMethod.oidc) {
if (environment.authSettings.devops.oidc.bearerToken) {
let devopsOidcToken = devopsOidcBearerToken();
if (devopsOidcToken) {
authHeaderKey = 'Authorization';
authHeaderValue = 'Bearer ' + environment.authSettings.devops.oidc.bearerToken;
authHeaderValue = 'Bearer ' + devopsOidcToken;
} else {
showError('DevOps SSO (Bearer) token missing')
}
Expand All @@ -313,9 +324,10 @@ export function setAuthHeader(forDevOps: boolean) {
} else {
let mainAuthMethod = environment.authSettings?.main?.method;
if (mainAuthMethod === AuthMethod.basic) {
if (environment.authSettings.main.basic.usernamePassword) {
let mainUserPass = mainUsernamePassword();
if (mainUserPass) {
authHeaderKey = 'Authorization';
authHeaderValue = 'Basic ' + window.btoa(environment.authSettings.main.basic.usernamePassword);
authHeaderValue = 'Basic ' + window.btoa(mainUserPass);
} else {
showError('Username/password missing')
}
Expand All @@ -327,16 +339,18 @@ export function setAuthHeader(forDevOps: boolean) {
showError('Pre-Authenticated username missing')
}
} else if (mainAuthMethod === AuthMethod.bearer) {
if (environment.authSettings.main.bearer.bearerToken) {
let mainToken = mainBearerToken();
if (mainToken) {
authHeaderKey = 'Authorization';
authHeaderValue = 'Bearer ' + environment.authSettings.main.bearer.bearerToken;
authHeaderValue = 'Bearer ' + mainToken;
} else {
showError('Bearer token missing')
}
} else if (mainAuthMethod === AuthMethod.oidc) {
if (environment.authSettings.main.oidc.bearerToken) {
let mainOidcToken = mainOidcBearerToken();
if (mainOidcToken) {
authHeaderKey = 'Authorization';
authHeaderValue = 'Bearer ' + environment.authSettings.main.oidc.bearerToken;
authHeaderValue = 'Bearer ' + mainOidcToken;
} else {
showError('SSO (Bearer) token missing')
}
Expand Down
2 changes: 2 additions & 0 deletions ui/modules/environments/authorization.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ <h5>Main authentication</h5>
<div class="input-group">
<label for="oidcProvider" class="input-group-text"><small>SSO provider</small></label>
<select class="form-select form-select-sm me-2" id="oidcProvider"></select>
<input type="hidden" class="form-control form-control-sm" id="oidcBearer" name="oidcBearer" />
<button class="btn btn-outline-secondary btn-sm" id="main-oidc-login" data-bs-dismiss="modal">Login</button>
<button class="btn btn-outline-secondary btn-sm" id="main-oidc-logout" data-bs-dismiss="modal">Logout</button>
</div>
Expand Down Expand Up @@ -92,6 +93,7 @@ <h5>DevOps authentication</h5>
<div class="input-group">
<label for="devOpsOidcProvider" class="input-group-text"><small>SSO provider</small></label>
<select class="form-select form-select-sm me-2" id="devOpsOidcProvider"></select>
<input type="hidden" class="form-control form-control-sm" id="oidcBearerDevOps" name="oidcBearerDevOps" />
<button class="btn btn-outline-secondary btn-sm" id="devops-oidc-login" data-bs-dismiss="modal">Login</button>
<button class="btn btn-outline-secondary btn-sm" id="devops-oidc-logout" data-bs-dismiss="modal">Logout</button>
</div>
Expand Down
78 changes: 56 additions & 22 deletions ui/modules/environments/authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ import {

let dom = {
mainBearerSection: null,
oidcBearer: null,
bearer: null,
mainBasicSection: null,
userName: null,
password: null,
devOpsBearerSection: null,
oidcBearerDevOps: null,
bearerDevOps: null,
devOpsBasicSection: null,
devOpsUserName: null,
Expand Down Expand Up @@ -67,6 +69,52 @@ export function setForDevops(forDevops: boolean) {
API.setAuthHeader(_forDevops);
}

export function mainUsernamePassword(): string {
return dom.userName.value + ':' + dom.password.value;
}

export function mainBearerToken(): string {
return dom.bearer.value;
}

export function mainOidcBearerToken(): string {
return dom.oidcBearer.value;
}

export function devopsUsernamePassword(): string {
return dom.devOpsUserName.value + ':' + dom.devOpsPassword.value;
}

export function devopsBearerToken(): string {
return dom.bearerDevOps.value;
}

export function devopsOidcBearerToken(): string {
return dom.oidcBearerDevOps.value;
}

export function fillMainUsernamePassword(usernamePassword: string) {
if (usernamePassword && usernamePassword.length > 0) {
dom.userName.value = usernamePassword.split(':')[0];
dom.password.value = usernamePassword.split(':')[1];
}
}

export function fillDevopsUsernamePassword(usernamePassword: string) {
if (usernamePassword && usernamePassword.length > 0) {
dom.devOpsUserName.value = usernamePassword.split(':')[0];
dom.devOpsPassword.value = usernamePassword.split(':')[1];
}
}

function fillMainOidcBearerToken(oidcToken: string) {
dom.oidcBearer.value = oidcToken;
}

function fillDevopsOidcBearerToken(oidcToken: string) {
dom.oidcBearerDevOps.value = oidcToken;
}

export function ready() {
Utils.getAllElementsById(dom);

Expand Down Expand Up @@ -111,10 +159,6 @@ export function ready() {
let environment = Environments.current();
environment.authSettings.main.method = AuthMethod[mainAuthMethod as keyof typeof AuthMethod];
environment.authSettings.devops.method = AuthMethod[devopsAuthMethod as keyof typeof AuthMethod];
environment.authSettings.main.basic.usernamePassword = dom.userName.value + ':' + dom.password.value;
environment.authSettings.devops.basic.usernamePassword = dom.devOpsUserName.value + ':' + dom.devOpsPassword.value;
environment.authSettings.main.bearer.bearerToken = dom.bearer.value;
environment.authSettings.devops.bearer.bearerToken = dom.bearerDevOps.value;
environment.authSettings.main.pre.dittoPreAuthenticatedUsername = dom.dittoPreAuthenticatedUsername.value;
environment.authSettings.main.oidc.defaultProvider = dom.oidcProvider.value;
environment.authSettings.devops.oidc.defaultProvider = dom.devOpsOidcProvider.value;
Expand Down Expand Up @@ -181,12 +225,12 @@ async function handleSingleSignOnCallback(urlSearchParams: URLSearchParams) {
if (user) {
let oidcState = user.state as OidcState
if (oidcState.mainAuth) {
environment.authSettings.main.method = AuthMethod.oidc
environment.authSettings.main.oidc.bearerToken = user[oidcProvider.extractBearerTokenFrom]
environment.authSettings.main.method = AuthMethod.oidc;
fillMainOidcBearerToken(user[oidcProvider.extractBearerTokenFrom]);
}
if (oidcState.devopsAuth) {
environment.authSettings.devops.method = AuthMethod.oidc
environment.authSettings.devops.oidc.bearerToken = user[oidcProvider.extractBearerTokenFrom]
environment.authSettings.devops.method = AuthMethod.oidc;
fillDevopsOidcBearerToken(user[oidcProvider.extractBearerTokenFrom]);
}
window.history.replaceState(null, null, `${settings.redirect_uri}?${atob(user.url_state)}`)
await Environments.environmentsJsonChanged(false)
Expand Down Expand Up @@ -220,10 +264,10 @@ async function performSingleSignOn(forMainAuth: boolean): Promise<boolean> {
if (user?.[oidcProvider.extractBearerTokenFrom] !== undefined || user?.expired === true) {
// a user is still logged in via a valid token stored in the browser's session storage
if (sameProviderForMainAndDevops) {
environment.authSettings.main.oidc.bearerToken = user[oidcProvider.extractBearerTokenFrom]
environment.authSettings.devops.oidc.bearerToken = user[oidcProvider.extractBearerTokenFrom]
fillMainOidcBearerToken(user[oidcProvider.extractBearerTokenFrom]);
fillDevopsOidcBearerToken(user[oidcProvider.extractBearerTokenFrom]);
} else {
oidc.bearerToken = user[oidcProvider.extractBearerTokenFrom]
fillMainOidcBearerToken(user[oidcProvider.extractBearerTokenFrom]);
}
return true
} else {
Expand Down Expand Up @@ -268,7 +312,7 @@ async function performSingleSignOut(oidc: OidcAuthSettings) {
} catch (e) {
showError(e)
} finally {
oidc.bearerToken = undefined
fillMainOidcBearerToken(undefined);
Environments.saveEnvironmentsToLocalStorage();
}
}
Expand All @@ -284,16 +328,6 @@ function dynamicallyShowOrHideSection(sectionEnabled: boolean, section: HTMLElem

export async function onEnvironmentChanged(initialPageLoad: boolean) {
let environment = Environments.current();
let usernamePassword = environment.authSettings?.main?.basic?.usernamePassword ?
environment.authSettings?.main?.basic?.usernamePassword : ':';
dom.userName.value = usernamePassword.split(':')[0];
dom.password.value = usernamePassword.split(':')[1];
usernamePassword = environment.authSettings?.devops?.basic?.usernamePassword ?
environment.authSettings?.devops?.basic?.usernamePassword : ':';
dom.devOpsUserName.value = usernamePassword.split(':')[0];
dom.devOpsPassword.value = usernamePassword.split(':')[1];
dom.bearer.value = environment.authSettings?.main?.bearer?.bearerToken ? environment.authSettings?.main?.bearer?.bearerToken : '';
dom.bearerDevOps.value = environment.authSettings?.devops?.bearer?.bearerToken ? environment.authSettings?.devops?.bearer?.bearerToken : '';
dom.dittoPreAuthenticatedUsername.value = environment.authSettings?.main?.pre?.dittoPreAuthenticatedUsername ?
environment.authSettings?.main?.pre?.dittoPreAuthenticatedUsername : '';
if (environment.authSettings?.oidc?.providers) {
Expand Down
19 changes: 8 additions & 11 deletions ui/modules/environments/environments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as Utils from '../utils.js';
/* eslint-disable prefer-const */
/* eslint-disable require-jsdoc */
import * as Authorization from './authorization.js';
import { fillDevopsUsernamePassword, fillMainUsernamePassword } from './authorization.js';
import environmentsHTML from './environments.html';
import defaultTemplates from './environmentTemplates.json';

Expand Down Expand Up @@ -50,16 +51,12 @@ type BasicAuthSettings = {
/** Whether the Basic Auth section should be enabled in the Authorize popup */
enabled: boolean,
/** The default username and password to pre-configure */
defaultUsernamePassword: string | null,
/** The cached username and password */
usernamePassword?: string
defaultUsernamePassword: string | null
}

type BearerAuthSettings = {
/** Whether the Bearer Auth section should be enabled in the Authorize popup */
enabled: boolean,
/** The cached bearer token */
bearerToken?: string
enabled: boolean
}

type PreAuthSettings = {
Expand Down Expand Up @@ -161,11 +158,11 @@ document.getElementById('environmentsHTML').innerHTML = environmentsHTML;

function Environment(env: Environment): void {
Object.assign(this, env);
this.authSettings.main.oidc.provider = env.authSettings?.main?.oidc?.defaultProvider
this.authSettings.main.basic.usernamePassword = env.authSettings?.main?.basic?.defaultUsernamePassword
this.authSettings.main.pre.dittoPreAuthenticatedUsername = env.authSettings?.main?.pre?.defaultDittoPreAuthenticatedUsername
this.authSettings.devops.oidc.provider = env.authSettings?.devops?.oidc?.defaultProvider
this.authSettings.devops.basic.usernamePassword = env.authSettings?.devops?.basic?.defaultUsernamePassword
this.authSettings.main.oidc.provider = env.authSettings?.main?.oidc?.defaultProvider;
fillMainUsernamePassword(env.authSettings?.main?.basic?.defaultUsernamePassword);
this.authSettings.main.pre.dittoPreAuthenticatedUsername = env.authSettings?.main?.pre?.defaultDittoPreAuthenticatedUsername;
this.authSettings.devops.oidc.provider = env.authSettings?.devops?.oidc?.defaultProvider;
fillDevopsUsernamePassword(env.authSettings?.devops?.basic?.defaultUsernamePassword);
}

export function currentEnvironmentSelector() {
Expand Down
12 changes: 3 additions & 9 deletions ui/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,25 +99,19 @@ export type OidcAuthSettings = {
/** Whether to automatically start SSO when the Authorize popup model loads */
autoSso: boolean,
/** The actually chosen OIDC provider (which can be changed by the user in the frontend) - must match a key in "AuthSettings.oidc" */
provider?: string,
/** The cached bearer token obtained via SSO */
bearerToken?: string
provider?: string
}

type BasicAuthSettings = {
/** Whether the Basic Auth section should be enabled in the Authorize popup */
enabled: boolean,
/** The default username and password to pre-configure */
defaultUsernamePassword: string | null,
/** The cached username and password */
usernamePassword?: string
defaultUsernamePassword: string | null
}

type BearerAuthSettings = {
/** Whether the Bearer Auth section should be enabled in the Authorize popup */
enabled: boolean,
/** The cached bearer token */
bearerToken?: string
enabled: boolean
}

type PreAuthSettings = {
Expand Down

0 comments on commit ed47f2e

Please sign in to comment.