mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
reimplement authentification process and regeneration system
This commit is contained in:
parent
12938553d7
commit
97e959f9d7
@ -101,15 +101,15 @@ class App extends React.Component {
|
|||||||
app.setLocation("/")
|
app.setLocation("/")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"destroyed_session": async () => {
|
"session.destroyed": async () => {
|
||||||
await this.flushState()
|
await this.flushState()
|
||||||
app.eventBus.emit("forceToLogin")
|
app.eventBus.emit("app.forceToLogin")
|
||||||
},
|
},
|
||||||
"forceToLogin": () => {
|
"session.regenerated": async () => {
|
||||||
window.app.setLocation("/login")
|
//await this.flushState()
|
||||||
app.eventBus.emit("app.createLogin")
|
//await this.initialization()
|
||||||
},
|
},
|
||||||
"invalid_session": async (error) => {
|
"session.invalid": async (error) => {
|
||||||
const token = await Session.token
|
const token = await Session.token
|
||||||
|
|
||||||
if (!this.state.session && !token) {
|
if (!this.state.session && !token) {
|
||||||
@ -119,7 +119,7 @@ class App extends React.Component {
|
|||||||
await this.sessionController.forgetLocalSession()
|
await this.sessionController.forgetLocalSession()
|
||||||
await this.flushState()
|
await this.flushState()
|
||||||
|
|
||||||
app.eventBus.emit("forceToLogin")
|
app.eventBus.emit("app.forceToLogin")
|
||||||
|
|
||||||
antd.notification.open({
|
antd.notification.open({
|
||||||
message: <Translation>
|
message: <Translation>
|
||||||
@ -131,6 +131,10 @@ class App extends React.Component {
|
|||||||
icon: <Icons.MdOutlineAccessTimeFilled />,
|
icon: <Icons.MdOutlineAccessTimeFilled />,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
"app.forceToLogin": () => {
|
||||||
|
window.app.setLocation("/login")
|
||||||
|
app.eventBus.emit("app.createLogin")
|
||||||
|
},
|
||||||
"websocket_connected": () => {
|
"websocket_connected": () => {
|
||||||
if (this.wsReconnecting) {
|
if (this.wsReconnecting) {
|
||||||
this.wsReconnectingTry = 0
|
this.wsReconnectingTry = 0
|
||||||
@ -383,7 +387,7 @@ class App extends React.Component {
|
|||||||
const token = await Session.token
|
const token = await Session.token
|
||||||
|
|
||||||
if (!token || token == null) {
|
if (!token || token == null) {
|
||||||
//window.app.eventBus.emit("no_session")
|
app.eventBus.emit("app.forceToLogin")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ export default class ApiCore extends Core {
|
|||||||
|
|
||||||
this.namespaces = Object()
|
this.namespaces = Object()
|
||||||
|
|
||||||
|
this.onExpiredExceptionEvent = false
|
||||||
|
this.excludedExpiredExceptionURL = ["/regenerate_session_token"]
|
||||||
|
|
||||||
this.ctx.registerPublicMethod("api", this)
|
this.ctx.registerPublicMethod("api", this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +39,52 @@ export default class ApiCore extends Core {
|
|||||||
return this.namespaces[namespace].endpoints
|
return this.namespaces[namespace].endpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleBeforeRequest = async (request) => {
|
||||||
|
if (this.onExpiredExceptionEvent) {
|
||||||
|
if (this.excludedExpiredExceptionURL.includes(request.url)) return
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
app.eventBus.once("session.regenerated", () => {
|
||||||
|
console.log(`Session has been regenerated, retrying request`)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRegenerationEvent = async (refreshToken, makeRequest) => {
|
||||||
|
window.app.eventBus.emit("session.expiredExceptionEvent", refreshToken)
|
||||||
|
|
||||||
|
this.onExpiredExceptionEvent = true
|
||||||
|
|
||||||
|
const expiredToken = await Session.token
|
||||||
|
|
||||||
|
// exclude regeneration endpoint
|
||||||
|
|
||||||
|
// send request to regenerate token
|
||||||
|
const response = await this.request("main", "post", "regenerateSessionToken", {
|
||||||
|
expiredToken: expiredToken,
|
||||||
|
refreshToken,
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(`Failed to regenerate token: ${error.message}`)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
return window.app.eventBus.emit("session.invalid", "Failed to regenerate token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// set new token
|
||||||
|
Session.token = response.token
|
||||||
|
|
||||||
|
//this.namespaces["main"].internalAbortController.abort()
|
||||||
|
|
||||||
|
this.onExpiredExceptionEvent = false
|
||||||
|
|
||||||
|
// emit event
|
||||||
|
window.app.eventBus.emit("session.regenerated")
|
||||||
|
}
|
||||||
|
|
||||||
connectBridge = (key, params) => {
|
connectBridge = (key, params) => {
|
||||||
this.namespaces[key] = this.createBridge(params)
|
this.namespaces[key] = this.createBridge(params)
|
||||||
}
|
}
|
||||||
@ -55,17 +104,21 @@ export default class ApiCore extends Core {
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleResponse = async (data) => {
|
const handleResponse = async (data, makeRequest) => {
|
||||||
// handle token regeneration
|
|
||||||
if (data.headers?.regenerated_token) {
|
|
||||||
Session.token = data.headers.regenerated_token
|
|
||||||
console.debug("[REGENERATION] New token generated")
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle 401 responses
|
// handle 401 responses
|
||||||
if (data instanceof Error) {
|
if (data instanceof Error) {
|
||||||
if (data.response.status === 401) {
|
if (data.response.status === 401) {
|
||||||
window.app.eventBus.emit("invalid_session")
|
// check if the server issue a refresh token on data
|
||||||
|
if (data.response.data.refreshToken) {
|
||||||
|
// handle regeneration event
|
||||||
|
await this.handleRegenerationEvent(data.response.data.refreshToken, makeRequest)
|
||||||
|
return await makeRequest()
|
||||||
|
} else {
|
||||||
|
return window.app.eventBus.emit("session.invalid", "Session expired, but the server did not issue a refresh token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.response.status === 403) {
|
||||||
|
return window.app.eventBus.emit("session.invalid", "Session not valid or not existent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,6 +131,7 @@ export default class ApiCore extends Core {
|
|||||||
wsOptions: {
|
wsOptions: {
|
||||||
autoConnect: false,
|
autoConnect: false,
|
||||||
},
|
},
|
||||||
|
onBeforeRequest: this.handleBeforeRequest,
|
||||||
onRequest: getSessionContext,
|
onRequest: getSessionContext,
|
||||||
onResponse: handleResponse,
|
onResponse: handleResponse,
|
||||||
...params,
|
...params,
|
||||||
|
@ -103,7 +103,7 @@ export default class Session {
|
|||||||
|
|
||||||
const result = await Session.bridge.delete.sessions({ user_id: session.user_id })
|
const result = await Session.bridge.delete.sessions({ user_id: session.user_id })
|
||||||
this.forgetLocalSession()
|
this.forgetLocalSession()
|
||||||
window.app.eventBus.emit("destroyed_session")
|
window.app.eventBus.emit("session.destroyed")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ export default class Session {
|
|||||||
|
|
||||||
const result = await Session.bridge.delete.session({ user_id: session.user_id, token: token })
|
const result = await Session.bridge.delete.session({ user_id: session.user_id, token: token })
|
||||||
this.forgetLocalSession()
|
this.forgetLocalSession()
|
||||||
window.app.eventBus.emit("destroyed_session")
|
window.app.eventBus.emit("session.destroyed")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,8 @@ export default class Server {
|
|||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
secretOrKey: this.server.oskid,
|
secretOrKey: this.server.oskid,
|
||||||
algorithms: ["sha1", "RS256", "HS256"],
|
algorithms: ["sha1", "RS256", "HS256"],
|
||||||
expiresIn: this.env.signLifetime ?? "1h",
|
expiresIn: this.env.signLifetime ?? "10s",
|
||||||
|
enforceRegenerationTokenExpiration: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
packages/server/src/controllers/CommentsController/index.js
Normal file
23
packages/server/src/controllers/CommentsController/index.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Controller } from "linebridge/dist/server"
|
||||||
|
import { User, Post, Comment } from "../../models"
|
||||||
|
import { Schematized } from "../../lib"
|
||||||
|
|
||||||
|
export default class CommentsController extends Controller {
|
||||||
|
static refName = "CommentsController"
|
||||||
|
|
||||||
|
get = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
post = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
put = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
delete = {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
import { Controller } from "linebridge/dist/server"
|
import { Controller } from "linebridge/dist/server"
|
||||||
import { Session } from "../../models"
|
|
||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
|
|
||||||
|
import { Session } from "../../models"
|
||||||
|
import { Token } from "../../lib"
|
||||||
|
|
||||||
export default class SessionController extends Controller {
|
export default class SessionController extends Controller {
|
||||||
static refName = "SessionController"
|
static refName = "SessionController"
|
||||||
|
|
||||||
@ -66,6 +68,22 @@ export default class SessionController extends Controller {
|
|||||||
return res.json(result)
|
return res.json(result)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"/regenerate_session_token": {
|
||||||
|
middlewares: ["useJwtStrategy"],
|
||||||
|
fn: async (req, res) => {
|
||||||
|
const { expiredToken, refreshToken } = req.body
|
||||||
|
|
||||||
|
const token = await Token.regenerateSession(expiredToken, refreshToken).catch((error) => {
|
||||||
|
res.status(400).json({ error: error.message })
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!token) return
|
||||||
|
|
||||||
|
return res.json({ token })
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete = {
|
delete = {
|
||||||
|
@ -1,32 +1,150 @@
|
|||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
import { nanoid } from "nanoid"
|
import { nanoid } from "nanoid"
|
||||||
import { Session, User } from "../../models"
|
import { Session, RegenerationToken } from "../../models"
|
||||||
|
|
||||||
|
export async function regenerateSession(expiredToken, refreshToken) {
|
||||||
|
// search for a regeneration token with the expired token (Should exist only one)
|
||||||
|
const regenerationToken = await RegenerationToken.findOne({ refreshToken: refreshToken })
|
||||||
|
|
||||||
|
if (!regenerationToken) {
|
||||||
|
throw new Error("Cannot find regeneration token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the regeneration token is valid and not expired
|
||||||
|
let decodedRefreshToken = null
|
||||||
|
let decodedExpiredToken = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
decodedRefreshToken = jwt.decode(refreshToken)
|
||||||
|
decodedExpiredToken = jwt.decode(expiredToken)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
// TODO: Storage this incident
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decodedRefreshToken) {
|
||||||
|
throw new Error("Cannot decode refresh token")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decodedExpiredToken) {
|
||||||
|
throw new Error("Cannot decode expired token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// is not needed to verify the expired token, because it suppossed to be expired
|
||||||
|
|
||||||
|
// verify refresh token
|
||||||
|
await jwt.verify(refreshToken, global.jwtStrategy.secretOrKey, async (err) => {
|
||||||
|
// check if is expired
|
||||||
|
if (err) {
|
||||||
|
if (err.message === "jwt expired") {
|
||||||
|
// check if server has enabled the enforcement of regeneration token expiration
|
||||||
|
if (global.jwtStrategy.enforceRegenerationTokenExpiration) {
|
||||||
|
// delete the regeneration token
|
||||||
|
await RegenerationToken.deleteOne({ refreshToken: refreshToken })
|
||||||
|
|
||||||
|
throw new Error("Regeneration token expired and cannot be regenerated due server has enabled enforcement security policy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the regeneration token is associated with the expired token
|
||||||
|
if (decodedRefreshToken.expiredToken !== expiredToken) {
|
||||||
|
throw new Error("Regeneration token is not associated with the expired token")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// find the session associated with the expired token
|
||||||
|
const session = await Session.findOne({ token: expiredToken })
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new Error("Cannot find session associated with the expired token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a new token
|
||||||
|
const newToken = await createNewAuthToken(decodedExpiredToken, {
|
||||||
|
updateSession: session._id,
|
||||||
|
})
|
||||||
|
|
||||||
|
// delete the regeneration token
|
||||||
|
await RegenerationToken.deleteOne({ refreshToken: refreshToken })
|
||||||
|
|
||||||
|
return newToken
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRegenerationToken(expiredToken) {
|
||||||
|
const regenerationToken = await RegenerationToken.findOne({ expiredToken }).catch((error) => false)
|
||||||
|
|
||||||
|
return regenerationToken ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createNewRegenerationToken(expiredToken) {
|
||||||
|
// check if token is only expired, if is corrupted, reject
|
||||||
|
let decoded = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
decoded = jwt.decode(expiredToken)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decoded) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if token exists on a session
|
||||||
|
const sessions = await Session.find({ user_id: decoded.user_id })
|
||||||
|
const currentSession = sessions.find((session) => session.token === expiredToken)
|
||||||
|
|
||||||
|
if (!currentSession) {
|
||||||
|
throw new Error("This token is not associated with any session")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new refresh token and sign it with maximum expiration time of 1 day
|
||||||
|
const refreshToken = jwt.sign(
|
||||||
|
{
|
||||||
|
expiredToken
|
||||||
|
},
|
||||||
|
global.jwtStrategy.secretOrKey,
|
||||||
|
{
|
||||||
|
expiresIn: "1d"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// create a new regeneration token and save it
|
||||||
|
const regenerationToken = new RegenerationToken({
|
||||||
|
expiredToken,
|
||||||
|
refreshToken,
|
||||||
|
})
|
||||||
|
|
||||||
|
await regenerationToken.save()
|
||||||
|
|
||||||
|
// return the regeneration token
|
||||||
|
return regenerationToken
|
||||||
|
}
|
||||||
|
|
||||||
export async function createNewAuthToken(user, options = {}) {
|
export async function createNewAuthToken(user, options = {}) {
|
||||||
const payload = {
|
const payload = {
|
||||||
user_id: user._id,
|
user_id: user._id ?? user.user_id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
refreshToken: nanoid(),
|
|
||||||
signLocation: global.signLocation,
|
signLocation: global.signLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
await User.findByIdAndUpdate(user._id, { refreshToken: payload.refreshToken })
|
return await signNewAuthToken(payload, options)
|
||||||
|
|
||||||
return await signNew(payload, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function signNew(payload, options = {}) {
|
export async function signNewAuthToken(payload, options = {}) {
|
||||||
if (options.updateSession) {
|
if (options.updateSession) {
|
||||||
const sessionData = await Session.findById(options.updateSession)
|
const sessionData = await Session.findOne({ _id: options.updateSession })
|
||||||
|
|
||||||
payload.session_uuid = sessionData.session_uuid
|
payload.session_uuid = sessionData.session_uuid
|
||||||
} else {
|
} else {
|
||||||
payload.session_uuid = nanoid()
|
payload.session_uuid = nanoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = jwt.sign(payload, options.secretOrKey, {
|
const token = jwt.sign(payload, global.jwtStrategy.secretOrKey, {
|
||||||
expiresIn: options.expiresIn ?? "1h",
|
expiresIn: global.jwtStrategy.expiresIn ?? "1h",
|
||||||
algorithm: options.algorithm ?? "HS256"
|
algorithm: global.jwtStrategy.algorithm ?? "HS256"
|
||||||
})
|
})
|
||||||
|
|
||||||
const session = {
|
const session = {
|
||||||
@ -47,7 +165,7 @@ export async function signNew(payload, options = {}) {
|
|||||||
} else {
|
} else {
|
||||||
let newSession = new Session(session)
|
let newSession = new Session(session)
|
||||||
|
|
||||||
newSession.save()
|
await newSession.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
return token
|
return token
|
||||||
|
@ -4,7 +4,7 @@ import jwt from "jsonwebtoken"
|
|||||||
|
|
||||||
export default (req, res, next) => {
|
export default (req, res, next) => {
|
||||||
function reject(description) {
|
function reject(description) {
|
||||||
return res.status(401).json({ error: `${description ?? "Invalid session"}` })
|
return res.status(403).json({ error: `${description ?? "Invalid session"}` })
|
||||||
}
|
}
|
||||||
|
|
||||||
const authHeader = req.headers?.authorization?.split(" ")
|
const authHeader = req.headers?.authorization?.split(" ")
|
||||||
@ -37,17 +37,37 @@ export default (req, res, next) => {
|
|||||||
return res.status(404).json({ error: "No user data found" })
|
return res.status(404).json({ error: "No user data found" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if cannot verify token, start regeneration process
|
||||||
if (err) {
|
if (err) {
|
||||||
if (decoded.refreshToken === userData.refreshToken) {
|
// first check if token is only expired, if is corrupted, reject
|
||||||
const regeneratedToken = await Token.createNewAuthToken(userData, {
|
if (err.name !== "TokenExpiredError") {
|
||||||
...global.jwtStrategy,
|
return reject("Invalid token, cannot regenerate")
|
||||||
updateSession: currentSession._id,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.setHeader("regenerated_token", regeneratedToken)
|
|
||||||
} else {
|
|
||||||
return reject("Token expired, cannot refresh token either")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let regenerationToken = null
|
||||||
|
|
||||||
|
// check if this expired token has a regeneration token associated
|
||||||
|
const associatedRegenerationToken = await Token.getRegenerationToken(token)
|
||||||
|
|
||||||
|
if (associatedRegenerationToken) {
|
||||||
|
regenerationToken = associatedRegenerationToken.refreshToken
|
||||||
|
} else {
|
||||||
|
// create a new regeneration token with the expired token
|
||||||
|
regenerationToken = await Token.createNewRegenerationToken(token).catch((error) => {
|
||||||
|
// in case of error, reject
|
||||||
|
reject(error.message)
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!regenerationToken) return
|
||||||
|
|
||||||
|
// now send the regeneration token to the client (start Expired Exception Event [EEE])
|
||||||
|
return res.status(401).json({
|
||||||
|
error: "Token expired",
|
||||||
|
refreshToken: regenerationToken.refreshToken,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
req.user = userData
|
req.user = userData
|
||||||
|
@ -16,10 +16,13 @@ const schemas = getSchemas()
|
|||||||
// server
|
// server
|
||||||
export const Config = mongoose.model("Config", schemas.Config, "config")
|
export const Config = mongoose.model("Config", schemas.Config, "config")
|
||||||
|
|
||||||
|
// sessions
|
||||||
|
export const Session = mongoose.model("Session", schemas.Session, "sessions")
|
||||||
|
export const RegenerationToken = mongoose.model("RegenerationToken", schemas.RegenerationToken, "regenerationTokens")
|
||||||
|
|
||||||
// users
|
// users
|
||||||
export const User = mongoose.model("User", schemas.User, "accounts")
|
export const User = mongoose.model("User", schemas.User, "accounts")
|
||||||
export const UserFollow = mongoose.model("UserFollow", schemas.UserFollow, "follows")
|
export const UserFollow = mongoose.model("UserFollow", schemas.UserFollow, "follows")
|
||||||
export const Session = mongoose.model("Session", schemas.Session, "sessions")
|
|
||||||
export const Role = mongoose.model("Role", schemas.Role, "roles")
|
export const Role = mongoose.model("Role", schemas.Role, "roles")
|
||||||
export const Badge = mongoose.model("Badge", schemas.Badge, "badges")
|
export const Badge = mongoose.model("Badge", schemas.Badge, "badges")
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
export { default as Session } from "./session"
|
||||||
|
export { default as RegenerationToken } from "./regenerationToken"
|
||||||
|
|
||||||
export { default as User } from "./user"
|
export { default as User } from "./user"
|
||||||
export { default as Role } from "./role"
|
export { default as Role } from "./role"
|
||||||
export { default as Session } from "./session"
|
|
||||||
export { default as Config } from "./config"
|
export { default as Config } from "./config"
|
||||||
export { default as Post } from "./post"
|
export { default as Post } from "./post"
|
||||||
export { default as Comment } from "./comment"
|
export { default as Comment } from "./comment"
|
||||||
|
10
packages/server/src/schemas/regenerationToken/index.js
Normal file
10
packages/server/src/schemas/regenerationToken/index.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export default {
|
||||||
|
expiredToken: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
refreshToken: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
export default {
|
export default {
|
||||||
refreshToken: { type: String, select: false },
|
|
||||||
username: { type: String, required: true },
|
username: { type: String, required: true },
|
||||||
password: { type: String, required: true, select: false },
|
password: { type: String, required: true, select: false },
|
||||||
cover: { type: String },
|
cover: { type: String },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user