UNPKG

realm-object-server

Version:

Realm Object Server

602 lines 34.5 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const chai_1 = require("chai"); const chai = require("chai"); chai.use(require("chai-as-promised")); const faker = require("faker"); const sinon = require("sinon"); const moment = require("moment"); const PasswordAuthProvider_1 = require("./PasswordAuthProvider"); const __1 = require(".."); const errors = require("../../errors"); const TestServer_1 = require("../../TestServer"); const index_1 = require("../../index"); const uuid = require("uuid"); describe("PasswordAuthProvider", () => { let provider; let server; let authService; function createServer(params = {}) { return __awaiter(this, void 0, void 0, function* () { server = new TestServer_1.TestServer(); provider = new PasswordAuthProvider_1.PasswordAuthProvider(Object.assign({ iterations: 1 }, params)); yield server.start({ authProviders: [provider], }); authService = server.getService("auth"); }); } afterEach(() => __awaiter(this, void 0, void 0, function* () { if (server) { yield server.shutdown(); } })); describe("password functions", () => { beforeEach(() => __awaiter(this, void 0, void 0, function* () { return createServer(); })); it("should be able to create a salt with the configured provider.iterations", function () { return __awaiter(this, void 0, void 0, function* () { const buffer = yield provider["createSalt"](provider["iterations"]); chai_1.expect(buffer).to.not.be.empty; }); }); it("should be able to compare a password ", function () { return __awaiter(this, void 0, void 0, function* () { const salt = yield provider["createSalt"](provider["saltLength"]); const hash = yield provider["hashPassword"]("iloverealm", salt, provider["iterations"], provider["keyLength"], provider["digest"]); const isMatch = yield provider["comparePassword"]("iloverealm", salt, provider["iterations"], provider["keyLength"], provider["digest"], hash); chai_1.expect(isMatch).to.be.true; }); }); it("should be false when comparing a bad password", function () { return __awaiter(this, void 0, void 0, function* () { const salt = yield provider["createSalt"](provider["saltLength"]); const hash = yield provider["hashPassword"]("iloverealm", salt, provider["iterations"], provider["keyLength"], provider["digest"]); const isMatch = yield provider["comparePassword"]("bad password", salt, provider["iterations"], provider["keyLength"], provider["digest"], hash); chai_1.expect(isMatch).to.be.false; }); }); it("should be false when changing the digest method", function () { return __awaiter(this, void 0, void 0, function* () { const salt = yield provider["createSalt"](provider["saltLength"]); const hash = yield provider["hashPassword"]("iloverealm", salt, provider["iterations"], provider["keyLength"], "sha512"); const isMatch = yield provider["comparePassword"]("iloverealm", salt, provider["iterations"], provider["keyLength"], "sha256", hash); chai_1.expect(isMatch).to.be.false; }); }); it("should be false when changing the iterations", function () { return __awaiter(this, void 0, void 0, function* () { const salt = yield provider["createSalt"](provider["saltLength"]); const hash = yield provider["hashPassword"]("iloverealm", salt, provider["iterations"], provider["keyLength"], provider["digest"]); const isMatch = yield provider["comparePassword"]("iloverealm", salt, provider["iterations"] + 5, provider["keyLength"], provider["digest"], hash); chai_1.expect(isMatch).to.be.false; }); }); it("should be false when changing the keyLength", function () { return __awaiter(this, void 0, void 0, function* () { const salt = yield provider["createSalt"](provider["saltLength"]); const hash = yield provider["hashPassword"]("iloverealm", salt, provider["iterations"], provider["keyLength"], provider["digest"]); const isMatch = yield provider["comparePassword"]("iloverealm", salt, provider["iterations"], provider["keyLength"] + 123, provider["digest"], hash); chai_1.expect(isMatch).to.be.false; }); }); }); describe("authenticateOrCreateUser", () => { beforeEach(() => createServer()); it("should throw an error when a username is not passed", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isRejected(provider.authenticateOrCreateUser({}), errors.realm.MissingParameters); })); it("should throw an error when a password is not passed", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isRejected(provider.authenticateOrCreateUser({ username: "user1", }), errors.realm.MissingParameters); })); it("should throw an error when register is false", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isRejected(provider.authenticateOrCreateUser({ username: "user1", password: "mypassword", register: false }), errors.realm.InvalidCredentials); })); it("should fulfill when register is true", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isFulfilled(provider.authenticateOrCreateUser({ username: "user1", password: "mypassword", register: true, })); })); }); describe("account enumeration protection", () => { beforeEach(() => createServer({ iterations: 20000, })); it("should take similar time to return as when credentials are valid", () => __awaiter(this, void 0, void 0, function* () { const username = uuid.v4(); const password = "123"; yield provider.authenticateOrCreateUser({ username, password, register: true, }); const { time: realTime } = yield index_1.measureTime(() => provider.authenticateOrCreateUser({ username, password, register: false })); const { time: wrongPasswordTime } = yield index_1.measureTime(() => provider.authenticateOrCreateUser({ username, password: "foo", register: false }).catch(() => { })); const { time: wrongUserTime } = yield index_1.measureTime(() => provider.authenticateOrCreateUser({ username: uuid.v4(), password, register: false }).catch(() => { })); const minExpectedTime = 0.5 * realTime; const maxExpectedTime = 1.5 * realTime; chai_1.assert.isTrue(minExpectedTime < wrongPasswordTime && wrongPasswordTime < maxExpectedTime, `Expected "wrong password time" (${wrongPasswordTime}) to be in the (${minExpectedTime}, ${maxExpectedTime}) range.`); chai_1.assert.isTrue(minExpectedTime < wrongUserTime && wrongUserTime < maxExpectedTime, `Expected "wrong user time" (${wrongUserTime}) to be in the (${minExpectedTime}, ${maxExpectedTime}) range.`); })); }); describe("update", () => { beforeEach(() => __awaiter(this, void 0, void 0, function* () { return createServer(); })); let username; let user; beforeEach(() => __awaiter(this, void 0, void 0, function* () { username = faker.internet.userName(); user = yield authService.createOrUpdateUser(username, "password", false); })); it("should raise an error when new_password is not passed", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isRejected(provider.update(user, { currentPassword: "oldPassword" }), errors.realm.MissingParameters); })); it("should change the user's password", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isFulfilled(provider.update(user, { new_password: "Rod.Smith" })); yield chai_1.assert.isFulfilled(provider["attemptToLogin"](username, "Rod.Smith")); })); }); describe("autoCreateAdminUser", () => { beforeEach(() => __awaiter(this, void 0, void 0, function* () { return createServer({ autoCreateAdminUser: true }); })); it("should create realm-admin upon autokeygen", function () { return __awaiter(this, void 0, void 0, function* () { const realmAdminUser = authService.getUserByProviderId("password", "realm-admin"); chai_1.assert.isDefined(realmAdminUser); chai_1.assert.isTrue(realmAdminUser.isAdmin); chai_1.assert.equal(realmAdminUser.accounts[0].provider, "password"); chai_1.assert.equal(realmAdminUser.accounts[0].providerId, "realm-admin"); const loggedInUser = yield provider["attemptToLogin"]("realm-admin", ""); chai_1.assert.isDefined(loggedInUser); chai_1.assert.equal(loggedInUser.userId, realmAdminUser.userId); chai_1.assert.equal(loggedInUser.accounts[0].provider, "password"); chai_1.assert.equal(loggedInUser.accounts[0].providerId, "realm-admin"); chai_1.assert.isTrue(loggedInUser.isAdmin); }); }); }); describe("createUserChangePasswordHashParametersLoginUser", () => { beforeEach(() => __awaiter(this, void 0, void 0, function* () { return yield createServer(); })); it("should login after changing password hash parameters", function () { return __awaiter(this, void 0, void 0, function* () { const user_1 = yield provider.authenticateOrCreateUser({ username: "some name", password: "mypassword", register: true, }); chai_1.assert.equal(user_1.accounts.length, 1); chai_1.assert.equal(user_1.accounts[0].provider, "password"); chai_1.assert.equal(user_1.accounts[0].providerId, "some name"); provider["iterations"] = 1000000; provider["keyLength"] = 123; provider["digest"] = "sha256"; const user_2 = yield provider.authenticateOrCreateUser({ username: "some name", password: "mypassword", register: false, }); chai_1.assert.equal(user_1.userId, user_2.userId); }); }); }); describe("deleteUser", () => { beforeEach(() => __awaiter(this, void 0, void 0, function* () { return createServer(); })); it("Password Realm should be empty after creating a user and deleting it", () => __awaiter(this, void 0, void 0, function* () { const user = yield provider.authenticateOrCreateUser({ username: "some name", password: "mypassword", register: true, }); const realm = provider["passwordRealm"]; const foundPasswordBefore = realm.objectForPrimaryKey("PasswordSaltHash", user.userId); chai_1.assert.exists(foundPasswordBefore); const userDeleted = yield provider.deleteUser(user.userId); chai_1.assert.isTrue(userDeleted); const userDeletedAgain = yield provider.deleteUser(user.userId); chai_1.assert.isFalse(userDeletedAgain); const foundPasswordAfter = realm.objectForPrimaryKey("PasswordSaltHash", user.userId); chai_1.assert.notExists(foundPasswordAfter); })); }); describe("createUserChangePasswordHashParametersLoginUser", () => { beforeEach(() => __awaiter(this, void 0, void 0, function* () { return createServer(); })); it("should fail when the user attempts to login after the password is deleted from the password Realm", () => __awaiter(this, void 0, void 0, function* () { const user = yield provider.authenticateOrCreateUser({ username: "some name", password: "mypassword", register: true, }); chai_1.assert.equal(user.accounts.length, 1); chai_1.assert.equal(user.accounts[0].provider, "password"); chai_1.assert.equal(user.accounts[0].providerId, "some name"); const userDeleted = yield provider.deleteUser(user.userId); chai_1.assert.isTrue(userDeleted); yield chai_1.assert.isRejected(provider.authenticateOrCreateUser({ username: "some name", password: "mypassword", register: false, })); })); }); describe("updateProviderAccount", () => { describe("when configured", () => { const userEmail = "user1@gmail.com"; let user; let emailHandler; let resetPasswordStub; let confirmEmailStub; beforeEach(() => __awaiter(this, void 0, void 0, function* () { emailHandler = { resetPassword: (email, token, userAgent, remoteIp) => __awaiter(this, void 0, void 0, function* () { }), confirmEmail: (email, token) => __awaiter(this, void 0, void 0, function* () { }), }; resetPasswordStub = sinon.stub(emailHandler, "resetPassword").callsFake(() => __awaiter(this, void 0, void 0, function* () { })); confirmEmailStub = sinon.stub(emailHandler, "confirmEmail").callsFake(() => __awaiter(this, void 0, void 0, function* () { })); yield createServer({ emailHandler }); user = yield provider.authenticateOrCreateUser({ username: userEmail, password: "mypassword", register: true, }); })); describe("when action is reset_password", () => { it("invokes the email handler", () => __awaiter(this, void 0, void 0, function* () { const userAgent = { os: "Windows", browser: "Chrome" }; const ip = "127.0.0.1"; yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "reset_password" }, false, userAgent, ip)); sinon.assert.calledOnce(resetPasswordStub); const callArgs = resetPasswordStub.getCall(0).args; chai_1.assert.strictEqual(callArgs[0], userEmail); chai_1.assert.isDefined(callArgs[1]); chai_1.assert.deepEqual(callArgs[2], userAgent); chai_1.assert.strictEqual(callArgs[3], ip); })); describe("when email handler throws", () => { it("should log error", () => __awaiter(this, void 0, void 0, function* () { const error = { title: "Email sending failed", reason: "Invalid email address" }; resetPasswordStub.callsFake(() => __awaiter(this, void 0, void 0, function* () { throw error; })); const authService = server.getService("auth"); let logStub; const logPromise = new Promise((resolve) => { logStub = sinon.stub(authService.logger, "error").callsFake(() => { resolve(); }); }); yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "reset_password" }, false, {}, "")); sinon.assert.calledOnce(resetPasswordStub); yield logPromise; sinon.assert.calledOnce(logStub); const logArgs = logStub.getCall(0).args; chai_1.assert.equal(logArgs[0], "Could not send reset email:"); chai_1.assert.deepEqual(logArgs[1], error); })); }); }); describe("when action is complete_reset", () => { it("fails when new_password is not provided", () => __awaiter(this, void 0, void 0, function* () { const err = yield chai_1.assert.isRejected(provider.updateProviderAccount(undefined, undefined, { action: "complete_reset", token: "123" }, false, {}, ""), errors.realm.MissingParameters); chai_1.assert.equal(err.invalidParams.length, 1); chai_1.assert.equal(err.invalidParams[0].name, "new_password"); })); it("fails when token is not provided", () => __awaiter(this, void 0, void 0, function* () { const err = yield chai_1.assert.isRejected(provider.updateProviderAccount(undefined, undefined, { action: "complete_reset", new_password: "" }, false, {}, ""), errors.realm.MissingParameters); chai_1.assert.equal(err.invalidParams.length, 1); chai_1.assert.equal(err.invalidParams[0].name, "token"); })); it("fails when token is invalid", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isRejected(provider.updateProviderAccount(undefined, undefined, { action: "complete_reset", new_password: "", token: "123" }, false, {}, ""), errors.realm.AccessDenied); })); it("fails when token is expired", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "reset_password" }, false, {}, "")); const token = resetPasswordStub.getCall(0).args[1]; const passwordRealm = provider["passwordRealm"]; const existing = passwordRealm.objectForPrimaryKey("PasswordResetRequest", token); chai_1.assert.isDefined(existing); passwordRealm.write(() => { existing.expires = moment().subtract(1, "hour").toDate(); }); yield chai_1.assert.isRejected(provider.updateProviderAccount(undefined, undefined, { action: "complete_reset", new_password: "", token }, false, {}, ""), errors.realm.AccessDenied); })); it("fails when token was consumed", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "reset_password" }, false, {}, "")); const token = resetPasswordStub.getCall(0).args[1]; yield chai_1.assert.isFulfilled(provider.updateProviderAccount(undefined, undefined, { action: "complete_reset", new_password: "123", token }, false, {}, "")); yield chai_1.assert.isRejected(provider.updateProviderAccount(undefined, undefined, { action: "complete_reset", new_password: "123", token }, false, {}, ""), errors.realm.AccessDenied); })); it("changes the password", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "reset_password" }, false, {}, "")); const token = resetPasswordStub.getCall(0).args[1]; yield chai_1.assert.isFulfilled(provider.updateProviderAccount(undefined, undefined, { action: "complete_reset", new_password: "123", token }, false, {}, "")); yield chai_1.assert.isRejected(provider.authenticateOrCreateUser({ username: userEmail, password: "mypassword", register: false, }), errors.realm.InvalidCredentials); yield chai_1.assert.isFulfilled(provider.authenticateOrCreateUser({ username: userEmail, password: "123", register: false, })); })); }); describe("when action is request_email_confirmation", () => { it("invokes the email handler", () => __awaiter(this, void 0, void 0, function* () { confirmEmailStub.resetHistory(); yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "request_email_confirmation" }, false, {}, "")); sinon.assert.calledOnce(confirmEmailStub); const callArgs = confirmEmailStub.getCall(0).args; chai_1.assert.strictEqual(callArgs[0], userEmail); chai_1.assert.isDefined(callArgs[1]); })); }); describe("when action is confirm_email", () => { const loginViaService = () => { const req = { socket: {} }; return authService["authenticate"](req, { provider: provider.name, username: userEmail, password: "mypassword", register: false, }); }; it("fails when token is not provided", () => __awaiter(this, void 0, void 0, function* () { const err = yield chai_1.assert.isRejected(provider.updateProviderAccount(undefined, undefined, { action: "confirm_email" }, false, {}, ""), errors.realm.MissingParameters); chai_1.assert.equal(err.invalidParams.length, 1); chai_1.assert.equal(err.invalidParams[0].name, "token"); })); it("fails when token is invalid", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isRejected(provider.updateProviderAccount(undefined, undefined, { action: "confirm_email", token: "123" }, false, {}, ""), errors.realm.AccessDenied); })); it("fails when token is expired", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "request_email_confirmation" }, false, {}, "")); const token = confirmEmailStub.getCall(0).args[1]; const passwordRealm = provider["passwordRealm"]; const existing = passwordRealm.objectForPrimaryKey("EmailConfirmationRequest", token); chai_1.assert.isDefined(existing); passwordRealm.write(() => { existing.expires = moment().subtract(1, "hour").toDate(); }); yield chai_1.assert.isRejected(provider.updateProviderAccount(undefined, undefined, { action: "confirm_email", token }, false, {}, ""), errors.realm.AccessDenied); })); it("fails when token was consumed", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "request_email_confirmation" }, false, {}, "")); const token = confirmEmailStub.getCall(0).args[1]; yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "confirm_email", token }, false, {}, "")); yield chai_1.assert.isRejected(provider.updateProviderAccount(userEmail, user, { action: "confirm_email", token }, false, {}, ""), errors.realm.AccessDenied); })); it("confirms the email", () => __awaiter(this, void 0, void 0, function* () { let isConfirmed = user.metadata.find(m => m.key === "isEmailConfirmed"); chai_1.assert.isTrue(!isConfirmed || isConfirmed.value === "false"); let serviceResponse = yield loginViaService(); let serviceIsConfirmed = serviceResponse.refresh_token.token_data.isEmailConfirmed; chai_1.assert.isFalse(serviceIsConfirmed); yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "request_email_confirmation" }, false, {}, "")); const token = confirmEmailStub.getCall(0).args[1]; yield chai_1.assert.isFulfilled(provider.updateProviderAccount(userEmail, user, { action: "confirm_email", token }, false, {}, "")); const userResponse = yield chai_1.assert.isFulfilled(provider.authenticateOrCreateUser({ username: userEmail, password: "mypassword", register: false, })); isConfirmed = userResponse.metadata.find(m => m.key === "isEmailConfirmed"); chai_1.assert.equal(isConfirmed.value, "true"); serviceResponse = yield loginViaService(); serviceIsConfirmed = serviceResponse.refresh_token.token_data.isEmailConfirmed; chai_1.assert.isTrue(serviceIsConfirmed); })); }); describe("when action is invalid", () => { it("throws an error", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isRejected(provider.updateProviderAccount(userEmail, user, { action: "some_action" }, false, {}, ""), errors.realm.InvalidParameters); })); }); }); describe("when not configured", () => { beforeEach(() => __awaiter(this, void 0, void 0, function* () { return yield createServer(); })); it("throws an error", () => __awaiter(this, void 0, void 0, function* () { yield chai_1.assert.isRejected(provider.updateProviderAccount("foo@bar.com", undefined, { action: "reset_password" }, false, {}, ""), errors.realm.NotSupported); })); }); }); describe.skip("performance tests", function () { this.timeout(1200000); beforeEach(() => createServer()); describe("createSalt", () => { it("sequential", () => __awaiter(this, void 0, void 0, function* () { const testSalt = (length) => __awaiter(this, void 0, void 0, function* () { const { time } = yield index_1.measureTime(() => __awaiter(this, void 0, void 0, function* () { for (let i = 0; i < 500; i++) { yield provider["createSalt"](length); } })); console.log(`createSalt(length: ${length}) Time: ${time / 500} ms`); }); yield testSalt(8); yield testSalt(16); yield testSalt(32); yield testSalt(64); yield testSalt(128); })); it("parallel", () => __awaiter(this, void 0, void 0, function* () { const testSalt = (length) => __awaiter(this, void 0, void 0, function* () { const { time } = yield index_1.measureTime(() => __awaiter(this, void 0, void 0, function* () { const promises = new Array(); for (let i = 0; i < 500; i++) { promises.push(provider["createSalt"](length)); } yield Promise.all(promises); })); console.log(`createSalt(length: ${length}) Time: ${time / 500} ms`); }); yield testSalt(8); yield testSalt(16); yield testSalt(32); yield testSalt(64); yield testSalt(128); })); }); describe("password hashing", () => { it("sequential", () => __awaiter(this, void 0, void 0, function* () { const testHashing = (iterations, saltLength = 32) => __awaiter(this, void 0, void 0, function* () { const { time } = yield index_1.measureTime(() => __awaiter(this, void 0, void 0, function* () { for (let i = 0; i < 100; i++) { const salt = yield provider["createSalt"](saltLength); yield provider["hashPassword"](faker.random.uuid(), salt, iterations, 512, "sha512"); } })); console.log(`iterations: ${iterations}, salt: ${saltLength}; Time: ${time / 100} ms`); }); yield testHashing(500); yield testHashing(1000); yield testHashing(2000); yield testHashing(5000); yield testHashing(7500); yield testHashing(10000); yield testHashing(20000); })); it("parallel", () => __awaiter(this, void 0, void 0, function* () { const testHashing = (iterations, saltLength = 32) => __awaiter(this, void 0, void 0, function* () { const { time } = yield index_1.measureTime(() => __awaiter(this, void 0, void 0, function* () { const hashPassword = () => __awaiter(this, void 0, void 0, function* () { const salt = yield provider["createSalt"](saltLength); yield provider["hashPassword"](faker.random.uuid(), salt, iterations, 512, "sha512"); }); const promises = new Array(); for (let i = 0; i < 100; i++) { promises.push(hashPassword()); } yield Promise.all(promises); })); console.log(`iterations: ${iterations}, salt: ${saltLength}; Time: ${time / 100} ms`); }); yield testHashing(500); yield testHashing(1000); yield testHashing(2000); yield testHashing(5000); yield testHashing(7500); yield testHashing(10000); yield testHashing(20000); })); }); }); }); describe("UserLoginWithWrongProvider", () => { it("should fail when a debug user attempts to log in with the password provider", () => __awaiter(this, void 0, void 0, function* () { const passwordProvider = new PasswordAuthProvider_1.PasswordAuthProvider({ iterations: 5 }); const nicknameProvider = new __1.NicknameAuthProvider(); const server = new TestServer_1.TestServer(); yield server.start({ authProviders: [ passwordProvider, nicknameProvider, ] }); const user = yield nicknameProvider.authenticateOrCreateUser({ data: "some name", }); chai_1.assert.equal(user.accounts.length, 1); chai_1.assert.equal(user.accounts[0].provider, "nickname"); chai_1.assert.equal(user.accounts[0].providerId, "some name"); yield chai_1.assert.isRejected(passwordProvider.authenticateOrCreateUser({ username: "some name", password: "mypassword", register: false, })); yield server.shutdown(); })); }); //# sourceMappingURL=PasswordAuthProvider.spec.js.map