mirror of
https://github.com/WGDashboard/WGDashboard.git
synced 2026-08-04 15:03:02 +00:00
Added OIDC for admin side
This commit is contained in:
@@ -11,6 +11,26 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WGDashboard</title>
|
||||
<base href="./">
|
||||
<style>
|
||||
#preloader{
|
||||
width: 100vw; height: 100vh; display: flex
|
||||
}
|
||||
#preloader_placeholder{
|
||||
width: 50px; height: 50px; background-color: #000; margin: auto;
|
||||
animation: 3s ease-in-out fadeInPreloader infinite;
|
||||
}
|
||||
@keyframes fadeInPreloader {
|
||||
0%{
|
||||
opacity: 0;
|
||||
}
|
||||
50%{
|
||||
opacity: 1;
|
||||
}
|
||||
100%{
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
const isViteDevMode = document.querySelector('script[src*="@vite/client"]') !== null;
|
||||
const base = document.querySelector('base');
|
||||
@@ -20,7 +40,13 @@
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="app">
|
||||
<div id="preloader">
|
||||
<div id="preloader_placeholder">
|
||||
<img style="width: 100%" src="./img/Logo-2-128x128.png" alt="WGDashboard Admin" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="./src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "4.3.3",
|
||||
"version": "4.3.4",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"module": "es2022",
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<script setup async>
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {fetchGet} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
const providers = ref([])
|
||||
const oidcEnabled = ref(false)
|
||||
|
||||
await fetchGet("/api/oidc/providers", {}, async (res) => {
|
||||
if (res.status){
|
||||
await Promise.all(Object.entries(res.data).map(([key, value]) => {
|
||||
return fetch(`${value.issuer}/.well-known/openid-configuration`).then((res) => {
|
||||
providers.value.push({
|
||||
Provider: key,
|
||||
ProviderInfo: value
|
||||
})
|
||||
}).catch(() => {
|
||||
console.log("Failed to request " + key)
|
||||
})
|
||||
}))
|
||||
oidcEnabled.value = providers.value.length > 0
|
||||
}
|
||||
})
|
||||
console.log(providers.value)
|
||||
const oidcUrl = (provider) => {
|
||||
const params = new URLSearchParams({
|
||||
client_id: provider.ProviderInfo.client_id,
|
||||
redirect_uri: window.location.protocol + '//' + window.location.host + window.location.pathname,
|
||||
response_type: 'code',
|
||||
state: provider.Provider,
|
||||
scope: 'openid email profile'
|
||||
})
|
||||
console.log(params.entries())
|
||||
const url = new URL(provider.ProviderInfo.openid_configuration.authorization_endpoint)
|
||||
url.search = params.toString()
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="oidcEnabled">
|
||||
<hr class="mb-4"/>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn bg-body border w-100 rounded-3"
|
||||
:href="oidcUrl(provider)"
|
||||
v-for="provider in providers" style="flex: 1 1 0;">
|
||||
<LocaleText :t="'Sign In With ' + provider.Provider"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
+57
-10
@@ -7,18 +7,65 @@ import '@vuepic/vue-datepicker/dist/main.css'
|
||||
import {createApp, markRaw} from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
import router from './router/router.js'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
|
||||
const app = createApp(App)
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const state = params.get('state')
|
||||
const code = params.get('code')
|
||||
|
||||
app.use(router)
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
pinia.use(({ store }) => {
|
||||
store.$router = markRaw(router)
|
||||
})
|
||||
if (state && code) {
|
||||
window.history.replaceState({}, '', window.location.pathname);
|
||||
console.log('After replaceState:', window.location.href);
|
||||
}
|
||||
|
||||
|
||||
app.use(pinia)
|
||||
app.mount('#app')
|
||||
|
||||
|
||||
const initApp = async () => {
|
||||
const { default: router } = await import('./router/router.js')
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
pinia.use(({ store }) => {
|
||||
store.$router = markRaw(router)
|
||||
})
|
||||
|
||||
|
||||
app.use(pinia)
|
||||
app.mount('#app')
|
||||
console.log('After router import:', window.location.href)
|
||||
}
|
||||
|
||||
export const getUrl = (url) => {
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
return url;
|
||||
}
|
||||
return `./.${url}`;
|
||||
}
|
||||
|
||||
if (state && code){
|
||||
await fetch(getUrl('/api/oidc/authenticate'), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
provider: state,
|
||||
code: code,
|
||||
redirect_uri: window.location.protocol + '//' + window.location.host + window.location.pathname
|
||||
})
|
||||
}).then(res => res.json()).then( async (res) => {
|
||||
if (res.status){
|
||||
window.location.replace(window.location.protocol + '//' + window.location.host + window.location.pathname)
|
||||
}else{
|
||||
await initApp()
|
||||
const store = DashboardConfigurationStore()
|
||||
store.newMessage('Server OIDC', res.message, 'danger')
|
||||
}
|
||||
})
|
||||
}else{
|
||||
await initApp()
|
||||
}
|
||||
|
||||
@@ -4,17 +4,21 @@ const getHeaders = () => {
|
||||
let headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
const store = DashboardConfigurationStore();
|
||||
const crossServer = store.getActiveCrossServer();
|
||||
if (crossServer){
|
||||
headers['wg-dashboard-apikey'] = crossServer.apiKey
|
||||
if (crossServer.headers){
|
||||
for (let header of Object.values(crossServer.headers)){
|
||||
if (header.key && header.value && !Object.keys(headers).includes(header.key)){
|
||||
headers[header.key] = header.value
|
||||
}
|
||||
}
|
||||
}
|
||||
try{
|
||||
const store = DashboardConfigurationStore();
|
||||
const crossServer = store.getActiveCrossServer();
|
||||
if (crossServer){
|
||||
headers['wg-dashboard-apikey'] = crossServer.apiKey
|
||||
if (crossServer.headers){
|
||||
for (let header of Object.values(crossServer.headers)){
|
||||
if (header.key && header.value && !Object.keys(headers).includes(header.key)){
|
||||
headers[header.key] = header.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (e) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +26,13 @@ const getHeaders = () => {
|
||||
}
|
||||
|
||||
export const getUrl = (url) => {
|
||||
const store = DashboardConfigurationStore();
|
||||
const apiKey = store.getActiveCrossServer();
|
||||
if (apiKey){
|
||||
return `${apiKey.host}${url}`
|
||||
}
|
||||
try{
|
||||
const store = DashboardConfigurationStore();
|
||||
const apiKey = store.getActiveCrossServer();
|
||||
if (apiKey){
|
||||
return `${apiKey.host}${url}`
|
||||
}
|
||||
}catch (e){}
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ import {GetLocale} from "@/utilities/locale.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import SignInInput from "@/components/signIn/signInInput.vue";
|
||||
import SignInTOTP from "@/components/signIn/signInTOTP.vue";
|
||||
import SignInOIDC from "@/components/signIn/signInOIDC.vue";
|
||||
|
||||
export default {
|
||||
name: "signin",
|
||||
components: {SignInTOTP, SignInInput, LocaleText, RemoteServerList, Message},
|
||||
components: {SignInOIDC, SignInTOTP, SignInInput, LocaleText, RemoteServerList, Message},
|
||||
async setup(){
|
||||
const store = DashboardConfigurationStore()
|
||||
let theme = "dark"
|
||||
@@ -171,7 +172,7 @@ export default {
|
||||
</form>
|
||||
<RemoteServerList v-else></RemoteServerList>
|
||||
|
||||
<div class="d-flex mt-3" v-if="!this.store.IsElectronApp">
|
||||
<div class="d-flex mt-3 flex-column" v-if="!this.store.IsElectronApp">
|
||||
<div class="form-check form-switch ms-auto">
|
||||
<input
|
||||
v-model="this.store.CrossServerConfiguration.Enable"
|
||||
@@ -184,6 +185,8 @@ export default {
|
||||
<LocaleText t="Access Remote Server"></LocaleText>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<SignInOIDC />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user