Skip to content

Commit

Permalink
Merge pull request #38 from Authress/coerce-regional-endpoint
Browse files Browse the repository at this point in the history
Coerce region endpoints to global authentication endpoint.
  • Loading branch information
wparad authored Feb 23, 2024
2 parents 86c208e + eb6e16f commit 8f1c2f5
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 12 deletions.
17 changes: 9 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,22 @@ class LoginClient {
* @param {Object} [logger] a configured logger object, optionally `console`, which can used to display debug and warning messages.
*/
constructor(settings, logger) {
this.settings = Object.assign({ applicationId: 'app_default' }, settings);
const settingsWithDefault = Object.assign({ applicationId: 'app_default' }, settings);
this.logger = logger || console;
const hostUrl = this.settings.authressApiUrl || this.settings.authressLoginHostUrl || this.settings.authenticationServiceUrl || '';
const hostUrl = settingsWithDefault.authressApiUrl || settingsWithDefault.authressLoginHostUrl || settingsWithDefault.authenticationServiceUrl || '';

if (!hostUrl) {
throw Error('Missing required property "authressApiUrl" in LoginClient constructor. Custom Authress Domain Host is required.');
}

this.applicationId = settingsWithDefault.applicationId;
this.hostUrl = sanitizeUrl(hostUrl);
this.httpClient = new HttpClient(this.hostUrl, logger);
this.lastSessionCheck = 0;

this.enableCredentials = this.getMatchingDomainInfo(this.hostUrl);

if (!settings.skipBackgroundCredentialsCheck) {
if (!settingsWithDefault.skipBackgroundCredentialsCheck) {
windowManager.onLoad(async () => {
await this.userSessionExists(true);
});
Expand Down Expand Up @@ -166,7 +167,7 @@ class LoginClient {
}

const userConfigurationScreenUrl = new URL('/settings', this.hostUrl);
userConfigurationScreenUrl.searchParams.set('client_id', this.settings.applicationId);
userConfigurationScreenUrl.searchParams.set('client_id', this.applicationId);
userConfigurationScreenUrl.searchParams.set('start_page', options && options.startPage || 'Profile');
userConfigurationScreenUrl.searchParams.set('redirect_uri', options && options.redirectUrl || windowManager.getCurrentLocation().href);
windowManager.assign(userConfigurationScreenUrl.toString());
Expand Down Expand Up @@ -309,7 +310,7 @@ class LoginClient {
// * This prevents canonical replay attacks, and fall through. If the user is already logged in, then the new log in attempt is ignored.
if (authRequest.nonce === urlSearchParams.get('nonce')) {
const code = urlSearchParams.get('code') === 'cookie' ? cookieManager.parse(document.cookie)['auth-code'] : urlSearchParams.get('code');
const request = { grant_type: 'authorization_code', redirect_uri: authRequest.redirectUrl, client_id: this.settings.applicationId, code, code_verifier: authRequest.codeVerifier };
const request = { grant_type: 'authorization_code', redirect_uri: authRequest.redirectUrl, client_id: this.applicationId, code, code_verifier: authRequest.codeVerifier };
try {
const tokenResult = await this.httpClient.post(`/authentication/${authRequest.nonce}/tokens`, this.enableCredentials, request);
const idToken = jwtManager.decode(tokenResult.data.id_token);
Expand Down Expand Up @@ -522,7 +523,7 @@ class LoginClient {
redirectUrl: selectedRedirectUrl, codeChallengeMethod: 'S256', codeChallenge,
connectionId, tenantLookupIdentifier,
connectionProperties,
applicationId: this.settings.applicationId
applicationId: this.applicationId
}, headers);
windowManager.assign(requestOptions.data.authenticationUrl);
} catch (error) {
Expand Down Expand Up @@ -578,7 +579,7 @@ class LoginClient {
redirectUrl: selectedRedirectUrl, codeChallengeMethod: 'S256', codeChallenge,
connectionId, tenantLookupIdentifier, inviteId,
connectionProperties,
applicationId: this.settings.applicationId,
applicationId: this.applicationId,
responseLocation, flowType, multiAccount
});
localStorage.setItem(AuthenticationRequestNonceKey, JSON.stringify({
Expand Down Expand Up @@ -651,7 +652,7 @@ class LoginClient {

const fullLogoutUrl = new URL('/logout', this.hostUrl);
fullLogoutUrl.searchParams.set('redirect_uri', redirectUrl || windowManager.getCurrentLocation().href);
fullLogoutUrl.searchParams.set('client_id', this.settings.applicationId);
fullLogoutUrl.searchParams.set('client_id', this.applicationId);
windowManager.assign(fullLogoutUrl.toString());
}
}
Expand Down
16 changes: 12 additions & 4 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
module.exports.sanitizeUrl = function sanitizeUrl(url) {
if (url.startsWith('http')) {
return url;
module.exports.sanitizeUrl = function sanitizeUrl(rawUrlStrng) {
let sanitizedUrl = rawUrlStrng;
if (!sanitizedUrl.startsWith('http')) {
sanitizedUrl = `https://${sanitizedUrl}`;
}

return `https://${url}`;
const url = new URL(sanitizedUrl);
const domainBaseUrlMatch = url.host.match(/^([a-z0-9-]+)[.][a-z0-9-]+[.]authress[.]io$/);
if (domainBaseUrlMatch) {
url.host = `${domainBaseUrlMatch[1]}.login.authress.io`;
sanitizedUrl = url.toString();
}

return sanitizedUrl.replace(/[/]+$/, '');
};
46 changes: 46 additions & 0 deletions tests/util.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const { describe, it, beforeEach, afterEach } = require('mocha');
const sinon = require('sinon');
const { expect } = require('chai');

const { LoginClient } = require('../src/index');

Check warning on line 5 in tests/util.test.js

View workflow job for this annotation

GitHub Actions / nodejs

'LoginClient' is assigned a value but never used
const windowManager = require('../src/windowManager');

Check warning on line 6 in tests/util.test.js

View workflow job for this annotation

GitHub Actions / nodejs

'windowManager' is assigned a value but never used
const { sanitizeUrl } = require('../src/util');

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

describe('util.js', () => {
describe('sanitizeUrl()', () => {
it('Returns http for localhost', () => {
const authressApiUrl = 'http://localhost:8080';
const result = sanitizeUrl(authressApiUrl);
expect(result).to.eql('http://localhost:8080');
});

it('Returns http for localstack', () => {
const authressApiUrl = 'http://authress.localstack.cloud:4556';
const result = sanitizeUrl(authressApiUrl);
expect(result).to.eql('http://authress.localstack.cloud:4556');
});

it('custom domain returns custom domain', () => {
const authressApiUrl = 'https://authress.company.com';
const result = sanitizeUrl(authressApiUrl);
expect(result).to.eql('https://authress.company.com');
});

it('raw authentication domain returns domain', () => {
const authressApiUrl = 'https://account.login.authress.io';
const result = sanitizeUrl(authressApiUrl);
expect(result).to.eql('https://account.login.authress.io');
});

it('Convert raw authorization region domain to global authentication. This can be necessary when an account incorrectly uses the authorization domain when really they need to use the authentication one.', () => {
const authressApiUrl = 'https://account.api-na-east.authress.io';
const result = sanitizeUrl(authressApiUrl);
expect(result).to.eql('https://account.login.authress.io');
});
});
});

0 comments on commit 8f1c2f5

Please sign in to comment.