diff --git a/client/components/modals/AccountModal.vue b/client/components/modals/AccountModal.vue
index bdb8711d78..ed8660a06f 100644
--- a/client/components/modals/AccountModal.vue
+++ b/client/components/modals/AccountModal.vue
@@ -111,7 +111,8 @@
-
{{ $strings.ButtonChangeRootPassword }}
+
Unlink OpenID
+
{{ $strings.ButtonChangeRootPassword }}
{{ $strings.ButtonSubmit }}
@@ -136,7 +137,8 @@ export default {
newUser: {},
isNew: true,
tags: [],
- loadingTags: false
+ loadingTags: false,
+ unlinkingFromOpenID: false
}
},
watch: {
@@ -180,7 +182,7 @@ export default {
return this.isNew ? this.$strings.HeaderNewAccount : this.$strings.HeaderUpdateAccount
},
isEditingRoot() {
- return this.account && this.account.type === 'root'
+ return this.account?.type === 'root'
},
libraries() {
return this.$store.state.libraries.libraries
@@ -198,6 +200,9 @@ export default {
},
tagsSelectionText() {
return this.newUser.permissions.selectedTagsNotAccessible ? this.$strings.LabelTagsNotAccessibleToUser : this.$strings.LabelTagsAccessibleToUser
+ },
+ hasOpenIDLink() {
+ return !!this.account?.hasOpenIDLink
}
},
methods: {
@@ -205,6 +210,31 @@ export default {
// Force close when navigating - used in UsersTable
if (this.$refs.modal) this.$refs.modal.setHide()
},
+ unlinkOpenID() {
+ const payload = {
+ message: 'Are you sure you want to unlink this user from OpenID?',
+ callback: (confirmed) => {
+ if (confirmed) {
+ this.unlinkingFromOpenID = true
+ this.$axios
+ .$patch(`/api/users/${this.account.id}/openid-unlink`)
+ .then(() => {
+ this.$toast.success('User unlinked from OpenID')
+ this.show = false
+ })
+ .catch((error) => {
+ console.error('Failed to unlink user from OpenID', error)
+ this.$toast.error('Failed to unlink user from OpenID')
+ })
+ .finally(() => {
+ this.unlinkingFromOpenID = false
+ })
+ }
+ },
+ type: 'yesNo'
+ }
+ this.$store.commit('globals/setConfirmPrompt', payload)
+ },
accessAllTagsToggled(val) {
if (val) {
if (this.newUser.itemTagsSelected?.length) {
diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js
index 86d2c78e59..726777516d 100644
--- a/server/controllers/UserController.js
+++ b/server/controllers/UserController.js
@@ -194,6 +194,23 @@ class UserController {
})
}
+ /**
+ * PATCH: /api/users/:id/openid-unlink
+ *
+ * @param {import('express').Request} req
+ * @param {import('express').Response} res
+ */
+ async unlinkFromOpenID(req, res) {
+ Logger.debug(`[UserController] Unlinking user "${req.reqUser.username}" from OpenID with sub "${req.reqUser.authOpenIDSub}"`)
+ req.reqUser.authOpenIDSub = null
+ if (await Database.userModel.updateFromOld(req.reqUser)) {
+ SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.reqUser.toJSONForBrowser())
+ res.sendStatus(200)
+ } else {
+ res.sendStatus(500)
+ }
+ }
+
// GET: api/users/:id/listening-sessions
async getListeningSessions(req, res) {
var listeningSessions = await this.getUserListeningSessionsHelper(req.params.id)
diff --git a/server/objects/user/User.js b/server/objects/user/User.js
index b503872d64..d926e8be0b 100644
--- a/server/objects/user/User.js
+++ b/server/objects/user/User.js
@@ -117,7 +117,8 @@ class User {
createdAt: this.createdAt,
permissions: this.permissions,
librariesAccessible: [...this.librariesAccessible],
- itemTagsSelected: [...this.itemTagsSelected]
+ itemTagsSelected: [...this.itemTagsSelected],
+ hasOpenIDLink: !!this.authOpenIDSub
}
if (minimal) {
delete json.mediaProgress
diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js
index 3deb403002..d18d93e657 100644
--- a/server/routers/ApiRouter.js
+++ b/server/routers/ApiRouter.js
@@ -130,7 +130,7 @@ class ApiRouter {
this.router.get('/users/:id', UserController.middleware.bind(this), UserController.findOne.bind(this))
this.router.patch('/users/:id', UserController.middleware.bind(this), UserController.update.bind(this))
this.router.delete('/users/:id', UserController.middleware.bind(this), UserController.delete.bind(this))
-
+ this.router.patch('/users/:id/openid-unlink', UserController.middleware.bind(this), UserController.unlinkFromOpenID.bind(this))
this.router.get('/users/:id/listening-sessions', UserController.middleware.bind(this), UserController.getListeningSessions.bind(this))
this.router.get('/users/:id/listening-stats', UserController.middleware.bind(this), UserController.getListeningStats.bind(this))