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