@vardario/cognito-client 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cognito-client.d.ts +292 -0
- package/lib/cognito-client.js +409 -0
- package/lib/cognito-client.test.d.ts +1 -0
- package/lib/cognito-client.test.js +99 -0
- package/lib/error.d.ts +55 -0
- package/lib/error.js +70 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/session-storage/cookie-session-storage/cookie-session-storage.d.ts +21 -0
- package/lib/session-storage/cookie-session-storage/cookie-session-storage.js +42 -0
- package/lib/session-storage/cookie-session-storage/index.d.ts +1 -0
- package/lib/session-storage/cookie-session-storage/index.js +1 -0
- package/lib/session-storage/index.d.ts +4 -0
- package/lib/session-storage/index.js +4 -0
- package/lib/session-storage/local-storage-session-storage.d.ts +20 -0
- package/lib/session-storage/local-storage-session-storage.js +38 -0
- package/lib/session-storage/memory-session-storage.d.ts +13 -0
- package/lib/session-storage/memory-session-storage.js +18 -0
- package/lib/session-storage/session-storage.d.ts +14 -0
- package/lib/session-storage/session-storage.js +5 -0
- package/lib/session-storage/session-storage.test.d.ts +1 -0
- package/lib/session-storage/session-storage.test.js +33 -0
- package/lib/test-utils.d.ts +17 -0
- package/lib/test-utils.js +81 -0
- package/lib/utils.d.ts +20 -0
- package/lib/utils.js +111 -0
- package/package.json +8 -9
- package/dist/index.js +0 -3814
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { SessionStorage } from './session-storage/index.js';
|
|
2
|
+
export interface UserAttribute {
|
|
3
|
+
Name: string;
|
|
4
|
+
Value: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Cognito related OAuth props.
|
|
8
|
+
*/
|
|
9
|
+
export interface OAuth2Props {
|
|
10
|
+
/**
|
|
11
|
+
* Cognito domain for OAuth2 token endpoints.
|
|
12
|
+
*/
|
|
13
|
+
cognitoDomain: string;
|
|
14
|
+
/**
|
|
15
|
+
* Requested OAuth scopes
|
|
16
|
+
* @example ['email', 'openid']
|
|
17
|
+
*/
|
|
18
|
+
scopes: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Redirect URL after a successful OAuth2 authentication.
|
|
21
|
+
*/
|
|
22
|
+
redirectUrl: string;
|
|
23
|
+
/**
|
|
24
|
+
* Response type.
|
|
25
|
+
*/
|
|
26
|
+
responseType: 'code';
|
|
27
|
+
}
|
|
28
|
+
export interface CognitoClientProps {
|
|
29
|
+
/**
|
|
30
|
+
* Cognito User Pool ID
|
|
31
|
+
* @example eu-central-1_lv6wixN9f
|
|
32
|
+
*/
|
|
33
|
+
userPoolId: string;
|
|
34
|
+
/**
|
|
35
|
+
* Cognito User Pool Client ID
|
|
36
|
+
*/
|
|
37
|
+
userPoolClientId: string;
|
|
38
|
+
/**
|
|
39
|
+
* Optional Cognito endpoint. Useful for local testing.
|
|
40
|
+
* If not defined the endpoint will be determined by @see userPoolId .
|
|
41
|
+
*/
|
|
42
|
+
endpoint?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Session storage.
|
|
45
|
+
* You can either choose on of the provided build in session
|
|
46
|
+
* storages. Or provider your own one based on @see SessionStorage .
|
|
47
|
+
*
|
|
48
|
+
* <ul>
|
|
49
|
+
* <li>
|
|
50
|
+
* @see CookieSessionStorage
|
|
51
|
+
* </li>
|
|
52
|
+
* <li>
|
|
53
|
+
* @see MemorySessionStorage
|
|
54
|
+
* </li>
|
|
55
|
+
* </ul>
|
|
56
|
+
*/
|
|
57
|
+
sessionStorage: SessionStorage;
|
|
58
|
+
/**
|
|
59
|
+
* Cognito OAuth related options. See @see OAuthProps .
|
|
60
|
+
*/
|
|
61
|
+
oAuth2?: OAuth2Props;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Cognito User Session
|
|
65
|
+
*/
|
|
66
|
+
export interface Session {
|
|
67
|
+
/**
|
|
68
|
+
* JWT Access Token
|
|
69
|
+
*/
|
|
70
|
+
accessToken: string;
|
|
71
|
+
/**
|
|
72
|
+
* JWT ID Token
|
|
73
|
+
*/
|
|
74
|
+
idToken: string;
|
|
75
|
+
/**
|
|
76
|
+
* JWT refresh token
|
|
77
|
+
*/
|
|
78
|
+
refreshToken: string;
|
|
79
|
+
/**
|
|
80
|
+
* Validity of the session in time stamp as milliseconds.
|
|
81
|
+
*/
|
|
82
|
+
expiresIn: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Represents the decoded values from a JWT ID token.
|
|
86
|
+
*/
|
|
87
|
+
export interface IdToken extends Record<string, string | string[] | number | boolean> {
|
|
88
|
+
'cognito:username': string;
|
|
89
|
+
'cognito:groups': string[];
|
|
90
|
+
email_verified: boolean;
|
|
91
|
+
email: string;
|
|
92
|
+
iss: string;
|
|
93
|
+
origin_jti: string;
|
|
94
|
+
aud: string;
|
|
95
|
+
event_id: string;
|
|
96
|
+
token_use: 'id';
|
|
97
|
+
auth_time: number;
|
|
98
|
+
exp: number;
|
|
99
|
+
iat: number;
|
|
100
|
+
jti: string;
|
|
101
|
+
sub: string;
|
|
102
|
+
}
|
|
103
|
+
export interface AccessToken extends Record<string, string | string[] | number | boolean> {
|
|
104
|
+
auth_time: number;
|
|
105
|
+
client_id: string;
|
|
106
|
+
event_id: string;
|
|
107
|
+
exp: number;
|
|
108
|
+
iat: number;
|
|
109
|
+
iss: string;
|
|
110
|
+
jti: string;
|
|
111
|
+
origin_jti: string;
|
|
112
|
+
scope: string;
|
|
113
|
+
sub: string;
|
|
114
|
+
token_use: 'access';
|
|
115
|
+
username: string;
|
|
116
|
+
}
|
|
117
|
+
export interface DecodedTokens {
|
|
118
|
+
idToken: IdToken;
|
|
119
|
+
accessToken: AccessToken;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* List of used and supported Cognito API calls.
|
|
123
|
+
* @see https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_Operations.html for more details
|
|
124
|
+
*/
|
|
125
|
+
export declare enum CognitoServiceTarget {
|
|
126
|
+
InitiateAuth = "InitiateAuth",
|
|
127
|
+
RespondToAuthChallenge = "RespondToAuthChallenge",
|
|
128
|
+
SignUp = "SignUp",
|
|
129
|
+
ConfirmSignUp = "ConfirmSignUp",
|
|
130
|
+
ChangePassword = "ChangePassword",
|
|
131
|
+
RevokeToken = "RevokeToken",
|
|
132
|
+
ForgotPassword = "ForgotPassword",
|
|
133
|
+
ConfirmForgotPassword = "ConfirmForgotPassword",
|
|
134
|
+
ResendConfirmationCode = "ResendConfirmationCode",
|
|
135
|
+
UpdateUserAttributes = "UpdateUserAttributes",
|
|
136
|
+
VerifyUserAttribute = "VerifyUserAttribute"
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Cognito supported federated identities public providers.
|
|
140
|
+
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html for more information.
|
|
141
|
+
*/
|
|
142
|
+
export declare enum CognitoIdentityProvider {
|
|
143
|
+
Cognito = "COGNITO",
|
|
144
|
+
Google = "Google",
|
|
145
|
+
Facebook = "Facebook",
|
|
146
|
+
Amazon = "LoginWithAmazon",
|
|
147
|
+
Apple = "SignInWithApple"
|
|
148
|
+
}
|
|
149
|
+
export interface AuthenticationResult {
|
|
150
|
+
AccessToken: string;
|
|
151
|
+
ExpiresIn: number;
|
|
152
|
+
IdToken: string;
|
|
153
|
+
TokenType: string;
|
|
154
|
+
RefreshToken: string;
|
|
155
|
+
}
|
|
156
|
+
export interface AuthenticationResponse {
|
|
157
|
+
AuthenticationResult: AuthenticationResult;
|
|
158
|
+
}
|
|
159
|
+
export interface ChallengeResponse {
|
|
160
|
+
ChallengeName: 'PASSWORD_VERIFIER';
|
|
161
|
+
ChallengeParameters: {
|
|
162
|
+
SALT: string;
|
|
163
|
+
SECRET_BLOCK: string;
|
|
164
|
+
SRP_B: string;
|
|
165
|
+
USERNAME: string;
|
|
166
|
+
USER_ID_FOR_SRP: string;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Lightweight AWS Cogito client without any AWS SDK dependencies.
|
|
171
|
+
*/
|
|
172
|
+
export declare class CognitoClient {
|
|
173
|
+
private readonly cognitoEndpoint;
|
|
174
|
+
private readonly cognitoPoolName;
|
|
175
|
+
private readonly userPoolClientId;
|
|
176
|
+
private readonly sessionStorage;
|
|
177
|
+
private readonly oAuth?;
|
|
178
|
+
constructor({ userPoolId, userPoolClientId, endpoint, sessionStorage, oAuth2: oAuth }: CognitoClientProps);
|
|
179
|
+
static getDecodedTokenFromSession(session: Session): DecodedTokens;
|
|
180
|
+
private cognitoRequest;
|
|
181
|
+
private static authResultToSession;
|
|
182
|
+
/**
|
|
183
|
+
*
|
|
184
|
+
* Performs user authentication with username and password through ALLOW_USER_SRP_AUTH .
|
|
185
|
+
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html for more details
|
|
186
|
+
*
|
|
187
|
+
* @param username Username
|
|
188
|
+
* @param password Password
|
|
189
|
+
* @throws {AuthException}
|
|
190
|
+
*/
|
|
191
|
+
authenticateUserSrp(username: string, password: string): Promise<Session>;
|
|
192
|
+
/**
|
|
193
|
+
*
|
|
194
|
+
* Performs user authentication with username and password through USER_PASSWORD_AUTH .
|
|
195
|
+
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html for more details
|
|
196
|
+
*
|
|
197
|
+
* @param username Username
|
|
198
|
+
* @param password Password
|
|
199
|
+
* @throws {AuthException}
|
|
200
|
+
*/
|
|
201
|
+
authenticateUser(username: string, password: string): Promise<Session>;
|
|
202
|
+
private refreshSession;
|
|
203
|
+
/**
|
|
204
|
+
* Returns the current auth session.
|
|
205
|
+
* The auth session is only defined when we previously had a successful user authentication.
|
|
206
|
+
* This function will also take care to refresh the session with the refresh token in case
|
|
207
|
+
* the current session has expired.
|
|
208
|
+
*
|
|
209
|
+
* @throws {AuthException}
|
|
210
|
+
*/
|
|
211
|
+
getSession(): Promise<Session | undefined>;
|
|
212
|
+
/**
|
|
213
|
+
*
|
|
214
|
+
* @param username Username
|
|
215
|
+
* @param password Password
|
|
216
|
+
*
|
|
217
|
+
* @throws {AuthException}
|
|
218
|
+
*/
|
|
219
|
+
signUp(username: string, password: string, userAttributes?: UserAttribute[]): Promise<{
|
|
220
|
+
id: string;
|
|
221
|
+
confirmed: boolean;
|
|
222
|
+
}>;
|
|
223
|
+
/**
|
|
224
|
+
* Confirms the user registration via verification code.
|
|
225
|
+
*
|
|
226
|
+
* @param username Username
|
|
227
|
+
* @param code Confirmation code the user gets through the registration E-Mail
|
|
228
|
+
*
|
|
229
|
+
* @throws {AuthException}
|
|
230
|
+
*/
|
|
231
|
+
confirmSignUp(username: string, code: string): Promise<void>;
|
|
232
|
+
/**
|
|
233
|
+
*
|
|
234
|
+
* @param currentPassword Current user password.
|
|
235
|
+
* @param newPassword New user password.
|
|
236
|
+
*
|
|
237
|
+
* @throws {AuthException}
|
|
238
|
+
*/
|
|
239
|
+
changePassword(currentPassword: string, newPassword: string): Promise<void>;
|
|
240
|
+
updateUserAttributes(userAttributes: UserAttribute[]): Promise<void>;
|
|
241
|
+
verifyUserAttribute(attributeName: string, code: string): Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* Sign out the user and remove the current user session.
|
|
244
|
+
*
|
|
245
|
+
* @throws {AuthException}
|
|
246
|
+
*/
|
|
247
|
+
signOut(): Promise<void>;
|
|
248
|
+
/**
|
|
249
|
+
* Request forgot password.
|
|
250
|
+
* @param username Username
|
|
251
|
+
*
|
|
252
|
+
* @throws {AuthException}
|
|
253
|
+
*/
|
|
254
|
+
forgotPassword(username: string): Promise<void>;
|
|
255
|
+
/**
|
|
256
|
+
* Confirms the new password via the given code send via cognito triggered by @see forgotPassword .
|
|
257
|
+
*
|
|
258
|
+
* @param username Username
|
|
259
|
+
* @param newPassword New password
|
|
260
|
+
* @param confirmationCode Confirmation code which the user got through E-mail
|
|
261
|
+
*
|
|
262
|
+
* @throws {AuthException}
|
|
263
|
+
*/
|
|
264
|
+
confirmForgotPassword(username: string, newPassword: string, confirmationCode: string): Promise<void>;
|
|
265
|
+
/**
|
|
266
|
+
* Triggers cognito to resend the confirmation code
|
|
267
|
+
* @param username Username
|
|
268
|
+
*/
|
|
269
|
+
resendConfirmationCode(username: string): Promise<void>;
|
|
270
|
+
/**
|
|
271
|
+
* Returns a link to Cognito`s Hosted UI for OAuth2 authentication.
|
|
272
|
+
* This method works in conjunction with @see handleCodeFlow .
|
|
273
|
+
*
|
|
274
|
+
* @param identityProvider When provided, this will generate a link which
|
|
275
|
+
* tells Cognito`s Hosted UI to redirect to the given federated identity provider.
|
|
276
|
+
*
|
|
277
|
+
* @throws {Error}
|
|
278
|
+
*/
|
|
279
|
+
generateOAuthSignInUrl(identityProvider?: CognitoIdentityProvider): string;
|
|
280
|
+
/**
|
|
281
|
+
*
|
|
282
|
+
* Handles Cognito`s OAuth2 code flow after redirection from Cognito`s Hosted UI.
|
|
283
|
+
* The method call assumes that @see generateOAuthSignInUrl was used to
|
|
284
|
+
* generated the link to the Hosted UI.
|
|
285
|
+
*
|
|
286
|
+
* @param returnUrl The full return URL from redirection after a successful OAuth2
|
|
287
|
+
* authentication.
|
|
288
|
+
*
|
|
289
|
+
* @throws {Error}
|
|
290
|
+
*/
|
|
291
|
+
handleCodeFlow(returnUrl: string): Promise<Session>;
|
|
292
|
+
}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import addSeconds from 'date-fns/addSeconds';
|
|
2
|
+
import { sha256 } from 'hash.js';
|
|
3
|
+
import { BigInteger } from 'jsbn';
|
|
4
|
+
import randomBytes from 'randombytes';
|
|
5
|
+
import { AuthError, AuthException, getAuthError } from './error.js';
|
|
6
|
+
import { calculateSignature, calculateU, decodeJwt, generateA, generateSmallA, getPasswordAuthenticationKey, } from './utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* List of used and supported Cognito API calls.
|
|
9
|
+
* @see https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_Operations.html for more details
|
|
10
|
+
*/
|
|
11
|
+
export var CognitoServiceTarget;
|
|
12
|
+
(function (CognitoServiceTarget) {
|
|
13
|
+
CognitoServiceTarget["InitiateAuth"] = "InitiateAuth";
|
|
14
|
+
CognitoServiceTarget["RespondToAuthChallenge"] = "RespondToAuthChallenge";
|
|
15
|
+
CognitoServiceTarget["SignUp"] = "SignUp";
|
|
16
|
+
CognitoServiceTarget["ConfirmSignUp"] = "ConfirmSignUp";
|
|
17
|
+
CognitoServiceTarget["ChangePassword"] = "ChangePassword";
|
|
18
|
+
CognitoServiceTarget["RevokeToken"] = "RevokeToken";
|
|
19
|
+
CognitoServiceTarget["ForgotPassword"] = "ForgotPassword";
|
|
20
|
+
CognitoServiceTarget["ConfirmForgotPassword"] = "ConfirmForgotPassword";
|
|
21
|
+
CognitoServiceTarget["ResendConfirmationCode"] = "ResendConfirmationCode";
|
|
22
|
+
CognitoServiceTarget["UpdateUserAttributes"] = "UpdateUserAttributes";
|
|
23
|
+
CognitoServiceTarget["VerifyUserAttribute"] = "VerifyUserAttribute";
|
|
24
|
+
})(CognitoServiceTarget || (CognitoServiceTarget = {}));
|
|
25
|
+
/**
|
|
26
|
+
* Cognito supported federated identities public providers.
|
|
27
|
+
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html for more information.
|
|
28
|
+
*/
|
|
29
|
+
export var CognitoIdentityProvider;
|
|
30
|
+
(function (CognitoIdentityProvider) {
|
|
31
|
+
CognitoIdentityProvider["Cognito"] = "COGNITO";
|
|
32
|
+
CognitoIdentityProvider["Google"] = "Google";
|
|
33
|
+
CognitoIdentityProvider["Facebook"] = "Facebook";
|
|
34
|
+
CognitoIdentityProvider["Amazon"] = "LoginWithAmazon";
|
|
35
|
+
CognitoIdentityProvider["Apple"] = "SignInWithApple";
|
|
36
|
+
})(CognitoIdentityProvider || (CognitoIdentityProvider = {}));
|
|
37
|
+
/**
|
|
38
|
+
* Lightweight AWS Cogito client without any AWS SDK dependencies.
|
|
39
|
+
*/
|
|
40
|
+
export class CognitoClient {
|
|
41
|
+
constructor({ userPoolId, userPoolClientId, endpoint, sessionStorage, oAuth2: oAuth }) {
|
|
42
|
+
const [cognitoPoolRegion, cognitoPoolName] = userPoolId.split('_');
|
|
43
|
+
this.cognitoEndpoint = (endpoint || `https://cognito-idp.${cognitoPoolRegion}.amazonaws.com`).replace(/\/$/, '');
|
|
44
|
+
this.cognitoPoolName = cognitoPoolName;
|
|
45
|
+
this.userPoolClientId = userPoolClientId;
|
|
46
|
+
this.sessionStorage = sessionStorage;
|
|
47
|
+
this.oAuth = oAuth;
|
|
48
|
+
}
|
|
49
|
+
static getDecodedTokenFromSession(session) {
|
|
50
|
+
const { payload: idToken } = decodeJwt(session.idToken);
|
|
51
|
+
const { payload: accessToken } = decodeJwt(session.accessToken);
|
|
52
|
+
return {
|
|
53
|
+
idToken,
|
|
54
|
+
accessToken,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async cognitoRequest(body, serviceTarget) {
|
|
58
|
+
const respondToAuthChallenge = await fetch(this.cognitoEndpoint, {
|
|
59
|
+
headers: {
|
|
60
|
+
'x-amz-target': `AWSCognitoIdentityProviderService.${serviceTarget}`,
|
|
61
|
+
'content-type': 'application/x-amz-json-1.1',
|
|
62
|
+
},
|
|
63
|
+
method: 'POST',
|
|
64
|
+
body: JSON.stringify(body),
|
|
65
|
+
});
|
|
66
|
+
if (respondToAuthChallenge.status < 200 || respondToAuthChallenge.status > 299) {
|
|
67
|
+
const errorMessage = (await respondToAuthChallenge.json());
|
|
68
|
+
throw getAuthError(errorMessage);
|
|
69
|
+
}
|
|
70
|
+
return respondToAuthChallenge.json();
|
|
71
|
+
}
|
|
72
|
+
static authResultToSession(authenticationResult) {
|
|
73
|
+
return {
|
|
74
|
+
accessToken: authenticationResult.AccessToken,
|
|
75
|
+
idToken: authenticationResult.IdToken,
|
|
76
|
+
expiresIn: addSeconds(new Date(), authenticationResult.ExpiresIn).getTime(),
|
|
77
|
+
refreshToken: authenticationResult.RefreshToken,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
*
|
|
82
|
+
* Performs user authentication with username and password through ALLOW_USER_SRP_AUTH .
|
|
83
|
+
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html for more details
|
|
84
|
+
*
|
|
85
|
+
* @param username Username
|
|
86
|
+
* @param password Password
|
|
87
|
+
* @throws {AuthException}
|
|
88
|
+
*/
|
|
89
|
+
async authenticateUserSrp(username, password) {
|
|
90
|
+
const smallA = generateSmallA();
|
|
91
|
+
const A = generateA(smallA);
|
|
92
|
+
const initiateAuthPayload = {
|
|
93
|
+
AuthFlow: 'USER_SRP_AUTH',
|
|
94
|
+
ClientId: this.userPoolClientId,
|
|
95
|
+
AuthParameters: {
|
|
96
|
+
USERNAME: username,
|
|
97
|
+
SRP_A: A.toString(16),
|
|
98
|
+
},
|
|
99
|
+
ClientMetadata: {},
|
|
100
|
+
};
|
|
101
|
+
const challenge = (await this.cognitoRequest(initiateAuthPayload, CognitoServiceTarget.InitiateAuth));
|
|
102
|
+
const B = new BigInteger(challenge.ChallengeParameters.SRP_B, 16);
|
|
103
|
+
const salt = new BigInteger(challenge.ChallengeParameters.SALT, 16);
|
|
104
|
+
const U = calculateU(A, B);
|
|
105
|
+
const hkdf = getPasswordAuthenticationKey(this.cognitoPoolName, challenge.ChallengeParameters.USER_ID_FOR_SRP, password, B, U, smallA, salt);
|
|
106
|
+
const { signature, timeStamp } = calculateSignature(this.cognitoPoolName, challenge.ChallengeParameters.USER_ID_FOR_SRP, challenge.ChallengeParameters.SECRET_BLOCK, hkdf);
|
|
107
|
+
const respondToAuthChallengePayload = {
|
|
108
|
+
ChallengeName: 'PASSWORD_VERIFIER',
|
|
109
|
+
ClientId: this.userPoolClientId,
|
|
110
|
+
ChallengeResponses: {
|
|
111
|
+
PASSWORD_CLAIM_SECRET_BLOCK: challenge.ChallengeParameters.SECRET_BLOCK,
|
|
112
|
+
PASSWORD_CLAIM_SIGNATURE: signature,
|
|
113
|
+
USERNAME: challenge.ChallengeParameters.USER_ID_FOR_SRP,
|
|
114
|
+
TIMESTAMP: timeStamp,
|
|
115
|
+
},
|
|
116
|
+
ClientMetadata: {},
|
|
117
|
+
};
|
|
118
|
+
const { AuthenticationResult } = await this.cognitoRequest(respondToAuthChallengePayload, CognitoServiceTarget.RespondToAuthChallenge);
|
|
119
|
+
const session = CognitoClient.authResultToSession(AuthenticationResult);
|
|
120
|
+
this.sessionStorage.setSession(session);
|
|
121
|
+
return session;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
*
|
|
125
|
+
* Performs user authentication with username and password through USER_PASSWORD_AUTH .
|
|
126
|
+
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html for more details
|
|
127
|
+
*
|
|
128
|
+
* @param username Username
|
|
129
|
+
* @param password Password
|
|
130
|
+
* @throws {AuthException}
|
|
131
|
+
*/
|
|
132
|
+
async authenticateUser(username, password) {
|
|
133
|
+
const initiateAuthPayload = {
|
|
134
|
+
AuthFlow: 'USER_PASSWORD_AUTH',
|
|
135
|
+
ClientId: this.userPoolClientId,
|
|
136
|
+
AuthParameters: {
|
|
137
|
+
USERNAME: username,
|
|
138
|
+
PASSWORD: password,
|
|
139
|
+
},
|
|
140
|
+
ClientMetadata: {},
|
|
141
|
+
};
|
|
142
|
+
const { AuthenticationResult } = (await this.cognitoRequest(initiateAuthPayload, CognitoServiceTarget.InitiateAuth));
|
|
143
|
+
const session = CognitoClient.authResultToSession(AuthenticationResult);
|
|
144
|
+
this.sessionStorage.setSession(session);
|
|
145
|
+
return session;
|
|
146
|
+
}
|
|
147
|
+
async refreshSession(session) {
|
|
148
|
+
const refreshTokenPayload = {
|
|
149
|
+
AuthFlow: 'REFRESH_TOKEN_AUTH',
|
|
150
|
+
ClientId: this.userPoolClientId,
|
|
151
|
+
AuthParameters: {
|
|
152
|
+
REFRESH_TOKEN: session.refreshToken,
|
|
153
|
+
},
|
|
154
|
+
ClientMetadata: {},
|
|
155
|
+
};
|
|
156
|
+
const { AuthenticationResult } = (await this.cognitoRequest(refreshTokenPayload, CognitoServiceTarget.InitiateAuth));
|
|
157
|
+
const newSession = CognitoClient.authResultToSession({
|
|
158
|
+
...AuthenticationResult,
|
|
159
|
+
RefreshToken: session.refreshToken,
|
|
160
|
+
});
|
|
161
|
+
this.sessionStorage.setSession(newSession);
|
|
162
|
+
return newSession;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Returns the current auth session.
|
|
166
|
+
* The auth session is only defined when we previously had a successful user authentication.
|
|
167
|
+
* This function will also take care to refresh the session with the refresh token in case
|
|
168
|
+
* the current session has expired.
|
|
169
|
+
*
|
|
170
|
+
* @throws {AuthException}
|
|
171
|
+
*/
|
|
172
|
+
async getSession() {
|
|
173
|
+
const session = this.sessionStorage.getSession();
|
|
174
|
+
if (session) {
|
|
175
|
+
if (new Date().getTime() >= session.expiresIn) {
|
|
176
|
+
return this.refreshSession(session);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return session;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
*
|
|
183
|
+
* @param username Username
|
|
184
|
+
* @param password Password
|
|
185
|
+
*
|
|
186
|
+
* @throws {AuthException}
|
|
187
|
+
*/
|
|
188
|
+
async signUp(username, password, userAttributes) {
|
|
189
|
+
const signUpPayload = {
|
|
190
|
+
ClientId: this.userPoolClientId,
|
|
191
|
+
Username: username,
|
|
192
|
+
Password: password,
|
|
193
|
+
UserAttributes: userAttributes,
|
|
194
|
+
};
|
|
195
|
+
const data = await this.cognitoRequest(signUpPayload, CognitoServiceTarget.SignUp);
|
|
196
|
+
return {
|
|
197
|
+
id: data.UserSub,
|
|
198
|
+
confirmed: data.UserConfirmed,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Confirms the user registration via verification code.
|
|
203
|
+
*
|
|
204
|
+
* @param username Username
|
|
205
|
+
* @param code Confirmation code the user gets through the registration E-Mail
|
|
206
|
+
*
|
|
207
|
+
* @throws {AuthException}
|
|
208
|
+
*/
|
|
209
|
+
async confirmSignUp(username, code) {
|
|
210
|
+
const confirmSignUpPayload = {
|
|
211
|
+
ClientId: this.userPoolClientId,
|
|
212
|
+
ConfirmationCode: code,
|
|
213
|
+
Username: username,
|
|
214
|
+
};
|
|
215
|
+
const result = await this.cognitoRequest(confirmSignUpPayload, CognitoServiceTarget.ConfirmSignUp);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
*
|
|
219
|
+
* @param currentPassword Current user password.
|
|
220
|
+
* @param newPassword New user password.
|
|
221
|
+
*
|
|
222
|
+
* @throws {AuthException}
|
|
223
|
+
*/
|
|
224
|
+
async changePassword(currentPassword, newPassword) {
|
|
225
|
+
const session = await this.getSession();
|
|
226
|
+
if (session === undefined) {
|
|
227
|
+
throw new AuthException('User must be authenticated', AuthError.UserNotAuthenticated);
|
|
228
|
+
}
|
|
229
|
+
const changePasswordPayload = {
|
|
230
|
+
PreviousPassword: currentPassword,
|
|
231
|
+
ProposedPassword: newPassword,
|
|
232
|
+
AccessToken: session.accessToken,
|
|
233
|
+
};
|
|
234
|
+
const result = await this.cognitoRequest(changePasswordPayload, CognitoServiceTarget.ChangePassword);
|
|
235
|
+
}
|
|
236
|
+
async updateUserAttributes(userAttributes) {
|
|
237
|
+
const session = await this.getSession();
|
|
238
|
+
if (session === undefined) {
|
|
239
|
+
throw new AuthException('User must be authenticated', AuthError.UserNotAuthenticated);
|
|
240
|
+
}
|
|
241
|
+
const updateUserAttributesPayload = {
|
|
242
|
+
UserAttributes: userAttributes,
|
|
243
|
+
AccessToken: session.accessToken,
|
|
244
|
+
};
|
|
245
|
+
const result = await this.cognitoRequest(updateUserAttributesPayload, CognitoServiceTarget.UpdateUserAttributes);
|
|
246
|
+
}
|
|
247
|
+
async verifyUserAttribute(attributeName, code) {
|
|
248
|
+
const session = await this.getSession();
|
|
249
|
+
if (session === undefined) {
|
|
250
|
+
throw new AuthException('User must be authenticated', AuthError.UserNotAuthenticated);
|
|
251
|
+
}
|
|
252
|
+
const verifyUserAttributePayload = {
|
|
253
|
+
AttributeName: attributeName,
|
|
254
|
+
Code: code,
|
|
255
|
+
AccessToken: session.accessToken,
|
|
256
|
+
};
|
|
257
|
+
const result = await this.cognitoRequest(verifyUserAttributePayload, CognitoServiceTarget.VerifyUserAttribute);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Sign out the user and remove the current user session.
|
|
261
|
+
*
|
|
262
|
+
* @throws {AuthException}
|
|
263
|
+
*/
|
|
264
|
+
async signOut() {
|
|
265
|
+
const session = await this.getSession();
|
|
266
|
+
if (session === undefined) {
|
|
267
|
+
throw new AuthException('User must be authenticated', AuthError.UserNotAuthenticated);
|
|
268
|
+
}
|
|
269
|
+
const revokeTokenPayload = {
|
|
270
|
+
Token: session.refreshToken,
|
|
271
|
+
ClientId: this.userPoolClientId,
|
|
272
|
+
};
|
|
273
|
+
this.sessionStorage.setSession(undefined);
|
|
274
|
+
await this.cognitoRequest(revokeTokenPayload, CognitoServiceTarget.RevokeToken);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Request forgot password.
|
|
278
|
+
* @param username Username
|
|
279
|
+
*
|
|
280
|
+
* @throws {AuthException}
|
|
281
|
+
*/
|
|
282
|
+
async forgotPassword(username) {
|
|
283
|
+
const forgotPasswordPayload = {
|
|
284
|
+
ClientId: this.userPoolClientId,
|
|
285
|
+
Username: username,
|
|
286
|
+
};
|
|
287
|
+
await this.cognitoRequest(forgotPasswordPayload, CognitoServiceTarget.ForgotPassword);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Confirms the new password via the given code send via cognito triggered by @see forgotPassword .
|
|
291
|
+
*
|
|
292
|
+
* @param username Username
|
|
293
|
+
* @param newPassword New password
|
|
294
|
+
* @param confirmationCode Confirmation code which the user got through E-mail
|
|
295
|
+
*
|
|
296
|
+
* @throws {AuthException}
|
|
297
|
+
*/
|
|
298
|
+
async confirmForgotPassword(username, newPassword, confirmationCode) {
|
|
299
|
+
const confirmForgotPasswordPayload = {
|
|
300
|
+
ClientId: this.userPoolClientId,
|
|
301
|
+
Username: username,
|
|
302
|
+
ConfirmationCode: confirmationCode,
|
|
303
|
+
Password: newPassword,
|
|
304
|
+
};
|
|
305
|
+
await this.cognitoRequest(confirmForgotPasswordPayload, CognitoServiceTarget.ConfirmForgotPassword);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Triggers cognito to resend the confirmation code
|
|
309
|
+
* @param username Username
|
|
310
|
+
*/
|
|
311
|
+
async resendConfirmationCode(username) {
|
|
312
|
+
const resendConfirmationCodePayLoad = {
|
|
313
|
+
ClientId: this.userPoolClientId,
|
|
314
|
+
Username: username,
|
|
315
|
+
};
|
|
316
|
+
await this.cognitoRequest(resendConfirmationCodePayLoad, CognitoServiceTarget.ResendConfirmationCode);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Returns a link to Cognito`s Hosted UI for OAuth2 authentication.
|
|
320
|
+
* This method works in conjunction with @see handleCodeFlow .
|
|
321
|
+
*
|
|
322
|
+
* @param identityProvider When provided, this will generate a link which
|
|
323
|
+
* tells Cognito`s Hosted UI to redirect to the given federated identity provider.
|
|
324
|
+
*
|
|
325
|
+
* @throws {Error}
|
|
326
|
+
*/
|
|
327
|
+
generateOAuthSignInUrl(identityProvider) {
|
|
328
|
+
if (this.oAuth === undefined) {
|
|
329
|
+
throw Error('You have to define oAuth options to use generateFederatedSignUrl');
|
|
330
|
+
}
|
|
331
|
+
const state = randomBytes(32).toString('hex');
|
|
332
|
+
const pkce = randomBytes(128).toString('hex');
|
|
333
|
+
const code_challenge = Buffer.from(sha256().update(pkce).digest())
|
|
334
|
+
.toString('base64')
|
|
335
|
+
.replace(/\+/g, '-')
|
|
336
|
+
.replace(/\//g, '_')
|
|
337
|
+
.replace(/=+$/, '');
|
|
338
|
+
const queryParams = new URLSearchParams();
|
|
339
|
+
queryParams.append('redirect_uri', this.oAuth.redirectUrl);
|
|
340
|
+
queryParams.append('response_type', this.oAuth.responseType);
|
|
341
|
+
queryParams.append('client_id', this.userPoolClientId);
|
|
342
|
+
identityProvider && queryParams.append('identity_provider', identityProvider);
|
|
343
|
+
queryParams.append('scope', this.oAuth.scopes.join(' '));
|
|
344
|
+
queryParams.append('state', state);
|
|
345
|
+
queryParams.append('code_challenge', code_challenge);
|
|
346
|
+
queryParams.append('code_challenge_method', 'S256');
|
|
347
|
+
this.sessionStorage.setOauthVerificationParams({
|
|
348
|
+
state,
|
|
349
|
+
pkce,
|
|
350
|
+
});
|
|
351
|
+
return `${this.oAuth.cognitoDomain}/oauth2/authorize?${queryParams.toString()}`;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
*
|
|
355
|
+
* Handles Cognito`s OAuth2 code flow after redirection from Cognito`s Hosted UI.
|
|
356
|
+
* The method call assumes that @see generateOAuthSignInUrl was used to
|
|
357
|
+
* generated the link to the Hosted UI.
|
|
358
|
+
*
|
|
359
|
+
* @param returnUrl The full return URL from redirection after a successful OAuth2
|
|
360
|
+
* authentication.
|
|
361
|
+
*
|
|
362
|
+
* @throws {Error}
|
|
363
|
+
*/
|
|
364
|
+
async handleCodeFlow(returnUrl) {
|
|
365
|
+
if (this.oAuth === undefined) {
|
|
366
|
+
throw Error('You have to define oAuth options to use handleCodeFlow');
|
|
367
|
+
}
|
|
368
|
+
const url = new URL(returnUrl);
|
|
369
|
+
const code = url.searchParams.get('code');
|
|
370
|
+
const state = url.searchParams.get('state');
|
|
371
|
+
if (code === null || state === null) {
|
|
372
|
+
throw Error('code or state parameter is missing from return url.');
|
|
373
|
+
}
|
|
374
|
+
const oAuthVerificationParams = this.sessionStorage.getOauthVerificationParams();
|
|
375
|
+
if (oAuthVerificationParams === undefined) {
|
|
376
|
+
throw new Error('OAuth verification parameters are missing, did you forgot to call generateOAuthSignInUrl ?');
|
|
377
|
+
}
|
|
378
|
+
if (oAuthVerificationParams.state !== state) {
|
|
379
|
+
throw new Error('state parameter does not match with previous value generated by previous call of generateOAuthSignInUrl .');
|
|
380
|
+
}
|
|
381
|
+
const urlParams = new URLSearchParams();
|
|
382
|
+
urlParams.append('grant_type', 'authorization_code');
|
|
383
|
+
urlParams.append('code', code);
|
|
384
|
+
urlParams.append('client_id', this.userPoolClientId);
|
|
385
|
+
urlParams.append('redirect_uri', this.oAuth.redirectUrl);
|
|
386
|
+
urlParams.append('code_verifier', oAuthVerificationParams.pkce);
|
|
387
|
+
const tokenEndpoint = `${this.oAuth.cognitoDomain}/oauth2/token`;
|
|
388
|
+
const response = await fetch(tokenEndpoint, {
|
|
389
|
+
method: 'POST',
|
|
390
|
+
headers: {
|
|
391
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
392
|
+
},
|
|
393
|
+
body: urlParams.toString(),
|
|
394
|
+
});
|
|
395
|
+
const { access_token, refresh_token, id_token, expires_in, token_type, error } = await response.json();
|
|
396
|
+
if (error) {
|
|
397
|
+
throw new Error(error);
|
|
398
|
+
}
|
|
399
|
+
const session = CognitoClient.authResultToSession({
|
|
400
|
+
AccessToken: access_token,
|
|
401
|
+
RefreshToken: refresh_token,
|
|
402
|
+
IdToken: id_token,
|
|
403
|
+
ExpiresIn: expires_in,
|
|
404
|
+
TokenType: token_type,
|
|
405
|
+
});
|
|
406
|
+
this.sessionStorage.setSession(session);
|
|
407
|
+
return session;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "isomorphic-fetch";
|