feat: initial passport user auth impl

This commit is contained in:
Wyatt Johnson
2018-07-20 14:30:07 -06:00
parent 2e4d23e900
commit bdd4bfc272
8 changed files with 116 additions and 18 deletions
@@ -3,6 +3,7 @@ import { Db } from "mongodb";
import passport, { Authenticator } from "passport";
import {
createJWTStrategy,
JWTSigningConfig,
SigningTokenOptions,
signTokenString,
@@ -20,10 +21,12 @@ export type VerifyCallback = (
export interface PassportOptions {
db: Db;
signingConfig: JWTSigningConfig;
}
export function createPassport({
db,
signingConfig,
}: PassportOptions): passport.Authenticator {
// Create the authenticator.
const auth = new Authenticator();
@@ -34,6 +37,9 @@ export function createPassport({
// Use the LocalStrategy.
auth.use(createLocalStrategy({ db }));
// Use the JWTStrategy.
auth.use(createJWTStrategy({ db, signingConfig }));
return auth;
}
@@ -74,7 +80,7 @@ export async function handleSuccessfulLogin(
}
/**
* authenticate will wrap a authenticators authenticate method with one that
* wrapAuthz will wrap a authenticators authenticate method with one that
* will return a valid login token for a valid login by a compatible strategy.
*
* @param authenticator the base authenticator instance
@@ -82,7 +88,7 @@ export async function handleSuccessfulLogin(
* @param name the name of the authenticator to use
* @param options any options to be passed to the authenticate call
*/
export const authenticate = (
export const wrapAuthz = (
authenticator: passport.Authenticator,
signingConfig: JWTSigningConfig,
name: string,
+76 -1
View File
@@ -1,8 +1,10 @@
import jwt, { SignOptions } from "jsonwebtoken";
import uuid from "uuid";
import { Db } from "mongodb";
import { Strategy } from "passport-strategy";
import { Config } from "talk-server/config";
import { User } from "talk-server/models/user";
import { retrieveUser, User } from "talk-server/models/user";
import { Request } from "talk-server/types/express";
const authHeaderRegex = /(\S+)\s+(\S+)/;
@@ -126,3 +128,76 @@ export async function signTokenString(
subject: user.id,
});
}
export interface JWTToken {
jti: string;
sub: string;
exp: number;
iss?: string;
}
export interface JWTStrategyOptions {
signingConfig: JWTSigningConfig;
db: Db;
}
export class JWTStrategy extends Strategy {
private signingConfig: JWTSigningConfig;
private db: Db;
public name: string;
constructor({ signingConfig, db }: JWTStrategyOptions) {
super();
this.name = "jwt";
this.signingConfig = signingConfig;
this.db = db;
}
public authenticate(req: Request) {
const { tenant } = req;
if (!tenant) {
// TODO: (wyattjoh) return a better error.
return this.error(new Error("tenant not found"));
}
// Lookup the token.
const token = extractJWTFromRequest(req);
if (!token) {
// TODO: (wyattjoh) return a better error.
return this.fail(new Error("no token on request"), 401);
}
jwt.verify(
token,
// Use the secret specified in the configuration.
this.signingConfig.secret,
{
// We need to verify that the token is for the specified tenant.
issuer: tenant.id,
// Use the algorithm specified in the configuration.
algorithms: [this.signingConfig.algorithm],
},
async (err: Error | undefined, { sub }: JWTToken) => {
if (err) {
return this.fail(err, 401);
}
try {
// Find the user.
const user = await retrieveUser(this.db, tenant.id, sub);
// Return them! The user may be null, but that's ok here.
this.success(user, null);
} catch (err) {
return this.error(err);
}
}
);
}
}
export function createJWTStrategy(options: JWTStrategyOptions) {
return new JWTStrategy(options);
}
@@ -182,7 +182,7 @@ export default class SSOStrategy extends Strategy {
*/
private wrapNewTokenHandler = (tenant: Tenant) => async (
err: Error | undefined,
decoded: OIDCIDToken | SSOToken
token: OIDCIDToken | SSOToken
) => {
if (err) {
return this.fail(err, 401);
@@ -190,7 +190,7 @@ export default class SSOStrategy extends Strategy {
try {
// Find or create the user based on the decoded token.
const user = await this.findOrCreateUser(tenant, decoded);
const user = await this.findOrCreateUser(tenant, token);
// The user was found or created!
return this.success(user, null);