+ describe('_otpSubmission', function () {
+ beforeEach(function () {
+ sinon.useFakeTimers(new Date());
+ sinon.stub(manager.mysteryBox, 'unpack').resolves({
+ authenticatedIdentifier: 'identifier',
+ attempt: 0,
+ epochMs: Date.now(),
+ });
+ manager.authenticator.checkOTP.resolves(Enum.OTPResult.Valid);
+ ctx.parsedBody.state = 'state_data';
+ ctx.parsedBody.otp = '123456';
+ });
+ it('returns false if no otp state', async function () {
+ delete ctx.parsedBody.state;
+ const result = await manager._otpSubmission(res, ctx);
+ assert(manager.mysteryBox.unpack.notCalled);
+ assert.strictEqual(result, false);
+ });
+ it('returns false when presented with invalid otp state', async function () {
+ manager.mysteryBox.unpack.rejects();
+ const result = await manager._otpSubmission(res, ctx);
+ assert(manager.mysteryBox.unpack.called);
+ assert.strictEqual(result, false);
+ });
+ it('returns true when submitted otp is invalid, but allowed to retry', async function () {
+ manager.authenticator.checkOTP.resolves(Enum.OTPResult.InvalidSoftFail);
+ const result = await manager._otpSubmission(res, ctx);
+ assert(manager.mysteryBox.unpack.called);
+ assert.strictEqual(result, true);
+ assert(res.end.called);
+ });
+ it('returns false when submitted otp is invalid and too many attempts', async function () {
+ manager.mysteryBox.unpack.resolves({
+ authenticatedIdentifier: 'identifier',
+ attempt: 10,
+ epochMs: Date.now(),
+ });
+ manager.authenticator.checkOTP.resolves(Enum.OTPResult.InvalidHardFail);
+ const result = await manager._otpSubmission(res, ctx);
+ assert(manager.mysteryBox.unpack.called);
+ assert.strictEqual(result, false);
+ });
+ it('returns false when submitted otp is invalid and too much time has passed', async function () {
+ manager.mysteryBox.unpack.resolves({
+ authenticatedIdentifier: 'identifier',
+ attempt: 0,
+ epochMs: Date.now() - 99999999,
+ });
+ manager.authenticator.checkOTP.resolves(Enum.OTPResult.InvalidHardFail);
+ const result = await manager._otpSubmission(res, ctx);
+ assert(manager.mysteryBox.unpack.called);
+ assert.strictEqual(result, false);
+ });
+ it('returns true when submitted otp is valid', async function () {
+ const result = await manager._otpSubmission(res, ctx);
+ assert(res.end.called);
+ assert.strictEqual(result, true);
+ });
+ it('covers unexpected otp response', async function () {
+ manager.authenticator.checkOTP.resolves('wrong');
+ assert.rejects(() => manager._otpSubmission(res, ctx), RangeError);
+ });
+ }); // _otpSubmission
+
+ describe('_localUserAuth', function () {
+ beforeEach(function () {
+ ctx.parsedBody.identifier = 'identifier';
+ ctx.parsedBody.credential = 'credential';
+ manager.authenticator.isValidIdentifierCredential.resolves(true);
+ sinon.stub(manager.mysteryBox, 'pack').resolves('box');
+ });
+ it('returns false if indieauth available', async function () {
+ ctx.parsedBody.me = 'https://example.com/';
+ const result = await manager._localUserAuth(res, ctx);
+ assert.strictEqual(result, false);
+ });
+ it('returns true if identifier is invalid', async function () {
+ manager.authenticator.isValidIdentifierCredential.resolves(false);
+ const result = await manager._localUserAuth(res, ctx);
+ assert.strictEqual(result, true);
+ assert(manager.authenticator.isValidIdentifierCredential.called);
+ assert(res.end.called);
+ });
+ it('returns true if valid identifier', async function () {
+ const result = await manager._localUserAuth(res, ctx);
+ assert.strictEqual(result, true);
+ assert(res.end.called);
+ });
+ it('returns true if valid identifier requires otp entry', async function () {
+ ctx.otpNeeded = true;
+ const result = await manager._localUserAuth(res, ctx);
+ assert.strictEqual(result, true);
+ assert(manager.mysteryBox.pack.called);
+ assert(res.end.called);
+ });
+ }); // _localUserAuth
+