login, logout, store, ...
This commit is contained in:
parent
896ca59ace
commit
b5a169282b
@ -2,12 +2,13 @@
|
||||
import { RouterLink, RouterView } from 'vue-router'
|
||||
import HelloWorld from './components/Welcome.vue'
|
||||
import { store } from './store.js'
|
||||
|
||||
import {clearSession} from "./store.js";
|
||||
/**
|
||||
* Logout the user.
|
||||
*/
|
||||
function logOut() {
|
||||
store.loggedIn = false;
|
||||
clearSession();
|
||||
location.href = "/";
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -22,7 +23,7 @@ function logOut() {
|
||||
<nav>
|
||||
<RouterLink to="/">Home</RouterLink>
|
||||
<RouterLink to="/about">About</RouterLink>
|
||||
<a v-if="store.session" href="" @click="logOut">Log Out</a>
|
||||
<a v-if="store.session" href="" @click="logOut()">Log Out</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -1,27 +0,0 @@
|
||||
<!--
|
||||
Dummy component that handles OAuth login callback requests.
|
||||
|
||||
Part of the basebox sample Todo app.
|
||||
https://basebox.tech
|
||||
-->
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
/**
|
||||
* onMounted lifecycle hook
|
||||
*/
|
||||
onMounted(() => {
|
||||
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "OAuthCallback"
|
||||
|
||||
mounted: {
|
||||
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
</script>
|
@ -10,6 +10,7 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
import OAUthError from "../components/OAUthError.vue";
|
||||
import { objectToQueryString } from "../util/net";
|
||||
import {createUser, oauthCallbackHandler} from "../util/oauth";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
@ -44,48 +45,23 @@ router.beforeEach(async (to, from) => {
|
||||
if (to.path === "/oauth-callback") {
|
||||
let queryString = objectToQueryString(to.query);
|
||||
console.info(`Got /oauth-callback with query string '${queryString}`);
|
||||
/* Pass the query parameters to the broker to complete login. We will get
|
||||
* the session token and user data in return.
|
||||
*
|
||||
* The URL we post to is broker's base URL and the `openid_connect_path`
|
||||
* setting from broker's config.
|
||||
*/
|
||||
const response = await fetch(`${store.baseboxHost}/oauth/complete-login?${queryString}`, {
|
||||
method: "POST",
|
||||
});
|
||||
if (!response.ok) {
|
||||
/* redirect to error component */
|
||||
console.error("Failed to complete login/get session data: " + response.statusText);
|
||||
return {
|
||||
name: 'oauth-error',
|
||||
query: {
|
||||
code: response.status,
|
||||
addlInfo: await response.text()
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass the query string to the handler function */
|
||||
try {
|
||||
await oauthCallbackHandler(queryString);
|
||||
/* Success; redirect to home */
|
||||
} catch (err) {
|
||||
console.error("Failed to finish login: " + err);
|
||||
}
|
||||
|
||||
/* store session data; this is a JSON object of the following form:
|
||||
{
|
||||
"token":"47caa9df-ac89-45a8-8b22-9c0925d6aed0",
|
||||
"username":"tester",
|
||||
"first_name":"Fred",
|
||||
"last_name":"Feuerstein",
|
||||
"roles":[
|
||||
"/user"
|
||||
],
|
||||
"claims":{
|
||||
"groups":[
|
||||
"/user"
|
||||
]
|
||||
}
|
||||
}
|
||||
*/
|
||||
store.session = await response.json();
|
||||
store.userName = store.session.first_name ? store.session.first_name : store.session.username;
|
||||
/* We have to create the logged-in user explicitly; see comment for `createUser()` */
|
||||
try {
|
||||
await createUser();
|
||||
} catch (err) {
|
||||
console.error("Failed to create user: " + err);
|
||||
}
|
||||
|
||||
/* redirect to home */
|
||||
return { name: 'home' };
|
||||
return {name: 'home'};
|
||||
}
|
||||
|
||||
});
|
||||
|
22
src/store.js
22
src/store.js
@ -11,11 +11,27 @@ export const store = reactive({
|
||||
/** Username of the currently logged-in user */
|
||||
userName: "stranger",
|
||||
|
||||
/** The host that runs basebox and waits for GraphQL requests */
|
||||
/** base URL of basebox broker host */
|
||||
baseboxHost: "http://127.0.0.1:8080",
|
||||
|
||||
/** basebox session data */
|
||||
session: null,
|
||||
session: {},
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Return true if the user is logged in.
|
||||
*/
|
||||
export function loggedIn() {
|
||||
return store.session && store.session.token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear session data.
|
||||
*/
|
||||
export function clearSession() {
|
||||
store.session = {};
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
@ -5,8 +5,7 @@
|
||||
* https://basebox.tech
|
||||
*/
|
||||
|
||||
import {store} from "../store";
|
||||
import router from "../router";
|
||||
import {clearSession, store} from "../store";
|
||||
|
||||
/**
|
||||
* GqlError - custom error thrown by the gqlQuery function.
|
||||
@ -114,7 +113,7 @@ export function gqlQuery(query)
|
||||
|
||||
console.info(fetchOpt);
|
||||
|
||||
return fetch(store.getters.gqlUrl, fetchOpt).then(
|
||||
return fetch(`${store.baseboxHost}/graphql`, fetchOpt).then(
|
||||
/* fetch success */
|
||||
async response => {
|
||||
if (response.ok) {
|
||||
@ -128,27 +127,25 @@ export function gqlQuery(query)
|
||||
} else {
|
||||
/* some errors... */
|
||||
return new Promise((resolve, reject) => {
|
||||
const error = new GqlError(rspJson);
|
||||
/* if this is a 401, we redirect to the login page */
|
||||
if (error.is401()) {
|
||||
router.push({
|
||||
name: "Login",
|
||||
query: {
|
||||
next: router.currentRoute.fullPath,
|
||||
}
|
||||
});
|
||||
}
|
||||
reject(new GqlError(rspJson));
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
/* low level/network error */
|
||||
/* if this is a 401, we clear the session data */
|
||||
if (response.status === 401) {
|
||||
console.error("Last request failed: unauthorized. Clearing session.");
|
||||
clearSession();
|
||||
return new Promise((resolve, reject) => reject("Unauthorized"));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
reject(new GqlError(response.statusText));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/* fetch failure */
|
||||
reason => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -44,23 +44,18 @@ import {gqlQuery} from "./net";
|
||||
*/
|
||||
export async function oauthCallbackHandler(queryString) {
|
||||
|
||||
fetch(`${store.baseboxHost}/oauth/complete-login?${queryString}`, {
|
||||
/* Pass query string to the broker to complete the login process. */
|
||||
const response = await fetch(`${store.baseboxHost}/oauth/complete-login?${queryString}`, {
|
||||
method: "POST",
|
||||
}).then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to get session data: " + response.statusText);
|
||||
}
|
||||
|
||||
/* Store session data */
|
||||
response.json(
|
||||
).then(rspJson => {
|
||||
store.session = rspJson;
|
||||
store.userName = store.session.first_name ? store.session.first_name : store.session.username;
|
||||
});
|
||||
|
||||
}).catch(e => {
|
||||
throw new Error("Failed to get session data: " + e.toString());
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to get session data: " + response.statusText);
|
||||
}
|
||||
|
||||
/* Store session data */
|
||||
const rspJson = await response.json();
|
||||
store.session = { ...rspJson };
|
||||
store.userName = store.session.first_name ? store.session.first_name : store.session.username;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,21 +65,17 @@ export async function oauthCallbackHandler(queryString) {
|
||||
* make sure that out user is known in the app database (basebox broker). After successful
|
||||
* login, this function must be called to do that.
|
||||
*
|
||||
* @param username unique username of new user
|
||||
* @param firstName first name of new user
|
||||
* @param lastName last name of new user
|
||||
* @returns {Promise<void>}
|
||||
* @throws Error if the user creation failed.
|
||||
*/
|
||||
async function createUser(username, firstName, lastName) {
|
||||
export async function createUser() {
|
||||
|
||||
gqlQuery(`query {
|
||||
await gqlQuery(`mutation {
|
||||
createUser(
|
||||
username: "${username}",
|
||||
name: "${firstName} ${lastName}"
|
||||
)`
|
||||
).catch(err => {
|
||||
|
||||
});
|
||||
|
||||
username: "${store.session.username}",
|
||||
name: "${store.session.first_name} ${store.session.last_name}"
|
||||
) {
|
||||
username
|
||||
}
|
||||
}`);
|
||||
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
|
||||
<script setup>
|
||||
import { store } from "../store";
|
||||
import {loggedIn} from "../store";
|
||||
import TodoRoot from "../components/TodoRoot.vue";
|
||||
|
||||
/**
|
||||
@ -22,12 +23,12 @@ function login() {
|
||||
<main>
|
||||
|
||||
<!-- Force user to log in before he/she can see tasks. -->
|
||||
<div v-if="!store.session" id="login-prompt">
|
||||
<div v-if="!loggedIn()" id="login-prompt">
|
||||
<p>Your are currently not logged in.</p>
|
||||
<button class="btn btn-primary" @click="login" type="button">Login</button>
|
||||
</div>
|
||||
|
||||
<TodoRoot v-if="store.session" />
|
||||
<TodoRoot v-if="loggedIn()" />
|
||||
|
||||
</main>
|
||||
</template>
|
||||
|
Loading…
Reference in New Issue
Block a user