mongoose-encryption icon indicating copy to clipboard operation
mongoose-encryption copied to clipboard

Authentication failed on aggregation with subdocuments

Open philippewinter opened this issue 4 years ago • 3 comments

Hi,

I'm trying to use aggregate on initial access to a document, I have encrypted the subdocument. Error: UnhandledPromiseRejectionWarning: Error: Authentication failed: Only some authenticated fields were selected by the query. Either all or none of the authenticated fields (quotes,_ct,_ac) should be selected for proper authentication.

My Code:

const companySchema = new mongoose.Schema({
    name: String,
    street: String,
    city: String,
    state: String,
    country: String,
    zip: String,
    base: String,
    taxID: String,
    phone: String,
    logo: String,
    csvImport: [{
        effDate: Date,
        prices: [{
            effDate: Date,
            expDate: Date,
            icao: String,
            obs: String,
            galFrom: Number,
            galTo: Number,
            price: Number,
        }]
    }],
    acfts: [acftSchema],
    uplifts: [upliftSchema],
    autoRelease: {
        type: Boolean,
        default: false
    },
    quotes: [quoteSchema],
    users: [userSchema],
    providers: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: "fuelProvider"
    }],
    cards: [cardSchema],
    customerNumber: [customerNumberSchema],
    settings: [companySettingsSchema],
    active: {
        type: Boolean,
        default: true
    },
    stripe: {
        id: String,
        email: String
    },
    disabledProviders: [mongoose.Schema.Types.ObjectId]
}, { timestamps: { createdAt: 'created_at' } });

companySchema.plugin(encrypt, {
    secret: process.env.encKey,
    additionalAuthenticatedFields: ['quotes'],
    encryptedFields: []
});

--------------------------------

var QuoteSchema = new mongoose.Schema({
    registrationMark: {
        type: String,
        uppercase: true
    },
    flightDate: Date,
    origin: {
        type: String,
        uppercase: true
    },
    arrivalDate: Date,
    destination: {
        type: String,
        uppercase: true
    },
    fbo: String,
    fuelQuantity: String,
    bids: [bidSchema],
    crew: [userSchema],
    sent: {
        type: Number,
        default: 1
    },
    nextLeg: mongoose.Schema.Types.ObjectId,
    tanker: {
        type: Boolean,
        default: false
    }
}, { timestamps: { createdAt: 'created_at' } });


QuoteSchema.plugin(encrypt, {
    secret: process.env.encKey,
    encryptedFields: ['fuelQuantity'],
});

--------------------------------

let company = await Company.aggregate([{ $match: { _id: req.user.parent()._id } }, { $sort: { 'quote.flightDate': 1 } }, {
			$project: {
				quotes: {
					$filter: {
						input: "$quotes",
						as: "quote",
						cond: { $and: [{ $gt: ["$$quote.flightDate", new Date(moment().startOf('day'))] }, { $lt: ["$$quote.flightDate", new Date(moment().add(30, 'days'))] }, { $in: ['$$quote.registrationMark', acftTail] }] }
					}
				}
			}
		}]);

philippewinter avatar Aug 19 '20 17:08 philippewinter

Same here

import { Document, model, Schema } from "mongoose";
import encrypt from "mongoose-encryption";
import mongooseHidden from "mongoose-hidden";
`
export interface ISettings {
    userId: string;
    mapbox: {
        user: string;
        publicToken: string;
        privateToken: string;
    };
}

export type SettingsModel = ISettings & Document;

export const SettingsSchema = new Schema(
    {
        userId: { type: Schema.Types.ObjectId, ref: "User", required: true },
        mapbox: {
            user: String,
            publicToken: String,
            privateToken: String,
        },
    },
    {
        timestamps: true,
    },
);

SettingsSchema.set("toJSON", {
    virtuals: true,
});

SettingsSchema.plugin(encrypt, {
    secret: "dupa",
    encryptedFields: ["mapbox.privateToken"],
});

SettingsSchema.plugin(mongooseHidden({ hidden: { __t: true } }));

export const Settings = model<SettingsModel>("Settings", SettingsSchema);

Then simple mongoose.find gives me "Authentication failed".

I trakced that function authenticateSync gives me error because

var authentic = bufferEqual(basicAC, expectedHMAC);     // Here bufferEqual returns false
if (!authentic){
    throw new Error('Authentication failed');
}

karenpommeroy avatar Feb 02 '21 19:02 karenpommeroy

@karenpommeroy I wonder if this is related to the mongooseHidden plugin you added. If you remove that, or perhaps simply reverse the order in which you add the plugins, does that solve the issue?

joegoldbeck avatar Apr 28 '21 02:04 joegoldbeck

For the original issue cited, aggregations in general aren't a tested aspect of this package. In general, I'd expect they might work if the fields involved aren't encrypted. However, in this particular case, the entire quotes subdocument is marked to be authenticated, but then only some of its fields are returned in the aggregation, which means authentication cannot be performed. Hence, the error returned!

joegoldbeck avatar Apr 28 '21 02:04 joegoldbeck