実現したいこと
- "@aws-sdk/client-cognito-identity-proivder"をインポートしたい
前提
TypeScriptでブラウザ上でAWS CognitoにSRP認証してトークンを取得するプログラムを作成しています。
ブラウザで実行時に、エラーが発生します。
コマンドライン上では動くのですが、ブラウザでは次のようなエラーメッセージが出ます。
作成しているソースコードはGitHubにアップしましたので、よろしければこちらもご確認お願いします。
発生している問題・エラーメッセージ
% rm -rf ./node_modules && npm install && npx tsc && http-server
上記実行後にブラウザ(Google Chrome)でlocalhost:8080
にアクセスし、コンソールを確認するとこのようなエラーが出てしまいます。
このエラーを解消したいです。
Uncaught TypeError: Failed to resolve module specifier "@aws-sdk/client-cognito-identity-provider". Relative references must start with either "/", "./", or "../".
該当のソースコード
index.html
html
1<!DOCTYPE html> 2<html> 3<head> 4 <meta charset="UTF-8"> 5</head> 6<body> 7 <script src="./dist/index.js"></script> 8</body> 9</html>
index.ts
ts
1import { 2 CognitoIdentityProviderClient, 3 InitiateAuthCommand, 4 InitiateAuthCommandOutput, 5 RespondToAuthChallengeCommand, 6 ChallengeNameType, 7 RespondToAuthChallengeCommandOutput 8} from '@aws-sdk/client-cognito-identity-provider'; 9 10// @ts-ignore 11import { default as AuthenticationHelperWrapper } from 'amazon-cognito-identity-js/lib/AuthenticationHelper.js' 12// @ts-ignore 13import { default as DateHelperWrapper } from 'amazon-cognito-identity-js/lib/DateHelper.js' 14// @ts-ignore 15import { default as BigIntegerWrapper } from 'amazon-cognito-identity-js/lib/BigInteger.js' 16 17import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager'; 18 19// @ts-ignore 20import { createHmac } from 'crypto'; 21 22const challengeResponse = async ({ 23 region, secretsManagerId, username, password, 24}: { 25 region: string, 26 secretsManagerId: string, 27 username: string, 28 password: string 29}) => { 30 31 const AuthenticationHelper = AuthenticationHelperWrapper.default; 32 const DateHelper = DateHelperWrapper.default; 33 const BigInteger = BigIntegerWrapper.default; 34 35 const secretsManagerClient = new SecretsManagerClient({ region: region }); 36 const command = new GetSecretValueCommand({ SecretId: secretsManagerId }); 37 38 const secretsManagerClientResponse = await secretsManagerClient.send(command); 39 40 const secretString = secretsManagerClientResponse.SecretString || 'failed to response secrets manager client'; 41 const secretValue = JSON.parse(secretString); 42 43 const CLIENT_SECRET: string = secretValue.COGNITO_CLIENT_SECRET 44 || 'missing client secret'; 45 const CLIENT_ID: string = secretValue.COGNITO_CLIENT_ID 46 || 'missing client id'; 47 const USER_POOL_ID: string = secretValue.COGNITO_USER_POOL_ID 48 || 'missing user pool id'; 49 const USERNAME: string = username; 50 const PASSWORD: string = password; 51 52 const client = new CognitoIdentityProviderClient({ region: REGION }); 53 54 const extractUserPoolName = (userPoolId: string) => { 55 const userPoolName = userPoolId.split('_')[1]; 56 return userPoolName; 57 } 58 59 const calculateSRP_A = async (userPoolId: string) => { 60 const userPoolName = extractUserPoolName(userPoolId); 61 const authenticationHelper = new AuthenticationHelper(userPoolName); 62 const SRP_A = authenticationHelper.largeAValue.toString(16); 63 return {SRP_A, authenticationHelper}; 64 } 65 const { SRP_A, authenticationHelper } = await calculateSRP_A(USER_POOL_ID); 66 67 68 const SECRET_HASH = createHmac('sha256', CLIENT_SECRET) 69 .update(USERNAME + CLIENT_ID) 70 .digest('base64'); 71 72 73 const initiateSrpAuthentication = async ({ 74 clientId, 75 username, 76 password, 77 srpA, 78 secretHash, 79 }: { 80 clientId: string, 81 username: string, 82 password: string, 83 srpA: string, 84 secretHash: string, 85 }) => { 86 const response = client.send( 87 new InitiateAuthCommand({ 88 ClientId: CLIENT_ID, 89 AuthFlow: 'USER_SRP_AUTH', 90 AuthParameters: { 91 USERNAME: USERNAME, 92 PASSWORD: PASSWORD, 93 SRP_A: SRP_A, 94 SECRET_HASH: SECRET_HASH, 95 }, 96 }) 97 ); 98 return response; 99 } 100 101 const initiateSrpAuthResponse = await initiateSrpAuthentication({ 102 clientId: CLIENT_ID, 103 username: USERNAME, 104 password: PASSWORD, 105 srpA: SRP_A, 106 secretHash: SECRET_HASH 107 }); 108 109 const hkdfResult = {hkdf: undefined as undefined | string}; 110 authenticationHelper.getPasswordAuthenticationKey( 111 USERNAME, 112 PASSWORD, 113 new BigInteger(initiateSrpAuthResponse.ChallengeParameters?.SRP_B, 16), 114 new BigInteger(initiateSrpAuthResponse.ChallengeParameters?.SALT, 16), 115 (err: unknown, result?: string) => { 116 hkdfResult.hkdf = result; 117 }, 118 ); 119 120 121 const dateHelper = new DateHelper(); 122 const DATE_NOW = dateHelper.getNowString(); 123 124 125 const generateSignature = ({ 126 userPoolId, 127 username, 128 secretBlock, 129 hkdf, 130 }: { 131 userPoolId: string, 132 username: string, 133 secretBlock: string, 134 hkdf: string, 135 }) => { 136 const userPoolName = extractUserPoolName(userPoolId); 137 138 const msg = Buffer.concat([ 139 Buffer.from(userPoolName, 'utf-8'), 140 Buffer.from(username, 'utf-8'), 141 142 Buffer.from(secretBlock, 'base64'), 143 Buffer.from(DATE_NOW, 'utf-8'), 144 ]) 145 146 const signature = createHmac('sha256', hkdf) 147 .update(msg) 148 .digest('base64'); 149 150 return signature; 151 } 152 153 const secretBlockResult = ( 154 response: InitiateAuthCommandOutput, 155 ) => { 156 const maybeSecretBlock = response.ChallengeParameters?.SECRET_BLOCK; 157 return maybeSecretBlock; 158 } 159 160 const SECRET_BLOCK = secretBlockResult(initiateSrpAuthResponse) 161 || 'failed to extract secret block'; 162 const HKDF = hkdfResult.hkdf 163 || 'failed to extract hkdf'; 164 165 const signature = generateSignature({ 166 userPoolId: USER_POOL_ID, 167 username: USERNAME, 168 secretBlock: SECRET_BLOCK, 169 hkdf: HKDF, 170 }); 171 172 const respondToAuthChallenge = async ({ 173 clientId, 174 username, 175 secretBlock, 176 dateNow, 177 }: { 178 clientId: string, 179 username: string, 180 secretBlock: string, 181 dateNow: string, 182 }) => { 183 const command = new RespondToAuthChallengeCommand({ 184 ChallengeName: ChallengeNameType.PASSWORD_VERIFIER, 185 ChallengeResponses: { 186 PASSWORD_CLAIM_SIGNATURE: signature, 187 PASSWORD_CLAIM_SECRET_BLOCK: secretBlock, 188 TIMESTAMP: dateNow, 189 USERNAME: username, 190 SECRET_HASH: SECRET_HASH, 191 }, 192 ClientId: clientId, 193 }); 194 195 return client.send(command); 196 }; 197 198 const challengeResponse: RespondToAuthChallengeCommandOutput = await respondToAuthChallenge({ 199 clientId: CLIENT_ID, 200 username: USERNAME, 201 secretBlock: SECRET_BLOCK, 202 dateNow: DATE_NOW, 203 }); 204 205 return challengeResponse; 206} 207 208 209const REGION: string = process.env.REGION || 'missing region'; 210const SECRETS_MANAGER_ID: string = process.env.SECRETS_MANAGER_ID || 'missing secrets manager id'; 211const USERNAME: string = process.env.COGNITO_USERNAME || 'missing cognito username'; 212const PASSWORD: string = process.env.COGNITO_PASSWORD || 'missing cognito password'; 213console.log(await challengeResponse({ 214 region: REGION, 215 secretsManagerId: SECRETS_MANAGER_ID, 216 username: USERNAME, 217 password: PASSWORD, 218}));
package.json
json
1{ 2 "name": "srp", 3 "version": "1.0.0", 4 "description": "", 5 "main": "index.js", 6 "type": "module", 7 "scripts": { 8 "test": "echo \"Error: no test specified\" && exit 1" 9 }, 10 "keywords": [], 11 "author": "", 12 "license": "ISC", 13 "devDependencies": { 14 "@types/node": "^20.2.3", 15 "typescript": "^5.0.4" 16 }, 17 "dependencies": { 18 "@aws-sdk/client-cognito-identity-provider": "^3.338.0", 19 "@aws-sdk/client-secrets-manager": "^3.338.0", 20 "amazon-cognito-identity-js": "^6.2.0", 21 "http-server": "^14.1.1" 22 } 23}
tsconfig.json
json
1{ 2 "compilerOptions": { 3 "target": "esnext", 4 "module": "esnext", 5 "moduleResolution": "nodenext", 6 "baseUrl": ".", 7 "outDir": "./dist", 8 "esModuleInterop": true, 9 "forceConsistentCasingInFileNames": true, 10 "strict": true, 11 "skipLibCheck": true, 12 "sourceMap": true, 13 "allowJs": true, 14 "noImplicitAny": true, 15 "noUnusedLocals": true, 16 "noImplicitThis": true, 17 "strictNullChecks": true, 18 "noImplicitReturns": true, 19 "preserveConstEnums": true, 20 "allowSyntheticDefaultImports": true 21 }, 22 "exclude": [ 23 "./node_modules" 24 ], 25 "include": [ 26 "./src/**/*.ts" 27 ] 28}
試したこと
console
1% rm -rf ./node_modules && npm install && npx tsc && node dist/index.js 2added 134 packages, and audited 135 packages in 669ms 3 414 packages are looking for funding 5 run `npm fund` for details 6 7found 0 vulnerabilities 8{ 9 '$metadata': { 10 httpStatusCode: 200, 11 requestId: '***<略>***', 12 extendedRequestId: undefined, 13 cfId: undefined, 14 attempts: 1, 15 totalRetryDelay: 0 16 }, 17 AuthenticationResult: { 18 AccessToken: 'eyJ***<略>***', 19 ExpiresIn: 3600, 20 IdToken: 'eyJ***<略>***', 21 RefreshToken: 'eyJ***<略>***', 22 TokenType: 'Bearer' 23 }, 24 ChallengeParameters: {} 25}
コマンドライン上では正しくトークンが返ってきます。
補足情報(FW/ツールのバージョンなど)
console
1% rm -rf node_modules 2% tree -L 3 . 3. 4├── dist 5│ ├── index.d.ts 6│ ├── index.js 7│ ├── index.js.map 8│ └── tsconfig.tsbuildinfo 9├── index.html 10├── package-lock.json 11├── package.json 12├── src 13│ └── index.ts 14└── tsconfig.json 15 163 directories, 9 files 17% npm version 18{ 19 srp: '1.0.0', 20 npm: '8.19.3', 21 node: '19.2.0', 22 <中略> 23} 24% npx tsc -v 25Version 5.0.4
まとめ
この問題の解決手法、ご存知の方もしおられましたらご教示いただけると幸いです。
拙い内容で恐縮ですが、どうぞよろしくお願いします。
追記
tsx
1let user = await Auth.signIn(username, password); 2if (user.challengeName === "NEW_PASSWORD_REQUIRED") { 3 user = await Auth.completeNewPassword(user, password); 4} 5const token = user.signInUserSession.idToken.jwtToken;
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2023/06/03 12:50
2023/06/03 12:52
2023/06/03 13:03 編集
2023/06/03 13:15