WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 7fd4d25

Browse files
authored
Merge pull request #19647 from mozilla/FXA-12540
feat(login): Allow third party auth for OAuthNative non-sync services
2 parents d6e32ca + b977f4f commit 7fd4d25

File tree

42 files changed

+797
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+797
-272
lines changed

packages/fxa-auth-client/lib/client.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1124,7 +1124,14 @@ export default class AuthClient {
11241124
}
11251125
}
11261126

1127-
async accountStatus(uid: hexstring, headers?: Headers) {
1127+
async accountStatus(
1128+
uid?: hexstring,
1129+
sessionToken?: hexstring,
1130+
headers?: Headers
1131+
): Promise<{ exists: boolean; locale?: string; hasPassword?: boolean }> {
1132+
if (sessionToken) {
1133+
return this.sessionGet('/account/status', sessionToken, headers);
1134+
}
11281135
return this.request('GET', `/account/status?uid=${uid}`, null, headers);
11291136
}
11301137

packages/fxa-auth-server/lib/routes/account.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,16 @@ export class AccountHandler {
14541454
async status(request: AuthRequest) {
14551455
const sessionToken = request.auth.credentials;
14561456
if (sessionToken) {
1457-
return { exists: true, locale: sessionToken.locale };
1457+
const account = await this.db.account(sessionToken.uid as string);
1458+
// Make sure the account still exists
1459+
if (!account) {
1460+
throw error.unknownAccount();
1461+
}
1462+
return {
1463+
exists: true,
1464+
locale: sessionToken.locale,
1465+
hasPassword: account.verifierSetAt > 0,
1466+
};
14581467
} else if (request.query.uid) {
14591468
const uid = request.query.uid;
14601469
try {

packages/fxa-auth-server/test/remote/totp_tests.js

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -156,125 +156,6 @@ const {
156156
});
157157
});
158158

159-
it('should allow verified sessions before totp enabled to delete totp token', () => {
160-
let client2, code;
161-
email = server.uniqueEmail();
162-
return Client.createAndVerify(
163-
config.publicUrl,
164-
email,
165-
password,
166-
server.mailbox,
167-
testOptions
168-
)
169-
.then((x) => {
170-
client = x;
171-
return client.login({ keys: true });
172-
})
173-
.then((response) => {
174-
assert.equal(
175-
response.verificationMethod,
176-
'email',
177-
'challenge method set to email'
178-
);
179-
assert.equal(
180-
response.verificationReason,
181-
'login',
182-
'challenge reason set to signin'
183-
);
184-
assert.equal(response.sessionVerified, false, 'session verified set to false');
185-
return server.mailbox.waitForEmail(email);
186-
})
187-
.then((emailData) => {
188-
code = emailData.headers['x-verify-code'];
189-
return client.verifyEmail(code);
190-
})
191-
.then(() => {
192-
// Login with a new client and enabled TOTP
193-
return Client.loginAndVerify(
194-
config.publicUrl,
195-
email,
196-
password,
197-
server.mailbox,
198-
{
199-
...testOptions,
200-
keys: true,
201-
}
202-
);
203-
})
204-
.then((x) => {
205-
client2 = x;
206-
return verifyTOTP(client2);
207-
})
208-
.then((res) => {
209-
// Delete totp from original client that only was email verified
210-
return client.deleteTotpToken().then((result) => {
211-
assert.ok(result, 'delete totp token successfully');
212-
return server.mailbox.waitForEmail(email);
213-
});
214-
})
215-
.then((emailData) => {
216-
assert.equal(
217-
emailData.headers['x-template-name'],
218-
'postRemoveTwoStepAuthentication'
219-
);
220-
221-
// Can create a new token
222-
return client.checkTotpTokenExists().then((result) => {
223-
assert.equal(result.exists, false, 'token does not exist');
224-
});
225-
});
226-
});
227-
228-
it('should not allow unverified sessions before totp enabled to delete totp token', () => {
229-
email = server.uniqueEmail();
230-
return Client.createAndVerify(
231-
config.publicUrl,
232-
email,
233-
password,
234-
server.mailbox,
235-
testOptions
236-
)
237-
.then((x) => {
238-
client = x;
239-
return client.login({ keys: true });
240-
})
241-
.then((response) => {
242-
assert.equal(
243-
response.verificationMethod,
244-
'email',
245-
'challenge method set to email'
246-
);
247-
assert.equal(
248-
response.verificationReason,
249-
'login',
250-
'challenge reason set to signin'
251-
);
252-
assert.equal(response.sessionVerified, false, 'session verified set to false');
253-
254-
return server.mailbox.waitForEmail(email);
255-
})
256-
.then(() => {
257-
// Login with a new client and enabled TOTP
258-
return Client.loginAndVerify(
259-
config.publicUrl,
260-
email,
261-
password,
262-
server.mailbox,
263-
{
264-
...testOptions,
265-
keys: true,
266-
}
267-
);
268-
})
269-
.then((client2) => verifyTOTP(client2))
270-
.then((res) => {
271-
// Attempt to delete totp from original unverified session
272-
return client.deleteTotpToken().then(assert.fail, (err) => {
273-
assert.equal(err.errno, 138, 'correct unverified session errno');
274-
});
275-
});
276-
});
277-
278159
it('should request `totp-2fa` on login if user has verified totp token', () => {
279160
return Client.login(config.publicUrl, email, password, {
280161
...testOptions,

packages/fxa-settings/src/components/App/index.test.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import GleanMetrics from '../../lib/glean';
3232
import config from '../../lib/config';
3333
import { currentAccount } from '../../lib/cache';
3434
import { MozServices } from '../../lib/types';
35-
import mockUseSyncEngines from '../../lib/hooks/useSyncEngines/mocks';
36-
import useSyncEngines from '../../lib/hooks/useSyncEngines';
35+
import mockUseFxAStatus from '../../lib/hooks/useFxAStatus/mocks';
36+
import useFxAStatus from '../../lib/hooks/useFxAStatus';
3737
import sentryMetrics from 'fxa-shared/sentry/browser';
3838
import { OAuthError } from '../../lib/oauth';
3939

@@ -44,7 +44,7 @@ jest.mock('@reach/router', () => {
4444
};
4545
});
4646

47-
jest.mock('../../lib/hooks/useSyncEngines', () => ({
47+
jest.mock('../../lib/hooks/useFxAStatus', () => ({
4848
__esModule: true,
4949
default: jest.fn(),
5050
}));
@@ -180,12 +180,12 @@ describe('metrics', () => {
180180
};
181181

182182
flowInit = jest.spyOn(Metrics, 'init');
183-
(useSyncEngines as jest.Mock).mockReturnValue(mockUseSyncEngines());
183+
(useFxAStatus as jest.Mock).mockReturnValue(mockUseFxAStatus());
184184
});
185185

186186
afterEach(() => {
187187
flowInit.mockReset();
188-
(useSyncEngines as jest.Mock).mockRestore();
188+
(useFxAStatus as jest.Mock).mockRestore();
189189
});
190190

191191
it('Initializes metrics flow data when present', async () => {
@@ -232,11 +232,11 @@ describe('metrics', () => {
232232

233233
describe('glean', () => {
234234
beforeEach(() => {
235-
(useSyncEngines as jest.Mock).mockReturnValue(mockUseSyncEngines());
235+
(useFxAStatus as jest.Mock).mockReturnValue(mockUseFxAStatus());
236236
});
237237

238238
afterEach(() => {
239-
(useSyncEngines as jest.Mock).mockRestore();
239+
(useFxAStatus as jest.Mock).mockRestore();
240240
});
241241
it('Initializes glean', async () => {
242242
(useInitialMetricsQueryState as jest.Mock).mockReturnValueOnce({
@@ -349,13 +349,13 @@ describe('loading spinner states', () => {
349349

350350
describe('AuthAndAccountSetupRoutes', () => {
351351
beforeEach(() => {
352-
(useSyncEngines as jest.Mock).mockReturnValue(mockUseSyncEngines());
352+
(useFxAStatus as jest.Mock).mockReturnValue(mockUseFxAStatus());
353353
});
354354

355355
afterEach(() => {
356-
(useSyncEngines as jest.Mock).mockRestore();
356+
(useFxAStatus as jest.Mock).mockRestore();
357357
});
358-
it('calls useSyncEngines with integration', async () => {
358+
it('calls useFxAStatus with integration', async () => {
359359
const mockIntegration = {
360360
getServiceName: () => MozServices.FirefoxSync,
361361
isSync: () => true,
@@ -385,8 +385,8 @@ describe('AuthAndAccountSetupRoutes', () => {
385385
);
386386
});
387387

388-
expect(useSyncEngines).toHaveBeenCalledTimes(1);
389-
expect(useSyncEngines).toHaveBeenCalledWith(mockIntegration);
388+
expect(useFxAStatus).toHaveBeenCalledTimes(1);
389+
expect(useFxAStatus).toHaveBeenCalledWith(mockIntegration);
390390
});
391391
});
392392

@@ -617,7 +617,7 @@ describe('SettingsRoutes', () => {
617617

618618
describe('Integration serviceName error handling', () => {
619619
beforeEach(() => {
620-
(useSyncEngines as jest.Mock).mockReturnValue(mockUseSyncEngines());
620+
(useFxAStatus as jest.Mock).mockReturnValue(mockUseFxAStatus());
621621
(useInitialMetricsQueryState as jest.Mock).mockReturnValue({
622622
loading: false,
623623
});
@@ -634,7 +634,7 @@ describe('Integration serviceName error handling', () => {
634634
});
635635

636636
afterEach(() => {
637-
(useSyncEngines as jest.Mock).mockRestore();
637+
(useFxAStatus as jest.Mock).mockRestore();
638638
(useInitialMetricsQueryState as jest.Mock).mockRestore();
639639
(useLocalSignedInQueryState as jest.Mock).mockRestore();
640640
(useSession as jest.Mock).mockRestore();

packages/fxa-settings/src/components/App/index.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import sentryMetrics from 'fxa-shared/sentry/browser';
4747
import LoadingSpinner from 'fxa-react/components/LoadingSpinner';
4848
import { ScrollToTop } from '../Settings/ScrollToTop';
4949
import SignupConfirmedSync from '../../pages/Signup/SignupConfirmedSync';
50-
import useSyncEngines from '../../lib/hooks/useSyncEngines';
50+
import useFxAStatus from '../../lib/hooks/useFxAStatus';
5151

5252
// Pages
5353
const IndexContainer = lazy(() => import('../../pages/Index/container'));
@@ -428,7 +428,7 @@ const AuthAndAccountSetupRoutes = ({
428428
gleanEnabled && GleanMetrics.pageLoad(location.pathname);
429429
}, [location.pathname, gleanEnabled]);
430430

431-
const useSyncEnginesResult = useSyncEngines(integration);
431+
const useFxAStatusResult = useFxAStatus(integration);
432432
try {
433433
// Handle getServiceName() errors that occur when OAuth scope validation fails.
434434
// This can happen when scopes are missing, invalid, or filtered out during
@@ -453,11 +453,11 @@ const AuthAndAccountSetupRoutes = ({
453453
{/* Index */}
454454
<IndexContainer
455455
path="/"
456-
{...{ integration, serviceName, flowQueryParams }}
456+
{...{ integration, serviceName, flowQueryParams, useFxAStatusResult }}
457457
/>
458458
<IndexContainer
459459
path="/oauth"
460-
{...{ integration, serviceName, flowQueryParams }}
460+
{...{ integration, serviceName, flowQueryParams, useFxAStatusResult }}
461461
/>
462462

463463
{/* Legal */}
@@ -479,7 +479,7 @@ const AuthAndAccountSetupRoutes = ({
479479
/>
480480
<SetPasswordContainer
481481
path="/post_verify/third_party_auth/set_password/*"
482-
{...{ flowQueryParams, integration, useSyncEnginesResult }}
482+
{...{ flowQueryParams, integration, useFxAStatusResult }}
483483
/>
484484

485485
{/* Reset password */}
@@ -523,19 +523,19 @@ const AuthAndAccountSetupRoutes = ({
523523
<ReportSigninContainer path="/report_signin/*" />
524524
<SigninContainer
525525
path="/oauth/force_auth/*"
526-
{...{ integration, serviceName, flowQueryParams }}
526+
{...{ integration, serviceName, flowQueryParams, useFxAStatusResult }}
527527
/>
528528
<SigninContainer
529529
path="/force_auth/*"
530-
{...{ integration, serviceName, flowQueryParams }}
530+
{...{ integration, serviceName, flowQueryParams, useFxAStatusResult }}
531531
/>
532532
<SigninContainer
533533
path="/oauth/signin/*"
534-
{...{ integration, serviceName, flowQueryParams }}
534+
{...{ integration, serviceName, flowQueryParams, useFxAStatusResult }}
535535
/>
536536
<SigninContainer
537537
path="/signin/*"
538-
{...{ integration, serviceName, flowQueryParams }}
538+
{...{ integration, serviceName, flowQueryParams, useFxAStatusResult }}
539539
/>
540540
<SigninBounced email={localAccount?.email} path="/signin_bounced/*" />
541541
<CompleteSigninContainer path="/complete_signin/*" />
@@ -596,7 +596,7 @@ const AuthAndAccountSetupRoutes = ({
596596
integration,
597597
serviceName,
598598
flowQueryParams,
599-
useSyncEnginesResult,
599+
useFxAStatusResult,
600600
}}
601601
/>
602602
<PrimaryEmailVerified
@@ -608,7 +608,7 @@ const AuthAndAccountSetupRoutes = ({
608608
{...{
609609
integration,
610610
flowQueryParams,
611-
useSyncEnginesResult,
611+
useFxAStatusResult,
612612
}}
613613
/>
614614
<SignupConfirmed
@@ -617,7 +617,7 @@ const AuthAndAccountSetupRoutes = ({
617617
/>
618618
<SignupConfirmedSync
619619
path="/signup_confirmed_sync/*"
620-
offeredSyncEngines={useSyncEnginesResult.offeredSyncEngines}
620+
offeredSyncEngines={useFxAStatusResult.offeredSyncEngines}
621621
{...{ integration }}
622622
/>
623623
<SignupConfirmed

packages/fxa-settings/src/lib/channels/firefox.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export type FxAStatusResponse = {
7171
multiService: boolean;
7272
pairing: boolean;
7373
choose_what_to_sync?: boolean;
74+
keys_optional?: boolean;
7475
};
7576
clientId?: string;
7677
signedInUser?: SignedInUser;

0 commit comments

Comments
 (0)