+
+
+
+
diff --git a/shell/pages/auth/login.vue b/shell/pages/auth/login.vue
index 1f2eec00c4..094d4196da 100644
--- a/shell/pages/auth/login.vue
+++ b/shell/pages/auth/login.vue
@@ -14,7 +14,6 @@ import Password from '@shell/components/form/Password';
import { sortBy } from '@shell/utils/sort';
import { configType } from '@shell/models/management.cattle.io.authconfig';
import { mapGetters } from 'vuex';
-import { importLogin } from '@shell/utils/dynamic-importer';
import { _ALL_IF_AUTHED, _MULTI } from '@shell/plugins/dashboard-store/actions';
import { MANAGEMENT, NORMAN } from '@shell/config/types';
import { SETTING } from '@shell/config/settings';
@@ -177,7 +176,7 @@ export default {
created() {
this.providerComponents = this.providers.map((name) => {
- return importLogin(configType[name]);
+ return this.$store.getters['type-map/importLogin'](configType[name] || name);
});
},
diff --git a/shell/pages/auth/verify.vue b/shell/pages/auth/verify.vue
index 146aeddb85..b4227aa139 100644
--- a/shell/pages/auth/verify.vue
+++ b/shell/pages/auth/verify.vue
@@ -43,6 +43,12 @@ export default {
try {
parsed = JSON.parse(base64Decode((stateStr)));
} catch (err) {
+ const out = store.getters['i18n/t'](`login.error`);
+
+ console.error('Failed to parse nonce'); // eslint-disable-line no-console
+
+ redirect(`/auth/login?err=${ escape(out) }`);
+
return;
}
diff --git a/shell/pkg/auto-import.js b/shell/pkg/auto-import.js
index fba96b6824..b7d462e2ad 100644
--- a/shell/pkg/auto-import.js
+++ b/shell/pkg/auto-import.js
@@ -1,6 +1,6 @@
const fs = require('fs');
const path = require('path');
-const contextFolders = ['chart', 'cloud-credential', 'content', 'detail', 'edit', 'list', 'machine-config', 'models', 'promptRemove', 'l10n', 'windowComponents', 'dialog', 'formatters'];
+const contextFolders = ['chart', 'cloud-credential', 'content', 'detail', 'edit', 'list', 'machine-config', 'models', 'promptRemove', 'l10n', 'windowComponents', 'dialog', 'formatters', 'login'];
const contextMap = contextFolders.reduce((map, obj) => {
map[obj] = true;
diff --git a/shell/store/auth.js b/shell/store/auth.js
index 802e0068b5..12a6b4ab79 100644
--- a/shell/store/auth.js
+++ b/shell/store/auth.js
@@ -160,7 +160,10 @@ export const actions = {
return findBy(authConfigs, 'id', id);
},
- setNonce({ dispatch }, opt) {
+ /**
+ * Create the basic json object used for the nonce (this includes the random nonce/state)
+ */
+ createNonce(ctx, opt) {
const out = { nonce: randomStr(16), to: 'vue' };
if ( opt.test ) {
@@ -171,7 +174,15 @@ export const actions = {
out.provider = opt.provider;
}
- const strung = JSON.stringify(out);
+ return out;
+ },
+
+ /**
+ * Save nonce details. Information it contains will be used to validate auth requests/responses
+ * Note - this may be structurally different than the nonce we encode and send
+ */
+ saveNonce(ctx, opt) {
+ const strung = JSON.stringify(opt);
this.$cookies.set(KEY, strung, {
path: '/',
@@ -182,6 +193,15 @@ export const actions = {
return strung;
},
+ /**
+ * Convert the nonce into something we can send
+ */
+ encodeNonce(ctx, nonce) {
+ const stringify = JSON.stringify(nonce);
+
+ return base64Encode(stringify, 'url');
+ },
+
async redirectTo({ state, commit, dispatch }, opt = {}) {
const provider = opt.provider;
let redirectUrl = opt.redirectUrl;
@@ -200,7 +220,13 @@ export const actions = {
returnToUrl = `${ window.location.origin }/verify-auth-azure`;
}
- const nonce = await dispatch('setNonce', opt);
+ // The base nonce that will be sent server way
+ const baseNonce = opt.nonce || await dispatch('createNonce', opt);
+
+ // Save a possibly expanded nonce
+ await dispatch('saveNonce', opt.persistNonce || baseNonce);
+ // Convert the base nonce in to something we can transmit
+ const encodedNonce = await dispatch('encodeNonce', baseNonce);
const fromQuery = unescape(parseUrl(redirectUrl).query?.[GITHUB_SCOPE] || '');
const scopes = fromQuery.split(/[, ]+/).filter(x => !!x);
@@ -216,8 +242,8 @@ export const actions = {
let url = removeParam(redirectUrl, GITHUB_SCOPE);
const params = {
- [GITHUB_SCOPE]: scopes.join(','),
- [GITHUB_NONCE]: base64Encode(nonce, 'url')
+ [GITHUB_SCOPE]: scopes.join(opt.scopesJoinChart || ','), // Some providers won't accept comma separated scopes
+ [GITHUB_NONCE]: encodedNonce
};
if (!url.includes(GITHUB_REDIRECT)) {
@@ -249,9 +275,16 @@ export const actions = {
return ERR_NONCE;
}
+ const body = { code };
+
+ // If the request came with a pkce code ensure we also sent that in the verify
+ if (parsed.pkceCodeVerifier) {
+ body.code_verifier = parsed.pkceCodeVerifier;
+ }
+
return dispatch('login', {
provider,
- body: { code }
+ body
});
},
diff --git a/shell/store/type-map.js b/shell/store/type-map.js
index 09372a3b64..1e1d2d8155 100644
--- a/shell/store/type-map.js
+++ b/shell/store/type-map.js
@@ -137,7 +137,7 @@ import {
ensureRegex, escapeHtml, escapeRegex, ucFirst, pluralize
} from '@shell/utils/string';
import {
- importChart, importList, importDetail, importEdit, listProducts, loadProduct, importCustomPromptRemove, resolveList, resolveEdit, resolveWindowComponent, importWindowComponent, resolveChart, resolveDetail, importDialog
+ importChart, importList, importDetail, importEdit, listProducts, loadProduct, importCustomPromptRemove, resolveList, resolveEdit, resolveWindowComponent, importWindowComponent, importLogin, resolveChart, resolveDetail, importDialog
} from '@shell/utils/dynamic-importer';
import { NAME as EXPLORER } from '@shell/config/product/explorer';
@@ -1152,6 +1152,12 @@ export const getters = {
};
},
+ importLogin(state, getters, rootState) {
+ return (authType) => {
+ return loadExtension(rootState, 'login', authType, importLogin);
+ };
+ },
+
componentFor(state, getters) {
return (type, subType) => {
let key = type;