質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
ウェブブラウザ

ウェブブラウザ(インターネットブラウザ)とは、www上に公開されている情報リソースをユーザーに視覚的提供・操作させる機能を持ったソフトウェアプログラムです。

コマンドライン

コマンドライン(別名:Command Line Interface)は、ユーザに命令の入力を促す(プロンプト)文字列の表示を行い、すべての操作をキーボードを用いて文字列を打ち込む事でプログラムを走らせるユーザインターフェースです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

AWS(Amazon Web Services)

Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

Q&A

解決済

1回答

1029閲覧

【TypeScript】ブラウザで実行する際のモジュールのインポート方法

akira_kano1101

総合スコア25

ウェブブラウザ

ウェブブラウザ(インターネットブラウザ)とは、www上に公開されている情報リソースをユーザーに視覚的提供・操作させる機能を持ったソフトウェアプログラムです。

コマンドライン

コマンドライン(別名:Command Line Interface)は、ユーザに命令の入力を促す(プロンプト)文字列の表示を行い、すべての操作をキーボードを用いて文字列を打ち込む事でプログラムを走らせるユーザインターフェースです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

AWS(Amazon Web Services)

Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

0グッド

0クリップ

投稿2023/05/25 05:04

編集2023/06/03 13:41

実現したいこと

  • "@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;

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

エラーの原因は、メッセージにあるとおりimportのあとのモジュール指定文字列が /, ./, ../ から始まってないからです。Webブラウザー上のJavaScriptでimportするには、以下の記事で解説されているとおり、モジュール指定をそのように書き換えるか、インポートマップを記述するかしないといけないようです。

そうすることで、そのエラーは消えると思いますが、Webブラウザーで動作させるためには他にも必要なことがあります。

  • importで指定したURLからモジュールがダウンロードできること
  • モジュールがES Moduleの形式で配布されていること

前者は自分のプロジェクト内のファイルの配置でなんとかなりそうですが、後者は形式が違っていたら簡単には対応できなさそうです。

この方法で動作するまで持っていくのは大変そうなので、別の、現在一般的な方法を取るのがいいでしょう。webpackやViteのようなフロントエンドアプリのビルドツールを使うことです。それらのツールにはモジュールバンドラーという機能が含まれており、複数のJavaScriptモジュールからなるアプリケーションを1つのjsファイルにまとめてくれます。

最近はViteが人気のようです。ご質問のアプリでは、以下の記事の「Vite + TypeScript」で紹介されているvanilla-tsというテンプレートを使うのが良さそうです。

投稿2023/06/03 04:49

tatsuya6502

総合スコア2035

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

akira_kano1101

2023/06/03 12:50

tatsuya6502さん ご回答ありがとうございます。 これを実現するために準備が色々と大変すぎたので、この方法は諦めることにしました。 代わりにAmplifyを使ってSRP認証をすることにしましたが、うまくいきましたのでそのソースコードを共有したいと思います。 https://github.com/kano1101/amplify-id-token それから、vanilla-tsに興味があるのでまた落ち着いたら調べてみようと思います。 ありがとうございました。
akira_kano1101

2023/06/03 12:52

せっかくなので一番大事な部分のソースコードを本文に追記しておきます。
tatsuya6502

2023/06/03 13:03 編集

解決してよかったです! vanilla-tsですが、-tsはTypeScriptを意味します。vanillaは何かのライブラリーの名前ではなくて、「特別な味付けなし」って意味です。アイスクリームのバニラ味と同じです。 Viteが提供する他のテンプレートはreact-tsとかvue-tsという名前になっており、順にReact、Vueをセットアップしてくれます。vanilla-tsはそういう追加のライブラリーは含まれていないテンプレートになります。
akira_kano1101

2023/06/03 13:15

なるほど!vanilla-tsの名前についての豆知識まで教えてくれてありがとうございます。 私はRustが好きなのですが、TypeScriptも好きですね!型がしっかりしているのが素敵です。 Viteもよく使われているのですね。そのうちお世話になりそうです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問