@vardario/cognito-client 0.1.5 → 0.1.7
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 +9 -9
- package/lib/cognito-client.js +54 -53
- package/lib/cognito-client.test.js +5 -5
- package/lib/utils.d.ts +3 -2
- package/lib/utils.js +59 -46
- package/package.json +2 -4
package/lib/cognito-client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SessionStorage } from
|
|
1
|
+
import { SessionStorage } from "./session-storage/index.js";
|
|
2
2
|
export interface UserAttribute {
|
|
3
3
|
Name: string;
|
|
4
4
|
Value: string;
|
|
@@ -23,7 +23,7 @@ export interface OAuth2Props {
|
|
|
23
23
|
/**
|
|
24
24
|
* Response type.
|
|
25
25
|
*/
|
|
26
|
-
responseType:
|
|
26
|
+
responseType: "code";
|
|
27
27
|
}
|
|
28
28
|
export interface CognitoClientProps {
|
|
29
29
|
/**
|
|
@@ -85,15 +85,15 @@ export interface Session {
|
|
|
85
85
|
* Represents the decoded values from a JWT ID token.
|
|
86
86
|
*/
|
|
87
87
|
export interface IdToken extends Record<string, string | string[] | number | boolean> {
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
"cognito:username": string;
|
|
89
|
+
"cognito:groups": string[];
|
|
90
90
|
email_verified: boolean;
|
|
91
91
|
email: string;
|
|
92
92
|
iss: string;
|
|
93
93
|
origin_jti: string;
|
|
94
94
|
aud: string;
|
|
95
95
|
event_id: string;
|
|
96
|
-
token_use:
|
|
96
|
+
token_use: "id";
|
|
97
97
|
auth_time: number;
|
|
98
98
|
exp: number;
|
|
99
99
|
iat: number;
|
|
@@ -111,7 +111,7 @@ export interface AccessToken extends Record<string, string | string[] | number |
|
|
|
111
111
|
origin_jti: string;
|
|
112
112
|
scope: string;
|
|
113
113
|
sub: string;
|
|
114
|
-
token_use:
|
|
114
|
+
token_use: "access";
|
|
115
115
|
username: string;
|
|
116
116
|
}
|
|
117
117
|
export interface DecodedTokens {
|
|
@@ -157,7 +157,7 @@ export interface AuthenticationResponse {
|
|
|
157
157
|
AuthenticationResult: AuthenticationResult;
|
|
158
158
|
}
|
|
159
159
|
export interface ChallengeResponse {
|
|
160
|
-
ChallengeName:
|
|
160
|
+
ChallengeName: "PASSWORD_VERIFIER";
|
|
161
161
|
ChallengeParameters: {
|
|
162
162
|
SALT: string;
|
|
163
163
|
SECRET_BLOCK: string;
|
|
@@ -175,7 +175,7 @@ export declare class CognitoClient {
|
|
|
175
175
|
private readonly userPoolClientId;
|
|
176
176
|
private readonly sessionStorage;
|
|
177
177
|
private readonly oAuth?;
|
|
178
|
-
constructor({ userPoolId, userPoolClientId, endpoint, sessionStorage, oAuth2: oAuth }: CognitoClientProps);
|
|
178
|
+
constructor({ userPoolId, userPoolClientId, endpoint, sessionStorage, oAuth2: oAuth, }: CognitoClientProps);
|
|
179
179
|
static getDecodedTokenFromSession(session: Session): DecodedTokens;
|
|
180
180
|
private cognitoRequest;
|
|
181
181
|
private static authResultToSession;
|
|
@@ -276,7 +276,7 @@ export declare class CognitoClient {
|
|
|
276
276
|
*
|
|
277
277
|
* @throws {Error}
|
|
278
278
|
*/
|
|
279
|
-
generateOAuthSignInUrl(identityProvider?: CognitoIdentityProvider): string
|
|
279
|
+
generateOAuthSignInUrl(identityProvider?: CognitoIdentityProvider): Promise<string>;
|
|
280
280
|
/**
|
|
281
281
|
*
|
|
282
282
|
* Handles Cognito`s OAuth2 code flow after redirection from Cognito`s Hosted UI.
|
package/lib/cognito-client.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import addSeconds from
|
|
2
|
-
import
|
|
3
|
-
import { BigInteger } from
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { calculateSignature, calculateU, decodeJwt, generateA, generateSmallA, getPasswordAuthenticationKey, } from './utils.js';
|
|
1
|
+
import { addSeconds } from "date-fns";
|
|
2
|
+
import hashJs from "hash.js";
|
|
3
|
+
import { BigInteger } from "jsbn";
|
|
4
|
+
import { AuthError, AuthException, getAuthError, } from "./error.js";
|
|
5
|
+
import { calculateSignature, calculateU, decodeJwt, generateA, generateSmallA, getPasswordAuthenticationKey, randomBytes, } from "./utils.js";
|
|
7
6
|
/**
|
|
8
7
|
* List of used and supported Cognito API calls.
|
|
9
8
|
* @see https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_Operations.html for more details
|
|
@@ -38,9 +37,9 @@ export var CognitoIdentityProvider;
|
|
|
38
37
|
* Lightweight AWS Cogito client without any AWS SDK dependencies.
|
|
39
38
|
*/
|
|
40
39
|
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(/\/$/,
|
|
40
|
+
constructor({ userPoolId, userPoolClientId, endpoint, sessionStorage, oAuth2: oAuth, }) {
|
|
41
|
+
const [cognitoPoolRegion, cognitoPoolName] = userPoolId.split("_");
|
|
42
|
+
this.cognitoEndpoint = (endpoint || `https://cognito-idp.${cognitoPoolRegion}.amazonaws.com`).replace(/\/$/, "");
|
|
44
43
|
this.cognitoPoolName = cognitoPoolName;
|
|
45
44
|
this.userPoolClientId = userPoolClientId;
|
|
46
45
|
this.sessionStorage = sessionStorage;
|
|
@@ -57,13 +56,14 @@ export class CognitoClient {
|
|
|
57
56
|
async cognitoRequest(body, serviceTarget) {
|
|
58
57
|
const respondToAuthChallenge = await fetch(this.cognitoEndpoint, {
|
|
59
58
|
headers: {
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
"x-amz-target": `AWSCognitoIdentityProviderService.${serviceTarget}`,
|
|
60
|
+
"content-type": "application/x-amz-json-1.1",
|
|
62
61
|
},
|
|
63
|
-
method:
|
|
62
|
+
method: "POST",
|
|
64
63
|
body: JSON.stringify(body),
|
|
65
64
|
});
|
|
66
|
-
if (respondToAuthChallenge.status < 200 ||
|
|
65
|
+
if (respondToAuthChallenge.status < 200 ||
|
|
66
|
+
respondToAuthChallenge.status > 299) {
|
|
67
67
|
const errorMessage = (await respondToAuthChallenge.json());
|
|
68
68
|
throw getAuthError(errorMessage);
|
|
69
69
|
}
|
|
@@ -87,10 +87,10 @@ export class CognitoClient {
|
|
|
87
87
|
* @throws {AuthException}
|
|
88
88
|
*/
|
|
89
89
|
async authenticateUserSrp(username, password) {
|
|
90
|
-
const smallA = generateSmallA();
|
|
90
|
+
const smallA = await generateSmallA();
|
|
91
91
|
const A = generateA(smallA);
|
|
92
92
|
const initiateAuthPayload = {
|
|
93
|
-
AuthFlow:
|
|
93
|
+
AuthFlow: "USER_SRP_AUTH",
|
|
94
94
|
ClientId: this.userPoolClientId,
|
|
95
95
|
AuthParameters: {
|
|
96
96
|
USERNAME: username,
|
|
@@ -105,7 +105,7 @@ export class CognitoClient {
|
|
|
105
105
|
const hkdf = getPasswordAuthenticationKey(this.cognitoPoolName, challenge.ChallengeParameters.USER_ID_FOR_SRP, password, B, U, smallA, salt);
|
|
106
106
|
const { signature, timeStamp } = calculateSignature(this.cognitoPoolName, challenge.ChallengeParameters.USER_ID_FOR_SRP, challenge.ChallengeParameters.SECRET_BLOCK, hkdf);
|
|
107
107
|
const respondToAuthChallengePayload = {
|
|
108
|
-
ChallengeName:
|
|
108
|
+
ChallengeName: "PASSWORD_VERIFIER",
|
|
109
109
|
ClientId: this.userPoolClientId,
|
|
110
110
|
ChallengeResponses: {
|
|
111
111
|
PASSWORD_CLAIM_SECRET_BLOCK: challenge.ChallengeParameters.SECRET_BLOCK,
|
|
@@ -131,7 +131,7 @@ export class CognitoClient {
|
|
|
131
131
|
*/
|
|
132
132
|
async authenticateUser(username, password) {
|
|
133
133
|
const initiateAuthPayload = {
|
|
134
|
-
AuthFlow:
|
|
134
|
+
AuthFlow: "USER_PASSWORD_AUTH",
|
|
135
135
|
ClientId: this.userPoolClientId,
|
|
136
136
|
AuthParameters: {
|
|
137
137
|
USERNAME: username,
|
|
@@ -146,7 +146,7 @@ export class CognitoClient {
|
|
|
146
146
|
}
|
|
147
147
|
async refreshSession(session) {
|
|
148
148
|
const refreshTokenPayload = {
|
|
149
|
-
AuthFlow:
|
|
149
|
+
AuthFlow: "REFRESH_TOKEN_AUTH",
|
|
150
150
|
ClientId: this.userPoolClientId,
|
|
151
151
|
AuthParameters: {
|
|
152
152
|
REFRESH_TOKEN: session.refreshToken,
|
|
@@ -224,7 +224,7 @@ export class CognitoClient {
|
|
|
224
224
|
async changePassword(currentPassword, newPassword) {
|
|
225
225
|
const session = await this.getSession();
|
|
226
226
|
if (session === undefined) {
|
|
227
|
-
throw new AuthException(
|
|
227
|
+
throw new AuthException("User must be authenticated", AuthError.UserNotAuthenticated);
|
|
228
228
|
}
|
|
229
229
|
const changePasswordPayload = {
|
|
230
230
|
PreviousPassword: currentPassword,
|
|
@@ -236,7 +236,7 @@ export class CognitoClient {
|
|
|
236
236
|
async updateUserAttributes(userAttributes) {
|
|
237
237
|
const session = await this.getSession();
|
|
238
238
|
if (session === undefined) {
|
|
239
|
-
throw new AuthException(
|
|
239
|
+
throw new AuthException("User must be authenticated", AuthError.UserNotAuthenticated);
|
|
240
240
|
}
|
|
241
241
|
const updateUserAttributesPayload = {
|
|
242
242
|
UserAttributes: userAttributes,
|
|
@@ -247,7 +247,7 @@ export class CognitoClient {
|
|
|
247
247
|
async verifyUserAttribute(attributeName, code) {
|
|
248
248
|
const session = await this.getSession();
|
|
249
249
|
if (session === undefined) {
|
|
250
|
-
throw new AuthException(
|
|
250
|
+
throw new AuthException("User must be authenticated", AuthError.UserNotAuthenticated);
|
|
251
251
|
}
|
|
252
252
|
const verifyUserAttributePayload = {
|
|
253
253
|
AttributeName: attributeName,
|
|
@@ -264,7 +264,7 @@ export class CognitoClient {
|
|
|
264
264
|
async signOut() {
|
|
265
265
|
const session = await this.getSession();
|
|
266
266
|
if (session === undefined) {
|
|
267
|
-
throw new AuthException(
|
|
267
|
+
throw new AuthException("User must be authenticated", AuthError.UserNotAuthenticated);
|
|
268
268
|
}
|
|
269
269
|
const revokeTokenPayload = {
|
|
270
270
|
Token: session.refreshToken,
|
|
@@ -324,26 +324,27 @@ export class CognitoClient {
|
|
|
324
324
|
*
|
|
325
325
|
* @throws {Error}
|
|
326
326
|
*/
|
|
327
|
-
generateOAuthSignInUrl(identityProvider) {
|
|
327
|
+
async generateOAuthSignInUrl(identityProvider) {
|
|
328
328
|
if (this.oAuth === undefined) {
|
|
329
|
-
throw Error(
|
|
329
|
+
throw Error("You have to define oAuth options to use generateFederatedSignUrl");
|
|
330
330
|
}
|
|
331
|
-
const state = randomBytes(32).toString(
|
|
332
|
-
const pkce = randomBytes(128).toString(
|
|
333
|
-
const code_challenge = Buffer.from(sha256().update(pkce).digest())
|
|
334
|
-
.toString(
|
|
335
|
-
.replace(/\+/g,
|
|
336
|
-
.replace(/\//g,
|
|
337
|
-
.replace(/=+$/,
|
|
331
|
+
const state = (await randomBytes(32)).toString("hex");
|
|
332
|
+
const pkce = (await randomBytes(128)).toString("hex");
|
|
333
|
+
const code_challenge = Buffer.from(hashJs.sha256().update(pkce).digest())
|
|
334
|
+
.toString("base64")
|
|
335
|
+
.replace(/\+/g, "-")
|
|
336
|
+
.replace(/\//g, "_")
|
|
337
|
+
.replace(/=+$/, "");
|
|
338
338
|
const queryParams = new URLSearchParams();
|
|
339
|
-
queryParams.append(
|
|
340
|
-
queryParams.append(
|
|
341
|
-
queryParams.append(
|
|
342
|
-
identityProvider &&
|
|
343
|
-
|
|
344
|
-
queryParams.append(
|
|
345
|
-
queryParams.append(
|
|
346
|
-
queryParams.append(
|
|
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 &&
|
|
343
|
+
queryParams.append("identity_provider", identityProvider);
|
|
344
|
+
queryParams.append("scope", this.oAuth.scopes.join(" "));
|
|
345
|
+
queryParams.append("state", state);
|
|
346
|
+
queryParams.append("code_challenge", code_challenge);
|
|
347
|
+
queryParams.append("code_challenge_method", "S256");
|
|
347
348
|
this.sessionStorage.setOauthVerificationParams({
|
|
348
349
|
state,
|
|
349
350
|
pkce,
|
|
@@ -363,36 +364,36 @@ export class CognitoClient {
|
|
|
363
364
|
*/
|
|
364
365
|
async handleCodeFlow(returnUrl) {
|
|
365
366
|
if (this.oAuth === undefined) {
|
|
366
|
-
throw Error(
|
|
367
|
+
throw Error("You have to define oAuth options to use handleCodeFlow");
|
|
367
368
|
}
|
|
368
369
|
const url = new URL(returnUrl);
|
|
369
|
-
const code = url.searchParams.get(
|
|
370
|
-
const state = url.searchParams.get(
|
|
370
|
+
const code = url.searchParams.get("code");
|
|
371
|
+
const state = url.searchParams.get("state");
|
|
371
372
|
if (code === null || state === null) {
|
|
372
|
-
throw Error(
|
|
373
|
+
throw Error("code or state parameter is missing from return url.");
|
|
373
374
|
}
|
|
374
375
|
const oAuthVerificationParams = this.sessionStorage.getOauthVerificationParams();
|
|
375
376
|
if (oAuthVerificationParams === undefined) {
|
|
376
|
-
throw new Error(
|
|
377
|
+
throw new Error("OAuth verification parameters are missing, did you forgot to call generateOAuthSignInUrl ?");
|
|
377
378
|
}
|
|
378
379
|
if (oAuthVerificationParams.state !== state) {
|
|
379
|
-
throw new Error(
|
|
380
|
+
throw new Error("state parameter does not match with previous value generated by previous call of generateOAuthSignInUrl .");
|
|
380
381
|
}
|
|
381
382
|
const urlParams = new URLSearchParams();
|
|
382
|
-
urlParams.append(
|
|
383
|
-
urlParams.append(
|
|
384
|
-
urlParams.append(
|
|
385
|
-
urlParams.append(
|
|
386
|
-
urlParams.append(
|
|
383
|
+
urlParams.append("grant_type", "authorization_code");
|
|
384
|
+
urlParams.append("code", code);
|
|
385
|
+
urlParams.append("client_id", this.userPoolClientId);
|
|
386
|
+
urlParams.append("redirect_uri", this.oAuth.redirectUrl);
|
|
387
|
+
urlParams.append("code_verifier", oAuthVerificationParams.pkce);
|
|
387
388
|
const tokenEndpoint = `${this.oAuth.cognitoDomain}/oauth2/token`;
|
|
388
389
|
const response = await fetch(tokenEndpoint, {
|
|
389
|
-
method:
|
|
390
|
+
method: "POST",
|
|
390
391
|
headers: {
|
|
391
|
-
|
|
392
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
392
393
|
},
|
|
393
394
|
body: urlParams.toString(),
|
|
394
395
|
});
|
|
395
|
-
const { access_token, refresh_token, id_token, expires_in, token_type, error } = await response.json();
|
|
396
|
+
const { access_token, refresh_token, id_token, expires_in, token_type, error, } = await response.json();
|
|
396
397
|
if (error) {
|
|
397
398
|
throw new Error(error);
|
|
398
399
|
}
|
|
@@ -76,9 +76,9 @@ describe("Cognito Client", () => {
|
|
|
76
76
|
expect(cognitoClient.authenticateUser(user.email, user.password)).rejects.toThrow();
|
|
77
77
|
await cognitoClient.authenticateUser(user.email, "newPassword");
|
|
78
78
|
});
|
|
79
|
-
test("generateOAuthSignInUrl", () => {
|
|
80
|
-
const _test = (cb, identityProvider) => {
|
|
81
|
-
const hostedUIUrl = cognitoClient.generateOAuthSignInUrl(identityProvider);
|
|
79
|
+
test("generateOAuthSignInUrl", async () => {
|
|
80
|
+
const _test = async (cb, identityProvider) => {
|
|
81
|
+
const hostedUIUrl = await cognitoClient.generateOAuthSignInUrl(identityProvider);
|
|
82
82
|
const { searchParams } = new URL(hostedUIUrl);
|
|
83
83
|
expect(searchParams.get("redirect_uri")).toBe(oAuth2.redirectUrl);
|
|
84
84
|
expect(searchParams.get("response_type")).toBe(oAuth2.responseType);
|
|
@@ -89,10 +89,10 @@ describe("Cognito Client", () => {
|
|
|
89
89
|
expect(searchParams.get("code_challenge_method")).toBe("S256");
|
|
90
90
|
cb(searchParams);
|
|
91
91
|
};
|
|
92
|
-
_test((searchParams) => {
|
|
92
|
+
await _test((searchParams) => {
|
|
93
93
|
expect(searchParams.get("identity_provider")).toBeNull();
|
|
94
94
|
});
|
|
95
|
-
_test((searchParams) => {
|
|
95
|
+
await _test((searchParams) => {
|
|
96
96
|
expect(searchParams.get("identity_provider")).toBe(CognitoIdentityProvider.Apple);
|
|
97
97
|
}, CognitoIdentityProvider.Apple);
|
|
98
98
|
});
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import { BigInteger } from
|
|
2
|
+
import { BigInteger } from "jsbn";
|
|
3
3
|
export declare function padHex(bigInt: BigInteger): string;
|
|
4
4
|
export declare function hashHexString(str: string): string;
|
|
5
5
|
export declare function hashBuffer(buffer: Buffer): string;
|
|
6
|
-
export declare function generateSmallA(): BigInteger
|
|
6
|
+
export declare function generateSmallA(): Promise<BigInteger>;
|
|
7
7
|
export declare function generateA(smallA: BigInteger): BigInteger;
|
|
8
8
|
export declare function calculateU(A: BigInteger, B: BigInteger): BigInteger;
|
|
9
9
|
export declare function calculateS(X: BigInteger, B: BigInteger, U: BigInteger, smallA: BigInteger): BigInteger;
|
|
@@ -18,3 +18,4 @@ export declare function decodeJwt<T = unknown>(jwt: string): {
|
|
|
18
18
|
payload: T;
|
|
19
19
|
signature: string;
|
|
20
20
|
};
|
|
21
|
+
export declare function randomBytes(num: number): Promise<Buffer>;
|
package/lib/utils.js
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
import formatInTimeZone from
|
|
2
|
-
import
|
|
3
|
-
import { BigInteger } from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
'43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF';
|
|
1
|
+
import formatInTimeZone from "date-fns-tz/formatInTimeZone";
|
|
2
|
+
import hashJs from "hash.js";
|
|
3
|
+
import { BigInteger } from "jsbn";
|
|
4
|
+
const initN = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
|
|
5
|
+
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
|
|
6
|
+
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
|
|
7
|
+
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
|
|
8
|
+
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
|
|
9
|
+
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
|
|
10
|
+
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
|
|
11
|
+
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
|
|
12
|
+
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
|
|
13
|
+
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
|
|
14
|
+
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" +
|
|
15
|
+
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" +
|
|
16
|
+
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" +
|
|
17
|
+
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
|
|
18
|
+
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" +
|
|
19
|
+
"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
|
|
21
20
|
const N = new BigInteger(initN, 16);
|
|
22
|
-
const g = new BigInteger(
|
|
21
|
+
const g = new BigInteger("2", 16);
|
|
23
22
|
const k = new BigInteger(hashHexString(`${padHex(N)}${padHex(g)}`), 16);
|
|
24
23
|
export function padHex(bigInt) {
|
|
25
24
|
const HEX_MSB_REGEX = /^[89a-f]/i;
|
|
@@ -29,29 +28,29 @@ export function padHex(bigInt) {
|
|
|
29
28
|
hexStr = HEX_MSB_REGEX.test(hexStr) ? `00${hexStr}` : hexStr;
|
|
30
29
|
if (isNegative) {
|
|
31
30
|
const invertedNibbles = hexStr
|
|
32
|
-
.split(
|
|
31
|
+
.split("")
|
|
33
32
|
.map((x) => {
|
|
34
33
|
const invertedNibble = ~parseInt(x, 16) & 0xf;
|
|
35
|
-
return
|
|
34
|
+
return "0123456789ABCDEF".charAt(invertedNibble);
|
|
36
35
|
})
|
|
37
|
-
.join(
|
|
36
|
+
.join("");
|
|
38
37
|
const flippedBitsBI = new BigInteger(invertedNibbles, 16).add(BigInteger.ONE);
|
|
39
38
|
hexStr = flippedBitsBI.toString(16);
|
|
40
|
-
if (hexStr.toUpperCase().startsWith(
|
|
39
|
+
if (hexStr.toUpperCase().startsWith("FF8")) {
|
|
41
40
|
hexStr = hexStr.substring(2);
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
return hexStr;
|
|
45
44
|
}
|
|
46
45
|
export function hashHexString(str) {
|
|
47
|
-
return hashBuffer(Buffer.from(str,
|
|
46
|
+
return hashBuffer(Buffer.from(str, "hex"));
|
|
48
47
|
}
|
|
49
48
|
export function hashBuffer(buffer) {
|
|
50
|
-
const hash = sha256().update(buffer).digest(
|
|
51
|
-
return new Array(64 - hash.length).join(
|
|
49
|
+
const hash = hashJs.sha256().update(buffer).digest("hex");
|
|
50
|
+
return new Array(64 - hash.length).join("0") + hash;
|
|
52
51
|
}
|
|
53
|
-
export function generateSmallA() {
|
|
54
|
-
return new BigInteger(randomBytes(128).toString(
|
|
52
|
+
export async function generateSmallA() {
|
|
53
|
+
return new BigInteger((await randomBytes(128)).toString("hex"), 16);
|
|
55
54
|
}
|
|
56
55
|
export function generateA(smallA) {
|
|
57
56
|
const A = g.modPow(smallA, N);
|
|
@@ -67,45 +66,59 @@ export function calculateS(X, B, U, smallA) {
|
|
|
67
66
|
}
|
|
68
67
|
export function calculateHKDF(ikm, salt) {
|
|
69
68
|
const infoBitsBuffer = Buffer.concat([
|
|
70
|
-
Buffer.from(
|
|
71
|
-
Buffer.from(String.fromCharCode(1),
|
|
69
|
+
Buffer.from("Caldera Derived Key", "utf8"),
|
|
70
|
+
Buffer.from(String.fromCharCode(1), "utf8"),
|
|
72
71
|
]);
|
|
73
|
-
const prk =
|
|
72
|
+
const prk = hashJs
|
|
73
|
+
.hmac(hashJs.sha256, salt)
|
|
74
74
|
.update(ikm)
|
|
75
75
|
.digest();
|
|
76
|
-
const hmacResult =
|
|
76
|
+
const hmacResult = hashJs
|
|
77
|
+
.hmac(hashJs.sha256, prk)
|
|
77
78
|
.update(infoBitsBuffer)
|
|
78
79
|
.digest();
|
|
79
80
|
return hmacResult.slice(0, 16);
|
|
80
81
|
}
|
|
81
82
|
export function getPasswordAuthenticationKey(poolName, username, password, B, U, smallA, salt) {
|
|
82
83
|
const usernamePassword = `${poolName}${username}:${password}`;
|
|
83
|
-
const usernamePasswordHash = hashBuffer(Buffer.from(usernamePassword,
|
|
84
|
+
const usernamePasswordHash = hashBuffer(Buffer.from(usernamePassword, "utf-8"));
|
|
84
85
|
const X = new BigInteger(hashHexString(padHex(salt) + usernamePasswordHash), 16);
|
|
85
86
|
const S = calculateS(X, B, U, smallA);
|
|
86
|
-
return calculateHKDF(Buffer.from(padHex(S),
|
|
87
|
+
return calculateHKDF(Buffer.from(padHex(S), "hex"), Buffer.from(padHex(U), "hex"));
|
|
87
88
|
}
|
|
88
89
|
export function calculateSignature(poolName, userId, secretBlock, hkdf) {
|
|
89
|
-
const timeStamp = formatInTimeZone(new Date(),
|
|
90
|
+
const timeStamp = formatInTimeZone(new Date(), "UTC", "EEE MMM d HH:mm:ss 'UTC' yyyy");
|
|
90
91
|
const concatBuffer = Buffer.concat([
|
|
91
|
-
Buffer.from(poolName,
|
|
92
|
-
Buffer.from(userId,
|
|
93
|
-
Buffer.from(secretBlock,
|
|
94
|
-
Buffer.from(timeStamp,
|
|
92
|
+
Buffer.from(poolName, "utf8"),
|
|
93
|
+
Buffer.from(userId, "utf8"),
|
|
94
|
+
Buffer.from(secretBlock, "base64"),
|
|
95
|
+
Buffer.from(timeStamp, "utf8"),
|
|
95
96
|
]);
|
|
96
|
-
const signature = Buffer.from(
|
|
97
|
+
const signature = Buffer.from(hashJs
|
|
98
|
+
.hmac(hashJs.sha256, hkdf)
|
|
97
99
|
.update(concatBuffer)
|
|
98
|
-
.digest()).toString(
|
|
100
|
+
.digest()).toString("base64");
|
|
99
101
|
return {
|
|
100
102
|
signature,
|
|
101
103
|
timeStamp,
|
|
102
104
|
};
|
|
103
105
|
}
|
|
104
106
|
export function decodeJwt(jwt) {
|
|
105
|
-
const [header, payload, signature] = jwt.split(
|
|
107
|
+
const [header, payload, signature] = jwt.split(".");
|
|
106
108
|
return {
|
|
107
|
-
header: JSON.parse(Buffer.from(header,
|
|
108
|
-
payload: JSON.parse(Buffer.from(payload,
|
|
109
|
+
header: JSON.parse(Buffer.from(header, "base64").toString("utf-8")),
|
|
110
|
+
payload: JSON.parse(Buffer.from(payload, "base64").toString("utf-8")),
|
|
109
111
|
signature: signature,
|
|
110
112
|
};
|
|
111
113
|
}
|
|
114
|
+
export async function randomBytes(num) {
|
|
115
|
+
if (globalThis.window) {
|
|
116
|
+
const bytes = new Uint8Array(num);
|
|
117
|
+
window.crypto.getRandomValues(bytes);
|
|
118
|
+
return Buffer.from(bytes);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
const { randomBytes } = await import("node:crypto");
|
|
122
|
+
return randomBytes(num);
|
|
123
|
+
}
|
|
124
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vardario/cognito-client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "lib/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
8
|
"/lib"
|
|
@@ -14,14 +14,12 @@
|
|
|
14
14
|
"date-fns": "^2.29.3",
|
|
15
15
|
"date-fns-tz": "^1.3.7",
|
|
16
16
|
"jsbn": "^1.1.0",
|
|
17
|
-
"randombytes": "^2.1.0",
|
|
18
17
|
"hash.js": "^1.1.7"
|
|
19
18
|
},
|
|
20
19
|
"devDependencies": {
|
|
21
20
|
"@aws-sdk/client-cognito-identity-provider": "^3.209.0",
|
|
22
21
|
"@types/jsbn": "^1.2.30",
|
|
23
22
|
"@types/jsdom": "^20.0.1",
|
|
24
|
-
"@types/randombytes": "^2.0.0",
|
|
25
23
|
"isomorphic-fetch": "^3.0.0",
|
|
26
24
|
"jsdom": "^20.0.2",
|
|
27
25
|
"testcontainers": "^9.0.0",
|