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
2 changes: 0 additions & 2 deletions packages/fxa-auth-server/config/dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,6 @@
"dcdb5ae7add825d2": "123done",
"5882386c6d801776": "firefox-desktop",
"98e6508e88680e1a": "fxa-settings",
"7377719276ad44ee": "pocket-mobile",
"749818d3f2e7857f": "pocket-web",
"8269bacd7bbc7f80": "thunderbird"
},
"clients": [
Expand Down
6 changes: 1 addition & 5 deletions packages/fxa-auth-server/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,8 +504,7 @@ const convictConf = convict({
subscriptionSupportUrl: {
doc: 'url to Mozilla subscription support page',
format: String,
default:
'https://support.mozilla.org/products',
default: 'https://support.mozilla.org/products',
},
redirectDomain: {
doc: 'Domain that mail urls are allowed to redirect to',
Expand Down Expand Up @@ -1212,9 +1211,6 @@ const convictConf = convict({
},
},
clientIdToServiceNames: {
// This is used by oauth/db/index.js to identify pocket client ids so that it
// can store them separately in mysql.
// It's also used for amplitude stats
doc: 'Mappings from client id to service name: { "id1": "name-1", "id2": "name-2" }',
default: {},
format: 'Object',
Expand Down
20 changes: 0 additions & 20 deletions packages/fxa-auth-server/lib/oauth/db/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ const REFRESH_LAST_USED_AT_UPDATE_AFTER_MS = config.get(
'oauthServer.refreshToken.updateAfter'
);

function getPocketIds(idNameMap) {
return Object.entries(idNameMap)
.filter(([_, name]) => name.startsWith('pocket'))
.map(([id, _]) => id);
}

const POCKET_IDS = getPocketIds(
config.get('oauthServer.clientIdToServiceNames')
);

class OauthDB extends ConnectedServicesDb {
get mysql() {
return this.db;
Expand Down Expand Up @@ -92,12 +82,6 @@ class OauthDB extends ConnectedServicesDb {
// We avoid revocation concerns with short-lived
// tokens, so we do not store them.
return token;
} else if (POCKET_IDS.includes(hex(vals.clientId))) {
// Pocket tokens are persisted past their expiration for legacy
// reasons: https://bugzilla.mozilla.org/show_bug.cgi?id=1547902
// since they are long lived we continue to store them in mysql
// so that redis can be exclusively ephemeral
await this.mysql._generateAccessToken(token);
} else {
await this.redis.setAccessToken(token);
}
Expand Down Expand Up @@ -217,10 +201,6 @@ class OauthDB extends ConnectedServicesDb {
await this.mysql._removeTokensAndCodes(uid);
}

getPocketIds() {
return POCKET_IDS;
}

async pruneAuthorizationCodes(ttlInMs) {
return await this.mysql._pruneAuthorizationCodes(
ttlInMs || config.get('oauthServer.expiration.code')
Expand Down
17 changes: 8 additions & 9 deletions packages/fxa-auth-server/lib/oauth/db/mysql/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ const QUERY_DEVELOPER_DELETE =
'LEFT JOIN clientDevelopers ON developers.developerId = clientDevelopers.developerId ' +
'WHERE developers.email=?';
// When listing access tokens, we deliberately do not exclude tokens that have expired.
// Such tokens will be cleaned up by a background job, except for those belonging to Pocket, which might
// one day come back to life as refresh tokens. (ref https://bugzilla.mozilla.org/show_bug.cgi?id=1547902).
// Such tokens will be cleaned up by a background job.
// There's minimal downside to showing tokens in the brief period between when they expire and when
// they get deleted from the db.
const QUERY_LIST_ACCESS_TOKENS_BY_UID =
Expand Down Expand Up @@ -539,14 +538,14 @@ class MysqlStore extends MysqlOAuthShared {
}

_getRefreshToken(token) {
return this._readOne(QUERY_REFRESH_TOKEN_FIND, [buf(token)]).then(function (
t
) {
if (t) {
t.scope = ScopeSet.fromString(t.scope);
return this._readOne(QUERY_REFRESH_TOKEN_FIND, [buf(token)]).then(
function (t) {
if (t) {
t.scope = ScopeSet.fromString(t.scope);
}
return t;
}
return t;
});
);
}

_touchRefreshToken(token, now) {
Expand Down
7 changes: 1 addition & 6 deletions packages/fxa-auth-server/lib/oauth/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,7 @@ exports.verify = async function verify(accessToken) {
+token.expiresAt >=
config.get('oauthServer.expiration.accessTokenExpiryEpoch');

// Pocket was one of FxA first clients and does not currently support refresh tokens.
// We currently can't expire any pocket tokens.
// Ref: https://bugzilla.mozilla.org/show_bug.cgi?id=1547902
const isPocket = db.getPocketIds().includes(token.clientId.toString('hex'));

if (expired && !isPocket) {
if (expired) {
throw OauthError.expiredToken(token.expiresAt);
}

Expand Down
55 changes: 0 additions & 55 deletions packages/fxa-auth-server/test/oauth/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2847,61 +2847,6 @@ describe('#integration - /v1', function () {
assert.equal(res.result.errno, 115);
});

describe('expired tokens from pocket clients', () => {
const clientId = '749818d3f2e7857f';
let accessTokenExpiryEpoch;

before(async () => {
await Server.close();
accessTokenExpiryEpoch = config.get(
'oauthServer.expiration.accessTokenExpiryEpoch'
);
config.set('oauthServer.expiration.accessTokenExpiryEpoch', undefined);
Server = await testServer.start();
assert.isDefined(Server);
});

after(async () => {
await Server.close();
config.set(
'oauthServer.expiration.accessTokenExpiryEpoch',
accessTokenExpiryEpoch
);
Server = await testServer.start();
assert.isDefined(Server);
});

it('should not reject expired tokens from pocket clients', async function () {
let res = await newToken(
{
ttl: 1,
},
{
clientId,
}
);

assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
assert.equal(res.result.expires_in, 1);

sandbox.useFakeTimers({
now: Date.now() + 1000 * 60 * 60, // 1 hr in future
shouldAdvanceTime: true,
});

res = await Server.api.post({
url: '/verify',
payload: {
token: res.result.access_token,
},
});

assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
});
});

describe('response', function () {
it('should return the correct response', function () {
return newToken({ scope: 'profile' })
Expand Down
64 changes: 0 additions & 64 deletions packages/fxa-auth-server/test/oauth/db/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -683,68 +683,4 @@ describe('#integration - db', function () {
assert.notOk(await db.getAccessToken(tokenIdHash));
});
});

describe('Access Token storage', () => {
describe('Pocket tokens', () => {
const pocketId = buf('749818d3f2e7857f');
const userId = buf(randomString(16));
const tokenData = {
clientId: pocketId,
name: 'pocket',
canGrant: false,
publicClient: true,
userId: userId,
email: '[email protected]',
scope: ScopeSet.fromArray(['no_scope']),
};

it('stores them in mysql', async () => {
const t = await db.generateAccessToken(tokenData);
const mysql = await db.mysql;
const tt = await mysql._getAccessToken(t.tokenId);
await db.removeAccessToken(t);
assert.equal(hex(tt.tokenId), hex(t.tokenId));
});

it('retrieves them with getAccessToken', async () => {
const t = await db.generateAccessToken(tokenData);
const tt = await db.getAccessToken(t.tokenId);
await db.removeAccessToken(t);
assert.equal(hex(tt.tokenId), hex(t.tokenId));
});

it('retrieves them with getAccessTokensByUid', async () => {
const t = await db.generateAccessToken(tokenData);
const tokens = await db.getAccessTokensByUid(userId);
await db.removeAccessToken(t);
assert.isArray(tokens);
assert.lengthOf(tokens, 1);
assert.deepEqual(tokens[0].tokenId, t.tokenId);
assert.deepEqual(tokens[0].clientId, t.clientId);
});

it('deletes them with removeAccessToken', async () => {
const t = await db.generateAccessToken(tokenData);
const tt = await db.getAccessToken(t.tokenId);
await db.removeAccessToken(t);
assert.isNotNull(tt);
const ttt = await db.getAccessToken(t.tokenId);
assert.isUndefined(ttt);
});

it('deletes them with deleteClientAuthorization', async () => {
const t = await db.generateAccessToken(tokenData);
await db.deleteClientAuthorization(t.clientId, t.userId);
const tt = await db.getAccessToken(t.tokenId);
assert.isUndefined(tt);
});

it('deletes them with removeTokensAndCodes', async () => {
const t = await db.generateAccessToken(tokenData);
await db.removeTokensAndCodes(t.userId);
const tt = await db.getAccessToken(t.tokenId);
assert.isUndefined(tt);
});
});
});
});
19 changes: 0 additions & 19 deletions packages/fxa-event-broker/src/jwtset/jwtset.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,23 +123,4 @@ export class JwtsetService {
uid: delEvent.uid,
});
}

public generateAppleMigrationSET(
appleMigrationEvent: set.appleMigrationEvent
): Promise<string> {
return this.generateSET({
uid: appleMigrationEvent.uid,
clientId: appleMigrationEvent.clientId,
events: {
[set.APPLE_USER_MIGRATION_ID]: {
fxaEmail: appleMigrationEvent.fxaEmail,
appleEmail: appleMigrationEvent.appleEmail,
transferSub: appleMigrationEvent.transferSub,
success: appleMigrationEvent.success,
err: appleMigrationEvent.err,
uid: appleMigrationEvent.uid,
},
},
});
}
}
12 changes: 0 additions & 12 deletions packages/fxa-event-broker/src/jwtset/set.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ export const PROFILE_EVENT_ID =
'https://schemas.accounts.firefox.com/event/profile-change';
export const SUBSCRIPTION_STATE_EVENT_ID =
'https://schemas.accounts.firefox.com/event/subscription-state-change';
export const APPLE_USER_MIGRATION_ID =
'https://schemas.accounts.firefox.com/event/apple-user-migration';

export type deleteEvent = {
clientId: string;
Expand Down Expand Up @@ -53,13 +51,3 @@ export type subscriptionEvent = {
isActive: boolean;
changeTime: number;
};

export type appleMigrationEvent = {
uid: string;
clientId: string;
fxaEmail: string;
appleEmail: string;
transferSub: string;
success: boolean;
err: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -195,17 +195,6 @@ export class PubsubProxyController {
accountLocked: message.accountLocked,
});
}
case dto.APPLE_USER_MIGRATION_EVENT: {
return await this.jwtset.generateAppleMigrationSET({
clientId,
uid: message.uid,
fxaEmail: message.fxaEmail,
appleEmail: message.appleEmail,
transferSub: message.transferSub,
success: message.success,
err: message.error,
});
}
default:
throw Error(`Invalid event: ${message.event}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,6 @@ const profileMessageForTotpEnabledChange = {
totpEnabled: true,
};

const appleMigrationMessage = {
event: 'appleUserMigration',
timestamp: now,
ts: now / 1000,
uid: '993d26bac72b471991b197b3d298a5de',
fxaEmail: '[email protected]',
appleEmail: '[email protected]',
transferSub: '123',
success: true,
err: '',
};

const updateStubMessage = (message: any) => {
return {
Body: JSON.stringify(message),
Expand Down Expand Up @@ -294,28 +282,6 @@ describe('QueueworkerService', () => {
]);
});

it('handles apple migration event', async () => {
const msg = updateStubMessage(appleMigrationMessage);
await (service as any).handleMessage(msg);

const topicName = 'rp749818d3f2e7857f';
expect(pubsub.topic).toBeCalledWith(topicName);
expect(pubsub.topic(topicName).publishMessage).toBeCalledTimes(1);
expect(pubsub.topic(topicName).publishMessage).toBeCalledWith({
json: {
appleEmail: '[email protected]',
err: '',
event: 'appleUserMigration',
fxaEmail: '[email protected]',
success: true,
timestamp: now,
transferSub: '123',
ts: now / 1000,
uid: '993d26bac72b471991b197b3d298a5de',
},
});
});

const fetchOnValidMessage = {
'delete message': baseDeleteMessage,
'legacy subscription message': baseSubscriptionUpdateLegacyMessage,
Expand Down
Loading