Skip to content

Commit adddf18

Browse files
committed
Invalidate all sessions by keeping track of session revision
1 parent c571ba0 commit adddf18

File tree

6 files changed

+45
-0
lines changed

6 files changed

+45
-0
lines changed

api/resolvers/user.js

+8
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,14 @@ export default {
898898

899899
await models.user.update({ where: { id: me.id }, data: { hideWelcomeBanner: true } })
900900
return true
901+
},
902+
invalidateSessions: async (parent, args, { me, models }) => {
903+
if (!me) {
904+
throw new GqlAuthenticationError()
905+
}
906+
907+
await models.user.update({ where: { id: me.id }, data: { sessionRev: { increment: 1 } } })
908+
return true
901909
}
902910
},
903911

api/typeDefs/user.js

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export default gql`
4444
generateApiKey(id: ID!): String
4545
deleteApiKey(id: ID!): User
4646
disableFreebies: Boolean
47+
invalidateSessions: Boolean
4748
}
4849
4950
type User {

pages/api/auth/[...nextauth].js

+6
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ function getCallbacks (req, res) {
9898
// token won't have an id on it for new logins, we add it
9999
// note: token is what's kept in the jwt
100100
token.id = Number(user.id)
101+
token.sessionRev = user.sessionRev || 0
101102

102103
// if referrer exists, set on user
103104
// isNewUser doesn't work for nostr/lightning auth because we create the user before nextauth can
@@ -143,6 +144,11 @@ function getCallbacks (req, res) {
143144
// and returns a new object session that's returned whenever get|use[Server]Session is called
144145
session.user.id = token.id
145146

147+
// invalidate this session if session revision mismatch
148+
session.user.sessionRev = token.sessionRev || 0 // if no sessionRev, set to 0, the user will have one after login
149+
const sessionRev = await prisma.user.findUnique({ where: { id: session.user.id }, select: { sessionRev: true } })
150+
if (session.user.sessionRev !== sessionRev?.sessionRev) return {}
151+
146152
return session
147153
}
148154
}

pages/settings/index.js

+27
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,7 @@ function AuthMethods ({ methods, apiKeyEnabled }) {
886886
)
887887
}
888888
})}
889+
<InvalidateSessions />
889890
<ApiKey apiKey={methods.apiKey} enabled={apiKeyEnabled} />
890891
</>
891892
)
@@ -1188,3 +1189,29 @@ const TipRandomField = () => {
11881189
</>
11891190
)
11901191
}
1192+
1193+
const InvalidateSessions = () => {
1194+
const showModal = useShowModal()
1195+
const router = useRouter()
1196+
1197+
const [invalidateSessions] = useMutation(gql`
1198+
mutation invalidateSessions {
1199+
invalidateSessions
1200+
}`
1201+
)
1202+
1203+
return (
1204+
<Button
1205+
variant='danger'
1206+
onClick={async () => {
1207+
const { data } = await invalidateSessions()
1208+
if (data.invalidateSessions) {
1209+
// TODO: Invalidate Sessions Obstacle
1210+
showModal(onClose => (router.push('/')))
1211+
}
1212+
}}
1213+
>
1214+
Invalidate Sessions
1215+
</Button>
1216+
)
1217+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "users" ADD COLUMN "sessionRev" INTEGER NOT NULL DEFAULT 0;

prisma/schema.prisma

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ model User {
123123
mcredits BigInt @default(0)
124124
receiveCreditsBelowSats Int @default(10)
125125
sendCreditsBelowSats Int @default(10)
126+
sessionRev Int @default(0)
126127
muters Mute[] @relation("muter")
127128
muteds Mute[] @relation("muted")
128129
ArcOut Arc[] @relation("fromUser")

0 commit comments

Comments
 (0)