diff --git a/doc/api.oa3 b/doc/api.oa3
index bbce1057..ebb0ba21 100644
--- a/doc/api.oa3
+++ b/doc/api.oa3
@@ -913,30 +913,12 @@ paths:
required: true
schema:
type: string
- - name: appName
+ - name: all
in: query
- description: name of connecting app
+ description: whether all app tokens should be cleared
required: false
schema:
- type: string
- - name: appVersion
- in: query
- description: version of connecting app
- required: false
- schema:
- type: string
- - name: platform
- in: query
- description: device platform
- required: false
- schema:
- type: string
- - name: deviceToken
- in: query
- description: deviceToken for push notification
- required: false
- schema:
- type: string
+ type: boolean
responses:
'200':
description: ok
diff --git a/net/server/internal/api_removeAgentToken.go b/net/server/internal/api_removeAgentToken.go
index 93bff182..cf4c7898 100644
--- a/net/server/internal/api_removeAgentToken.go
+++ b/net/server/internal/api_removeAgentToken.go
@@ -10,6 +10,9 @@ import (
//RemoveAgentToken
func RemoveAgentToken(w http.ResponseWriter, r *http.Request) {
+ // logout of all devices
+ logoutMode := r.FormValue("all") == "true"
+
// parse authentication token
target, access, err := ParseToken(r.FormValue("agent"))
if err != nil {
@@ -17,30 +20,54 @@ func RemoveAgentToken(w http.ResponseWriter, r *http.Request) {
return
}
- // load session
- var session store.Session
- if err = store.DB.Where("account_id = ? AND token = ?", target, access).Find(&session).Error; err != nil {
- if errors.Is(err, gorm.ErrRecordNotFound) {
- ErrResponse(w, http.StatusNotFound, err);
- } else {
+ if logoutMode {
+ var sessions []store.Session
+ if err = store.DB.Where("account_id = ?", target, access).Find(&sessions).Error; err != nil {
ErrResponse(w, http.StatusInternalServerError, err);
+ return;
}
- return;
- }
- // delete session
- err = store.DB.Transaction(func(tx *gorm.DB) error {
- if res := tx.Where("session_id = ?", session.ID).Delete(&store.PushEvent{}).Error; res != nil {
- return res
+ // delete all sessions
+ err = store.DB.Transaction(func(tx *gorm.DB) error {
+ for _, session := range sessions {
+ if res := tx.Where("session_id = ?", session.ID).Delete(&store.PushEvent{}).Error; res != nil {
+ return res
+ }
+ if res := tx.Where("id = ?", session.ID).Delete(&store.Session{}).Error; res != nil {
+ return res
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ ErrResponse(w, http.StatusInternalServerError, err)
+ return
}
- if res := tx.Where("id = ?", session.ID).Delete(&store.Session{}).Error; res != nil {
- return res
+ } else {
+ var session store.Session
+ if err = store.DB.Where("account_id = ? AND token = ?", target, access).Find(&session).Error; err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ ErrResponse(w, http.StatusNotFound, err);
+ } else {
+ ErrResponse(w, http.StatusInternalServerError, err);
+ }
+ return;
+ }
+
+ // delete session
+ err = store.DB.Transaction(func(tx *gorm.DB) error {
+ if res := tx.Where("session_id = ?", session.ID).Delete(&store.PushEvent{}).Error; res != nil {
+ return res
+ }
+ if res := tx.Where("id = ?", session.ID).Delete(&store.Session{}).Error; res != nil {
+ return res
+ }
+ return nil
+ })
+ if err != nil {
+ ErrResponse(w, http.StatusInternalServerError, err)
+ return
}
- return nil
- })
- if err != nil {
- ErrResponse(w, http.StatusInternalServerError, err)
- return
}
WriteResponse(w, nil)
diff --git a/net/web/src/api/clearLogin.js b/net/web/src/api/clearLogin.js
index 8dcf4bde..cc2887ff 100644
--- a/net/web/src/api/clearLogin.js
+++ b/net/web/src/api/clearLogin.js
@@ -1,7 +1,10 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
-export async function clearLogin(token) {
- let logout = await fetchWithTimeout(`/account/apps?agent=${token}`, { method: 'DELETE' })
+export async function clearLogin(token, all) {
+console.log("LOGOUT: ", token, all);
+
+ const param = all ? '&all=true' : ''
+ const logout = await fetchWithTimeout(`/account/apps?agent=${token}${param}`, { method: 'DELETE' })
checkResponse(logout)
}
diff --git a/net/web/src/context/useAppContext.hook.js b/net/web/src/context/useAppContext.hook.js
index eec9b889..7b5bbfee 100644
--- a/net/web/src/context/useAppContext.hook.js
+++ b/net/web/src/context/useAppContext.hook.js
@@ -71,8 +71,8 @@ export function useAppContext(websocket) {
}
const actions = {
- logout: async () => {
- await appLogout();
+ logout: async (all) => {
+ await appLogout(all);
},
access: async (token) => {
await appAccess(token)
@@ -140,10 +140,10 @@ export function useAppContext(websocket) {
return access.created;
}
- const appLogout = async () => {
+ const appLogout = async (all) => {
clearSession();
try {
- await clearLogin(appToken.current);
+ await clearLogin(appToken.current, all);
}
catch (err) {
console.log(err);
diff --git a/net/web/src/session/identity/Identity.jsx b/net/web/src/session/identity/Identity.jsx
index f8d53d7d..a41a00d8 100644
--- a/net/web/src/session/identity/Identity.jsx
+++ b/net/web/src/session/identity/Identity.jsx
@@ -1,6 +1,7 @@
-import { Modal, Dropdown, Menu, Tooltip } from 'antd';
+import { useRef } from 'react';
+import { Modal, Switch, Dropdown, Menu, Tooltip } from 'antd';
import { Logo } from 'logo/Logo';
-import { IdentityWrapper, ErrorNotice, InfoNotice } from './Identity.styled';
+import { IdentityWrapper, LogoutContent, ErrorNotice, InfoNotice } from './Identity.styled';
import { useIdentity } from './useIdentity.hook';
import { LogoutOutlined, InfoCircleOutlined, ExclamationCircleOutlined, DownOutlined } from '@ant-design/icons';
@@ -8,14 +9,19 @@ export function Identity({ openAccount, openCards, cardUpdated }) {
const [modal, modalContext] = Modal.useModal();
const { state, actions } = useIdentity();
+ const all = useRef(false);
const logout = () => {
modal.confirm({
title: 'Are you sure you want to logout?',
icon: ,
+ content: e.stopPropagation()}>
+ Logout of All Devices
+ {all.current = e}} size="small" />
+ ,
bodyStyle: { padding: 16 },
onOk() {
- actions.logout();
+ actions.logout(all.current);
},
onCancel() {},
});
diff --git a/net/web/src/session/identity/Identity.styled.js b/net/web/src/session/identity/Identity.styled.js
index 7514fa2e..0c122d4b 100644
--- a/net/web/src/session/identity/Identity.styled.js
+++ b/net/web/src/session/identity/Identity.styled.js
@@ -60,6 +60,18 @@ export const IdentityWrapper = styled.div`
}
`;
+export const LogoutContent = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 8px;
+
+ .logoutMode {
+ padding-right: 8px;
+ color: ${Colors.text};
+ }
+`
+
export const ErrorNotice = styled.div`
color: ${Colors.alert};
`
diff --git a/net/web/src/session/identity/useIdentity.hook.js b/net/web/src/session/identity/useIdentity.hook.js
index 637e6ece..911e4c95 100644
--- a/net/web/src/session/identity/useIdentity.hook.js
+++ b/net/web/src/session/identity/useIdentity.hook.js
@@ -33,7 +33,9 @@ export function useIdentity() {
}, [app.state]);
const actions = {
- logout: app.actions.logout,
+ logout: (all) => {
+ app.actions.logout(all);
+ },
};
return { state, actions };