diff --git a/addons/auth/addon/authenticators/base.js b/addons/auth/addon/authenticators/base.js index a3cdafabd..a48aaaeac 100644 --- a/addons/auth/addon/authenticators/base.js +++ b/addons/auth/addon/authenticators/base.js @@ -58,6 +58,7 @@ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator { */ async validateToken(token, tokenID) { const tokenValidationURL = this.buildTokenValidationEndpointURL(tokenID); + // Note: waitForPromise is needed to provide the necessary integration with @ember/test-helpers // visit https://www.npmjs.com/package/@ember/test-waiters for more info. const response = await waitForPromise( @@ -66,6 +67,12 @@ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator { headers: { Authorization: `Bearer ${token}` }, }), ); + // Note: Always consume response body in order to avoid memory leaks + // visit https://undici.nodejs.org/#/?id=garbage-collection for more info. + // We do not use the undici package but the link informs us that garbage + // collection is undefined when response body is not consumed. + await response.json(); + // 401 and 404 responses mean the token is invalid, whereas other types of // error responses do not tell us about the validity of the token. if (response.status === 401 || response.status === 404) return reject(); diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index dc1063d5e..6b38c6c05 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -9,7 +9,7 @@ import { reject } from 'rsvp'; import { waitForPromise } from '@ember/test-waiters'; /** - * The OIDC base authenticator encapsulates the multistep OIDC flow. + * The OIDC base authenticator encapsulates the multi-step OIDC flow. * * 1. Start authentication flow: this step is actually a combination of two * sub steps: @@ -97,13 +97,19 @@ export default class OIDCAuthenticator extends BaseAuthenticator { }); // Fetch the endpoint and get the response JSON const response = await waitForPromise(fetch(url, { method: 'post', body })); + + // Note: Always consume response body in order to avoid memory leaks + // visit https://undici.nodejs.org/#/?id=garbage-collection for more info. + // We do not use the undici package but the link informs us that garbage + // collection is undefined when response body is not consumed. + const json = await response.json(); + if (response.status === 202) { // The token isn't ready yet, keep trying. return false; } else if (response.status < 400) { // Response was successful, meaning a token was obtained. // Authenticate with the session service using the response JSON. - const json = await response.json(); await this.session.authenticate('authenticator:oidc', json); return true; } else { diff --git a/addons/auth/tests/unit/authenticators/base-test.js b/addons/auth/tests/unit/authenticators/base-test.js index 6bcf988f6..3a7f2f38c 100644 --- a/addons/auth/tests/unit/authenticators/base-test.js +++ b/addons/auth/tests/unit/authenticators/base-test.js @@ -52,7 +52,7 @@ module('Unit | Authenticator | base', function (hooks) { const authenticator = this.owner.lookup('authenticator:base'); server.get(authenticator.buildTokenValidationEndpointURL(id), () => { assert.ok(true, 'token validation was requested'); - return [200]; + return [200, {}, '{}']; }); await authenticator.restore(mockData); }); @@ -64,7 +64,7 @@ module('Unit | Authenticator | base', function (hooks) { const authenticator = this.owner.lookup('authenticator:base'); server.get(authenticator.buildTokenValidationEndpointURL(id), () => { assert.ok(true, 'token validation was requested'); - return [401]; + return [401, {}, '{}']; }); try { await authenticator.restore(mockData); @@ -80,7 +80,7 @@ module('Unit | Authenticator | base', function (hooks) { const authenticator = this.owner.lookup('authenticator:base'); server.get(authenticator.buildTokenValidationEndpointURL(id), () => { assert.ok(true, 'token validation was requested'); - return [404]; + return [404, {}, '{}']; }); try { await authenticator.restore(mockData);