improved login page / login status

This commit is contained in:
Tobias Kurze
2019-11-26 15:25:36 +01:00
parent a9a94d6f3c
commit 42366def40
6 changed files with 191 additions and 61 deletions

View File

@@ -28,8 +28,6 @@
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav>
<b-nav-item to="/about">About</b-nav-item>
<b-nav-item v-if="authenticated" to="/logout">Logout</b-nav-item>
<b-nav-item v-else="authenticated" to="/login">Login</b-nav-item>
<b-nav-item v-if="authenticated" :to="{name: 'rooms'}">{{ $t('Rooms') }}</b-nav-item>
<b-nav-item v-if="authenticated" :to="{name: 'recorders'}">{{ $t('Recorders') }}</b-nav-item>
@@ -57,18 +55,25 @@
</b-dropdown-item>
</b-nav-item-dropdown>
<b-nav-item-dropdown right>
<b-nav-item-dropdown v-if="authenticated" right>
<!-- Using 'button-content' slot -->
<template slot="button-content"><em>User</em></template>
<b-dropdown-item :to="{name: 'profile'}">Profile</b-dropdown-item>
<b-dropdown-item to="/logout">Sign Out</b-dropdown-item>
<b-dropdown-item to="/logout" @click.prevent="logout()">Sign Out</b-dropdown-item>
</b-nav-item-dropdown>
<b-nav-item v-else to="/login">Login</b-nav-item>
</b-navbar-nav>
</b-collapse>
</b-navbar>
<span>({{tokenValidity}})</span> |
<span>({{refreshTokenValidity}})</span>
<span v-if="tokenValidity">({{$t('Session will timeout in: ')}}{{tokenValidity}})</span>
<span v-else>{{$t('Session has expired!')}} <a v-if="refreshTokenValidity" href="~"
@click.prevent="refreshToken()">{{$t('Click here to refresh session')}}</a></span>
<span v-if="refreshTokenValidity">&nbsp;|&nbsp;({{refreshTokenValidity}})
<input type="checkbox" id="auto_renew_cb" v-model="autoRenewSession">
<label for="auto_renew_cb">{{$t('auto renew session')}}</label>
</span>
<span v-else>{{$t('Can\'t renew session please login again!')}}</span>
</div>
<router-view/>
</div>
@@ -94,6 +99,7 @@
langs: ['de', 'en', 'es'],
dismissSecs: 5,
dismissCountDown: 0,
autoRenewSession: false,
};
},
methods: {
@@ -105,6 +111,14 @@
countDownChanged(dismissCountDown) {
this.dismissCountDown = dismissCountDown;
},
logout() {
this.$store.dispatch('logout', {revokeRefreshToken: true});
this.$router.push({name: 'home'});
},
refreshToken() {
this.$store.dispatch('refreshToken');
}
},
mounted() {
EventBus.$on('failedLoadingRecorders', (msg) => {
@@ -113,23 +127,37 @@
EventBus.$on('failedLoadingRooms', (msg) => {
this.showErrorMessage(msg);
});
EventBus.$on('failedRefreshingToken', (msg)=>{
EventBus.$on('failedRefreshingToken', (msg) => {
this.refreshFailed = true;
});
this.$nextTick(() => {
window.setInterval(() => {
// this.$log.debug(getRemainingJwtValiditySeconds(this.$store.state.access_token));
this.tokenValidity = getRemainingJwtValiditySeconds(this.$store.state.access_token);
this.refreshTokenValidity = getRemainingJwtValiditySeconds(this.$store.state.refresh_token);
//this.$log.debug(getRemainingJwtValiditySeconds(this.$store.state.access_token));
let tokenValidity = getRemainingJwtValiditySeconds(this.$store.state.access_token);
//this.tokenValidity = this.tokenValidity.format('mm:ss');
let refreshTokenValidity = getRemainingJwtValiditySeconds(this.$store.state.refresh_token);
// this.$log.debug(this.$store.state);
if (this.tokenValidity < 50 && this.refreshTokenValidity > 30 && !this.refreshFailed) {
if (tokenValidity < 50 && refreshTokenValidity > 30 && this.autoRenewSession && !this.refreshFailed) {
this.$store.dispatch('refreshToken'); // renew access token
}
if(this.refreshFailed){
if (this.autoRenewSession && this.refreshFailed) {
this.$store.dispatch('resetToken'); // delete all token info if refresh fails
this.$router.push({ name: 'login'});
this.$router.push({name: 'login'});
this.refreshFailed = false;
}
if (isNaN(tokenValidity)) {
this.tokenValidity = false;
} else {
this.tokenValidity = new Date(1000 * tokenValidity).toISOString().substr(14, 5);
}
if (isNaN(tokenValidity)) {
this.refreshTokenValidity = false;
} else {
this.refreshTokenValidity = new Date(1000 * refreshTokenValidity).toISOString().substr(11, 8);
}
}, 1000);
});

View File

@@ -23,6 +23,10 @@ export function authenticate(userData: any) {
return axios.post(`${API_URL}/auth/login`, userData);
}
export function revokeRefreshKey(jwt: any) {
return axios.post(`${API_URL}/auth/revokeRefreshToken`, { headers: { Authorization: `Bearer ${jwt}` } });
}
export function register(userData: any) {
return axios.post(`${API_URL}/auth/register`, userData);
}

View File

@@ -2,54 +2,55 @@
<template>
<div>
<div v-if="!authenticated">
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<h2 class="title">Login or Register</h2>
<p class="subtitle error-msg">{{ errorMsg }}</p>
</div>
</div>
<ul>
<li v-for="(provider, index) in loginProviders" v-bind:id="index">
<a :href="provider.url">{{ index }} ({{provider.type}})</a>
</li>
</ul>
<h3>Users</h3>
<ul>
<li v-for="(user) in users">
<a href="#">({{user.id}}) {{user.first_name}}</a>
</li>
</ul>
<button v-on:click="oidc_login">Login via OIDC</button>
<a href="http://localhost:5443/api/auth/oidc?redirect_url=/login">OIDC with redirect</a>
</section>
<section class="section">
<div class="container">
<div class="field">
<label class="label is-large" for="email">Email:</label>
<div class="control">
<input type="email" class="input is-large" id="email" v-model="email">
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<h2 class="title">Login or Register</h2>
<p class="subtitle error-msg">{{ errorMsg }}</p>
</div>
</div>
<div class="field">
<label class="label is-large" for="password">Password:</label>
<div class="control">
<input type="password" class="input is-large" id="password" v-model="password">
<ul>
<li v-for="(provider, index) in loginProviders" v-bind:id="index">
<a :href="provider.url">{{ index }} ({{provider.type}})</a>
</li>
</ul>
<h3>Users</h3>
<ul>
<li v-for="(user) in users">
<a href="#">({{user.id}}) {{user.first_name}}</a>
</li>
</ul>
<button v-on:click="oidc_login">Login via OIDC</button>
<a href="http://localhost:5443/api/auth/oidc?redirect_url=/login">OIDC with redirect</a>
</section>
<section class="section">
<div class="container">
<div class="field">
<label class="label is-large" for="email">Email:</label>
<div class="control">
<input type="email" class="input is-large" id="email" v-model="email">
</div>
</div>
<div class="field">
<label class="label is-large" for="password">Password:</label>
<div class="control">
<input type="password" class="input is-large" id="password" v-model="password">
</div>
</div>
</div>
<div class="control">
<a class="button is-large is-primary" @click="authenticate">Login</a>
<a class="button is-large is-success" @click="register">Register</a>
</div>
<div class="control">
<a class="button is-large is-primary" @click="authenticate">Login</a>
<a class="button is-large is-success" @click="register">Register</a>
</div>
</div>
</section>
</div>
</section>
</div>
<div v-else>
<h1>{{ $t('You are already logged in!') }}</h1>
<p><a href="some_logout_url">{{$t('logout')}}</a></p>
<h2>{{$t('Redirecting you to')}} <router-link :to="{name: redirectTarget}">{{redirectTarget}}</router-link> {{$t('in')}} {{redirectTime}} {{$t('seconds')}}.</h2>
<p><a href="" @click.prevent="logout()">{{$t('logout')}}</a></p>
</div>
</div>
</template>
@@ -64,6 +65,9 @@
email: '',
password: '',
errorMsg: '',
redirectMsg: '',
redirectTarget: 'profile',
redirectTime: 5,
loginProviders: [],
};
},
@@ -79,6 +83,10 @@
oidc_login() {
this.$store.dispatch('oidc_login', '\\oidc_login_redirection');
},
logout() {
this.$store.dispatch('logout', {revokeRefreshToken: true});
this.$router.push({name: 'home'});
},
},
mounted() {
// this.$parent.$data.isLoading = true;
@@ -104,6 +112,22 @@
this.$cookies.remove('tokens');
}
this.$log.debug(this.$cookies.keys());
this.$nextTick(() => {
if (this.authenticated) {
if (this.$route.query.redirectionTarget) {
this.redirectTarget = this.$route.query.redirectionTarget;
}
let interval = window.setInterval(() => {
console.log(this.redirectTime);
this.redirectTime = this.redirectTime - 1;
if (this.redirectTime < 0) {
clearInterval(interval);
this.$router.push({name: this.redirectTarget});
}
}, 1000);
}
});
},
beforeDestroy() {
EventBus.$off('failedRegistering');

View File

@@ -14,7 +14,7 @@ import {
postNewSurvey,
authenticate,
register,
oidc_login, fetchUsers, getFreshToken, fetchProfile, fetchUserGroups,
oidc_login, fetchUsers, getFreshToken, fetchProfile, fetchUserGroups, revokeRefreshKey,
} from '@/api';
import {isValidJwt, EventBus} from '@/utils';
@@ -184,6 +184,17 @@ const actions = {
EventBus.$emit('failedAuthentication', error);
});
},
logout(context: any, revokeRefreshToken: any) {
context.commit('setTokens', {tokens: {access_token: '', refresh_token: ''}});
if(revokeRefreshToken){
return revokeRefreshKey(context.state.access_token)
.catch((error) => {
Vue.$log.warn('Error Authenticating (Could not revoke refresh token): ', error);
EventBus.$emit('failedRevokingRefreshToken', error);
});
}
//this.clearAll();
},
oidc_login(context: any, redirectionUrl: any) {
// context.commit('setUserData', { userData });
return oidc_login(redirectionUrl)