added logout function and persistent pinia store

This commit is contained in:
Jona Kläß
2024-05-02 15:21:33 +02:00
parent cb68ec0d35
commit 64edf9b81c
10 changed files with 104 additions and 202 deletions

View File

@@ -1,17 +1,12 @@
<script setup lang="ts">
import { RouterView } from 'vue-router';
import ProtectedData from './components/ProtectedData.vue';
defineOptions({
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App'
});
</script>
<template>
<header>
<img alt="Quasar logo" src="./assets/quasar-logo-vertical.svg" width="200vw"/>
<ProtectedData />
</header>
<router-view />
</template>

6
src/boot/pinia.ts Normal file
View File

@@ -0,0 +1,6 @@
import { boot } from 'quasar/wrappers';
import { createPinia } from 'pinia';
export default boot(({ app }) => {
app.use(createPinia());
});

View File

@@ -1,50 +1,29 @@
<script lang="ts">
//import { useKeycloakStore } from '../stores/keycloakStore';
import { defineComponent } from 'vue';
import { defineComponent, onMounted, ref } from 'vue';
import { useUserStore } from 'stores/user-store';
import { useKeycloakStore } from 'stores/keycloakStore';
/*const protectedData = ref("");
const keycloakStore = useKeycloakStore();
function fetchProtectedData() {
const keycloak = keycloakStore.getKeycloak;
if(!keycloak){
return;
}
let myHeaders = new Headers();
if(!keycloak.token){
return;
}
myHeaders.append("Authorization", "Bearer " + keycloak.token);
let requestOptons = {
headers: myHeaders,
};
fetch("localhost:"+ import.meta.env.PORT_BACKEND + "/api/protected", requestOptons)
.then((response) => response.text())
.then((data) => {
protectedData.value = data;
console.info("Protected data: ", data);
}).catch((e)=>console.error("Error fetching protected data: ", e));
}
onMounted(() => {
console.info("Protected component mounted");
fetchProtectedData();
});*/
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'ProtectedData',
components: {},
methods: {
logout() {
const keycloakStore = useKeycloakStore();
keycloakStore.logout();
}
},
setup() {
return useUserStore();
},
const userStore = useUserStore();
const router = useRouter();
const user = ref();
const userLoaded = ref(false);
const logout = () => {
userStore.logout();
router.push('/');
};
onMounted(() => {
console.log("IM MOUNTEEED: " + userStore.userLoaded + " " + userStore.user)
userLoaded.value = userStore.userLoaded;
user.value = userStore.user;
return { user, userLoaded };
});
return { user, userLoaded, logout };
},
})
</script>
@@ -57,7 +36,7 @@ export default defineComponent({
<p>{{ user.username }}</p>
<p>{{ user.keycloakID }}</p>
</div>
<q-btn @click="logout()" label="Logout" />
<q-btn @click="logout()" label="Logout"/>
</template>
<style scoped>

View File

@@ -4,60 +4,23 @@ import {createPinia} from 'pinia';
import App from './App.vue';
import Keycloak from 'keycloak-js';
import { useKeycloakStore } from 'stores/keycloakStore';
import {useUserStore} from 'stores/user-store';
const app = createApp(App);
app.use(createPinia());
const pinia = createPinia();
app.use(pinia);
/**
* Initialisierungsoptionen für Keycloak.
* @param url URL zu Keycloak, Port definiert in keycloak_init.sh und quasar.config.js-Datei. Müssen identisch sein!
* @param realm Name des Realms, hinterlegt in realm-export.json von Keycloak und quasar.config.js-Datei. Müssen identisch sein!
* @param clientId Name des Clients, hinterlegt in realm-export.json von Keycloak und quasar.config.js-Datei. Müssen identisch sein!
* @param onLoad = 'login-required': Hierdurch wird zum Aufrufen der ganzen Applikation ein login benötigt.
* @param redirectUri: URL, zu der nach dem Login weitergeleitet wird. Hierbei wird die Port des Frontends aus der .env-Datei verwendet.
*/
/**
* Variablen sind in quasar.config.js definiert und werden hier verwendet.
* @param keycloakPort Port, auf dem Keycloak läuft, hinterlegt in docker-compose.yml
* @param frontendPort Port, auf dem das Frontend läuft, hinterlegt in quasar.config.js und realm-export.json von Keycloak für die Redirect-URL
* @param realm Name des Realms, hinterlegt in realm-export.json von Keycloak
* @param clientId Name des Clients, hinterlegt in realm-export.json von Keycloak
*/
const keycloakPort = "" + process.env.PORT_KEYCLOAK;
const frontendPort = "" + process.env.PORT_FRONTEND;
const realm = "" + process.env.KEYCLOAK_REALM;
const clientId = "" + process.env.KEYCLOAK_CLIENT_ID;
const userStore = useUserStore();
const keycloakStore = useKeycloakStore();
const keycloak = new Keycloak({
url: 'http://localhost:' + keycloakPort + '/',
realm: realm,
clientId: clientId
});
try {
await keycloak.init({
onLoad: 'login-required', //authentifizierung erforderlich
redirectUri: 'http://localhost:' + frontendPort + '/test/'
}).then( (auth) => {
console.log("Authenticated: " + auth);
});
} catch (error) {
console.error(error);
}
console.log(keycloak);
console.log(keycloak.subject);
const keycloak: Keycloak = await userStore.initKeycloak();
// Anfrage ans Backend
if (keycloak.subject != null) {
if (keycloak.subject) {
userStore.keycloakId = keycloak.subject;
//userStore.loadPtpUsersByKeycloakID(keycloak.subject);
userStore.getPtpUserByKeycloakID(keycloak.subject);
await userStore.getPtpUserByKeycloakID(keycloak.subject);
/*console.log(userStore.userLoaded);
if(userStore.userLoaded) {
console.log("Hier ist der Bruder: " + JSON.stringify(userStore.user));
@@ -69,4 +32,3 @@ if (keycloak.subject != null) {
console.error("hier ist was schlimmes passiert menno");
}
keycloakStore.setKeycloak(keycloak);

View File

@@ -1,47 +1,25 @@
<template>
<q-page class="row items-center justify-evenly">
<example-component
title="Example component"
active
:todos="todos"
:meta="meta"
></example-component>
<q-btn v-if="!userStore.keycloak" @click="login()" label="Login" />
<q-btn @click="$router.push('/test')" label="Test" />
</q-page>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Todo, Meta } from 'components/models';
import ExampleComponent from 'components/ExampleComponent.vue';
<script lang="ts">
import { defineComponent } from 'vue';
import { useUserStore } from 'stores/user-store';
defineOptions({
name: 'IndexPage'
});
const todos = ref<Todo[]>([
{
id: 1,
content: 'ct1'
export default defineComponent({
components: {
},
{
id: 2,
content: 'ct2'
},
{
id: 3,
content: 'ct3'
},
{
id: 4,
content: 'ct4'
},
{
id: 5,
content: 'ct5'
setup() {
const userStore = useUserStore();
const login = () => {
userStore.login();
};
return { login, userStore };
}
]);
const meta = ref<Meta>({
totalCount: 1200
});
</script>

View File

@@ -1,9 +1,11 @@
<script setup lang="ts">
import ProtectedData from 'components/ProtectedData.vue';
</script>
<template>
<h1>Hallloooooooooo</h1>
<ProtectedData />
</template>
<style scoped>

View File

@@ -1,32 +0,0 @@
import { store } from 'quasar/wrappers'
import { createPinia } from 'pinia'
import { Router } from 'vue-router';
/*
* When adding new properties to stores, you should also
* extend the `PiniaCustomProperties` interface.
* @see https://pinia.vuejs.org/core-concepts/plugins.html#typing-new-store-properties
*/
declare module 'pinia' {
export interface PiniaCustomProperties {
readonly router: Router;
}
}
/*
* If not building with SSR mode, you can
* directly export the Store instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Store instance.
*/
export default store((/* { ssrContext } */) => {
const pinia = createPinia()
// You can add Pinia plugins here
// pinia.use(SomePiniaPlugin)
return pinia
})

View File

@@ -1,20 +0,0 @@
import type Keycloak from 'keycloak-js';
import { defineStore } from 'pinia';
//import api from 'axios';
export const useKeycloakStore = defineStore('keycloakStore', {
state: ()=>({
keycloak: null as Keycloak| null,
}),
getters: {
getKeycloak: (state) => state.keycloak,
},
actions:{
setKeycloak(keycloak: Keycloak): void {
this.keycloak = keycloak;
},
async logout(){
await this.keycloak?.logout();
}
},
});

View File

@@ -1,26 +1,66 @@
import { defineStore } from 'pinia';
import {api} from 'boot/axios';
import {ptpUser, responseModel} from 'src/models';
import Keycloak from 'keycloak-js';
import { api } from 'src/boot/axios';
import { ptpUser, responseModel } from 'src/models';
import Keycloak, { KeycloakInitOptions, KeycloakLoginOptions } from 'keycloak-js';
export const useUserStore = defineStore('userStore', {
state: () => ({
user: {"keycloakID":null,"firstName":null,"lastName":null,"username":null},
user: null as ptpUser | null,
userLoaded: false,
keycloak: null as Keycloak | null,
keycloakId: "",
}),
getters: {
},
getters: {},
actions: {
getPtpUserByKeycloakID(keycloakID : string) {
this.loadPtpUsersByKeycloakID(keycloakID).then((response : responseModel) => {
async getPtpUserByKeycloakID(keycloakID : string) {
await this.loadPtpUsersByKeycloakID(keycloakID).then((response : responseModel) => {
this.user = response.response;
this.userLoaded = true;
console.log("ich habe das gesetzt: " + JSON.stringify(this.user) + " / " + this.userLoaded);
});
},
async loadPtpUsersByKeycloakID(keycloakID : string) : Promise<responseModel> {
return (await api.get("/users/login/"+keycloakID)).data;
},
async initKeycloak() {
/**
* Variablen sind in quasar.config.js definiert und werden hier verwendet.
* @param keycloakPort Port, auf dem Keycloak läuft, hinterlegt in docker-compose.yml
* @param frontendPort Port, auf dem das Frontend läuft, hinterlegt in quasar.config.js und realm-export.json von Keycloak für die Redirect-URL
* @param realm Name des Realms, hinterlegt in realm-export.json von Keycloak
* @param clientId Name des Clients, hinterlegt in realm-export.json von Keycloak
*/
const keycloakPort: string = "" + process.env.PORT_KEYCLOAK;
const frontendPort: string = "" + process.env.PORT_FRONTEND;
const realm : string= "" + process.env.KEYCLOAK_REALM;
const clientId: string = "" + process.env.KEYCLOAK_CLIENT_ID;
const keycloak: Keycloak = new Keycloak({
url: 'http://localhost:' + keycloakPort + '/',
realm: realm,
clientId: clientId
});
const initOptions: KeycloakInitOptions = {
onLoad: 'login-required', //erzwingt Login
redirectUri: 'http://localhost:' + frontendPort + '/',
checkLoginIframe: false,
locale: 'login-required',
}
try {
await keycloak.init(initOptions).then( (auth: boolean): void => {
console.log("Authenticated: " + auth);
});
} catch (error) {
console.error(error);
}
this.keycloak = keycloak;
return keycloak;
},
async login() {
if(!this.keycloak){
await this.initKeycloak();
}else await this.keycloak?.login( {prompt: 'login'} as KeycloakLoginOptions);
},
async logout() {
window.location.href= 'http://localhost:' + process.env.PORT_KEYCLOAK + '/realms/' + process.env.KEYCLOAK_REALM + '/protocol/openid-connect/logout?post_logout_redirect_uri=http%3A%2F%2Flocalhost:' + process.env.PORT_FRONTEND + '&client_id=' + process.env.KEYCLOAK_CLIENT_ID;
}
},
});

View File

@@ -3,12 +3,4 @@
"compilerOptions": {
"baseUrl": "."
},
"exclude": [
"./dist",
"./.quasar",
"./node_modules",
"./src-capacitor",
"./src-cordova",
"./quasar.config.*.temporary.compiled*"
]
}