mirror of
https://github.com/wassname/talk.git
synced 2026-09-10 12:43:11 +08:00
[next] User Mutations (#2133)
* feat: added support for Token's * feat: added mutations - updateUserUsername - updateUserDisplayName - updateUserAvatar - updateUserEmail - updateUserRole * lint: removed old commented out code * fix: linting
This commit is contained in:
@@ -4,6 +4,7 @@ import { Db } from "mongodb";
|
||||
import { Config } from "talk-server/config";
|
||||
import TenantContext from "talk-server/graph/tenant/context";
|
||||
import { TaskQueue } from "talk-server/queue";
|
||||
import { JWTSigningConfig } from "talk-server/services/jwt";
|
||||
import { AugmentedRedis } from "talk-server/services/redis";
|
||||
import { Request } from "talk-server/types/express";
|
||||
|
||||
@@ -12,6 +13,7 @@ export interface TenantContextMiddlewareOptions {
|
||||
redis: AugmentedRedis;
|
||||
queue: TaskQueue;
|
||||
config: Config;
|
||||
signingConfig: JWTSigningConfig;
|
||||
}
|
||||
|
||||
export const tenantContext = ({
|
||||
@@ -19,6 +21,7 @@ export const tenantContext = ({
|
||||
redis,
|
||||
queue,
|
||||
config,
|
||||
signingConfig,
|
||||
}: TenantContextMiddlewareOptions): RequestHandler => (
|
||||
req: Request,
|
||||
res,
|
||||
@@ -48,6 +51,7 @@ export const tenantContext = ({
|
||||
user: req.user,
|
||||
tenantCache: cache.tenant,
|
||||
queue,
|
||||
signingConfig,
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -9,21 +9,61 @@ import { retrieveUser } from "talk-server/models/user";
|
||||
import { checkBlacklistJWT, JWTSigningConfig } from "talk-server/services/jwt";
|
||||
|
||||
export interface JWTToken {
|
||||
// aud: string;
|
||||
/**
|
||||
* jti is the Token identifier. With normal login tokens, this is a randomly
|
||||
* generated uuid, which is added to a blacklist when the User "logs out". For
|
||||
* Personal Access Tokens, this is the Token identifier.
|
||||
*/
|
||||
jti: string;
|
||||
|
||||
/**
|
||||
* sub is the ID of the User that this Token is associated with.
|
||||
*/
|
||||
sub: string;
|
||||
exp: number;
|
||||
|
||||
/**
|
||||
* iss is the ID of the Tenant that this Token is associated with.
|
||||
*/
|
||||
iss: string;
|
||||
|
||||
/**
|
||||
* exp is the optional expiry for the tokens. Personal Access Token's do not
|
||||
* have an expiry associated with them, hence why it's optional.
|
||||
*/
|
||||
exp?: number;
|
||||
|
||||
/**
|
||||
* pat, when true, indicates that this Token is a Personal Access Token, and
|
||||
* it's `jti` claim should be treated as the Token ID. These tokens cannot be
|
||||
* logged out and instead must be deactivated.
|
||||
*/
|
||||
pat?: boolean;
|
||||
}
|
||||
|
||||
export function isJWTToken(token: JWTToken | object): token is JWTToken {
|
||||
return (
|
||||
// typeof (token as JWTToken).aud === "string" &&
|
||||
typeof (token as JWTToken).jti === "string" &&
|
||||
typeof (token as JWTToken).sub === "string" &&
|
||||
typeof (token as JWTToken).exp === "number" &&
|
||||
typeof (token as JWTToken).iss === "string"
|
||||
);
|
||||
if (
|
||||
typeof (token as JWTToken).jti !== "string" ||
|
||||
typeof (token as JWTToken).sub !== "string" ||
|
||||
typeof (token as JWTToken).iss !== "string"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof (token as JWTToken).exp !== "undefined" &&
|
||||
typeof (token as JWTToken).exp !== "number"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof (token as JWTToken).pat !== "undefined" &&
|
||||
typeof (token as JWTToken).pat !== "boolean"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export interface JWTVerifierOptions {
|
||||
@@ -61,12 +101,31 @@ export class JWTVerifier {
|
||||
|
||||
logger.trace({ responseTime }, "jwt verification complete");
|
||||
|
||||
// Check to see if the token has been blacklisted, as these tokens can be
|
||||
// revoked.
|
||||
await checkBlacklistJWT(this.redis, token.jti);
|
||||
// Check to see if this is a Personal Access Token, these tokens cannot be
|
||||
// blacklisted.
|
||||
if (!token.pat) {
|
||||
// Check to see if the token has been blacklisted, as these tokens can be
|
||||
// revoked.
|
||||
await checkBlacklistJWT(this.redis, token.jti);
|
||||
}
|
||||
|
||||
// Find the user.
|
||||
const user = await retrieveUser(this.mongo, tenant.id, token.sub);
|
||||
if (token.pat) {
|
||||
// As this is a Personal Access Token, ensure that the Token is valid by
|
||||
// checking it against the User's tokens.
|
||||
if (!user) {
|
||||
throw new Error(
|
||||
"personal access token referenced user that wasn't found"
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure that the token exists on the User.
|
||||
const foundToken = user.tokens.find(({ id }) => id === token.jti);
|
||||
if (!foundToken) {
|
||||
throw new Error("personal access token does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
// Return the user now that we have found them!.
|
||||
return user;
|
||||
|
||||
Reference in New Issue
Block a user