diff --git a/server-node/src/auth/auth-controller.ts.ts b/server-node/src/auth/auth-controller.ts similarity index 100% rename from server-node/src/auth/auth-controller.ts.ts rename to server-node/src/auth/auth-controller.ts diff --git a/server-node/src/auth/passport-config.ts b/server-node/src/auth/passport-config.ts index a4daaa1e8..b23225e9d 100644 --- a/server-node/src/auth/passport-config.ts +++ b/server-node/src/auth/passport-config.ts @@ -6,17 +6,21 @@ import { HeaderAPIKeyStrategy as HeaderAPIKeyStrategy } from "passport-headerapi export function initialize(passport: typeof import("passport")) { passport.use("local", new LocalStrategy( function(username, password, done) { - User.findOne({ username: username }, async function (err, user) { + User.findOne({ username: username }, function (err, user) { if (err) { return done(err, false); } if (!user) { return done(null, false); } - if (!await User.verifyPassword(user, password)) { - return done(null, false); - } - return done(null, user); + + User.verifyPassword(user, password, (error, success) => { + if(error) return done(error, false); + + if(!success) return done(null, false); + + return done(null, user) + }); }); } )); diff --git a/server-node/src/auth/user/user-controller.ts b/server-node/src/auth/user/user-controller.ts index 611829511..ae922d82c 100644 --- a/server-node/src/auth/user/user-controller.ts +++ b/server-node/src/auth/user/user-controller.ts @@ -1,5 +1,6 @@ import { Error as SequelizeError, Op } from "sequelize"; -import { Password, User } from "./user-model"; +import { APIKey, Password, User } from "./user-model"; +import crypto from "crypto"; type PickOne = Pick & { [K in keyof Omit]?: never }; @@ -28,28 +29,49 @@ export function findOne(params: {id?: number, username?: string, apikey?: string ); } -export async function verifyPassword(user: User, password: string): Promise { - const passwordRecord = await user.getPassword(); - if(!passwordRecord) { - throw new Error("This user does not have a password set!"); - } - return passwordRecord.password == password; // TODO: Replace with web-crypto -} - +// TODO: Allow other authentication methods export function createUser(params: { username: string, password: string }, cb: (err: SequelizeError | null, user: User | null) => void ) { - User.create({ username: params.username, authenticationMethod: "password" }).then(async user => { - user.setPassword(await Password.create({ - password: params.password, // TODO: Replace with web-crypto - })).then(password => { - cb(null, user as any as User) - }).catch(e => - cb(e, null) - ); + User.create({ username: params.username }).then(async (user) => { + const salt = crypto.randomBytes(16).toString('hex'); + + hashPassword(params.password, salt, async (err, derivedKey) => { + if(err || !derivedKey) { + return cb(err, null); + } + + user.setPassword(await Password.create({ + password: derivedKey, + salt: salt + })).then(password => { + cb(null, user as any as User); + }).catch(e => { + cb(e, null); + }); + }) }).catch(e => cb(e, null) ); } -export function createAPIKey(user: User, apikey?: string) { +export async function verifyPassword(user: User, password: string, cb: (error: Error | null, success: boolean | null) => void) { + const passwordRecord = await user.getPassword(); + if(!passwordRecord) { + return cb(new Error("This user does not have a password set!"), null); + } + hashPassword(password, passwordRecord.salt, (err, derivedKey) => { + if(err) return cb(err, null); + return cb(null, passwordRecord.password == derivedKey); + }); +} + +function hashPassword(password: string, salt: string, cb: (err: Error | null, derivedKey: string | null) => void) { + crypto.pbkdf2(password, salt, 100000, 64, 'sha512', (err, derivedKey) => { + if (err) return cb(err, null); + cb(null, derivedKey.toString('hex')); + }); +} + +export function createAPIKey(user: User, cb: (err: SequelizeError | null, apikey: APIKey | null) => void ) { + user.addAPIKey() } \ No newline at end of file diff --git a/server-node/src/auth/user/user-model.ts b/server-node/src/auth/user/user-model.ts index f31047e36..61542f395 100644 --- a/server-node/src/auth/user/user-model.ts +++ b/server-node/src/auth/user/user-model.ts @@ -14,7 +14,6 @@ export class User extends Model, InferCreationAttributes; declare username: string; declare mail?: string; - declare authenticationMethod: string; declare getPassword: HasOneGetAssociationMixin; // Note the null assertions! declare setPassword: HasOneSetAssociationMixin; @@ -29,7 +28,7 @@ export class User extends Model, InferCreationAttributes; declare hasAccessRules: HasManyHasAssociationsMixin; declare countAccessRules: HasManyCountAssociationsMixin; - declare createAccessRule: HasManyCreateAssociationMixin; + declare createAccessRule: HasManyCreateAssociationMixin; declare getAPIKeys: HasManyGetAssociationsMixin; // Note the null assertions! declare addAPIKey: HasManyAddAssociationMixin; @@ -40,7 +39,7 @@ export class User extends Model, InferCreationAttributes; declare hasAPIKeys: HasManyHasAssociationsMixin; declare countAPIKeys: HasManyCountAssociationsMixin; - declare createAPIKey: HasManyCreateAssociationMixin; + declare createAPIKey: HasManyCreateAssociationMixin; declare createdAt: CreationOptional; declare updatedAt: CreationOptional; @@ -49,6 +48,7 @@ export class User extends Model, InferCreationAttributes, InferCreationAttributes> { declare id: CreationOptional; declare password: string; + declare salt: string; declare ownerId: ForeignKey; declare owner?: NonAttribute; @@ -61,8 +61,8 @@ export class AccessRule extends Model, InferCreation declare id: CreationOptional; declare grants: string; - declare userId: ForeignKey; - declare user?: NonAttribute; + declare ownerId: ForeignKey; + declare owner?: NonAttribute; declare createdAt: CreationOptional; declare updatedAt: CreationOptional; @@ -72,8 +72,8 @@ export class APIKey extends Model, InferCreationAttribut declare id: CreationOptional; declare apikey: string; - declare userId: ForeignKey; - declare user?: NonAttribute; + declare ownerId: ForeignKey; + declare owner?: NonAttribute; declare createdAt: CreationOptional; declare updatedAt: CreationOptional; diff --git a/server-node/src/data/sequelize-relations.ts b/server-node/src/data/sequelize-relations.ts index a0a53c7df..8c4fd6159 100644 --- a/server-node/src/data/sequelize-relations.ts +++ b/server-node/src/data/sequelize-relations.ts @@ -23,10 +23,6 @@ User.init( type: DataTypes.STRING, unique: true, }, - authenticationMethod: { - type: DataTypes.STRING, - allowNull: false, - }, createdAt: DataTypes.DATE, updatedAt: DataTypes.DATE, }, @@ -41,6 +37,7 @@ Password.init( primaryKey: true }, password: DataTypes.STRING, + salt: DataTypes.STRING, createdAt: DataTypes.DATE, updatedAt: DataTypes.DATE, }, diff --git a/server-node/src/index.ts b/server-node/src/index.ts index c63688311..218e81b39 100644 --- a/server-node/src/index.ts +++ b/server-node/src/index.ts @@ -49,10 +49,11 @@ import api from "./routes/api/api-controller"; * auth */ -console.log(import.meta.env) +console.log("env", import.meta.env) if(import.meta.env.VITE_AUTH_ENABLED === "True") { - import("./auth/auth-controller.ts").then(router => router.connect(app)).finally(() => { + console.log("Attatching Auth") + import("./auth/auth-controller").then(router => router.connect(app)).finally(() => { /* * api */