Skip to content

Commit

Permalink
feat: Added a logout event when the user triggers a logout to allow o…
Browse files Browse the repository at this point in the history
…thers connected clients to logout as well (closes #1076)
  • Loading branch information
claustres committed Feb 3, 2025
1 parent 07652f1 commit def30d5
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 8 deletions.
2 changes: 1 addition & 1 deletion core/api/services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export default async function () {

const authConfig = app.get('authentication')
if (authConfig) {
await app.createService('users', { modelsPath, servicesPath })
await app.createService('users', { modelsPath, servicesPath, methods: ['logout'], events: ['logout'] })
debug('\'users\' service created')
await app.createService('account', {
servicesPath,
Expand Down
5 changes: 5 additions & 0 deletions core/api/services/users/users.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
logout (user) {
this.emit('logout', user)
}
}
2 changes: 1 addition & 1 deletion core/client/components/account/KDeleteAccountManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ async function onDelete () {
}).onOk(async (data) => {
try {
await api.getService('users').remove(User._id)
// Redirecting to logut will logut the user but logout an inexsiting user will raise an error
// Redirecting to logout will logout the user but logout an inexsiting user will raise an error
// We prefer to clean the token manually instead
// router.push({ name: 'logout' })
Store.set('user', null)
Expand Down
4 changes: 3 additions & 1 deletion core/client/components/screen/KLogoutScreen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import config from 'config'
import { ref } from 'vue'
import KScreen from './KScreen.vue'
import { logout } from '../../utils/utils.session.js'
import { Store } from '../../store.js'
// Data
const actions = ref(_.get(config, 'screens.logout.actions', []))
const user = Store.get('user')
// Immediate
logout()
if (user) logout()
</script>
4 changes: 3 additions & 1 deletion core/client/components/screen/KOAuthLogoutScreen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import KScreen from './KScreen.vue'
import { logout } from '../../utils/utils.session.js'
import { Store } from '../../store.js'
// Data
const route = useRoute()
const actions = ref(_.get(config, 'screens.logout.actions', []))
// When called with a prameter this means we should logout from the OAuth provider as well
// In this case we do not show the logout screen as the OAuth provider has its own
const provider = ref(route.params.provider)
const user = Store.get('user')
// Hooks
onMounted(async () => {
await logout()
if (user) await logout()
// When called with a prameter this means we should logout from the OAuth provider as well
if (provider.value) {
location.href = `oauth-logout/${provider.value}`
Expand Down
5 changes: 5 additions & 0 deletions core/client/composables/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ export function useSession (options = {}) {
}
// Then redirection
Events.on('user-abilities-changed', redirect)
api.on('logout', () => {
// Used to automatically redirect when the user has requested a logout from another client
// We don't use redirect() here as in this case the user is already logout and it would redirect to login instead
router.push({ name: 'logout' })
})

try {
await restoreSession()
Expand Down
2 changes: 1 addition & 1 deletion core/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default async function initialize () {
Exporter.initialize(_.get(config, 'exporter'))
Schema.initialize(_.get(config, 'schema'))

// Listen to the 'patched' event on the users
// Listen to events on the users
utils.subscribeToUserChanges()

// Register the readers
Expand Down
21 changes: 18 additions & 3 deletions core/client/utils/utils.session.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ export async function restoreSession () {
await authenticate(authentication)
} catch (error) {
// This ensure an old token is not kept e.g. when the user has been deleted
// await logout()
// It actually causes a call to the remove method on the authentication service, which fails due to missing access token
// See https://github.com/kalisio/kdk/issues/757, as a consequence we prefer to clean the token manually instead
// await logout()
await api.authentication.removeAccessToken()
// Rethrow for caller to handle
throw error
Expand Down Expand Up @@ -97,14 +97,29 @@ export async function updateUser (user) {
}
}

export async function logoutUser (user) {
// User has logout from another client session, logout here as well.
// No check required for user ID as only the target user should receive the logout event.
// We cannot use the logout route directly or api.logout() as this will trigger a new remove request to the authentication service.
// Here we just want to set the application state "as if" the user as requested a logout without doing it actually.
await LocalCache.removeItem('authentication')
await api.authentication.removeAccessToken()
await api.authentication.reset()
Store.set('user', null)
// In this specific case as we bypass actual authentication the events will not be emitted
api.emit('logout', user)
}

export function subscribeToUserChanges () {
// Listen to the 'patched' event on the users
// Listen to the 'patched'/'logout' event on the users
const users = api.getService('users')
users.on('patched', updateUser)
users.on('logout', logoutUser)
}

export function unsubscribeToUserChanges () {
// Listen to the 'patched' event on the users
// Listen to the 'patched'/'logout' event on the users
const users = api.getService('users')
users.off('patched', updateUser)
users.off('logout', logoutUser)
}

0 comments on commit def30d5

Please sign in to comment.