feat(xo-server/api): close connection when session expires (#4071)
See xoa-support#1389
This commit is contained in:
parent
9f6fc785bc
commit
851bcf9816
@ -5,8 +5,16 @@ import { getUserPublicProperties } from '../utils'
|
|||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
export async function signIn(credentials) {
|
export async function signIn(credentials) {
|
||||||
const user = await this.authenticateUser(credentials)
|
const { session } = this
|
||||||
this.session.set('user_id', user.id)
|
|
||||||
|
const { user, expiration } = await this.authenticateUser(credentials)
|
||||||
|
session.set('user_id', user.id)
|
||||||
|
|
||||||
|
if (expiration === undefined) {
|
||||||
|
session.unset('expiration')
|
||||||
|
} else {
|
||||||
|
session.set('expiration', expiration)
|
||||||
|
}
|
||||||
|
|
||||||
return getUserPublicProperties(user)
|
return getUserPublicProperties(user)
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ async function setUpPassport(express, xo, { authentication: authCfg }) {
|
|||||||
xo.registerPassportStrategy(
|
xo.registerPassportStrategy(
|
||||||
new LocalStrategy(async (username, password, done) => {
|
new LocalStrategy(async (username, password, done) => {
|
||||||
try {
|
try {
|
||||||
const user = await xo.authenticateUser({ username, password })
|
const { user } = await xo.authenticateUser({ username, password })
|
||||||
done(null, user)
|
done(null, user)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
done(null, false, { message: error.message })
|
done(null, false, { message: error.message })
|
||||||
@ -518,6 +518,11 @@ const setUpApi = (webServer, xo, config) => {
|
|||||||
|
|
||||||
// Connect the WebSocket to the JSON-RPC server.
|
// Connect the WebSocket to the JSON-RPC server.
|
||||||
socket.on('message', message => {
|
socket.on('message', message => {
|
||||||
|
const expiration = connection.get('expiration', undefined)
|
||||||
|
if (expiration !== undefined && expiration < Date.now()) {
|
||||||
|
return void connection.close()
|
||||||
|
}
|
||||||
|
|
||||||
jsonRpc.write(message)
|
jsonRpc.write(message)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -565,7 +570,7 @@ const setUpConsoleProxy = (webServer, xo) => {
|
|||||||
{
|
{
|
||||||
const { token } = parseCookies(req.headers.cookie)
|
const { token } = parseCookies(req.headers.cookie)
|
||||||
|
|
||||||
const user = await xo.authenticateUser({ token })
|
const { user } = await xo.authenticateUser({ token })
|
||||||
if (!(await xo.hasPermissions(user.id, [[id, 'operate']]))) {
|
if (!(await xo.hasPermissions(user.id, [[id, 'operate']]))) {
|
||||||
throw invalidCredentials()
|
throw invalidCredentials()
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ export default class {
|
|||||||
|
|
||||||
const user = await xo.getUserByName(username, true)
|
const user = await xo.getUserByName(username, true)
|
||||||
if (user && (await xo.checkUserPassword(user.id, password))) {
|
if (user && (await xo.checkUserPassword(user.id, password))) {
|
||||||
return user.id
|
return { userId: user.id }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -48,7 +48,8 @@ export default class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return (await xo.getAuthenticationToken(tokenId)).user_id
|
const token = await xo.getAuthenticationToken(tokenId)
|
||||||
|
return { expiration: token.expiration, userId: token.user_id }
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -88,6 +89,10 @@ export default class {
|
|||||||
// A provider can return:
|
// A provider can return:
|
||||||
// - `undefined`/`null` if the user could not be authenticated
|
// - `undefined`/`null` if the user could not be authenticated
|
||||||
// - the identifier of the authenticated user
|
// - the identifier of the authenticated user
|
||||||
|
// - an object containing:
|
||||||
|
// - `userId`
|
||||||
|
// - optionally `expiration` to indicate when the session is no longer
|
||||||
|
// valid
|
||||||
// - an object with a property `username` containing the name
|
// - an object with a property `username` containing the name
|
||||||
// of the authenticated user
|
// of the authenticated user
|
||||||
const result = await provider(credentials)
|
const result = await provider(credentials)
|
||||||
@ -97,9 +102,20 @@ export default class {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.username
|
if (typeof result === 'string') {
|
||||||
? await this._xo.registerUser(undefined, result.username)
|
return {
|
||||||
: await this._xo.getUser(result)
|
user: await this._getUser(result),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { userId, username, expiration } = result
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: await (userId !== undefined
|
||||||
|
? this._xo.getUser(userId)
|
||||||
|
: this._xo.registerUser(undefined, username)),
|
||||||
|
expiration,
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// DEPRECATED: Authentication providers may just throw `null`
|
// DEPRECATED: Authentication providers may just throw `null`
|
||||||
// to indicate they could not authenticate the user without
|
// to indicate they could not authenticate the user without
|
||||||
@ -109,7 +125,9 @@ export default class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticateUser(credentials) {
|
async authenticateUser(
|
||||||
|
credentials
|
||||||
|
): Promise<{| user: Object, expiration?: number |}> {
|
||||||
// don't even attempt to authenticate with empty password
|
// don't even attempt to authenticate with empty password
|
||||||
const { password } = credentials
|
const { password } = credentials
|
||||||
if (password === '') {
|
if (password === '') {
|
||||||
|
Loading…
Reference in New Issue
Block a user