Merged PR 4: タスク 1385: API実装(認証/トークン発行)

## 概要
[Task 1385](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/OMDSDictation/_workitems/edit/1385)
- IDトークンを受け取り、リフレッシュトークンとアクセストークンを発行するAPIを実装
- リフレッシュトークンを受け取り、アクセストークンを発行するAPIを実装
  - アクセストークン、リフレッシュトークンの中身は中身を使用するPBIが存在しないので仮のもの

## レビューポイント
- 本PBIで必要な範囲の機能を十分持っているか
- コードに不足部分、問題点などはなさそうか
- テスト項目は妥当か
- アクセストークン、リフレッシュトークンの中身はまだ仮で問題ないか

## 動作確認状況
- ローカルで動作確認
This commit is contained in:
湯本 開 2023-03-07 23:35:53 +00:00
parent 41f0213fe9
commit c1ed541d87
14 changed files with 1236 additions and 120 deletions

View File

@ -6,5 +6,8 @@ DB_ROOT_PASS=omdsdbpass
DB_USERNAME=omdsdbuser DB_USERNAME=omdsdbuser
DB_PASSWORD=omdsdbpass DB_PASSWORD=omdsdbpass
NO_COLOR=TRUE NO_COLOR=TRUE
ACCESS_TOKEN_LIFETIME_WEB=1600
REFRESH_TOKEN_LIFETIME_WEB=86400
REFRESH_TOKEN_LIFETIME_DEFAULT=2592000
TENANT_NAME=adb2codmsdev TENANT_NAME=adb2codmsdev
SIGNIN_FLOW_NAME=b2c_1_signin_dev SIGNIN_FLOW_NAME=b2c_1_signin_dev

View File

@ -1,3 +1,7 @@
STAGE=local STAGE=local
CORS=TRUE CORS=TRUE
PORT=8081 PORT=8081
AZURE_TENANT_ID=xxxxxxxx
AZURE_CLIENT_ID=xxxxxxxx
AZURE_CLIENT_SECRET=xxxxxxxx
KEY_VAULT_NAME=kv-odms-secret-dev

View File

@ -9,6 +9,9 @@
"version": "0.0.1", "version": "0.0.1",
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {
"@azure/identity": "^3.1.3",
"@azure/keyvault-secrets": "^4.6.0",
"@nestjs/axios": "^0.1.0",
"@nestjs/common": "^8.0.0", "@nestjs/common": "^8.0.0",
"@nestjs/config": "^2.2.0", "@nestjs/config": "^2.2.0",
"@nestjs/core": "^8.0.0", "@nestjs/core": "^8.0.0",
@ -285,6 +288,251 @@
"openapi-types": ">=7" "openapi-types": ">=7"
} }
}, },
"node_modules/@azure/abort-controller": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
"integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==",
"dependencies": {
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@azure/core-auth": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz",
"integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@azure/core-client": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.2.tgz",
"integrity": "sha512-ye5554gnVnXdfZ64hptUtETgacXoRWxYv1JF5MctoAzTSH5dXhDPZd9gOjDPyWMcLIk58pnP5+p5vGX6PYn1ag==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.4.0",
"@azure/core-rest-pipeline": "^1.9.1",
"@azure/core-tracing": "^1.0.0",
"@azure/core-util": "^1.0.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@azure/core-http-compat": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz",
"integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==",
"dependencies": {
"@azure/abort-controller": "^1.0.4",
"@azure/core-client": "^1.3.0",
"@azure/core-rest-pipeline": "^1.3.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@azure/core-lro": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.1.tgz",
"integrity": "sha512-JHQy/bA3NOz2WuzOi5zEk6n/TJdAropupxUT521JIJvW7EXV2YN2SFYZrf/2RHeD28QAClGdynYadZsbmP+nyQ==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@azure/core-paging": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz",
"integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==",
"dependencies": {
"tslib": "^2.2.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@azure/core-rest-pipeline": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz",
"integrity": "sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.4.0",
"@azure/core-tracing": "^1.0.1",
"@azure/core-util": "^1.0.0",
"@azure/logger": "^1.0.0",
"form-data": "^4.0.0",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
"tslib": "^2.2.0",
"uuid": "^8.3.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@azure/core-tracing": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
"integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
"dependencies": {
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@azure/core-util": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.1.1.tgz",
"integrity": "sha512-A4TBYVQCtHOigFb2ETiiKFDocBoI1Zk2Ui1KpI42aJSIDexF7DHQFpnjonltXAIU/ceH+1fsZAWWgvX6/AKzog==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@azure/identity": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@azure/identity/-/identity-3.1.3.tgz",
"integrity": "sha512-y0jFjSfHsVPwXSwi3KaSPtOZtJZqhiqAhWUXfFYBUd/+twUBovZRXspBwLrF5rJe0r5NyvmScpQjL+TYDTQVvw==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-client": "^1.4.0",
"@azure/core-rest-pipeline": "^1.1.0",
"@azure/core-tracing": "^1.0.0",
"@azure/core-util": "^1.0.0",
"@azure/logger": "^1.0.0",
"@azure/msal-browser": "^2.32.2",
"@azure/msal-common": "^9.0.2",
"@azure/msal-node": "^1.14.6",
"events": "^3.0.0",
"jws": "^4.0.0",
"open": "^8.0.0",
"stoppable": "^1.1.0",
"tslib": "^2.2.0",
"uuid": "^8.3.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@azure/identity/node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/@azure/identity/node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"dependencies": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"node_modules/@azure/keyvault-secrets": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.6.0.tgz",
"integrity": "sha512-MDqsyODCGC2srqLKmO6MFw9WdgLrbPsfCNxgbekHXEd6XKM6KKyBlup5joj96EmdfZnXDFriecAIpj0Dtu81RQ==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-client": "^1.5.0",
"@azure/core-http-compat": "^1.3.0",
"@azure/core-lro": "^2.2.0",
"@azure/core-paging": "^1.1.1",
"@azure/core-rest-pipeline": "^1.8.0",
"@azure/core-tracing": "^1.0.0",
"@azure/core-util": "^1.0.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@azure/logger": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz",
"integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==",
"dependencies": {
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@azure/msal-browser": {
"version": "2.33.0",
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.33.0.tgz",
"integrity": "sha512-c7CVh1tfUfxiWkEIhoIb11hL4PGo4hz0M+gMy34ATagAKdLK7qyEu/5AXJWAf5lz5eE+vQhm7+LKiuETrcXXGw==",
"dependencies": {
"@azure/msal-common": "^10.0.0"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@azure/msal-browser/node_modules/@azure/msal-common": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-10.0.0.tgz",
"integrity": "sha512-/LghpT93jsZLy55QzTsRZWMx6R1Mjc1Aktwps8sKSGE3WbrGwbSsh2uhDlpl6FMcKChYjJ0ochThWwwOodrQNg==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@azure/msal-common": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-9.1.1.tgz",
"integrity": "sha512-we9xR8lvu47fF0h+J8KyXoRy9+G/fPzm3QEa2TrdR3jaVS3LKAyE2qyMuUkNdbVkvzl8Zr9f7l+IUSP22HeqXw==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@azure/msal-node": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.15.0.tgz",
"integrity": "sha512-fwC5M0c8pxOAzmScPbpx7j28YVTDebUaizlVF7bR0xvlU0r3VWW5OobCcr9ybqKS6wGyO7u4EhXJS9rjRWAuwA==",
"dependencies": {
"@azure/msal-common": "^10.0.0",
"jsonwebtoken": "^9.0.0",
"uuid": "^8.3.0"
},
"engines": {
"node": "10 || 12 || 14 || 16 || 18"
}
},
"node_modules/@azure/msal-node/node_modules/@azure/msal-common": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-10.0.0.tgz",
"integrity": "sha512-/LghpT93jsZLy55QzTsRZWMx6R1Mjc1Aktwps8sKSGE3WbrGwbSsh2uhDlpl6FMcKChYjJ0ochThWwwOodrQNg==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
@ -1466,6 +1714,29 @@
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
"dev": true "dev": true
}, },
"node_modules/@nestjs/axios": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.1.1.tgz",
"integrity": "sha512-rLEq6yfho2CZyOcxP+P4Q3FjkNuiiHDyzj3Cr9i4Kdn3Ng09ygtOB4++jjXPREc6650pOFfzNtw18QH7bfLnQA==",
"dependencies": {
"axios": "1.2.1"
},
"peerDependencies": {
"@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0",
"reflect-metadata": "^0.1.12",
"rxjs": "^6.0.0 || ^7.0.0"
}
},
"node_modules/@nestjs/axios/node_modules/axios": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz",
"integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/@nestjs/cli": { "node_modules/@nestjs/cli": {
"version": "8.2.8", "version": "8.2.8",
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-8.2.8.tgz", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-8.2.8.tgz",
@ -1516,15 +1787,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@nestjs/cli/node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true,
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/@nestjs/cli/node_modules/json5": { "node_modules/@nestjs/cli/node_modules/json5": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
@ -2342,6 +2604,14 @@
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
"integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="
}, },
"node_modules/@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
"engines": {
"node": ">= 10"
}
},
"node_modules/@tsconfig/node10": { "node_modules/@tsconfig/node10": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@ -3073,6 +3343,17 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/ajv": { "node_modules/ajv": {
"version": "8.12.0", "version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
@ -4292,6 +4573,14 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
"engines": {
"node": ">=8"
}
},
"node_modules/delayed-stream": { "node_modules/delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -4840,6 +5129,14 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/execa": { "node_modules/execa": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@ -5691,6 +5988,31 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
"dependencies": {
"@tootallnate/once": "2",
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/human-signals": { "node_modules/human-signals": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@ -5872,6 +6194,20 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extglob": { "node_modules/is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -5964,6 +6300,17 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dependencies": {
"is-docker": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/isarray": { "node_modules/isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -7877,6 +8224,22 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/open": {
"version": "8.4.2",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
"is-wsl": "^2.2.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/openapi-types": { "node_modules/openapi-types": {
"version": "12.1.0", "version": "12.1.0",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz",
@ -9103,6 +9466,15 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/stoppable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
"integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==",
"engines": {
"node": ">=4",
"npm": ">=6"
}
},
"node_modules/streamsearch": { "node_modules/streamsearch": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@ -10313,16 +10685,6 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/webpack/node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/whatwg-url": { "node_modules/whatwg-url": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
@ -10774,6 +11136,206 @@
"call-me-maybe": "^1.0.1" "call-me-maybe": "^1.0.1"
} }
}, },
"@azure/abort-controller": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz",
"integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==",
"requires": {
"tslib": "^2.2.0"
}
},
"@azure/core-auth": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz",
"integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
}
},
"@azure/core-client": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.2.tgz",
"integrity": "sha512-ye5554gnVnXdfZ64hptUtETgacXoRWxYv1JF5MctoAzTSH5dXhDPZd9gOjDPyWMcLIk58pnP5+p5vGX6PYn1ag==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.4.0",
"@azure/core-rest-pipeline": "^1.9.1",
"@azure/core-tracing": "^1.0.0",
"@azure/core-util": "^1.0.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
}
},
"@azure/core-http-compat": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz",
"integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==",
"requires": {
"@azure/abort-controller": "^1.0.4",
"@azure/core-client": "^1.3.0",
"@azure/core-rest-pipeline": "^1.3.0"
}
},
"@azure/core-lro": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.1.tgz",
"integrity": "sha512-JHQy/bA3NOz2WuzOi5zEk6n/TJdAropupxUT521JIJvW7EXV2YN2SFYZrf/2RHeD28QAClGdynYadZsbmP+nyQ==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
}
},
"@azure/core-paging": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz",
"integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==",
"requires": {
"tslib": "^2.2.0"
}
},
"@azure/core-rest-pipeline": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz",
"integrity": "sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.4.0",
"@azure/core-tracing": "^1.0.1",
"@azure/core-util": "^1.0.0",
"@azure/logger": "^1.0.0",
"form-data": "^4.0.0",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
"tslib": "^2.2.0",
"uuid": "^8.3.0"
}
},
"@azure/core-tracing": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
"integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
"requires": {
"tslib": "^2.2.0"
}
},
"@azure/core-util": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.1.1.tgz",
"integrity": "sha512-A4TBYVQCtHOigFb2ETiiKFDocBoI1Zk2Ui1KpI42aJSIDexF7DHQFpnjonltXAIU/ceH+1fsZAWWgvX6/AKzog==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
}
},
"@azure/identity": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@azure/identity/-/identity-3.1.3.tgz",
"integrity": "sha512-y0jFjSfHsVPwXSwi3KaSPtOZtJZqhiqAhWUXfFYBUd/+twUBovZRXspBwLrF5rJe0r5NyvmScpQjL+TYDTQVvw==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-client": "^1.4.0",
"@azure/core-rest-pipeline": "^1.1.0",
"@azure/core-tracing": "^1.0.0",
"@azure/core-util": "^1.0.0",
"@azure/logger": "^1.0.0",
"@azure/msal-browser": "^2.32.2",
"@azure/msal-common": "^9.0.2",
"@azure/msal-node": "^1.14.6",
"events": "^3.0.0",
"jws": "^4.0.0",
"open": "^8.0.0",
"stoppable": "^1.1.0",
"tslib": "^2.2.0",
"uuid": "^8.3.0"
},
"dependencies": {
"jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"requires": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
}
}
},
"@azure/keyvault-secrets": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.6.0.tgz",
"integrity": "sha512-MDqsyODCGC2srqLKmO6MFw9WdgLrbPsfCNxgbekHXEd6XKM6KKyBlup5joj96EmdfZnXDFriecAIpj0Dtu81RQ==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-client": "^1.5.0",
"@azure/core-http-compat": "^1.3.0",
"@azure/core-lro": "^2.2.0",
"@azure/core-paging": "^1.1.1",
"@azure/core-rest-pipeline": "^1.8.0",
"@azure/core-tracing": "^1.0.0",
"@azure/core-util": "^1.0.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
}
},
"@azure/logger": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz",
"integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==",
"requires": {
"tslib": "^2.2.0"
}
},
"@azure/msal-browser": {
"version": "2.33.0",
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.33.0.tgz",
"integrity": "sha512-c7CVh1tfUfxiWkEIhoIb11hL4PGo4hz0M+gMy34ATagAKdLK7qyEu/5AXJWAf5lz5eE+vQhm7+LKiuETrcXXGw==",
"requires": {
"@azure/msal-common": "^10.0.0"
},
"dependencies": {
"@azure/msal-common": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-10.0.0.tgz",
"integrity": "sha512-/LghpT93jsZLy55QzTsRZWMx6R1Mjc1Aktwps8sKSGE3WbrGwbSsh2uhDlpl6FMcKChYjJ0ochThWwwOodrQNg=="
}
}
},
"@azure/msal-common": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-9.1.1.tgz",
"integrity": "sha512-we9xR8lvu47fF0h+J8KyXoRy9+G/fPzm3QEa2TrdR3jaVS3LKAyE2qyMuUkNdbVkvzl8Zr9f7l+IUSP22HeqXw=="
},
"@azure/msal-node": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.15.0.tgz",
"integrity": "sha512-fwC5M0c8pxOAzmScPbpx7j28YVTDebUaizlVF7bR0xvlU0r3VWW5OobCcr9ybqKS6wGyO7u4EhXJS9rjRWAuwA==",
"requires": {
"@azure/msal-common": "^10.0.0",
"jsonwebtoken": "^9.0.0",
"uuid": "^8.3.0"
},
"dependencies": {
"@azure/msal-common": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-10.0.0.tgz",
"integrity": "sha512-/LghpT93jsZLy55QzTsRZWMx6R1Mjc1Aktwps8sKSGE3WbrGwbSsh2uhDlpl6FMcKChYjJ0ochThWwwOodrQNg=="
}
}
},
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
@ -11695,6 +12257,26 @@
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
"dev": true "dev": true
}, },
"@nestjs/axios": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.1.1.tgz",
"integrity": "sha512-rLEq6yfho2CZyOcxP+P4Q3FjkNuiiHDyzj3Cr9i4Kdn3Ng09ygtOB4++jjXPREc6650pOFfzNtw18QH7bfLnQA==",
"requires": {
"axios": "1.2.1"
},
"dependencies": {
"axios": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz",
"integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==",
"requires": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
}
}
},
"@nestjs/cli": { "@nestjs/cli": {
"version": "8.2.8", "version": "8.2.8",
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-8.2.8.tgz", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-8.2.8.tgz",
@ -11735,12 +12317,6 @@
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
} }
}, },
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true
},
"json5": { "json5": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
@ -12304,6 +12880,11 @@
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
"integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="
}, },
"@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="
},
"@tsconfig/node10": { "@tsconfig/node10": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@ -12930,6 +13511,14 @@
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
"devOptional": true "devOptional": true
}, },
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"requires": {
"debug": "4"
}
},
"ajv": { "ajv": {
"version": "8.12.0", "version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
@ -13824,6 +14413,11 @@
"clone": "^1.0.2" "clone": "^1.0.2"
} }
}, },
"define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="
},
"delayed-stream": { "delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -14231,6 +14825,11 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
}, },
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
},
"execa": { "execa": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@ -14888,6 +15487,25 @@
"toidentifier": "1.0.1" "toidentifier": "1.0.1"
} }
}, },
"http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
"requires": {
"@tootallnate/once": "2",
"agent-base": "6",
"debug": "4"
}
},
"https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"requires": {
"agent-base": "6",
"debug": "4"
}
},
"human-signals": { "human-signals": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@ -15026,6 +15644,11 @@
"has": "^1.0.3" "has": "^1.0.3"
} }
}, },
"is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
},
"is-extglob": { "is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -15085,6 +15708,14 @@
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="
}, },
"is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"requires": {
"is-docker": "^2.0.0"
}
},
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -16585,6 +17216,16 @@
"mimic-fn": "^2.1.0" "mimic-fn": "^2.1.0"
} }
}, },
"open": {
"version": "8.4.2",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
"requires": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
"is-wsl": "^2.2.0"
}
},
"openapi-types": { "openapi-types": {
"version": "12.1.0", "version": "12.1.0",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz",
@ -17526,6 +18167,11 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
}, },
"stoppable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
"integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="
},
"streamsearch": { "streamsearch": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@ -18299,15 +18945,6 @@
"terser-webpack-plugin": "^5.1.3", "terser-webpack-plugin": "^5.1.3",
"watchpack": "^2.4.0", "watchpack": "^2.4.0",
"webpack-sources": "^3.2.3" "webpack-sources": "^3.2.3"
},
"dependencies": {
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true,
"peer": true
}
} }
}, },
"webpack-node-externals": { "webpack-node-externals": {

View File

@ -25,6 +25,9 @@
"swgbundle:sentinel": "swagger-cli bundle -o openapi/build/bundle.yml -t yaml openapi/sentinel_ems/sentinel_ems.yml" "swgbundle:sentinel": "swagger-cli bundle -o openapi/build/bundle.yml -t yaml openapi/sentinel_ems/sentinel_ems.yml"
}, },
"dependencies": { "dependencies": {
"@azure/identity": "^3.1.3",
"@azure/keyvault-secrets": "^4.6.0",
"@nestjs/axios": "^0.1.0",
"@nestjs/common": "^8.0.0", "@nestjs/common": "^8.0.0",
"@nestjs/config": "^2.2.0", "@nestjs/config": "^2.2.0",
"@nestjs/core": "^8.0.0", "@nestjs/core": "^8.0.0",

View File

@ -0,0 +1,3 @@
import { isVerifyError, sign, verify } from './jwt';
export { isVerifyError, sign, verify };

View File

@ -0,0 +1,250 @@
import { sign, verify, isVerifyError } from './jwt';
import base64url from 'base64url';
test('success sign and verify', () => {
const token = sign({ value: 'testvalue' }, 5 * 60, privateKey);
const payload = verify<{ value: 'testvalue' }>(token, publicKey);
if (isVerifyError(payload)) {
throw new Error(`${payload.reason} | ${payload.message}`);
}
expect(payload.value).toBe('testvalue');
});
test('failed sign and verify (jwt expired)', () => {
// 有効期限を0秒にすることで、検証を行った時点で有効期限切れにする
const token = sign({ value: 'testvalue' }, 0, privateKey);
const payload = verify<{ value: 'testvalue' }>(token, publicKey);
if (!isVerifyError(payload)) {
throw new Error(JSON.stringify(payload));
}
expect(payload.reason).toBe('ExpiredError');
});
test('failed sign and verify (invalid key pair)', () => {
const token = sign({ value: 'testvalue' }, 5 * 60, privateKey);
// 秘密鍵と対ではない公開鍵を使用して検証する
const payload = verify<{ value: 'testvalue' }>(token, anotherPublicKey);
if (!isVerifyError(payload)) {
throw new Error(JSON.stringify(payload));
}
expect(payload.reason).toBe('InvalidToken');
expect(payload.message).toBe('invalid signature');
});
test('failed sign and verify (invalid public key)', () => {
const token = sign({ value: 'testvalue' }, 5 * 60, privateKey);
// 公開鍵の形式になっていない文字列を使用して検証する
const payload = verify<{ value: 'testvalue' }>(token, fakePublicKey);
if (!isVerifyError(payload)) {
throw new Error(JSON.stringify(payload));
}
expect(payload.reason).toBe('InvalidToken');
expect(payload.message).toBe(
'secretOrPublicKey must be an asymmetric key when using RS256',
);
});
test('failed sign (invalid private key)', () => {
expect(() => {
// 不正な秘密鍵で署名しようとする場合はエラーがthrowされる
sign({ value: 'testvalue' }, 5 * 60, fakePrivateKey);
}).toThrowError();
});
test('success rewrite-token verify (as is)', () => {
const token = sign({ value: 'testvalue' }, 5 * 60, privateKey);
const { header, payload, verifySignature } = splitToken(token);
{
// 何も操作せずに構築しなおした場合、成功する
const validToken = rebuildToken(header, payload, verifySignature);
const value = verify<{ value: string }>(validToken, publicKey);
if (isVerifyError(value)) {
throw new Error(`${value.reason} | ${value.message}`);
}
expect(value.value).toBe('testvalue');
}
});
test('failed rewrite-token verify (override algorithm)', () => {
const token = sign({ value: 'testvalue' }, 5 * 60, privateKey);
const { payload, verifySignature } = splitToken(token);
{
// 検証アルゴリズムを「検証なし」に書き換える
const headerObject = { alg: 'none' };
const payloadObject = JSON.parse(payload) as {
value: string;
iat: number;
exp: number;
};
// 内容を操作して構築しなおした場合、失敗する
const customToken = rebuildToken(
JSON.stringify(headerObject),
JSON.stringify(payloadObject),
verifySignature,
);
const value = verify<{ value: string }>(customToken, publicKey);
if (!isVerifyError(value)) {
throw new Error(JSON.stringify(payload));
}
expect(value.reason).toBe('InvalidToken');
expect(value.message).toBe('invalid algorithm');
}
});
test('failed rewrite-token verify (override expire)', () => {
const token = sign({ value: 'testvalue' }, 5 * 60, privateKey);
const { header, payload, verifySignature } = splitToken(token);
{
// expの値を操作する
const payloadObject = JSON.parse(payload) as {
value: string;
iat: number;
exp: number;
};
payloadObject.exp = payloadObject.exp + 100000;
// 内容を操作して構築しなおした場合、失敗する
const customToken = rebuildToken(
header,
JSON.stringify(payloadObject),
verifySignature,
);
const value = verify<{ value: string }>(customToken, publicKey);
if (!isVerifyError(value)) {
throw new Error(JSON.stringify(payload));
}
expect(value.reason).toBe('InvalidToken');
expect(value.message).toBe('invalid signature');
}
});
// JWT改竄テスト用ユーティリティ
const splitToken = (
token: string,
): { header: string; payload: string; verifySignature: string } => {
const splited = token.split('.');
const header = base64url.decode(splited[0]);
const payload = base64url.decode(splited[1]);
const verifySignature = splited[2];
return { header, payload, verifySignature };
};
// JWT改竄テスト用ユーティリティ
const rebuildToken = (
header: string,
payload: string,
verifySignature: string,
): string => {
const rebuild_header = base64url.encode(header);
const rebuild_payload = base64url.encode(payload);
return `${rebuild_header}.${rebuild_payload}.${verifySignature}`;
};
// テスト用に生成した秘密鍵
const privateKey = [
'-----BEGIN RSA PRIVATE KEY-----',
'MIIEpAIBAAKCAQEAsTVLNpW0/FzVCU7qo1DDjOkYWx6s/jE56YOOc3UzaaG/zb1F',
'GyfRoUUgS4DnQxPNz9oM63RpQlhvG6UCwx23tL7p3PS0ZCsLeggcyLctbJAzLy/a',
'fF9ABoreorqp/AaEs+Vdwbykb+M+nB2Sxsc57Tli2x8NiOZr5dafs3vMuIIKNsBa',
'FAugFrd2ApxXR04jBRAorZRRFPtECE7D+hxDalw5DCd0mmdY0vrbRsgkbej0Zzzq',
'zukJVXTMjy1YScqi3I9gLx2hLVmpK76Gtxn21AIcn8P3rKZmDyPH+9KNfWC8+ubF',
'+VuY6nItlCgiSyTKErAp6M9pyRHKbPpdUM3aIQIDAQABAoIBAQCk7fkmwIdGKhCN',
'LUns3opiZ8AnbpGLs702vR6kDvze35BoqDPdZl4RPwkjvMGBCLmRLly/+ATPnwcq',
'L5Y2iz4jl1yKLaaHZBi2Zz6DARnh5QP+cwdiojQw4qb7xcfXrSltVZjBbBWPnWz0',
'WAH3yAz94V9Emc47EFpz/CF/J0YOokxY8GlR4cwfK6NE0goAjzmatwV3IVFeR/eE',
'x6JZAmd/0HMfOn3k/NumAMCJXKnZMQBAMQ3AduTO2lbZm+29yBqymtzTGFjrj0gm',
'+E/ibD8vVzh0toPvUfPIqetdRT8vkUJ5UHhAkz9Vzvqhr6BhYhc2ft0x/z7HpaiX',
'cDqnaRLBAoGBAODdPEktK1VOVXhOuikZBUHXU25iQdQRbM4kCtWiE8lBZ/f+6OPc',
'BN+OedYMDhpFe/oFqGU4t610SPO1CdVRPnWHhMSabjh9G3gqOZjSW5tEAgT2wi+H',
'IOVXnsos1qCMFdXWgVZw6F8wNcui9VabGic/EOqMRihEeSOjcradTSQFAoGBAMm+',
'y2wZ8usanIDzADgTJnA4kBZzhIxK6qcPf3tPVXKuFUOFWwzGiDXeXTwM0sWN7kGb',
'iymqhTWlYETQ3C6jPXTJiyOSco1rw45wO+xSHeQvUzXpk+9whbVAlhTcoVGiKz+9',
'BS7+3+lKtBzXDNADxQfSGjiGb+ceilBGLV+WurRtAoGAPxn2a/aP/X1hAMTe+t95',
'mTNqx0Qtguxs4yA8Jh04fjarjW1sP10jxPR/fjCd2IN9OflSey1CZhuGyVUZcFI/',
'O84O1PkdSx7YkY0P4rHNYTHhezEf5yR9d75x4fxZMm59RifO3coLe4LU5dNSE76s',
'xSyue5NnsK8ea4DXlSVpW10CgYAfHz3GWWJt/lbyVYpNHDcrzK39qKhj9BKq3ust',
'nJlz7YL+PY5ENERC+yCq6NeC/lgo6tPXA6U1F2P4ebfdwfTzFTxPqoHdayhpysqT',
'tD9EOkC96mCV6WfXBDWi1j5Ul43QcVphW5QzKwEKCerCFDLK+BBvc93Da6SuqYTK',
'YDhBKQKBgQDKtNe8CjHRvkWoyKErMMpv5D0ce/yWq+oAaoqW1QKwngPyaiDeDwqM',
'iOJzQxtvK4YqMYQdkgj5VLfWzeazd28RLODZua6phe776zuUv93LHTvYq/8RZfhk',
'JIQJ7GETBnHmoTemwmJiSdVDsjJdtsyR4XRjIDNR5bGe7NNbZJpCUw==',
'-----END RSA PRIVATE KEY-----',
].join('\n');
// テスト用に生成した公開鍵
const publicKey = [
'-----BEGIN PUBLIC KEY-----',
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsTVLNpW0/FzVCU7qo1DD',
'jOkYWx6s/jE56YOOc3UzaaG/zb1FGyfRoUUgS4DnQxPNz9oM63RpQlhvG6UCwx23',
'tL7p3PS0ZCsLeggcyLctbJAzLy/afF9ABoreorqp/AaEs+Vdwbykb+M+nB2Sxsc5',
'7Tli2x8NiOZr5dafs3vMuIIKNsBaFAugFrd2ApxXR04jBRAorZRRFPtECE7D+hxD',
'alw5DCd0mmdY0vrbRsgkbej0ZzzqzukJVXTMjy1YScqi3I9gLx2hLVmpK76Gtxn2',
'1AIcn8P3rKZmDyPH+9KNfWC8+ubF+VuY6nItlCgiSyTKErAp6M9pyRHKbPpdUM3a',
'IQIDAQAB',
'-----END PUBLIC KEY-----',
].join('\n');
// テスト用に作成した、違う秘密鍵から生成した公開鍵
const anotherPublicKey = [
'-----BEGIN PUBLIC KEY-----',
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt1WsgrjpjsEfRa7vqlR3',
'2mGxErXpvC+uRQnFtSXdP4tEYicPb1cNFUcu5xW6attTyzKHKMzwJrvmKEKVYGig',
'n43rM+UyW79DNOQWQQblCHAc3hMolLWC+Tkw7xL4JhzZLH0rm5DF52YNYSicV1S9',
'RpxYEeyHUa+ExV82lT47ySWAwg+yPwtDeDPMbOxHXqyw1wdqR2WVuxsQBaIRQgMk',
'EL/qObQjA4e5jOOwERRvVLxzjhnldUZcG0cYGDfjPTewRYfCeXzMx2YM4Uo0vx0x',
'2ZIY+im061GvfugX4/31xB5YEi+62qIwuSL5UpKjMv5yx1cvIqO76Ro3XNwsR+81',
'KQIDAQAB',
'-----END PUBLIC KEY-----',
].join('\n');
// 秘密鍵のように見えるが想定する形式と違う
const fakePrivateKey = [
'-----BAGIN RSA PRIVATE KEY-----',
'MIIEpAIBAAKCAQEAsTVLNpW0/FzVCU7qo1DDjOkYWx6s/jE56YOOc3UzaaG/zb1F',
'GyfRoUUgS4DnQxPNz9oM63RpQlhvG6UCwx23tL7p3PS0ZCsLeggcyLctbJAzLy/a',
'fF9ABoreorqp/AaEs+Vdwbykb+M+nB2Sxsc57Tli2x8NiOZr5dafs3vMuIIKNsBa',
'FAugFrd2ApxXR04jBRAorZRRFPtECE7D+hxDalw5DCd0mmdY0vrbRsgkbej0Zzzq',
'zukJVXTMjy1YScqi3I9gLx2hLVmpK76Gtxn21AIcn8P3rKZmDyPH+9KNfWC8+ubF',
'+VuY6nItlCgiSyTKErAp6M9pyRHKbPpdUM3aIQIDAQABAoIBAQCk7fkmwIdGKhCN',
'LUns3opiZ8AnbpGLs702vR6kDvze35BoqDPdZl4RPwkjvMGBCLmRLly/+ATPnwcq',
'L5Y2iz4jl1yKLaaHZBi2Zz6DARnh5QP+cwdiojQw4qb7xcfXrSltVZjBbBWPnWz0',
'WAH3yAz94V9Emc47EFpz/CF/J0YOokxY8GlR4cwfK6NE0goAjzmatwV3IVFeR/eE',
'x6JZAmd/0HMfOn3k/NumAMCJXKnZMQBAMQ3AduTO2lbZm+29yBqymtzTGFjrj0gm',
'+E/ibD8vVzh0toPvUfPIqetdRT8vkUJ5UHhAkz9Vzvqhr6BhYhc2ft0x/z7HpaiX',
'cDqnaRLBAoGBAODdPEktK1VOVXhOuikZBUHXU25iQdQRbM4kCtWiE8lBZ/f+6OPc',
'BN+OedYMDhpFe/oFqGU4t610SPO1CdVRPnWHhMSabjh9G3gqOZjSW5tEAgT2wi+H',
'IOVXnsos1qCMFdXWgVZw6F8wNcui9VabGic/EOqMRihEeSOjcradTSQFAoGBAMm+',
'y2wZ8usanIDzADgTJnA4kBZzhIxK6qcPf3tPVXKuFUOFWwzGiDXeXTwM0sWN7kGb',
'iymqhTWlYETQ3C6jPXTJiyOSco1rw45wO+xSHeQvUzXpk+9whbVAlhTcoVGiKz+9',
'BS7+3+lKtBzXDNADxQfSGjiGb+ceilBGLV+WurRtAoGAPxn2a/aP/X1hAMTe+t95',
'mTNqx0Qtguxs4yA8Jh04fjarjW1sP10jxPR/fjCd2IN9OflSey1CZhuGyVUZcFI/',
'O84O1PkdSx7YkY0P4rHNYTHhezEf5yR9d75x4fxZMm59RifO3coLe4LU5dNSE76s',
'xSyue5NnsK8ea4DXlSVpW10CgYAfHz3GWWJt/lbyVYpNHDcrzK39qKhj9BKq3ust',
'nJlz7YL+PY5ENERC+yCq6NeC/lgo6tPXA6U1F2P4ebfdwfTzFTxPqoHdayhpysqT',
'tD9EOkC96mCV6WfXBDWi1j5Ul43QcVphW5QzKwEKCerCFDLK+BBvc93Da6SuqYTK',
'YDhBKQKBgQDKtNe8CjHRvkWoyKErMMpv5D0ce/yWq+oAaoqW1QKwngPyaiDeDwqM',
'iOJzQxtvK4YqMYQdkgj5VLfWzeazd28RLODZua6phe776zuUv93LHTvYq/8RZfhk',
'JIQJ7GETBnHmoTemwmJiSdVDsjJdtsyR4XRjIDNR5bGe7NNbZJpCUw==',
'-----END RSA PRIVATE KEY-----',
].join('\n');
// 公開鍵のように見えるが想定する形式と違う
const fakePublicKey = [
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt1WsgrjpjsEfRa7vqlR3',
'2mGxErXpvC+uRQnFtSXdP4tEYicPb1cNFUcu5xW6attTyzKHKMzwJrvmKEKVYGig',
'n43rM+UyW79DNOQWQQblCHAc3hMolLWC+Tkw7xL4JhzZLH0rm5DF52YNYSicV1S9',
'RpxYEeyHUa+ExV82lT47ySWAwg+yPwtDeDPMbOxHXqyw1wdqR2WVuxsQBaIRQgMk',
'EL/qObQjA4e5jOOwERRvVLxzjhnldUZcG0cYGDfjPTewRYfCeXzMx2YM4Uo0vx0x',
'2ZIY+im061GvfugX4/31xB5YEi+62qIwuSL5UpKjMv5yx1cvIqO76Ro3XNwsR+81',
'KQIDAQAB',
].join('\n');

View File

@ -0,0 +1,90 @@
import * as jwt from 'jsonwebtoken';
export type VerifyError = {
reason: 'ExpiredError' | 'InvalidToken' | 'InvalidTimeStamp' | 'Unknown';
message: string;
};
export const isVerifyError = (arg: unknown): arg is VerifyError => {
const value = arg as VerifyError;
if (value.message === undefined) {
return false;
}
if (value.reason === undefined) {
return false;
}
switch (value.reason) {
case 'ExpiredError':
case 'InvalidTimeStamp':
case 'InvalidToken':
case 'Unknown':
return true;
default:
return false;
}
};
/**
* Payloadと秘密鍵を使用して署名されたJWTを生成します
* @param {T} payload payloadの型
* @param {number} expirationSeconds ()
* @param {string} privateKey 使
* @return {string}
* @throws {Error} Errorオブジェクト
*/
export const sign = <T extends object>(
payload: T,
expirationSeconds: number,
privateKey: string,
): string => {
try {
const token = jwt.sign(payload, privateKey, {
expiresIn: expirationSeconds,
algorithm: 'RS256',
});
return token;
} catch (e) {
throw e;
}
};
/**
* tokenと公開鍵を使用して検証済みJWTのpayloadを取得します
* @param {string} token JWT
* @param {string} publicKey 使
* @return {T | VerifyError} Payload
*/
export const verify = <T extends object>(
token: string,
publicKey: string,
): T | VerifyError => {
try {
const payload = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
}) as T;
return payload;
} catch (e) {
if (e instanceof jwt.TokenExpiredError) {
return {
reason: 'ExpiredError',
message: e.message,
};
} else if (e instanceof jwt.NotBeforeError) {
return {
reason: 'InvalidTimeStamp',
message: e.message,
};
} else if (e instanceof jwt.JsonWebTokenError) {
return {
reason: 'InvalidToken',
message: e.message,
};
} else {
return {
reason: 'Unknown',
message: e.message,
};
}
}
};

View File

@ -1,20 +1,11 @@
export type RefreshToken = { import type {
scope: string; AccessToken,
}; B2cMetadata,
IDToken,
JwkSignKey,
RefreshToken,
} from './types';
import { isIDToken } from './typeguard';
export type AccessToken = { export type { AccessToken, B2cMetadata, IDToken, JwkSignKey, RefreshToken };
scope: string; export { isIDToken };
};
export type B2cMetadata = {
issuer: string;
};
export type JwkSignKey = {
kid: string;
nbf: number;
use: string;
kty: string;
e: string;
n: string;
};

View File

@ -0,0 +1,18 @@
import jwt from 'jsonwebtoken';
import { IDToken } from './types';
export const isIDToken = (
payload: jwt.JwtPayload | string,
): payload is IDToken => {
if (typeof payload === 'string') return false;
const token = payload as IDToken;
if (token.sub === undefined) return false;
if (token.emails === undefined) return false;
if (token.exp === undefined) return false;
if (token.iat === undefined) return false;
if (token.family_name === undefined) return false;
if (token.given_name === undefined) return false;
if (token.nonce === undefined) return false;
return true;
};

View File

@ -0,0 +1,32 @@
export type RefreshToken = {
userId: string;
scope: string;
};
export type AccessToken = {
userId: string;
scope: string;
};
export type IDToken = {
emails: string[];
family_name: string;
given_name: string;
nonce: string;
sub: string; // AzureAD B2C ID
exp: number; // 有効期限
iat: number; // 発行日時
};
export type B2cMetadata = {
issuer: string;
};
export type JwkSignKey = {
kid: string;
nbf: number;
use: string;
kty: string;
e: string;
n: string;
};

View File

@ -1,10 +1,18 @@
import { Body, Controller, Headers, HttpStatus, Post } from '@nestjs/common'; import {
Body,
Controller,
Headers,
HttpException,
HttpStatus,
Post,
} from '@nestjs/common';
import { import {
ApiResponse, ApiResponse,
ApiOperation, ApiOperation,
ApiBearerAuth, ApiBearerAuth,
ApiTags, ApiTags,
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { ErrorResponse } from '../../common/error/types/types'; import { ErrorResponse } from '../../common/error/types/types';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import { import {
@ -36,12 +44,19 @@ export class AuthController {
}) })
@ApiOperation({ operationId: 'token' }) @ApiOperation({ operationId: 'token' })
async token(@Body() body: TokenRequest): Promise<TokenResponse> { async token(@Body() body: TokenRequest): Promise<TokenResponse> {
const payload = await this.authService.getVerifiedIdToken(body.idToken); console.log(body);
console.log(payload); const idToken = await this.authService.getVerifiedIdToken(body.idToken);
const refreshToken = await this.authService.generateRefreshToken(
idToken,
body.type,
);
const accessToken = await this.authService.generateAccessToken(
refreshToken,
);
return { return {
accessToken: '', accessToken,
refreshToken: '', refreshToken,
}; };
} }
@ -65,15 +80,20 @@ export class AuthController {
@ApiOperation({ operationId: 'accessToken' }) @ApiOperation({ operationId: 'accessToken' })
async accessToken(@Headers() headers): Promise<AccessTokenResponse> { async accessToken(@Headers() headers): Promise<AccessTokenResponse> {
console.log(headers['authorization']); console.log(headers['authorization']);
const header = headers['authorization'];
if (typeof header === 'string') {
if (header.startsWith('Bearer ')) {
const refreshToken = header.substring('Bearer '.length, header.length);
const accessToken = await this.authService.generateAccessToken(
refreshToken,
);
const authHeader = headers['authorization']; return { accessToken };
}
let refreshToken = '';
if (typeof authHeader === 'string') {
refreshToken = authHeader.replace('Bearer ', '');
} }
console.log(refreshToken); throw new HttpException(
makeErrorResponse('E009999'),
return { accessToken: '' }; HttpStatus.UNAUTHORIZED,
);
} }
} }

View File

@ -1,18 +1,86 @@
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'; import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { isVerifyError, sign, verify } from '../../common/jwt';
import {
AccessToken,
IDToken,
isIDToken,
RefreshToken,
} from '../../common/token';
import { AdB2cService } from '../../gateways/adb2c/adb2c.service'; import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
import { CryptoService } from '../../gateways/crypto/crypto.service'; import { CryptoService } from '../../gateways/crypto/crypto.service';
@Injectable() @Injectable()
export class AuthService { export class AuthService {
constructor( constructor(
private adB2cService: AdB2cService, private readonly cryptoService: CryptoService,
private cryptoService: CryptoService, private readonly adB2cService: AdB2cService,
private readonly configService: ConfigService,
) {} ) {}
private readonly logger = new Logger(AuthService.name); private readonly logger = new Logger(AuthService.name);
async getVerifiedIdToken(token: string): Promise<any> { /**
* Generates refresh token
* @param idToken AzureAD B2Cにより発行されたIDトークン
* @param type (web/desktop/mobile)
* @returns refresh token
*/
async generateRefreshToken(idToken: IDToken, type: string): Promise<string> {
const lifetimeWeb = this.configService.get('REFRESH_TOKEN_LIFETIME_WEB');
const lifetimeDefault = this.configService.get(
'REFRESH_TOKEN_LIFETIME_DEFAULT',
);
// 要求された環境用トークンの寿命を決定
const refreshTokenLifetime = type === 'web' ? lifetimeWeb : lifetimeDefault;
const privateKey = await this.cryptoService.getPrivateKey();
const token = sign<RefreshToken>(
{
scope: 'test',
userId: idToken.sub,
},
refreshTokenLifetime,
privateKey,
);
return token;
}
/**
* Generates access token
* @param refreshToken (jwt)
* @returns access token(jwt)
*/
async generateAccessToken(refreshToken: string): Promise<string> {
const lifetime = this.configService.get('ACCESS_TOKEN_LIFETIME_WEB');
const privateKey = await this.cryptoService.getPrivateKey();
const pubkey = await this.cryptoService.getPublicKey();
const token = verify<RefreshToken>(refreshToken, pubkey);
if (isVerifyError(token)) {
throw new Error(`${token.reason} | ${token.message}`);
}
const accessToken = sign<AccessToken>(
{
scope: token.scope,
userId: token.userId,
},
lifetime,
privateKey,
);
return accessToken;
}
/**
* Gets id token
* @param token
* @returns id token
*/
async getVerifiedIdToken(token: string): Promise<IDToken> {
this.logger.log(`[IN] ${this.getVerifiedIdToken.name}`); this.logger.log(`[IN] ${this.getVerifiedIdToken.name}`);
let kid = ''; let kid = '';
@ -48,6 +116,9 @@ export class AuthService {
issuer: [issuer], issuer: [issuer],
}); });
if (!isIDToken(verifiedToken)) {
throw new Error('invalid format ID token');
}
return verifiedToken; return verifiedToken;
} catch (e) { } catch (e) {
if (e instanceof Error) { if (e instanceof Error) {

View File

@ -1,69 +1,66 @@
import { DefaultAzureCredential } from '@azure/identity';
import { SecretClient } from '@azure/keyvault-secrets';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import jwkToPem from 'jwk-to-pem'; import jwkToPem from 'jwk-to-pem';
import { JwkSignKey } from 'src/common/token'; import { JwkSignKey } from '../../common/token';
@Injectable() @Injectable()
export class CryptoService { export class CryptoService {
constructor(private readonly configService: ConfigService) {}
private readonly logger = new Logger(CryptoService.name); private readonly logger = new Logger(CryptoService.name);
private readonly credential: DefaultAzureCredential;
private readonly url: string;
constructor(private readonly configService: ConfigService) {
// DefaultAzureCredentialが内部で直接的に環境変数を読むので、明示的にセットする
process.env.AZURE_TENANT_ID = configService.get('AZURE_TENANT_ID');
process.env.AZURE_CLIENT_ID = configService.get('AZURE_CLIENT_ID');
process.env.AZURE_CLIENT_SECRET = configService.get('AZURE_CLIENT_SECRET');
// If you're using MSI, DefaultAzureCredential should "just work".
// Otherwise, DefaultAzureCredential expects the following three environment variables:
// - AZURE_TENANT_ID: The tenant ID in Azure Active Directory
// - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant
// - AZURE_CLIENT_SECRET: The client secret for the registered application
this.credential = new DefaultAzureCredential();
this.url =
'https://' + configService.get('KEY_VAULT_NAME') + '.vault.azure.net';
}
/**
* Gets private key
* @returns private key(PEM)
*/
async getPrivateKey(): Promise<string> { async getPrivateKey(): Promise<string> {
try { try {
// XXX 動作確認用 const client = new SecretClient(this.url, this.credential);
const privateKey = [ const secret = await client.getSecret('token-private-key');
'-----BEGIN RSA PRIVATE KEY-----', if (secret.value) {
'MIIEpAIBAAKCAQEAsTVLNpW0/FzVCU7qo1DDjOkYWx6s/jE56YOOc3UzaaG/zb1F', return secret.value;
'GyfRoUUgS4DnQxPNz9oM63RpQlhvG6UCwx23tL7p3PS0ZCsLeggcyLctbJAzLy/a', } else {
'fF9ABoreorqp/AaEs+Vdwbykb+M+nB2Sxsc57Tli2x8NiOZr5dafs3vMuIIKNsBa', throw new Error('private key was empty');
'FAugFrd2ApxXR04jBRAorZRRFPtECE7D+hxDalw5DCd0mmdY0vrbRsgkbej0Zzzq', }
'zukJVXTMjy1YScqi3I9gLx2hLVmpK76Gtxn21AIcn8P3rKZmDyPH+9KNfWC8+ubF',
'+VuY6nItlCgiSyTKErAp6M9pyRHKbPpdUM3aIQIDAQABAoIBAQCk7fkmwIdGKhCN',
'LUns3opiZ8AnbpGLs702vR6kDvze35BoqDPdZl4RPwkjvMGBCLmRLly/+ATPnwcq',
'L5Y2iz4jl1yKLaaHZBi2Zz6DARnh5QP+cwdiojQw4qb7xcfXrSltVZjBbBWPnWz0',
'WAH3yAz94V9Emc47EFpz/CF/J0YOokxY8GlR4cwfK6NE0goAjzmatwV3IVFeR/eE',
'x6JZAmd/0HMfOn3k/NumAMCJXKnZMQBAMQ3AduTO2lbZm+29yBqymtzTGFjrj0gm',
'+E/ibD8vVzh0toPvUfPIqetdRT8vkUJ5UHhAkz9Vzvqhr6BhYhc2ft0x/z7HpaiX',
'cDqnaRLBAoGBAODdPEktK1VOVXhOuikZBUHXU25iQdQRbM4kCtWiE8lBZ/f+6OPc',
'BN+OedYMDhpFe/oFqGU4t610SPO1CdVRPnWHhMSabjh9G3gqOZjSW5tEAgT2wi+H',
'IOVXnsos1qCMFdXWgVZw6F8wNcui9VabGic/EOqMRihEeSOjcradTSQFAoGBAMm+',
'y2wZ8usanIDzADgTJnA4kBZzhIxK6qcPf3tPVXKuFUOFWwzGiDXeXTwM0sWN7kGb',
'iymqhTWlYETQ3C6jPXTJiyOSco1rw45wO+xSHeQvUzXpk+9whbVAlhTcoVGiKz+9',
'BS7+3+lKtBzXDNADxQfSGjiGb+ceilBGLV+WurRtAoGAPxn2a/aP/X1hAMTe+t95',
'mTNqx0Qtguxs4yA8Jh04fjarjW1sP10jxPR/fjCd2IN9OflSey1CZhuGyVUZcFI/',
'O84O1PkdSx7YkY0P4rHNYTHhezEf5yR9d75x4fxZMm59RifO3coLe4LU5dNSE76s',
'xSyue5NnsK8ea4DXlSVpW10CgYAfHz3GWWJt/lbyVYpNHDcrzK39qKhj9BKq3ust',
'nJlz7YL+PY5ENERC+yCq6NeC/lgo6tPXA6U1F2P4ebfdwfTzFTxPqoHdayhpysqT',
'tD9EOkC96mCV6WfXBDWi1j5Ul43QcVphW5QzKwEKCerCFDLK+BBvc93Da6SuqYTK',
'YDhBKQKBgQDKtNe8CjHRvkWoyKErMMpv5D0ce/yWq+oAaoqW1QKwngPyaiDeDwqM',
'iOJzQxtvK4YqMYQdkgj5VLfWzeazd28RLODZua6phe776zuUv93LHTvYq/8RZfhk',
'JIQJ7GETBnHmoTemwmJiSdVDsjJdtsyR4XRjIDNR5bGe7NNbZJpCUw==',
'-----END RSA PRIVATE KEY-----',
].join('\n');
return privateKey;
} catch (e) { } catch (e) {
this.logger.error(`error=${e}`); this.logger.error(`error=${e}`);
throw e; throw e;
} finally { } finally {
this.logger.log(`[OUT] ${this.getPublicKey.name}`); this.logger.log(`[OUT] ${this.getPrivateKey.name}`);
} }
} }
/**
* Gets public key
* @returns public key(PEM)
*/
async getPublicKey(): Promise<string> { async getPublicKey(): Promise<string> {
try { try {
// XXX 動作確認用 const client = new SecretClient(this.url, this.credential);
const publicKey = [ const secret = await client.getSecret('token-public-key');
'-----BEGIN PUBLIC KEY-----', if (secret.value) {
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsTVLNpW0/FzVCU7qo1DD', return secret.value;
'jOkYWx6s/jE56YOOc3UzaaG/zb1FGyfRoUUgS4DnQxPNz9oM63RpQlhvG6UCwx23', } else {
'tL7p3PS0ZCsLeggcyLctbJAzLy/afF9ABoreorqp/AaEs+Vdwbykb+M+nB2Sxsc5', throw new Error('public key was empty');
'7Tli2x8NiOZr5dafs3vMuIIKNsBaFAugFrd2ApxXR04jBRAorZRRFPtECE7D+hxD', }
'alw5DCd0mmdY0vrbRsgkbej0ZzzqzukJVXTMjy1YScqi3I9gLx2hLVmpK76Gtxn2',
'1AIcn8P3rKZmDyPH+9KNfWC8+ubF+VuY6nItlCgiSyTKErAp6M9pyRHKbPpdUM3a',
'IQIDAQAB',
'-----END PUBLIC KEY-----',
].join('\n');
return publicKey;
} catch (e) { } catch (e) {
this.logger.error(`error=${e}`); this.logger.error(`error=${e}`);
throw e; throw e;

View File

@ -13,10 +13,7 @@ async function bootstrap() {
// バリデーター(+型の自動変換機能)を適用 // バリデーター(+型の自動変換機能)を適用
app.useGlobalPipes( app.useGlobalPipes(
new ValidationPipe({ new ValidationPipe({ transform: true, forbidUnknownValues: false }),
transform: true,
forbidUnknownValues: false,
}),
); );
if (process.env.STAGE === 'local') { if (process.env.STAGE === 'local') {