mirror of
https://github.com/wassname/talk.git
synced 2026-07-18 12:40:13 +08:00
* refactor: removed unused subscription code
* refactor: removed management api's
* refactor: cleanup of connections
* refactor: refactored comments edge
* refactor: simplified connection resolving
* feat: added story connection edge
* fix: added story index
* feat: added user pagination and user edge
* fix: added filter to comment query
* fix: removed unused resolvers
* fix: creating a comment reply should require auth
* refactor: cleanup of graph files
* feat: removed display name, made username non-unique
* fix: fixed tests
* fix: fixed tests
* fix: added more api docs
* fix: fixed bug with installer
* refactor: fixes and updates
* fix: added linting for graphql, fixed schema
* feat: added docker build tests
* fix: upped output timeout
* fix: fixed stacktraces in production builds
* fix: removed `git add`
- `git add` was causing issues with
partial staged changs on files
* feat: improved error messaging for auth
* refactor: cleaned up queue names
* fix: merge error
143 lines
3.5 KiB
TypeScript
143 lines
3.5 KiB
TypeScript
import { Redis } from "ioredis";
|
|
import jwt, { SignOptions } from "jsonwebtoken";
|
|
import { Bearer } from "permit";
|
|
import uuid from "uuid/v4";
|
|
|
|
import { Config } from "talk-server/config";
|
|
import { AuthenticationError } from "talk-server/errors";
|
|
import { User } from "talk-server/models/user";
|
|
import { Request } from "talk-server/types/express";
|
|
|
|
export enum AsymmetricSigningAlgorithm {
|
|
RS256 = "RS256",
|
|
RS384 = "RS384",
|
|
RS512 = "RS512",
|
|
ES256 = "ES256",
|
|
ES384 = "ES384",
|
|
ES512 = "ES512",
|
|
}
|
|
|
|
export enum SymmetricSigningAlgorithm {
|
|
HS256 = "HS256",
|
|
HS384 = "HS384",
|
|
HS512 = "HS512",
|
|
}
|
|
|
|
export type JWTSigningAlgorithm =
|
|
| AsymmetricSigningAlgorithm
|
|
| SymmetricSigningAlgorithm;
|
|
|
|
export interface JWTSigningConfig {
|
|
secret: Buffer;
|
|
algorithm: JWTSigningAlgorithm;
|
|
}
|
|
|
|
export function createAsymmetricSigningConfig(
|
|
algorithm: AsymmetricSigningAlgorithm,
|
|
secret: string
|
|
): JWTSigningConfig {
|
|
return {
|
|
// Secrets have their newlines encoded with newline literals.
|
|
secret: Buffer.from(secret.replace(/\\n/g, "\n"), "utf8"),
|
|
algorithm,
|
|
};
|
|
}
|
|
|
|
export function createSymmetricSigningConfig(
|
|
algorithm: SymmetricSigningAlgorithm,
|
|
secret: string
|
|
): JWTSigningConfig {
|
|
return {
|
|
secret: Buffer.from(secret, "utf8"),
|
|
algorithm,
|
|
};
|
|
}
|
|
|
|
function isSymmetricSigningAlgorithm(
|
|
algorithm: string | SymmetricSigningAlgorithm
|
|
): algorithm is SymmetricSigningAlgorithm {
|
|
return algorithm in SymmetricSigningAlgorithm;
|
|
}
|
|
|
|
function isAsymmetricSigningAlgorithm(
|
|
algorithm: string | AsymmetricSigningAlgorithm
|
|
): algorithm is AsymmetricSigningAlgorithm {
|
|
return algorithm in AsymmetricSigningAlgorithm;
|
|
}
|
|
|
|
/**
|
|
* Parses the config and provides the signing config.
|
|
*
|
|
* @param config the server configuration
|
|
*/
|
|
export function createJWTSigningConfig(config: Config): JWTSigningConfig {
|
|
const secret = config.get("signing_secret");
|
|
const algorithm = config.get("signing_algorithm");
|
|
if (isSymmetricSigningAlgorithm(algorithm)) {
|
|
return createSymmetricSigningConfig(algorithm, secret);
|
|
} else if (isAsymmetricSigningAlgorithm(algorithm)) {
|
|
return createAsymmetricSigningConfig(algorithm, secret);
|
|
}
|
|
|
|
throw new AuthenticationError(`invalid algorithm=${algorithm} specified`);
|
|
}
|
|
|
|
export type SigningTokenOptions = Pick<
|
|
SignOptions,
|
|
"jwtid" | "audience" | "issuer" | "expiresIn" | "notBefore"
|
|
>;
|
|
|
|
export const signTokenString = async (
|
|
{ algorithm, secret }: JWTSigningConfig,
|
|
user: User,
|
|
options: SigningTokenOptions
|
|
) =>
|
|
jwt.sign({}, secret, {
|
|
jwtid: uuid(),
|
|
// TODO: (wyattjoh) evaluate allowing configuration?
|
|
expiresIn: "1 day",
|
|
...options,
|
|
subject: user.id,
|
|
algorithm,
|
|
});
|
|
|
|
export const signPATString = async (
|
|
{ algorithm, secret }: JWTSigningConfig,
|
|
user: User,
|
|
options: SigningTokenOptions
|
|
) =>
|
|
jwt.sign({ pat: true }, secret, {
|
|
...options,
|
|
subject: user.id,
|
|
algorithm,
|
|
});
|
|
|
|
export function extractJWTFromRequest(req: Request) {
|
|
const permit = new Bearer({
|
|
basic: "password",
|
|
query: "accessToken",
|
|
});
|
|
|
|
return permit.check(req) || null;
|
|
}
|
|
|
|
function generateJTIRevokedKey(jti: string) {
|
|
// jtir: JTI Revoked namespace.
|
|
return `jtir:${jti}`;
|
|
}
|
|
|
|
export async function revokeJWT(redis: Redis, jti: string, validFor: number) {
|
|
await redis.setex(
|
|
generateJTIRevokedKey(jti),
|
|
Math.ceil(validFor),
|
|
Date.now()
|
|
);
|
|
}
|
|
|
|
export async function checkJWTRevoked(redis: Redis, jti: string) {
|
|
const expiredAtString = await redis.get(generateJTIRevokedKey(jti));
|
|
if (expiredAtString) {
|
|
throw new AuthenticationError("JWT was revoked");
|
|
}
|
|
}
|