Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oauth->auth phase 1 #2497

Merged
merged 1 commit into from Oct 2, 2019
Merged

oauth->auth phase 1 #2497

merged 1 commit into from Oct 2, 2019

Conversation

dannycoates
Copy link
Contributor

No description provided.

@dannycoates dannycoates force-pushed the auth-oauth branch 10 times, most recently from d1aa649 to da5bf7b Compare September 16, 2019 22:56
Copy link
Contributor Author

@dannycoates dannycoates left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking for r?

What this does...

It starts using the code from oauth-server directly in the auth-server instead of making a http request. One goal here was minimal changes, I think I almost got that. Cleaning up orphaned code will be in a later phase.

I removed the local/oauthdb tests since they looked pretty specific to the http api calls and functionality seemed pretty well covered by the remote tests. lmk if that isn't correct.

In prod, or any other non-local-dev setup we'll need to start passing some oauth-server config into auth-server, mainly DB and a few others. I'll be making config PRs to fxa-dev and clouds-deployment. In local dev and ci this is handled in mysql_servers.json. Unfortunately servers.json won't work during the transition since multiple processes now need access to the data.

@@ -30,6 +30,7 @@ module.exports = config => {
}),
response: Joi.object({
access_token: validators.accessToken.required(),
id_token: validators.assertion.optional(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to add this for this line in the test to work https://github.com/mozilla/fxa/blob/master/packages/fxa-auth-server/test/remote/oauth_tests.js#L188

I don't know if it's ok for this to be returning an id_token or not, and I don't know why it would be different from before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is right. If you look at the test, the original scope requested for the grant contained openid. With @rfk's PR to make refresh token grants default to the original grant scopes, openid would have been propagated when using the refresh token to request the new access token. Now, for why this test wouldn't have failed before your change here is a mystery though it does look like the test on line 196 can be re-instated because @rfk's PR fixed #1867.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

res2 still has an empty scope in this test 🤷‍♂️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... Strange. And unexpected. @rfk - with your fix we should expect scope to be populated there, wouldn't we?

@dannycoates - I don't think this is related to your PR, so carry on.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, actually I think it's super weird to return an id_token in a refresh grant and we should instead update #2410 to skip the "openid" scope. I filed #2548 to follow up.

I agree that @dannycoates shouldn't worry about this for his PR though.

@@ -23,6 +15,9 @@ const CODE = 'df6dcfe7bf6b54a65db5742cbcdce5c0a84a5da81a0bb6bdf5fc793eef041fc6';
const PKCE_CODE_VERIFIER = 'au3dqDz2dOB0_vSikXCUf4S8Gc-37dL-F7sGxtxpR3R';
const DISABLED_CLIENT_ID = 'd15ab1edd15ab1ed';

realConfig.set('disabledClients', [DISABLED_CLIENT_ID]);
delete require.cache[require.resolve(routeModulePath)];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving config.js into config/index.js broke the sandbox/proxyquire thing and I was too lazy to figure out how to fix it, so I just did this instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine for now. Since there will be more changes for the config, we can probably revisit it later.

@@ -0,0 +1,100 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved config.js here because I thought I was going to have 2 config sets based on whether oauth or the key server was loading it and using separate files for each, but they ended up both being pretty much the same. I moved the schema out for the same reason but idk if leaving it there is better or not.

);

// This is sneaky and gross, but temporary.
if (process.mainModule.filename.includes('key_server')) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are the differences when loading from auth-server, luckily I don't think any of the necessary ENVs in the schema overlap auth-server's

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an easy way to check this to make sure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im doing this now as i make the config PRs to cloudops and fxa-dev

@@ -53,7 +53,7 @@ if grep -e "$MODULE" -e 'all' $DIR/../packages/test.list; then
fi

cd ../../
npx pm2 delete servers.json && npx pm2 start servers.json
npx pm2 delete mysql_servers.json && npx pm2 start mysql_servers.json
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests require mysql for now since two processes access the oauth data


const routes = new Map(
oauthRoutes.map(route => [route.path, route.config.handler])
);

module.exports = (log, config) => {
const OAuthAPI = createBackendServiceAPI(log, config, 'oauth', {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this for the validators

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. Should those validators just be moved to the routes in oauth.js? If not, can you leave a note about accessing the validators via the exported api property? If we keep the validators where they are, should we consider removing path and method from the oauthdb files since the route handlers are called directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will all change in phase 2. I left most things in place to keep the diff smaller

@@ -28,7 +28,9 @@ const {
registerSuite('support form without valid session', {
tests: {
'go to support form, redirects to signin': function() {
return this.remote.then(openPage(SUPPORT_URL, selectors.SIGNIN.HEADER));
return this.remote
.then(clearBrowserState())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test started failing without clearing the state first. The screenshot looked like it was already logged in. idk why this changed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a timing issue. I imagine things are ever so slightly faster now with fewer HTTP requests to make. You might want to add a {force: true} parameter, the default way of clearing state is also somewhat brittle to timing issues.

@dannycoates dannycoates marked this pull request as ready for review September 17, 2019 03:11
@dannycoates dannycoates changed the title wip on oauth->auth phase 1 oauth->auth phase 1 Sep 17, 2019
@dannycoates dannycoates requested a review from a team September 17, 2019 03:16
@vbudhram vbudhram added this to the Train 147: FxA milestone Sep 17, 2019
Copy link
Contributor

@shane-tomlinson shane-tomlinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very exiting @dannycoates and the overall direction looks good. When locally testing using the memory database, it seems the auth and oauth servers create two separate memory instances. If a token is created in the Auth server's memory instance, that same token cannot be verified if the OAuth server's /verify endpoint is used.

You can see this in action by applying this diff: https://paste.mozilla.org/GfiGcdUv

Then sign up & verify an account and try to go to the /settings page. The token keeps coming back as invalid. I'm not sure what the best way forward is because IIUC at some point soon, all the routes for both services will be served by one process. We could say in the meantime MySQL is the only way forward, or we could start a small shared memory DB service?

`${conf.get('env')}.json`
);

// This is sneaky and gross, but temporary.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the plan to get rid of the sneaky/grossness?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phase 2 will copy oauth config into auth-server's main config.

// This is sneaky and gross, but temporary.
if (process.mainModule.filename.includes('key_server')) {
if (fs.existsSync(envConfig)) {
conf.loadFile(envConfig);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we yell loudly if the environment config file does not exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file doesn't exist in prod, it's all environment variables.

);

// This is sneaky and gross, but temporary.
if (process.mainModule.filename.includes('key_server')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an easy way to check this to make sure?

conf.loadFile(envConfig);
}
conf.set('mysql.createSchema', false);
conf.validate();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come the key server doesn't enforce strict mode but the oauth server does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auth-server only requires a subset of the oauth config in this phase. Strict makes us set more variables than we need in this case.

@@ -27,6 +19,9 @@ const PKCE_CODE_CHALLENGE = 'iyW5ScKr22v_QL-rcW_EGlJrDSOymJvrlXlw4j7JBiQ';
const PKCE_CODE_CHALLENGE_METHOD = 'S256';
const DISABLED_CLIENT_ID = 'd15ab1edd15ab1ed';

realConfig.set('disabledClients', [DISABLED_CLIENT_ID]);
delete require.cache[require.resolve(routeModulePath)];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL. This looks much easier than using proxyquire and the mocks module.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, however I vaguely remember running into subtle problems when using this. It puts the puts the pressure on the developer to know which version of the module has been loaded where.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noted. imho we ought to be injecting the config instead of requireing it, but that's a debate for another time


const routes = new Map(
oauthRoutes.map(route => [route.path, route.config.handler])
);

module.exports = (log, config) => {
const OAuthAPI = createBackendServiceAPI(log, config, 'oauth', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. Should those validators just be moved to the routes in oauth.js? If not, can you leave a note about accessing the validators via the exported api property? If we keep the validators where they are, should we consider removing path and method from the oauthdb files since the route handlers are called directly?


'use strict';

const { assert } = require('chai');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need any portion of these tests or is the code exercised elsewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets covered by remote/oauth_tests.js and the content-server functional tests. I didn't do an exhaustive comparison because the new callRoute and backendService.sendRequest are so similar

let password;
let server;

before(async () => {
testUtils.disableLogs();
oauthServer = await oauthServerModule.create();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕺 amazing!

@@ -28,7 +28,9 @@ const {
registerSuite('support form without valid session', {
tests: {
'go to support form, redirects to signin': function() {
return this.remote.then(openPage(SUPPORT_URL, selectors.SIGNIN.HEADER));
return this.remote
.then(clearBrowserState())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a timing issue. I imagine things are ever so slightly faster now with fewer HTTP requests to make. You might want to add a {force: true} parameter, the default way of clearing state is also somewhat brittle to timing issues.

@@ -0,0 +1,300 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the intent of using a separate schema here so that one config module can be used to load configs for both services?

Copy link
Contributor

@vbudhram vbudhram left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dannycoates Nice work! I don't really have any issues with approach here. Although, it is a little unclear what the next steps, maybe create a checklist of items?

We could say in the meantime MySQL is the only way forward

I think this is fair, I really don't know who uses the memory database (except for local dev) and we have talked about nuking it for a while.

@@ -23,6 +15,9 @@ const CODE = 'df6dcfe7bf6b54a65db5742cbcdce5c0a84a5da81a0bb6bdf5fc793eef041fc6';
const PKCE_CODE_VERIFIER = 'au3dqDz2dOB0_vSikXCUf4S8Gc-37dL-F7sGxtxpR3R';
const DISABLED_CLIENT_ID = 'd15ab1edd15ab1ed';

realConfig.set('disabledClients', [DISABLED_CLIENT_ID]);
delete require.cache[require.resolve(routeModulePath)];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine for now. Since there will be more changes for the config, we can probably revisit it later.

@@ -27,6 +19,9 @@ const PKCE_CODE_CHALLENGE = 'iyW5ScKr22v_QL-rcW_EGlJrDSOymJvrlXlw4j7JBiQ';
const PKCE_CODE_CHALLENGE_METHOD = 'S256';
const DISABLED_CLIENT_ID = 'd15ab1edd15ab1ed';

realConfig.set('disabledClients', [DISABLED_CLIENT_ID]);
delete require.cache[require.resolve(routeModulePath)];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, however I vaguely remember running into subtle problems when using this. It puts the puts the pressure on the developer to know which version of the module has been loaded where.

@dannycoates
Copy link
Contributor Author

@shane-tomlinson I removed servers.json to prevent confusion about which to run. We ought to be able to restore it in phase 2, or we can use this as an excuse to deprecate the memory db for good.

Copy link
Contributor

@shane-tomlinson shane-tomlinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Down with the in-memory database! Down with the separation of the auth and oauth servers! r+

@dannycoates
Copy link
Contributor Author

Once config PRs are good I'll merge this

@shane-tomlinson
Copy link
Contributor

@dannycoates looks like some merge conflicts were introduced. Mind rebasing? Other than a rebase, is there more you want to do here?

@@ -1,236 +0,0 @@
{
"apps": [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to update the docs that servers.json is not a thing anymore? or should we take mysql_servers and call it servers now?

@dannycoates
Copy link
Contributor Author

@dannycoates looks like some merge conflicts were introduced. Mind rebasing? Other than a rebase, is there more you want to do here?

rebased. I'd like to get the config PRs reviewed before I merge.

@dannycoates dannycoates merged commit 7ad1f3b into master Oct 2, 2019
@clouserw clouserw added needs:qa Development work is done. This is ready for QA. qa+ Critical flow or high chance of regression. QA should focus on testing this issue. labels Oct 11, 2019
@dannycoates
Copy link
Contributor Author

Deployment risks:

code: LOW (no logic changes, only glue code. running by devs and on latest for 2 weeks, all tests passing)
config: LOW (will get caught in stage if misconfigured)
operations: LOW-MEDIUM (slightly reduced load on oauth-server, reduced network overall, slightly increased load on auth-server, more db connections to oauth db)

@dannycoates
Copy link
Contributor Author

dannycoates commented Oct 11, 2019

Testing

These areas are most affected by this change:

  • Ensure logging in with oauth works (123done)
  • Ensure oauth sessions (123done) display on the settings page in 'Devices & apps'

@shane-tomlinson shane-tomlinson deleted the auth-oauth branch October 14, 2019 11:56
@ValentinaPC ValentinaPC removed the needs:qa Development work is done. This is ready for QA. label Jul 29, 2020
@ValentinaPC
Copy link

Verified as fixed in stage, using Fx78.0.2 and Windows 10 (64-bit).
Both scenarios work as expected.
Postfix screenshot below.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
qa+ Critical flow or high chance of regression. QA should focus on testing this issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants