mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +08:00
[next] Tasks (#1777)
* feat: initial support for synced tenants * fix: cleanup * fix: logger now respects logging level * fix: cache now ignores updates issued from itself * feat: print subscriber count * feat: initial moderation + validation for new comments * fix: added Promiseable type * feat: initial actions impl * feat: more moderation phases * fix: handle settings inheritence * fix: moved settings into new file * fix: defaults and documentation * fix: replace merge with object spread * feat: added integration with akismet * fix: support tenant cache for oidc strategy * fix: fixed compile * fix: import ordering * feat: added bull for queue support * feat: support for scraping * fix: fixes for scraper - Implemented simple metascraper replacement (to resolve security advisory warning) - Implemented simle dotize replacement (to resolve not working version that couldn't handle date objects) - Plugged in asset scraping to asset creation process * fix: handles array values * feat: added initial scraper implementation * feat: seperate queues but share config * fix: simplified auth data access * feat: moved more settings into the graph * feat: improved mailer design * fix: fixed issue with dotize * fix: fixed some issues with adapter * fix: queue cleanup * feat: added organizationName to Tenant * feat: email rendering * review: support es6 imports * fix: restore old ci step * fix: adjusted logging messages
This commit is contained in:
@@ -141,19 +141,18 @@ export function createJWTSigningConfig(config: Config): JWTSigningConfig {
|
||||
|
||||
export type SigningTokenOptions = Pick<SignOptions, "audience" | "issuer">;
|
||||
|
||||
export async function signTokenString(
|
||||
export const signTokenString = async (
|
||||
{ algorithm, secret }: JWTSigningConfig,
|
||||
user: User,
|
||||
options: SigningTokenOptions
|
||||
) {
|
||||
return jwt.sign({}, secret, {
|
||||
) =>
|
||||
jwt.sign({}, secret, {
|
||||
...options,
|
||||
jwtid: uuid.v4(),
|
||||
algorithm,
|
||||
expiresIn: "1 day", // TODO: (wyattjoh) evaluate allowing configuration?
|
||||
subject: user.id,
|
||||
});
|
||||
}
|
||||
|
||||
export interface JWTToken {
|
||||
jti: string;
|
||||
@@ -208,17 +207,23 @@ export class JWTStrategy extends Strategy {
|
||||
// Use the algorithm specified in the configuration.
|
||||
algorithms: [this.signingConfig.algorithm],
|
||||
},
|
||||
async (err: Error | undefined, { jti, sub }: JWTToken) => {
|
||||
async (err: Error | undefined, decoded: JWTToken) => {
|
||||
if (err) {
|
||||
return this.fail(err, 401);
|
||||
}
|
||||
|
||||
try {
|
||||
// Check to see if the token has been blacklisted.
|
||||
await checkBlacklistJWT(this.redis, jti);
|
||||
if (!decoded) {
|
||||
// There was no token on the request, so there was no user, so let's
|
||||
// mark that the strategy was successful.
|
||||
return this.success(null, null);
|
||||
}
|
||||
|
||||
// Find the user referenced by the token.
|
||||
const user = await retrieveUser(this.mongo, tenant.id, sub);
|
||||
try {
|
||||
// Find the user.
|
||||
const user = await retrieveUser(this.mongo, tenant.id, decoded.sub);
|
||||
|
||||
// Check to see if the token has been blacklisted.
|
||||
await checkBlacklistJWT(this.redis, decoded.jti);
|
||||
|
||||
// Return them! The user may be null, but that's ok here.
|
||||
this.success(user, null);
|
||||
|
||||
@@ -5,11 +5,12 @@ import { Db } from "mongodb";
|
||||
import { Strategy as OAuth2Strategy, VerifyCallback } from "passport-oauth2";
|
||||
import { Strategy } from "passport-strategy";
|
||||
|
||||
import { Config } from "talk-common/config";
|
||||
import { validate } from "talk-server/app/request/body";
|
||||
import { reconstructURL } from "talk-server/app/url";
|
||||
import { GQLUSER_ROLE } from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { OIDCAuthIntegration } from "talk-server/models/settings";
|
||||
import {
|
||||
GQLOIDCAuthIntegration,
|
||||
GQLUSER_ROLE,
|
||||
} from "talk-server/graph/tenant/schema/__generated__/types";
|
||||
import { Tenant } from "talk-server/models/tenant";
|
||||
import { OIDCProfile, retrieveUserWithProfile } from "talk-server/models/user";
|
||||
import TenantCache from "talk-server/services/tenant/cache";
|
||||
@@ -83,7 +84,9 @@ const signingKeyFactory = (client: jwks.JwksClient): jwt.KeyFunction => (
|
||||
});
|
||||
};
|
||||
|
||||
function getEnabledIntegration(tenant: Tenant) {
|
||||
function getEnabledIntegration(
|
||||
tenant: Tenant
|
||||
): Required<GQLOIDCAuthIntegration> {
|
||||
const integration = tenant.auth.integrations.oidc;
|
||||
if (!integration) {
|
||||
// TODO: return a better error.
|
||||
@@ -96,7 +99,21 @@ function getEnabledIntegration(tenant: Tenant) {
|
||||
throw new Error("integration not enabled");
|
||||
}
|
||||
|
||||
return integration;
|
||||
if (
|
||||
!integration.name ||
|
||||
!integration.clientID ||
|
||||
!integration.clientSecret ||
|
||||
!integration.authorizationURL ||
|
||||
!integration.tokenURL ||
|
||||
!integration.jwksURI ||
|
||||
!integration.issuer
|
||||
) {
|
||||
// TODO: return a better error.
|
||||
throw new Error("integration not configured");
|
||||
}
|
||||
|
||||
// TODO: (wyattjoh) for some reason, type guards above to not allow coercion to this required type.
|
||||
return integration as Required<GQLOIDCAuthIntegration>;
|
||||
}
|
||||
|
||||
export const OIDCIDTokenSchema = Joi.object()
|
||||
@@ -179,7 +196,6 @@ const OIDC_SCOPE = "openid email profile";
|
||||
export interface OIDCStrategyOptions {
|
||||
mongo: Db;
|
||||
tenantCache: TenantCache;
|
||||
config: Config;
|
||||
}
|
||||
|
||||
export default class OIDCStrategy extends Strategy {
|
||||
@@ -188,11 +204,11 @@ export default class OIDCStrategy extends Strategy {
|
||||
private mongo: Db;
|
||||
private cache: TenantCacheAdapter<StrategyItem>;
|
||||
|
||||
constructor({ mongo, tenantCache, config }: OIDCStrategyOptions) {
|
||||
constructor({ mongo, tenantCache }: OIDCStrategyOptions) {
|
||||
super();
|
||||
|
||||
this.mongo = mongo;
|
||||
this.cache = new TenantCacheAdapter(tenantCache, config);
|
||||
this.cache = new TenantCacheAdapter(tenantCache);
|
||||
|
||||
// Connect the cache adapter.
|
||||
this.cache.subscribe();
|
||||
@@ -201,7 +217,7 @@ export default class OIDCStrategy extends Strategy {
|
||||
private lookupJWKSClient(
|
||||
req: Request,
|
||||
tenantID: string,
|
||||
oidc: OIDCAuthIntegration
|
||||
oidc: Required<GQLOIDCAuthIntegration>
|
||||
) {
|
||||
let entry = this.cache.get(tenantID);
|
||||
if (!entry) {
|
||||
@@ -257,7 +273,7 @@ export default class OIDCStrategy extends Strategy {
|
||||
|
||||
// Get the integration from the tenant. If needed, it will be used to create
|
||||
// a new strategy.
|
||||
let integration: OIDCAuthIntegration;
|
||||
let integration: Required<GQLOIDCAuthIntegration>;
|
||||
try {
|
||||
integration = getEnabledIntegration(tenant);
|
||||
} catch (err) {
|
||||
@@ -297,7 +313,7 @@ export default class OIDCStrategy extends Strategy {
|
||||
|
||||
private createStrategy(
|
||||
req: Request,
|
||||
integration: OIDCAuthIntegration
|
||||
integration: Required<GQLOIDCAuthIntegration>
|
||||
): OAuth2Strategy {
|
||||
const { clientID, clientSecret, authorizationURL, tokenURL } = integration;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user