Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { renderSync } from 'sass';
import {
writeFileSync,
mkdirSync,
existsSync,
rmdirSync,
readdirSync,
} from 'fs';
import { writeFileSync, mkdirSync, existsSync, rmSync, readdirSync } from 'fs';
import path from 'path';

const getDirectories = (source: string) =>
Expand All @@ -23,22 +17,21 @@ const templates = getDirectories(path.join(__dirname, 'templates'));
const layouts = getDirectories(path.join(__dirname, 'layouts'));

async function compileSass(dir: string, subdir: string) {
let styleResult: Record<any, any> = {};
try {
styleResult = renderSync({
const styleResult = renderSync({
file: dir,
outFile: subdir,
});
writeFileSync(subdir, styleResult.css, 'utf8');
} catch (e) {
console.log(e);
console.error(e);
}
writeFileSync(subdir, styleResult.css, 'utf8');
}

async function main(directories: Record<any, any>) {
// remove css directory if already present
if (existsSync(path.join(__dirname, 'css'))) {
rmdirSync(path.join(__dirname, 'css'), { recursive: true });
rmSync(path.join(__dirname, 'css'), { recursive: true, force: true });
}
mkdirSync(path.join(__dirname, 'css'));

Expand Down
12 changes: 9 additions & 3 deletions packages/fxa-auth-server/test/oauth/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2064,7 +2064,10 @@ describe('#integration - /v1', function () {

assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
assert.equal(res.result.scope, 'email https://identity.mozilla.com/apps/notes');
assert.equal(
res.result.scope,
'email https://identity.mozilla.com/apps/notes'
);
});
});
});
Expand Down Expand Up @@ -2296,7 +2299,10 @@ describe('#integration - /v1', function () {
assert.equal(res.statusCode, 200);
assert.ok(res.result.access_token);
// Should contain all requested scopes (including invalid ones)
assert.equal(res.result.scope, 'profile email invalid:scope another:invalid');
assert.equal(
res.result.scope,
'profile email invalid:scope another:invalid'
);
});
});
});
Expand Down Expand Up @@ -3337,7 +3343,7 @@ describe('#integration - /v1', function () {
assert.equal(clients2[0].client_id, client2Id.toString('hex'));
});

it('should seperately list different refresh tokens from the same client', async () => {
it('should separately list different refresh tokens from the same client', async () => {
await makeAccessToken(client1, user1, ['profile']);
await makeAccessToken(client1, user1, ['other', 'scope']);
await makeRefreshToken(client2, user1, ['profile']);
Expand Down
10 changes: 7 additions & 3 deletions packages/fxa-shared/db/models/auth/session-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import {
} from '../../transformers';
import { convertError, notFound } from '../../mysql';

//TODO FIXME unhardcode the 28 day expiry
function notExpired(token: SessionToken) {
return !!(token.deviceId || token.createdAt > Date.now() - 2419200000);
return !!(
token.deviceId ||
token.createdAt > Date.now() - SessionToken.sessionExpiryMs
);
}

const VERIFICATION_METHOD = {
export const VERIFICATION_METHOD = {
email: 0,
'email-2fa': 1,
'totp-2fa': 2,
Expand Down Expand Up @@ -54,10 +56,12 @@ export function verificationMethodToString(
export class SessionToken extends BaseToken {
public static tableName = 'sessionTokens';
public static idColumn = 'tokenId';
public static sessionExpiryMs = 2419200000; // 28 days

protected $uuidFields = [
'tokenId',
'uid',

'tokenData',
'deviceId',
'tokenVerificationId',
Expand Down
121 changes: 121 additions & 0 deletions packages/fxa-shared/test/db/models/auth/session-token.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import 'mocha';
import { assert } from 'chai';
import sinon from 'sinon';
import { SessionToken } from '../../../../db/models/auth';
import { uuidTransformer } from '../../../../db/transformers';
import crypto from 'crypto';

const toRandomBuff = (size) =>
uuidTransformer.to(crypto.randomBytes(size).toString('hex'));

describe('SessionToken Unit Tests', () => {
let sandbox;

beforeEach(() => {
sandbox = sinon.createSandbox();
});

afterEach(() => {
sandbox.restore();
});

it('filters out expired tokens (older than 28 days by default)', async () => {
const now = Date.now();
// 28 days in milliseconds is 2419200000
const validTime = now - 2419200000 + 10000; // 10 seconds remaining
const expiredTime = now - 2419200000 - 10000; // 10 seconds overdue

const uidBuff = toRandomBuff(16);

const validRow = {
tokenId: toRandomBuff(16),
tokenData: toRandomBuff(16),
uid: uidBuff,
createdAt: validTime,
lastAccessTime: validTime,
authAt: 1,
verificationMethod: 0,
verifiedAt: 0,
mustVerify: 0,
deviceId: null, // explicit null for no device
emailVerified: 1,
email: 'test@test.com',
emailCode: 'code',
deviceCallbackIsExpired: 0,
};

const expiredRow = {
...validRow,
tokenId: toRandomBuff(16),
createdAt: expiredTime,
};

sandbox.stub(SessionToken, 'callProcedure').resolves({
rows: [validRow, expiredRow],
});

const tokens = await SessionToken.findByUid(
'0123456789abcdef0123456789abcdef'
);

assert.equal(
tokens.length,
1,
'Should return exactly 1 token (the valid one)'
);
assert.equal(tokens[0].createdAt, validTime);
});

it('respects configured expiry', async () => {
const originalExpiry = SessionToken.sessionExpiryMs;
try {
// Set expiry to 1 hour
SessionToken.sessionExpiryMs = 60 * 60 * 1000; // 1 hour
const now = Date.now();
const validTime = now - 30 * 60 * 1000; // 30 mins old (valid)
const expiredTime = now - 90 * 60 * 1000; // 90 mins old (expired)

const uidBuff = toRandomBuff(16);

const validRow = {
tokenId: toRandomBuff(16),
tokenData: toRandomBuff(16),
uid: uidBuff,
createdAt: validTime,
lastAccessTime: validTime,
authAt: 1,
verificationMethod: 0,
verifiedAt: 0,
mustVerify: 0,
deviceId: null,
emailVerified: 1,
email: 'test@test.com',
emailCode: 'code',
deviceCallbackIsExpired: 0,
};

const expiredRow = {
...validRow,
tokenId: toRandomBuff(16),
createdAt: expiredTime,
};

sandbox.stub(SessionToken, 'callProcedure').resolves({
rows: [validRow, expiredRow],
});

const tokens = await SessionToken.findByUid(
'0123456789abcdef0123456789abcdef'
);

assert.equal(
tokens.length,
1,
'Should return exactly 1 token (the valid one)'
);
assert.equal(tokens[0].createdAt, validTime);
} finally {
SessionToken.sessionExpiryMs = originalExpiry;
}
});
});
Loading