diff --git a/server/api/@types/user.ts b/server/api/@types/user.ts index a63758b..0186a7a 100644 --- a/server/api/@types/user.ts +++ b/server/api/@types/user.ts @@ -1,5 +1,12 @@ import type { EntityId } from './brandedId'; +export type ChallengeVal = { + secretBlock: string; + pubA: string; + pubB: string; + secB: string; +}; + export type UserEntity = { id: EntityId['user']; name: string; @@ -11,4 +18,5 @@ export type UserEntity = { refreshToken: string; userPoolId: EntityId['userPool']; createdTime: number; + challenge?: ChallengeVal; }; diff --git a/server/domain/user/model/userMethod.ts b/server/domain/user/model/userMethod.ts index e307e07..bf19429 100644 --- a/server/domain/user/model/userMethod.ts +++ b/server/domain/user/model/userMethod.ts @@ -1,9 +1,20 @@ +import type { Jwks, UserSrpAuthTarget } from 'api/@types/auth'; import type { EntityId } from 'api/@types/brandedId'; -import type { UserEntity } from 'api/@types/user'; +import type { ChallengeVal, UserEntity } from 'api/@types/user'; +import type { UserPoolClientEntity, UserPoolEntity } from 'api/@types/userPool'; import assert from 'assert'; +import crypto from 'crypto'; +import { genConfirmationCode } from 'domain/user/service/genConfirmationCode'; +import { genTokens } from 'domain/user/service/genTokens'; +import { + calculateScramblingParameter, + calculateSessionKey, +} from 'domain/user/service/srp/calcSessionKey'; +import { calculateSignature } from 'domain/user/service/srp/calcSignature'; +import { calculateSrpB } from 'domain/user/service/srp/calcSrpB'; +import { getPoolName } from 'domain/user/service/srp/util'; import { brandedId } from 'service/brandedId'; import { ulid } from 'ulid'; -import { genConfirmationCode } from '../service/genConfirmationCode'; export const userMethod = { createUser: (val: { @@ -29,4 +40,64 @@ export const userMethod = { return { ...user, verified: true }; }, + createChallenge: ( + user: UserEntity, + params: UserSrpAuthTarget['reqBody']['AuthParameters'], + ): { + userWithChallenge: UserEntity; + ChallengeParameters: UserSrpAuthTarget['resBody']['ChallengeParameters']; + } => { + const { B, b } = calculateSrpB(user.verifier); + const secretBlock = crypto.randomBytes(64).toString('base64'); + + const challenge: ChallengeVal = { + pubB: B, + secB: b, + pubA: params.SRP_A, + secretBlock, + }; + return { + userWithChallenge: { ...user, challenge }, + ChallengeParameters: { + SALT: user.salt, + SECRET_BLOCK: secretBlock, + SRP_B: B, + USERNAME: user.name, + USER_ID_FOR_SRP: user.name, + }, + }; + }, + srpAuth: (params: { + user: UserEntity; + timestamp: string; + clientSignature: string; + jwks: Jwks; + pool: UserPoolEntity; + poolClient: UserPoolClientEntity; + }): { + AccessToken: string; + IdToken: string; + } => { + assert(params.user.challenge); + const { pubA: A, pubB: B, secB: b } = params.user.challenge; + const poolname = getPoolName(params.user.userPoolId); + const scramblingParameter = calculateScramblingParameter(A, B); + const sessionKey = calculateSessionKey({ A, B, b, v: params.user.verifier }); + const signature = calculateSignature({ + poolname, + username: params.user.name, + secretBlock: params.user.challenge.secretBlock, + timestamp: params.timestamp, + scramblingParameter, + key: sessionKey, + }); + assert(signature === params.clientSignature); + + return genTokens({ + privateKey: params.pool.privateKey, + userPoolClientId: params.poolClient.id, + jwks: params.jwks, + user: params.user, + }); + }, }; diff --git a/server/domain/user/repository/toUserEntity.ts b/server/domain/user/repository/toUserEntity.ts index 86913d4..285a326 100644 --- a/server/domain/user/repository/toUserEntity.ts +++ b/server/domain/user/repository/toUserEntity.ts @@ -12,6 +12,15 @@ export const toUserEntity = (prismaUser: User): UserEntity => { refreshToken: prismaUser.refreshToken, verified: prismaUser.verified, confirmationCode: prismaUser.confirmationCode, + challenge: + prismaUser.secretBlock && prismaUser.pubA && prismaUser.pubB && prismaUser.secB + ? { + secretBlock: prismaUser.secretBlock, + pubA: prismaUser.pubA, + pubB: prismaUser.pubB, + secB: prismaUser.secB, + } + : undefined, userPoolId: brandedId.userPool.entity.parse(prismaUser.userPoolId), createdTime: prismaUser.createdAt.getTime(), }; diff --git a/server/domain/user/repository/userCommand.ts b/server/domain/user/repository/userCommand.ts index d2b5eec..1abfdb1 100644 --- a/server/domain/user/repository/userCommand.ts +++ b/server/domain/user/repository/userCommand.ts @@ -11,6 +11,10 @@ export const userCommand = { verified: user.verified, refreshToken: user.refreshToken, confirmationCode: user.confirmationCode, + secretBlock: user.challenge?.secretBlock, + pubA: user.challenge?.pubA, + pubB: user.challenge?.pubB, + secB: user.challenge?.secB, }, create: { id: user.id, diff --git a/server/domain/user/service/genCredentials.ts b/server/domain/user/service/genCredentials.ts index 91b9a57..da3f227 100644 --- a/server/domain/user/service/genCredentials.ts +++ b/server/domain/user/service/genCredentials.ts @@ -1,8 +1,21 @@ import type { EntityId } from 'api/@types/brandedId'; -import assert from 'assert'; import crypto from 'crypto'; -import { g, N } from './srp/constants'; -import { calculatePrivateKey, toBuffer } from './srp/util'; +import { g, N, Nbytes } from './srp/constants'; +import { calculatePrivateKey, getPoolName, toBufferWithLength } from './srp/util'; + +export const genVerifier = (params: { + poolId: EntityId['userPool']; + username: string; + password: string; + salt: string; +}): string => { + // extract pool name from poolId (poolId format: userPoolId_poolName) + const poolName = getPoolName(params.poolId); + const privateKey = calculatePrivateKey(poolName, params.username, params.password, params.salt); + // verifier = g ^ privateKey % N + const verifier = toBufferWithLength(g.modPow(privateKey, N), Nbytes).toString('hex'); + return verifier; +}; export const genCredentials = (params: { poolId: EntityId['userPool']; @@ -10,11 +23,6 @@ export const genCredentials = (params: { password: string; }): { salt: string; verifier: string } => { const salt = crypto.randomBytes(16).toString('hex'); - // extract pool name from poolId (poolId format: userPoolId_poolName) - const poolName = params.poolId.split('_')[1]; - assert(poolName, 'Invalid poolId'); - const privateKey = calculatePrivateKey(poolName, params.username, params.password, salt); - // verifier = g^privateKey % N - const verifier = toBuffer(g.modPow(privateKey, N)).toString('hex'); + const verifier = genVerifier({ ...params, salt }); return { salt, verifier }; }; diff --git a/server/domain/user/service/srp/calcClientSignature.ts b/server/domain/user/service/srp/calcClientSignature.ts new file mode 100644 index 0000000..3303be4 --- /dev/null +++ b/server/domain/user/service/srp/calcClientSignature.ts @@ -0,0 +1,51 @@ +import assert from 'assert'; +import { calculateScramblingParameter } from 'domain/user/service/srp/calcSessionKey'; +import { calculateSignature } from 'domain/user/service/srp/calcSignature'; +import { N, Nbytes, g, multiplierParam } from 'domain/user/service/srp/constants'; +import { + calculatePrivateKey, + fromBuffer, + getPoolName, + padHex, + toBufferWithLength, +} from 'domain/user/service/srp/util'; +import { BigInteger } from 'jsbn'; +import { DEFAULT_USER_POOL_ID } from 'service/envValues'; + +export const calcClientSignature = (params: { + A: string; + a: BigInteger; + B: string; + username: string; + password: string; + salt: string; + timestamp: string; + secretBlock: string; +}): string => { + const poolname = getPoolName(DEFAULT_USER_POOL_ID); + const Bint = new BigInteger(padHex(params.B), 16); + + assert(Bint.compareTo(BigInteger.ZERO) > 0, 'B should be greater than 0'); + assert(Bint.compareTo(N) < 0, 'A should be less than N'); + + const privateKey = calculatePrivateKey(poolname, params.username, params.password, params.salt); + + const scramblingParameter = calculateScramblingParameter(params.A, params.B); + + const S = toBufferWithLength( + // S = (B - k * (g ^ x)) ^ (a + u * x) + Bint.subtract(multiplierParam.multiply(g.modPow(privateKey, N))) + .modPow(params.a.add(fromBuffer(scramblingParameter).multiply(privateKey)), N) + .mod(N), + Nbytes, + ); + + return calculateSignature({ + poolname, + username: params.username, + secretBlock: params.secretBlock, + timestamp: params.timestamp, + scramblingParameter, + key: S, + }); +}; diff --git a/server/domain/user/service/srp/calcSessionKey.ts b/server/domain/user/service/srp/calcSessionKey.ts new file mode 100644 index 0000000..3c7e263 --- /dev/null +++ b/server/domain/user/service/srp/calcSessionKey.ts @@ -0,0 +1,39 @@ +import assert from 'assert'; +import crypto from 'crypto'; +import { BigInteger } from 'jsbn'; +import { N, Nbytes } from './constants'; +import { fromBuffer, padHex, toBufferWithLength } from './util'; + +export const calculateScramblingParameter = (A: string, B: string): Buffer => { + // H(A | B) + const ABuffer = Buffer.from(padHex(A), 'hex'); + const BBuffer = Buffer.from(padHex(B), 'hex'); + const hash = crypto.createHash('sha256').update(ABuffer).update(BBuffer).digest(); + return hash; +}; + +export const calculateSessionKey = (params: { + A: string; + B: string; + b: string; + v: string; +}): Buffer => { + const Aint = new BigInteger(padHex(params.A), 16); + const Bint = new BigInteger(padHex(params.B), 16); + const bInt = new BigInteger(padHex(params.b), 16); + const vInt = new BigInteger(params.v, 16); + + assert(Aint.compareTo(BigInteger.ZERO) > 0, 'A should be greater than 0'); + assert(Aint.compareTo(N) < 0, 'A should be less than N'); + assert(Bint.compareTo(BigInteger.ZERO) > 0, 'B should be greater than 0'); + assert(Bint.compareTo(N) < 0, 'A should be less than N'); + + const scramblingParameter = calculateScramblingParameter(params.A, params.B); + + // u = H(A,B) % N + const u = vInt.modPow(fromBuffer(scramblingParameter), N); + // S = (B - k * g^x) ^ (a + u * x) % N + const S = Aint.multiply(u).modPow(bInt, N).mod(N); + + return toBufferWithLength(S, Nbytes); +}; diff --git a/server/domain/user/service/srp/calcSignature.ts b/server/domain/user/service/srp/calcSignature.ts new file mode 100644 index 0000000..83897d0 --- /dev/null +++ b/server/domain/user/service/srp/calcSignature.ts @@ -0,0 +1,30 @@ +import crypto from 'crypto'; +import { infoBits } from './constants'; +import { padBufferToHex } from './util'; + +export const calculateSignature = (params: { + poolname: string; + username: string; + secretBlock: string; + timestamp: string; + scramblingParameter: Buffer; + key: Buffer; +}): string => { + const secretBlockBuffer = Buffer.from(params.secretBlock, 'base64'); + + const prk = crypto + .createHmac('sha256', Buffer.from(padBufferToHex(params.scramblingParameter), 'hex')) + .update(Buffer.from(padBufferToHex(params.key), 'hex')) + .digest(); + + const hmac = crypto.createHmac('sha256', prk).update(infoBits).digest(); + const hkdf = hmac.subarray(0, 16); + + return crypto + .createHmac('sha256', hkdf) + .update(params.poolname) + .update(params.username) + .update(secretBlockBuffer) + .update(params.timestamp) + .digest('base64'); +}; diff --git a/server/domain/user/service/srp/calcSrpB.ts b/server/domain/user/service/srp/calcSrpB.ts new file mode 100644 index 0000000..d15b313 --- /dev/null +++ b/server/domain/user/service/srp/calcSrpB.ts @@ -0,0 +1,18 @@ +import crypto from 'crypto'; +import { BigInteger } from 'jsbn'; +import { N, g, multiplierParam } from './constants'; +import { fromBuffer, toBuffer } from './util'; + +export const calculateSrpB = ( + v: string, +): { + b: string; + B: string; +} => { + const b = crypto.randomBytes(32); + const bInt = fromBuffer(b); + const vInt = new BigInteger(v, 16); + // kv + g^b + const B = toBuffer(multiplierParam.multiply(vInt).add(g.modPow(bInt, N)).mod(N)); + return { b: b.toString('hex'), B: B.toString('hex') }; +}; diff --git a/server/domain/user/service/srp/constants.ts b/server/domain/user/service/srp/constants.ts index d63ad6b..a239ac9 100644 --- a/server/domain/user/service/srp/constants.ts +++ b/server/domain/user/service/srp/constants.ts @@ -1,9 +1,18 @@ import { BigInteger } from 'jsbn'; +import { getHash } from './util'; const Nhex = `FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF`; export const N = new BigInteger(Nhex, 16); export const Nbits = N.bitLength(); export const Nbytes = Nbits / 8; export const g = new BigInteger('2'); +export const infoBits = 'Caldera Derived Key\x01'; -export const HASH_TYPE = 'sha256'; +const getMultiplierParam = (): BigInteger => { + const Nhex = `00${N.toString(16)}`; + const ghex = `0${g.toString(16)}`; + const hash = getHash(Buffer.from(Nhex + ghex, 'hex'), 32); + return new BigInteger(hash, 16); +}; + +export const multiplierParam = getMultiplierParam(); diff --git a/server/domain/user/service/srp/util.ts b/server/domain/user/service/srp/util.ts index 1dbb188..fec121f 100644 --- a/server/domain/user/service/srp/util.ts +++ b/server/domain/user/service/srp/util.ts @@ -1,9 +1,9 @@ +import assert from 'assert'; import crypto from 'crypto'; import { BigInteger } from 'jsbn'; -import { HASH_TYPE, Nbytes } from './constants'; export const getHash = (data: Buffer | string, length: number): string => { - const hash = crypto.createHash(HASH_TYPE).update(data).digest('hex'); + const hash = crypto.createHash('sha256').update(data).digest('hex'); return hash.padStart(length * 2, '0'); }; @@ -14,11 +14,36 @@ export const calculatePrivateKey = ( password: string, salt: string, ): BigInteger => { + // x = H(s,p) const hash = getHash(`${poolname}${username}:${password}`, 32); - const buffer = Buffer.from(salt + hash, 'hex'); + const buffer = Buffer.from(padHex(salt) + hash, 'hex'); return new BigInteger(getHash(buffer, 32), 16); }; +export const padHex = (hex: string): string => { + return hex.replace(/^([89A-Fa-f])/, '00$1'); +}; + +export const padBufferToHex = (buffer: Buffer): string => { + return padHex(buffer.toString('hex')); +}; + export const toBuffer = (bigInt: BigInteger): Buffer => { - return Buffer.from(bigInt.toString(16).padStart(Nbytes * 2, '0'), 'hex'); + const str = bigInt.toString(16); + return Buffer.from(str, 'hex'); +}; + +export const toBufferWithLength = (bigInt: BigInteger, length: number): Buffer => { + const str = bigInt.toString(16).padStart(length * 2, '0'); + return Buffer.from(str, 'hex'); +}; + +export const fromBuffer = (buffer: Buffer): BigInteger => { + return new BigInteger(buffer.toString('hex'), 16); +}; + +export const getPoolName = (poolId: string): string => { + const name = poolId.split('_')[1]; + assert(name, 'Invalid poolId'); + return name; }; diff --git a/server/domain/user/useCase/authUseCase.ts b/server/domain/user/useCase/authUseCase.ts index be37479..515ea02 100644 --- a/server/domain/user/useCase/authUseCase.ts +++ b/server/domain/user/useCase/authUseCase.ts @@ -8,16 +8,16 @@ import type { UserSrpAuthTarget, } from 'api/@types/auth'; import assert from 'assert'; +import { userMethod } from 'domain/user/model/userMethod'; +import { userCommand } from 'domain/user/repository/userCommand'; +import { userQuery } from 'domain/user/repository/userQuery'; +import { genCredentials } from 'domain/user/service/genCredentials'; +import { genTokens } from 'domain/user/service/genTokens'; import { userPoolQuery } from 'domain/userPool/repository/userPoolQuery'; import { jwtDecode } from 'jwt-decode'; import { transaction } from 'service/prismaClient'; import { sendMail } from 'service/sendMail'; import type { AccessTokenJwt } from 'service/types'; -import { userMethod } from '../model/userMethod'; -import { userCommand } from '../repository/userCommand'; -import { userQuery } from '../repository/userQuery'; -import { genCredentials } from '../service/genCredentials'; -import { genTokens } from '../service/genTokens'; export const authUseCase = { signUp: (req: SignUpTarget['reqBody']): Promise => @@ -66,15 +66,16 @@ export const authUseCase = { const user = await userQuery.findByName(tx, req.AuthParameters.USERNAME); assert(user.verified); + const { userWithChallenge, ChallengeParameters } = userMethod.createChallenge( + user, + req.AuthParameters, + ); + + await userCommand.save(tx, userWithChallenge); + return { ChallengeName: 'PASSWORD_VERIFIER', - ChallengeParameters: { - SALT: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - SECRET_BLOCK: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', - SRP_B: 'ccccccccccccccccccccccccccccccccc', - USERNAME: req.AuthParameters.USERNAME, - USER_ID_FOR_SRP: req.AuthParameters.USERNAME, - }, + ChallengeParameters, }; }), refreshTokenAuth: ( @@ -112,15 +113,20 @@ export const authUseCase = { const jwks = await userPoolQuery.findJwks(tx, user.userPoolId); assert(pool.id === poolClient.userPoolId); + assert(user.challenge?.secretBlock === req.ChallengeResponses.PASSWORD_CLAIM_SECRET_BLOCK); + + const tokens = userMethod.srpAuth({ + user, + timestamp: req.ChallengeResponses.TIMESTAMP, + clientSignature: req.ChallengeResponses.PASSWORD_CLAIM_SIGNATURE, + jwks, + pool, + poolClient, + }); return { AuthenticationResult: { - ...genTokens({ - privateKey: pool.privateKey, - userPoolClientId: poolClient.id, - jwks, - user, - }), + ...tokens, ExpiresIn: 3600, RefreshToken: user.refreshToken, TokenType: 'Bearer', diff --git a/server/prisma/migrations/20240618111807_/migration.sql b/server/prisma/migrations/20240618111807_/migration.sql new file mode 100644 index 0000000..ca327cb --- /dev/null +++ b/server/prisma/migrations/20240618111807_/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "pubA" TEXT; +ALTER TABLE "User" ADD COLUMN "pubB" TEXT; +ALTER TABLE "User" ADD COLUMN "secB" TEXT; +ALTER TABLE "User" ADD COLUMN "secretBlock" TEXT; diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index c2682ef..e75cf43 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -17,6 +17,10 @@ model User { verifier String refreshToken String createdAt DateTime + secretBlock String? + pubA String? + pubB String? + secB String? UserPool UserPool @relation(fields: [userPoolId], references: [id]) userPoolId String } diff --git a/server/tests/api/apiClient.ts b/server/tests/api/apiClient.ts index ab24a8a..5d9e5f6 100644 --- a/server/tests/api/apiClient.ts +++ b/server/tests/api/apiClient.ts @@ -3,6 +3,7 @@ import api from 'api/$api'; import type { UserEntity } from 'api/@types/user'; import axios from 'axios'; import { genConfirmationCode } from 'domain/user/service/genConfirmationCode'; +import { genVerifier } from 'domain/user/service/genCredentials'; import { genTokens } from 'domain/user/service/genTokens'; import { brandedId } from 'service/brandedId'; import { COOKIE_NAME } from 'service/constants'; @@ -18,14 +19,21 @@ export const noCookieClient = api( ); export const createUserClient = async (): Promise => { + const salt = 'test-client-salt'; + const verifier = genVerifier({ + poolId: DEFAULT_USER_POOL_ID, + username: 'test-client', + password: 'test-client-password', + salt, + }); const user: UserEntity = { id: brandedId.user.entity.parse(ulid()), email: `${ulid()}@example.com`, name: 'test-client', verified: true, confirmationCode: genConfirmationCode(), - salt: 'salt', - verifier: 'verifier', + salt, + verifier, refreshToken: ulid(), userPoolId: DEFAULT_USER_POOL_ID, createdTime: Date.now(), diff --git a/server/tests/api/signIn.test.ts b/server/tests/api/signIn.test.ts index b2d6764..dbe86aa 100644 --- a/server/tests/api/signIn.test.ts +++ b/server/tests/api/signIn.test.ts @@ -1,57 +1,76 @@ import assert from 'assert'; +import crypto from 'crypto'; +import { calcClientSignature } from 'domain/user/service/srp/calcClientSignature'; +import { N, g } from 'domain/user/service/srp/constants'; +import { fromBuffer, toBuffer } from 'domain/user/service/srp/util'; import { DEFAULT_USER_POOL_CLIENT_ID } from 'service/envValues'; import { expect, test } from 'vitest'; -import { createUserClient, noCookieClient } from './apiClient'; +import { createUserClient } from './apiClient'; test('signIn', async () => { - await createUserClient(); - await noCookieClient.$post({ + const noCookieClient = await createUserClient(); + const a = crypto.randomBytes(32); + const A = toBuffer(g.modPow(fromBuffer(a), N)); + const res1 = await noCookieClient.$post({ headers: { 'x-amz-target': 'AWSCognitoIdentityProviderService.InitiateAuth' }, body: { AuthFlow: 'USER_SRP_AUTH', - AuthParameters: { USERNAME: 'test-client', SRP_A: 'string' }, + AuthParameters: { USERNAME: 'test-client', SRP_A: A.toString('hex') }, ClientId: DEFAULT_USER_POOL_CLIENT_ID, }, }); - const res1 = await noCookieClient.$post({ + assert('ChallengeParameters' in res1); + const secretBlock = res1.ChallengeParameters.SECRET_BLOCK; + const signature = calcClientSignature({ + secretBlock, + username: 'test-client', + password: 'test-client-password', + salt: 'test-client-salt', + timestamp: 'Thu Jan 01 00:00:00 UTC 1970', + A: A.toString('hex'), + a: fromBuffer(a), + B: res1.ChallengeParameters.SRP_B, + }); + + const res2 = await noCookieClient.$post({ headers: { 'x-amz-target': 'AWSCognitoIdentityProviderService.RespondToAuthChallenge' }, body: { ChallengeName: 'PASSWORD_VERIFIER', ChallengeResponses: { - PASSWORD_CLAIM_SECRET_BLOCK: 'string', - PASSWORD_CLAIM_SIGNATURE: 'string', - TIMESTAMP: 'string', + PASSWORD_CLAIM_SECRET_BLOCK: secretBlock, + PASSWORD_CLAIM_SIGNATURE: signature, + TIMESTAMP: 'Thu Jan 01 00:00:00 UTC 1970', USERNAME: 'test-client', }, ClientId: DEFAULT_USER_POOL_CLIENT_ID, }, }); - assert('AuthenticationResult' in res1); - assert('RefreshToken' in res1.AuthenticationResult); + assert('AuthenticationResult' in res2); + assert('RefreshToken' in res2.AuthenticationResult); await noCookieClient.$post({ headers: { 'x-amz-target': 'AWSCognitoIdentityProviderService.GetUser' }, - body: { AccessToken: res1.AuthenticationResult.AccessToken }, + body: { AccessToken: res2.AuthenticationResult.AccessToken }, }); await noCookieClient.post({ headers: { 'x-amz-target': 'AWSCognitoIdentityProviderService.InitiateAuth' }, body: { AuthFlow: 'REFRESH_TOKEN_AUTH', - AuthParameters: { REFRESH_TOKEN: res1.AuthenticationResult.RefreshToken }, + AuthParameters: { REFRESH_TOKEN: res2.AuthenticationResult.RefreshToken }, ClientId: DEFAULT_USER_POOL_CLIENT_ID, }, }); - const res2 = await noCookieClient.post({ + const res3 = await noCookieClient.post({ headers: { 'x-amz-target': 'AWSCognitoIdentityProviderService.RevokeToken' }, body: { - Token: res1.AuthenticationResult.RefreshToken, + Token: res2.AuthenticationResult.RefreshToken, ClientId: DEFAULT_USER_POOL_CLIENT_ID, }, }); - expect(res2.status).toBe(200); + expect(res3.status).toBe(200); });