1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-05 02:39:33 +02:00

Add ability to list and revoke token sessions

This commit is contained in:
Chocobozzz 2025-07-30 11:33:07 +02:00
parent a53ed039b8
commit 57caf25611
No known key found for this signature in database
GPG key ID: 583A612D890159BE
40 changed files with 1158 additions and 138 deletions

View file

@ -1,4 +1,3 @@
import express from 'express'
import OAuth2Server, {
InvalidClientError,
InvalidGrantError,
@ -8,16 +7,18 @@ import OAuth2Server, {
UnauthorizedClientError,
UnsupportedGrantTypeError
} from '@node-oauth/oauth2-server'
import { pick } from '@peertube/peertube-core-utils'
import { HttpStatusCode, ServerErrorCode, UserRegistrationState } from '@peertube/peertube-models'
import { sha1 } from '@peertube/peertube-node-utils'
import { randomBytesPromise } from '@server/helpers/core-utils.js'
import { isOTPValid } from '@server/helpers/otp.js'
import { CONFIG } from '@server/initializers/config.js'
import { UserRegistrationModel } from '@server/models/user/user-registration.js'
import { MOAuthClient } from '@server/types/models/index.js'
import { sha1 } from '@peertube/peertube-node-utils'
import { HttpStatusCode, ServerErrorCode, UserRegistrationState } from '@peertube/peertube-models'
import express from 'express'
import { OTP } from '../../initializers/constants.js'
import { BypassLogin, getAccessToken, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model.js'
import { Hooks } from '../plugins/hooks.js'
import { BypassLogin, getAccessToken, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model.js'
class MissingTwoFactorError extends Error {
code = HttpStatusCode.UNAUTHORIZED_401
@ -40,9 +41,7 @@ class RegistrationApprovalRejected extends Error {
}
/**
*
* Reimplement some functions of OAuth2Server to inject external auth methods
*
*/
const oAuthServer = new OAuth2Server({
// Wants seconds
@ -99,18 +98,25 @@ async function handleOAuthToken (req: express.Request, options: { refreshTokenAu
throw new UnauthorizedClientError('Unauthorized client: `grant_type` is invalid')
}
const ip = req.ip
const userAgent = req.headers['user-agent']
if (grantType === 'password') {
return handlePasswordGrant({
request,
client,
bypassLogin
bypassLogin,
ip,
userAgent
})
}
return handleRefreshGrant({
request,
client,
refreshTokenAuthName
refreshTokenAuthName,
ip,
userAgent
})
}
@ -122,11 +128,10 @@ function handleOAuthAuthenticate (
}
export {
MissingTwoFactorError,
InvalidTwoFactorError,
handleOAuthAuthenticate,
handleOAuthToken,
handleOAuthAuthenticate
InvalidTwoFactorError,
MissingTwoFactorError
}
// ---------------------------------------------------------------------------
@ -135,6 +140,8 @@ async function handlePasswordGrant (options: {
request: Request
client: MOAuthClient
bypassLogin?: BypassLogin
ip: string
userAgent: string
}) {
const { client } = options
@ -177,7 +184,16 @@ async function handlePasswordGrant (options: {
}
}
const token = await buildToken()
const now = new Date()
const token = await buildToken({
loginDevice: options.userAgent,
loginIP: options.ip,
loginDate: now,
lastActivityDevice: options.userAgent,
lastActivityIP: options.ip,
lastActivityDate: now
})
return saveToken(token, client, user, { bypassLogin })
}
@ -186,6 +202,8 @@ async function handleRefreshGrant (options: {
request: Request
client: MOAuthClient
refreshTokenAuthName: string
ip: string
userAgent: string
}) {
const { request, client, refreshTokenAuthName } = options
@ -209,7 +227,17 @@ async function handleRefreshGrant (options: {
await revokeToken({ refreshToken: refreshToken.refreshToken })
const token = await buildToken()
const token = await buildToken({
lastActivityDevice: options.userAgent,
lastActivityIP: options.ip,
lastActivityDate: new Date(),
...pick(refreshToken.token, [
'loginDevice',
'loginIP',
'loginDate'
])
})
return saveToken(token, client, refreshToken.user, { refreshTokenAuthName })
}
@ -227,13 +255,29 @@ function getTokenExpiresAt (type: 'access' | 'refresh') {
return new Date(Date.now() + lifetime)
}
async function buildToken () {
async function buildToken (options: {
loginDevice: string
loginIP: string
loginDate: Date
lastActivityDevice: string
lastActivityIP: string
lastActivityDate: Date
}) {
const [ accessToken, refreshToken ] = await Promise.all([ generateRandomToken(), generateRandomToken() ])
return {
accessToken,
refreshToken,
accessTokenExpiresAt: getTokenExpiresAt('access'),
refreshTokenExpiresAt: getTokenExpiresAt('refresh')
refreshTokenExpiresAt: getTokenExpiresAt('refresh'),
...pick(options, [
'loginDevice',
'loginIP',
'loginDate',
'lastActivityDevice',
'lastActivityIP',
'lastActivityDate'
])
}
}