添加 authserver 剩余 API 的实现。

This commit is contained in:
Qumolama.d 2022-05-04 16:48:09 +08:00
parent cf9f9bea7c
commit d8782c6b1f
Signed by: Lama3L9R
GPG Key ID: 1762AFC05157CE18
3 changed files with 329 additions and 3 deletions

View File

@ -29,6 +29,10 @@ export const server = fastify({
config.custom.preRouting(server)
server.route(AuthenticateRoutings.authenticate)
server.route(AuthenticateRoutings.refresh)
server.route(AuthenticateRoutings.validate)
server.route(AuthenticateRoutings.invalidate)
server.route(AuthenticateRoutings.signout)
config.custom.postRouting(server)
/*

View File

@ -4,7 +4,7 @@ const { Schema } = mongoose
export const TokenSchema = new Schema({
uuid: String,
token: String,
clientToken: String,
expireDate: Number,
deadDate: Number,
state: String, // alive, linbo, dead
})

View File

@ -55,10 +55,19 @@ export const authenticate = {
}
},
preHandler: async function(req, rep) {
this.conf.custom.overridePrehandler('/authserver/authenticate')
this.conf.custom.overridePrehandler('/authserver/authenticate', req, rep)
},
handler: async function (req, rep) {
let { username, password, clientToken, requestUser, agent } = req.body
if(!username || !password || !agent || agent.name.toLowerCase() !== 'minecraft') {
rep.code(418).send({
error: "ForbiddenOperationException",
errorMessage: "无效应用名,此服务端仅支援 Minecraft",
cause: "此服务器只支持 agent.name: minecraft"
})
}
const player = await this.models.Player.findOne({ email: username, password: createHash('sha256').update(password).digest().toString('hex').toLowerCase() })
if(!player || !player.permissions.some((it) => {
return it.node === 'login' && it.allowed && (it.duration === 0 || it.startDate + it.duration > Date.now())
@ -100,6 +109,13 @@ export const authenticate = {
}
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.CAPE = {
url: player.textures.cape,
metadata
}
}
const profile = {
uuid: uuidToNoSymboUUID(player.uuid),
name: player.username,
@ -114,9 +130,9 @@ export const authenticate = {
new this.models.Token({
uuid: player.uuid,
token: token,
clientToken: clientToken,
expireDate: Date.now() + 1000 * 60 * 60 * 24 * 15,
deadDate: Date.now() + 1000 * 60 * 60 * 24 * 30,
state: 'alive'
}).save()
return await rep.send({
@ -126,5 +142,311 @@ export const authenticate = {
selectedProfile: profile,
user: account
})
}
}
export const refresh = {
method: 'POST',
url: '/authserver/refresh',
schema: {
body: {
"type": "object",
"properties": {
"accessToken": {
"type": "string"
},
"clientToken": {
"type": "string",
"optional": true
},
"requestUser": {
"type": "boolean",
"optional": true
},
"selectedProfile": {
"optional": true,
...PlayerModel.PlayerSeriliazationSchema
}
}
},
response: {
200: {
"type": "object",
"properties": {
"accessToken": {
"type": "string"
},
"clientToken": {
"type": "string"
},
"selectedProfile": PlayerModel.PlayerSeriliazationSchema,
"user": PlayerModel.PlayerAccountSerializationSchema
}
}
}
},
preHandler: async function(req, rep) {
this.conf.custom.overridePrehandler('/authserver/refresh', req, rep)
},
handler: async function (req, rep) {
const { accessToken, clientToken, requestUser, selectedProfile } = req.body
const query = {
token: accessToken
}
if(clientToken) {
query.clientToken = clientToken
}
const token = await this.models.Token.findOne(query)
if(!token) {
return await rep.code(401).send({
error: "Unauthorized",
errorMessage: "accessToken无效",
cause: "accessToken无效"
})
}
const { deadDate, uuid } = token
if(deadDate < Date.now()) {
return await rep.code(401).send({
error: "Unauthorized",
errorMessage: "accessToken无效",
cause: "accessToken无效"
})
}
const [newToken, key] = generateToken(token.uuid)
this.log.info(`/authserver/authenticate > 为玩家 ${token.uuid} 刷新令牌: ${token.uuid}${newToken} | 随机 key = ${key}`)
await this.models.Token.updateOne({
token: accessToken,
clientToken: clientToken ?? undefined
}, {
$set: {
expireDate: 0,
deadDate: 0
}
})
new this.models.Token({
uuid: uuid,
token: newToken,
clientToken: clientToken ?? token.clientToken,
expireDate: Date.now() + 1000 * 60 * 60 * 24 * 15,
deadDate: Date.now() + 1000 * 60 * 60 * 24 * 30,
}).save()
const response = {
accessToken: newToken,
clientToken: clientToken ?? token.clientToken,
}
if(requestUser || selectedProfile) {
const player = await this.models.Player.findOne({ uuid })
if(requestUser) {
response.user = {
id: uuidToNoSymboUUID(player.uuid),
properties: [
{
name: "preferredLanguage",
value: "zh_CN"
}
]
}
}
if(selectedProfile) {
const textures = {
timestamp: 0,
profileId: uuidToNoSymboUUID(player.uuid),
profileName: player.username,
textures: { }
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.SKIN = {
url: player.textures.skin,
metadata
}
}
if(player.textures.skin && player.textures.skin != 0) { // Must be '!=' if this change to '!==' will never works
textures.textures.CAPE = {
url: player.textures.cape,
metadata
}
}
response.selectedProfile = {
uuid: uuidToNoSymboUUID(uuid),
name: player.username,
properties: [
{
name: "texturs",
value: Buffer.from(JSON.stringify(textures)).toString('base64')
}
]
}
}
}
return await rep.send(response)
}
}
export const validate = {
method: 'POST',
url: '/authserver/validate',
schema: {
body: {
"type": "object",
"properties": {
"accessToken": {
"type": "string"
},
"clientToken": {
"type": "string",
"optional": true
}
}
},
response: {
204: {
"type": "null"
}
}
},
preHandler: async function(req, rep) {
this.conf.custom.overridePrehandler('/authserver/validate', req, rep)
},
handler: async function (req, rep) {
const { accessToken, clientToken } = req.body
const query = {
token: accessToken
}
if(clientToken) {
query.clientToken = clientToken
}
const token = await this.models.Token.findOne(query)
if(!token) {
return await rep.code(401).send({
error: "Unauthorized",
errorMessage: "accessToken无效",
cause: "accessToken无效"
})
}
const { expireDate } = token
if(expireDate < Date.now()) {
return await rep.code(401).send({
error: "Unauthorized",
errorMessage: "accessToken无效",
cause: "accessToken无效"
})
}
return await rep.code(204).send()
}
}
export const invalidate = {
method: 'POST',
url: '/authserver/invalidate',
schema: {
body: {
"type": "object",
"properties": {
"accessToken": {
"type": "string"
},
"clientToken": {
"type": "string",
"optional": true
}
}
},
response: {
204: {
"type": "null"
}
}
}
,
preHandler: async function(req, rep) {
this.conf.custom.overridePrehandler('/authserver/invalidate', req, rep)
},
handler: async function (req, rep) {
const { accessToken } = req.body
const { modifiedCount } = await this.models.Token.updateOne({
token: accessToken
}, {
$set: {
expireDate: 0,
deadDate: 0
}
})
if(modifiedCount === 0) {
return await rep.code(401).send({
error: "Unauthorized",
errorMessage: "accessToken无效",
cause: "accessToken无效"
})
}
return await rep.code(204).send()
}
}
export const signout = {
method: 'POST',
url: '/authserver/signout',
schema: {
body: {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"password": {
"type": "string",
"optional": true
}
}
},
response: {
204: {
"type": "null"
}
}
},
preHandler: async function(req, rep) {
this.conf.custom.overridePrehandler('/authserver/logout', req, rep)
},
handler: async function (req, rep) {
const { username, password } = req.body
const player = await this.models.Player.findOne({ email: username, password: createHash('sha256').update(password).digest().toString('hex').toLowerCase() })
if(!player) {
return await rep.code(401).send({
error: "Unauthorized",
errorMessage: "用户名或密码错误",
cause: "用户名或密码错误"
})
}
await this.models.Token.deleteMany({
uuid: player.uuid
})
rep.code(204).send()
}
}