Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions packages/fxa-auth-server/lib/routes/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const { recordSecurityEvent } = require('./utils/security-event');
const { getOptionalCmsEmailConfig } = require('./utils/account');
const { Container } = require('typedi');
const { RelyingPartyConfigurationManager } = require('@fxa/shared/cms');
const authMethods = require('../authMethods');

module.exports = function (
log,
Expand Down Expand Up @@ -275,15 +276,56 @@ module.exports = function (
schema: isA.object({
state: isA.string().required(),
uid: isA.string().regex(HEX_STRING).required(),
details: isA.object({
accountEmailVerified: isA.boolean(),
sessionVerificationMethod: isA.string().allow(null),
sessionVerificationSuccessful: isA.boolean(),
sessionVerificationMeetsMinimumAAL: isA.boolean(),
}),
}),
},
},
handler: async function (request) {
log.begin('Session.status', request);
const sessionToken = request.auth.credentials;
const account = await db.account(sessionToken.uid);

// Make sure the account still exists
if (!account) {
throw error.unknownAccount();
}

// Check account assurance level
const accountAmr = await authMethods.availableAuthenticationMethods(
db,
account
);
const accountAal = authMethods.maximumAssuranceLevel(accountAmr);
const sessionAal = sessionToken.authenticatorAssuranceLevel;

// Build response
const accountEmailVerified =
account.emails?.primaryEmail?.isVerified || false;

const sessionVerificationMethod = sessionToken.verificationMethod;

// See verified-session-token auth strategy
const sessionVerificationSuccessful =
sessionToken.tokenVerificationId == null &&
sessionToken.tokenVerified !== false;

// Account Assurance Level
const sessionVerificationMeetsMinimumAAL = sessionAal >= accountAal;

return {
state: sessionToken.state,
uid: sessionToken.uid,
details: {
accountEmailVerified,
sessionVerificationMethod,
sessionVerificationSuccessful,
sessionVerificationMeetsMinimumAAL,
},
};
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ const AppError = require('../../../../lib/error');
const {
strategy,
} = require('../../../../lib/routes/auth-schemes/verified-session-token');
const authMethods = require('../../../../lib/authMethods');

const HAWK_HEADER = 'Hawk id="123", ts="123", nonce="123", mac="123"';

describe('lib/routes/auth-schemes/verified-session-token', () => {
let config;
Expand All @@ -21,10 +18,6 @@ describe('lib/routes/auth-schemes/verified-session-token', () => {
let request;
let getCredentialsFunc;

before(() => {
sinon.stub(authMethods, 'availableAuthenticationMethods');
});

beforeEach(() => {
// Default valid state. This state should pass email verified check, session token verified check,
// and account assurance level check.
Expand Down Expand Up @@ -53,12 +46,10 @@ describe('lib/routes/auth-schemes/verified-session-token', () => {
authenticatorAssuranceLevel: 1,
};

authMethods.availableAuthenticationMethods = sinon.fake.resolves(
new Set(['pwd', 'email'])
);

request = {
headers: { authorization: HAWK_HEADER },
headers: {
authorization: 'Hawk id="123", ts="123", nonce="123", mac="123"',
},
auth: { mode: 'required' },
route: { path: '/foo/{id}' },
};
Expand Down Expand Up @@ -209,9 +200,10 @@ describe('lib/routes/auth-schemes/verified-session-token', () => {

it('fails when AAL mismatch', async () => {
// Force account AAL=2 by returning otp along with pwd/email
authMethods.availableAuthenticationMethods = sinon.fake.resolves(
new Set(['pwd', 'email', 'otp'])
);
db.totpToken = sinon.fake.resolves({
verified: true,
enabled: true,
});

const authStrategy = strategy(getCredentialsFunc, db, config, statsd)();
try {
Expand All @@ -231,9 +223,10 @@ describe('lib/routes/auth-schemes/verified-session-token', () => {

it('skips AAL check when configured', async () => {
// Force account AAL=2 by returning otp along with pwd/email
authMethods.availableAuthenticationMethods = sinon.fake.resolves(
new Set(['pwd', 'email', 'otp'])
);
db.totpToken = sinon.fake.resolves({
enabled: true,
verified: true,
});

// Skip AAL check for path
config.authStrategies.verifiedSessionToken.skipAalCheckForRoutes = '/foo.*';
Expand Down
Loading