Skip to content

Commit e273894

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

File tree

7 files changed

+49
-0
lines changed

7 files changed

+49
-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

+2
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 {
@@ -197,6 +198,7 @@ export default gql`
197198
walletsUpdatedAt: Date
198199
proxyReceive: Boolean
199200
directReceive: Boolean
201+
sessionRev: Int
200202
receiveCreditsBelowSats: Int!
201203
sendCreditsBelowSats: Int!
202204
}

fragments/users.js

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ ${STREAK_FIELDS}
5454
walletsUpdatedAt
5555
proxyReceive
5656
directReceive
57+
sessionRev
5758
}
5859
optional {
5960
isContributor
@@ -117,6 +118,7 @@ export const SETTINGS_FIELDS = gql`
117118
apiKeyEnabled
118119
proxyReceive
119120
directReceive
121+
sessionRev
120122
receiveCreditsBelowSats
121123
sendCreditsBelowSats
122124
}

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

+28
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useState, useMemo } from 'react'
88
import { gql, useMutation, useQuery } from '@apollo/client'
99
import { getGetServerSideProps } from '@/api/ssrApollo'
1010
import LoginButton from '@/components/login-button'
11+
import LogoutObstacle from '@/components/nav/common'
1112
import { signIn } from 'next-auth/react'
1213
import { LightningAuth } from '@/components/lightning-auth'
1314
import { SETTINGS, SET_SETTINGS } from '@/fragments/users'
@@ -886,6 +887,7 @@ function AuthMethods ({ methods, apiKeyEnabled }) {
886887
)
887888
}
888889
})}
890+
<InvalidateSessions />
889891
<ApiKey apiKey={methods.apiKey} enabled={apiKeyEnabled} />
890892
</>
891893
)
@@ -1188,3 +1190,29 @@ const TipRandomField = () => {
11881190
</>
11891191
)
11901192
}
1193+
1194+
const InvalidateSessions = () => {
1195+
const showModal = useShowModal()
1196+
const router = useRouter()
1197+
1198+
const [invalidateSessions] = useMutation(gql`
1199+
mutation invalidateSessions {
1200+
invalidateSessions
1201+
}`
1202+
)
1203+
1204+
return (
1205+
<Button
1206+
variant='danger'
1207+
onClick={async () => {
1208+
const { data } = await invalidateSessions()
1209+
if (data.invalidateSessions) {
1210+
// TODO: Invalidate Sessions Obstacle
1211+
showModal(onClose => (router.push('/')))
1212+
}
1213+
}}
1214+
>
1215+
Invalidate Sessions
1216+
</Button>
1217+
)
1218+
}
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)