Skip to content

Commit 8fc61db

Browse files
authored
chore: Reduce size of auth modules (#483)
1 parent 3875708 commit 8fc61db

13 files changed

+151
-229
lines changed

src/CommandQueue.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { CredentialProvider, Credentials } from '@aws-sdk/types';
1+
import {
2+
AwsCredentialIdentity,
3+
AwsCredentialIdentityProvider
4+
} from '@aws-sdk/types';
25
import { Plugin } from 'plugins/Plugin';
36
import { INSTALL_SCRIPT } from './utils/constants';
47
import { PartialConfig, Orchestration } from './orchestration/Orchestration';
@@ -52,7 +55,7 @@ export class CommandQueue {
5255

5356
private commandHandlerMap: CommandFunctions = {
5457
setAwsCredentials: (
55-
payload: Credentials | CredentialProvider
58+
payload: AwsCredentialIdentity | AwsCredentialIdentityProvider
5659
): void => {
5760
this.orchestration.setAwsCredentials(payload);
5861
},

src/dispatch/Authentication.ts

Lines changed: 30 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
11
import { CognitoIdentityClient } from './CognitoIdentityClient';
22
import { Config } from '../orchestration/Orchestration';
3-
import { Credentials } from '@aws-sdk/types';
3+
import { AwsCredentialIdentity } from '@aws-sdk/types';
44
import { FetchHttpHandler } from '@aws-sdk/fetch-http-handler';
5-
import { StsClient } from './StsClient';
65
import { CRED_KEY, CRED_RENEW_MS } from '../utils/constants';
76

8-
export class Authentication {
9-
private cognitoIdentityClient: CognitoIdentityClient;
10-
private stsClient: StsClient;
11-
private config: Config;
12-
private credentials: Credentials | undefined;
7+
export abstract class Authentication {
8+
protected cognitoIdentityClient: CognitoIdentityClient;
9+
protected config: Config;
10+
protected credentials: AwsCredentialIdentity | undefined;
1311

1412
constructor(config: Config) {
1513
const region: string = config.identityPoolId!.split(':')[0];
1614
this.config = config;
17-
this.stsClient = new StsClient({
18-
fetchRequestHandler: new FetchHttpHandler(),
19-
region
20-
});
2115
this.cognitoIdentityClient = new CognitoIdentityClient({
2216
fetchRequestHandler: new FetchHttpHandler(),
2317
region
@@ -33,7 +27,7 @@ export class Authentication {
3327
* re-authenticate every time the client loads, which (1) improves the performance of the RUM web client and (2)
3428
* reduces the load on AWS services Cognito and STS.
3529
*
36-
* While storing credentials in localStorage puts the cookie at greater risk of being leaked through an
30+
* While storing credentials in localStorage puts the credential at greater risk of being leaked through an
3731
* XSS attack, there is no impact if the credentials were to be leaked. This is because (1) the identity pool ID
3832
* and role ARN are public and (2) the credentials are for an anonymous (guest) user.
3933
*
@@ -51,10 +45,10 @@ export class Authentication {
5145
* Taken together, (1) and (2) mean that if these temporary credentials were to be leaked, the leaked credentials
5246
* would not allow a bad actor to gain access to anything which they did not already have public access to.
5347
*
54-
* Implements CredentialsProvider = Provider<Credentials>
48+
* Implements AwsCredentialIdentityProvider = Provider<AwsCredentialIdentity>
5549
*/
5650
public ChainAnonymousCredentialsProvider =
57-
async (): Promise<Credentials> => {
51+
async (): Promise<AwsCredentialIdentity> => {
5852
return this.AnonymousCredentialsProvider()
5953
.catch(this.AnonymousStorageCredentialsProvider)
6054
.catch(this.AnonymousCognitoCredentialsProvider);
@@ -63,27 +57,28 @@ export class Authentication {
6357
/**
6458
* Provides credentials for an anonymous (guest) user. These credentials are read from a member variable.
6559
*
66-
* Implements CredentialsProvider = Provider<Credentials>
60+
* Implements AwsCredentialIdentityProvider = Provider<AwsCredentialIdentity>
6761
*/
68-
private AnonymousCredentialsProvider = async (): Promise<Credentials> => {
69-
return new Promise<Credentials>((resolve, reject) => {
70-
if (this.renewCredentials()) {
71-
// The credentials have expired.
72-
return reject();
73-
}
74-
resolve(this.credentials!);
75-
});
76-
};
62+
private AnonymousCredentialsProvider =
63+
async (): Promise<AwsCredentialIdentity> => {
64+
return new Promise<AwsCredentialIdentity>((resolve, reject) => {
65+
if (this.renewCredentials()) {
66+
// The credentials have expired.
67+
return reject();
68+
}
69+
resolve(this.credentials!);
70+
});
71+
};
7772

7873
/**
7974
* Provides credentials for an anonymous (guest) user. These credentials are read from localStorage.
8075
*
81-
* Implements CredentialsProvider = Provider<Credentials>
76+
* Implements AwsCredentialIdentityProvider = Provider<AwsCredentialIdentity>
8277
*/
8378
private AnonymousStorageCredentialsProvider =
84-
async (): Promise<Credentials> => {
85-
return new Promise<Credentials>((resolve, reject) => {
86-
let credentials: Credentials;
79+
async (): Promise<AwsCredentialIdentity> => {
80+
return new Promise<AwsCredentialIdentity>((resolve, reject) => {
81+
let credentials: AwsCredentialIdentity;
8782
try {
8883
credentials = JSON.parse(localStorage.getItem(CRED_KEY)!);
8984
} catch (e) {
@@ -106,44 +101,18 @@ export class Authentication {
106101
};
107102

108103
/**
109-
* Provides credentials for an anonymous (guest) user. These credentials are retrieved from Cognito's basic
110-
* (classic) authflow.
104+
* Provides credentials for an anonymous (guest) user. These credentials are retrieved from Cognito's enhanced
105+
* authflow.
111106
*
112107
* See https://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html
113108
*
114-
* Implements CredentialsProvider = Provider<Credentials>
109+
* Implements AwsCredentialIdentityProvider = Provider<AwsCredentialIdentity>
115110
*/
116-
private AnonymousCognitoCredentialsProvider =
117-
async (): Promise<Credentials> => {
118-
return this.cognitoIdentityClient
119-
.getId({
120-
IdentityPoolId: this.config.identityPoolId as string
121-
})
122-
.then((getIdResponse) =>
123-
this.cognitoIdentityClient.getOpenIdToken(getIdResponse)
124-
)
125-
.then((getOpenIdTokenResponse) =>
126-
this.stsClient.assumeRoleWithWebIdentity({
127-
RoleArn: this.config.guestRoleArn as string,
128-
RoleSessionName: 'cwr',
129-
WebIdentityToken: getOpenIdTokenResponse.Token
130-
})
131-
)
132-
.then((credentials: Credentials) => {
133-
this.credentials = credentials;
134-
try {
135-
localStorage.setItem(
136-
CRED_KEY,
137-
JSON.stringify(credentials)
138-
);
139-
} catch (e) {
140-
// Ignore
141-
}
142-
143-
return credentials;
144-
});
145-
};
111+
protected abstract AnonymousCognitoCredentialsProvider: () => Promise<AwsCredentialIdentity>;
146112

113+
/**
114+
* Returns {@code true} when the credentials need to be renewed.
115+
*/
147116
private renewCredentials(): boolean {
148117
if (!this.credentials || !this.credentials.expiration) {
149118
return true;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Config } from '../orchestration/Orchestration';
2+
import { AwsCredentialIdentity } from '@aws-sdk/types';
3+
import { FetchHttpHandler } from '@aws-sdk/fetch-http-handler';
4+
import { StsClient } from './StsClient';
5+
import { CRED_KEY } from '../utils/constants';
6+
import { Authentication } from './Authentication';
7+
8+
export class BasicAuthentication extends Authentication {
9+
private stsClient: StsClient;
10+
11+
constructor(config: Config) {
12+
super(config);
13+
const region: string = config.identityPoolId!.split(':')[0];
14+
this.stsClient = new StsClient({
15+
fetchRequestHandler: new FetchHttpHandler(),
16+
region
17+
});
18+
}
19+
20+
/**
21+
* Provides credentials for an anonymous (guest) user. These credentials are retrieved from Cognito's basic
22+
* (classic) authflow.
23+
*
24+
* See https://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html
25+
*
26+
* Implements AwsCredentialIdentityProvider = Provider<AwsCredentialIdentity>
27+
*/
28+
protected AnonymousCognitoCredentialsProvider =
29+
async (): Promise<AwsCredentialIdentity> => {
30+
return this.cognitoIdentityClient
31+
.getId({
32+
IdentityPoolId: this.config.identityPoolId as string
33+
})
34+
.then((getIdResponse) =>
35+
this.cognitoIdentityClient.getOpenIdToken(getIdResponse)
36+
)
37+
.then((getOpenIdTokenResponse) =>
38+
this.stsClient.assumeRoleWithWebIdentity({
39+
RoleArn: this.config.guestRoleArn as string,
40+
RoleSessionName: 'cwr',
41+
WebIdentityToken: getOpenIdTokenResponse.Token
42+
})
43+
)
44+
.then((credentials: AwsCredentialIdentity) => {
45+
this.credentials = credentials;
46+
try {
47+
localStorage.setItem(
48+
CRED_KEY,
49+
JSON.stringify(credentials)
50+
);
51+
} catch (e) {
52+
// Ignore
53+
}
54+
return credentials;
55+
});
56+
};
57+
}

src/dispatch/CognitoIdentityClient.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable no-underscore-dangle */
22
import { HttpHandler, HttpRequest } from '@aws-sdk/protocol-http';
3-
import { Credentials } from '@aws-sdk/types';
3+
import { AwsCredentialIdentity } from '@aws-sdk/types';
44
import { responseToJson } from './utils';
55
import { IDENTITY_KEY } from '../utils/constants';
66

@@ -14,19 +14,6 @@ const GET_TOKEN_TARGET = 'AWSCognitoIdentityService.GetOpenIdToken';
1414
const GET_CREDENTIALS_TARGET =
1515
'AWSCognitoIdentityService.GetCredentialsForIdentity';
1616

17-
interface CognitoProviderParameters {
18-
/**
19-
* The unique identifier for the identity pool from which an identity should
20-
* be retrieved or generated.
21-
*/
22-
identityPoolId: string;
23-
/**
24-
* The SDK client with which the credential provider will contact the Amazon
25-
* Cognito service.
26-
*/
27-
client: CognitoIdentityClient;
28-
}
29-
3017
interface CognitoCredentials {
3118
AccessKeyId: string;
3219
Expiration: number;
@@ -123,7 +110,7 @@ export class CognitoIdentityClient {
123110

124111
public getCredentialsForIdentity = async (
125112
identityId: string
126-
): Promise<Credentials> => {
113+
): Promise<AwsCredentialIdentity> => {
127114
try {
128115
const requestPayload = JSON.stringify({ IdentityId: identityId });
129116
const credentialRequest = this.getHttpRequest(

src/dispatch/Dispatch.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { CredentialProvider, Credentials, HttpResponse } from '@aws-sdk/types';
1+
import {
2+
AwsCredentialIdentityProvider,
3+
AwsCredentialIdentity,
4+
HttpResponse
5+
} from '@aws-sdk/types';
26
import { EventCache } from '../event-cache/EventCache';
37
import { DataPlaneClient } from './DataPlaneClient';
48
import { BeaconHttpHandler } from './BeaconHttpHandler';
@@ -22,7 +26,7 @@ const NO_CRED_MSG = 'CWR: Cannot dispatch; no AWS credentials.';
2226
export type ClientBuilder = (
2327
endpoint: URL,
2428
region: string,
25-
credentials: CredentialProvider | Credentials | undefined
29+
credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider
2630
) => DataPlaneClient;
2731

2832
export class Dispatch {
@@ -85,7 +89,9 @@ export class Dispatch {
8589
* @param credentials A set of AWS credentials from the application's authflow.
8690
*/
8791
public setAwsCredentials(
88-
credentialProvider: Credentials | CredentialProvider
92+
credentialProvider:
93+
| AwsCredentialIdentity
94+
| AwsCredentialIdentityProvider
8995
): void {
9096
this.rum = this.buildClient(
9197
this.endpoint,
@@ -95,7 +101,7 @@ export class Dispatch {
95101
if (typeof credentialProvider === 'function') {
96102
// In case a beacon in the first dispatch, we must pre-fetch credentials into a cookie so there is no delay
97103
// to fetch credentials while the page is closing.
98-
(credentialProvider as () => Promise<Credentials>)();
104+
(credentialProvider as () => Promise<AwsCredentialIdentity>)();
99105
}
100106
}
101107

0 commit comments

Comments
 (0)