diff --git a/src/authInfo.ts b/src/authInfo.ts index 28a40fa6a9..e96c233ec0 100644 --- a/src/authInfo.ts +++ b/src/authInfo.ts @@ -386,6 +386,35 @@ export class AuthInfo extends AsyncCreatable { return false; } + /** + * Parse a sfdx auth url, usually obtained by `authInfo.getSfdxAuthUrl`. + * + * @example + * ``` + * await AuthInfo.create(AuthInfo.parseSfdxAuthUrl(sfdxAuthUrl)); + * ``` + * @param sfdxAuthUrl + */ + public static parseSfdxAuthUrl(sfdxAuthUrl: string) { + const match = sfdxAuthUrl.match( + /^force:\/\/([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]*):([a-zA-Z0-9._-]+)@([a-zA-Z0-9._-]+)/ + ); + + if (!match) { + throw new SfdxError( + 'Invalid sfdx auth url. Must be in the format `force://::@`. The instanceUrl must not have the protocol set.', + 'INVALID_SFDX_AUTH_URL' + ); + } + const [, clientId, clientSecret, refreshToken, loginUrl] = match; + return { + clientId, + clientSecret, + refreshToken, + loginUrl: `https://${loginUrl}` + }; + } + // The regular expression that filters files stored in $HOME/.sfdx private static authFilenameFilterRegEx: RegExp = /^[^.][^@]*@[^.]+(\.[^.\s]+)+\.json$/; @@ -690,14 +719,16 @@ export class AuthInfo extends AsyncCreatable { // Update the auth fields WITH encryption this.update(authConfig); } else { - const username = ensure(this.getUsername()); - authConfig = await this.loadAuthFromConfig(username); + authConfig = await this.loadAuthFromConfig(ensure(this.getUsername())); // Update the auth fields WITHOUT encryption (already encrypted) this.update(authConfig, false); } - // Cache the fields by username (fields are encrypted) - AuthInfo.cache.set(ensure(this.getUsername()), this.fields); + const username = this.getUsername(); + if (username) { + // Cache the fields by username (fields are encrypted) + AuthInfo.cache.set(username, this.fields); + } return this; } diff --git a/test/unit/authInfoTest.ts b/test/unit/authInfoTest.ts index c7f7406cde..c4e35639c1 100644 --- a/test/unit/authInfoTest.ts +++ b/test/unit/authInfoTest.ts @@ -611,6 +611,31 @@ describe('AuthInfo', () => { }); }); + it('should not cache when no username is supplied', async () => { + const cacheSize = AuthInfo['cache'].size; + + const authResponse = { + access_token: testMetadata.accessToken, + instance_url: testMetadata.instanceUrl, + id: '00DAuthInfoTest_orgId/005AuthInfoTest_userId', + refresh_token: testMetadata.refreshToken + }; + + // Stub the http requests (OAuth2.requestToken() and the request for the username) + _postParmsStub.returns(Promise.resolve(authResponse)); + + // Create the AuthInfo instance with no username + await AuthInfo.create({ + oauth2Options: { + refreshToken: testMetadata.refreshToken, + loginUrl: testMetadata.loginUrl, + clientId: testMetadata.clientId + } + }); + + expect(AuthInfo['cache'].size).to.equal(cacheSize); + }); + it('should return a JWT AuthInfo instance when passed a username from an auth file', async () => { const username = 'authInfoTest_username_jwt-NOT-CACHED'; @@ -1547,4 +1572,45 @@ describe('AuthInfo', () => { } }); }); + + describe('parseSfdxAuthUrl()', () => { + it('should parse the correct url with no client secret', () => { + const options = AuthInfo.parseSfdxAuthUrl( + 'force://PlatformCLI::5Aep861_OKMvio5gy8xCNsXxybPdupY9fVEZyeVOvb4kpOZx5Z1QLB7k7n5flEqEWKcwUQEX1I.O5DCFwjlYUB.@test.my.salesforce.com' + ); + + expect(options.refreshToken).to.equal( + '5Aep861_OKMvio5gy8xCNsXxybPdupY9fVEZyeVOvb4kpOZx5Z1QLB7k7n5flEqEWKcwUQEX1I.O5DCFwjlYUB.' + ); + expect(options.clientId).to.equal('PlatformCLI'); + expect(options.clientSecret).to.equal(''); + expect(options.loginUrl).to.equal('https://test.my.salesforce.com'); + }); + + it('should parse the correct url with client secret', () => { + const options = AuthInfo.parseSfdxAuthUrl( + 'force://3MVG9SemV5D80oBfPBCgboxuJ9cOMLWNM1DDOZ8zgvJGsz13H3J66coUBCFF3N0zEgLYijlkqeWk4ot_Q2.4o:438437816653243682:5Aep861_OKMvio5gy8xCNsXxybPdupY9fVEZyeVOvb4kpOZx5Z1QLB7k7n5flEqEWKcwUQEX1I.O5DCFwjlYUB.@test.my.salesforce.com' + ); + + expect(options.refreshToken).to.equal( + '5Aep861_OKMvio5gy8xCNsXxybPdupY9fVEZyeVOvb4kpOZx5Z1QLB7k7n5flEqEWKcwUQEX1I.O5DCFwjlYUB.' + ); + expect(options.clientId).to.equal( + '3MVG9SemV5D80oBfPBCgboxuJ9cOMLWNM1DDOZ8zgvJGsz13H3J66coUBCFF3N0zEgLYijlkqeWk4ot_Q2.4o' + ); + expect(options.clientSecret).to.equal('438437816653243682'); + expect(options.loginUrl).to.equal('https://test.my.salesforce.com'); + }); + + it('should throw with incorrect url', () => { + try { + AuthInfo.parseSfdxAuthUrl( + 'PlatformCLI::5Aep861_OKMvio5gy8xCNsXxybPdupY9fVEZyeVOvb4kpOZx5Z1QLB7k7n5flEqEWKcwUQEX1I.O5DCFwjlYUB.@test.my.salesforce.com' + ); + assert.fail(); + } catch (e) { + expect(e.name).to.equal('INVALID_SFDX_AUTH_URL'); + } + }); + }); });