initial
This commit is contained in:
13
idp/.env.skeleton
Normal file
13
idp/.env.skeleton
Normal file
@@ -0,0 +1,13 @@
|
||||
# Database
|
||||
MYSQL_USER=db_user
|
||||
MYSQL_PASSWORD=PAssword123
|
||||
MYSQL_DATABASE=local_db
|
||||
DATABASE_HOST=localhost
|
||||
DATABASE_PORT=3306
|
||||
MYSQL_ROOT_PASSWORD=kjsdahflöijsdiu
|
||||
|
||||
# security
|
||||
IGNORE_PASSWORDS=FALSE # hier kann man sich nur mit dem Username einloggen, nicht zu empfehlen ausser zum debuggen
|
||||
JWT_SECRET=super-geheimes-secret
|
||||
JWT_EXPIRES_IN=10m
|
||||
JWT_REFRESH_EXPIRES_IN=1w
|
||||
25
idp/.eslintrc.js
Normal file
25
idp/.eslintrc.js
Normal file
@@ -0,0 +1,25 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
||||
56
idp/.gitignore
vendored
Normal file
56
idp/.gitignore
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
/build
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# temp directory
|
||||
.temp
|
||||
.tmp
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
4
idp/.prettierrc
Normal file
4
idp/.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
73
idp/README.md
Normal file
73
idp/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
<p align="center">
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||
</p>
|
||||
<!--[](https://opencollective.com/nest#backer)
|
||||
[](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
## Running the app
|
||||
|
||||
```bash
|
||||
# development
|
||||
$ npm run start
|
||||
|
||||
# watch mode
|
||||
$ npm run start:dev
|
||||
|
||||
# production mode
|
||||
$ npm run start:prod
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
# unit tests
|
||||
$ npm run test
|
||||
|
||||
# e2e tests
|
||||
$ npm run test:e2e
|
||||
|
||||
# test coverage
|
||||
$ npm run test:cov
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
Nest is [MIT licensed](LICENSE).
|
||||
276
idp/client/3rdpartylicenses.txt
Normal file
276
idp/client/3rdpartylicenses.txt
Normal file
@@ -0,0 +1,276 @@
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Package: @angular/core
|
||||
License: "MIT"
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Package: rxjs
|
||||
License: "Apache-2.0"
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Package: tslib
|
||||
License: "0BSD"
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
--------------------------------------------------------------------------------
|
||||
Package: @angular/common
|
||||
License: "MIT"
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Package: @angular/platform-browser
|
||||
License: "MIT"
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Package: @angular/router
|
||||
License: "MIT"
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Package: @angular/forms
|
||||
License: "MIT"
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Package: zone.js
|
||||
License: "MIT"
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2024 Google LLC. https://angular.io/license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
BIN
idp/client/favicon.ico
Normal file
BIN
idp/client/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
13
idp/client/index.html
Normal file
13
idp/client/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-critters-container>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>IdpClient</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="styles-5INURTSO.css"></head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="polyfills-SCHOHYNV.js" type="module"></script><script src="main-ZEV53HFZ.js" type="module"></script></body>
|
||||
</html>
|
||||
9
idp/client/main-ZEV53HFZ.js
Normal file
9
idp/client/main-ZEV53HFZ.js
Normal file
File diff suppressed because one or more lines are too long
2
idp/client/polyfills-SCHOHYNV.js
Normal file
2
idp/client/polyfills-SCHOHYNV.js
Normal file
File diff suppressed because one or more lines are too long
0
idp/client/styles-5INURTSO.css
Normal file
0
idp/client/styles-5INURTSO.css
Normal file
8
idp/nest-cli.json
Normal file
8
idp/nest-cli.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
}
|
||||
9818
idp/package-lock.json
generated
Normal file
9818
idp/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
85
idp/package.json
Normal file
85
idp/package.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "idp",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/config": "^3.2.3",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/passport": "^10.0.3",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@nestjs/serve-static": "^4.0.2",
|
||||
"@nestjs/typeorm": "^10.0.2",
|
||||
"bcrypt": "^5.1.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"mysql2": "^3.11.0",
|
||||
"nestjs-form-data": "^1.9.91",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "^0.3.20",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
"@nestjs/schematics": "^10.0.0",
|
||||
"@nestjs/testing": "^10.0.0",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/supertest": "^6.0.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"eslint": "^8.42.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"prettier": "^3.0.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"supertest": "^6.3.3",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-loader": "^9.4.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.1.3"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
12
idp/src/app.controller.ts
Normal file
12
idp/src/app.controller.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Controller, Get, Res } from '@nestjs/common';
|
||||
import { join } from 'path';
|
||||
import { Response } from 'express';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
@Get('*')
|
||||
handleClientRoutes(@Res() res: Response) {
|
||||
console.log("handle")
|
||||
res.sendFile(join(__dirname, '..', 'client', 'index.html'));
|
||||
}
|
||||
}
|
||||
50
idp/src/app.module.ts
Normal file
50
idp/src/app.module.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Injectable, MiddlewareConsumer, Module, NestMiddleware, NestModule, RequestMethod } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
import path, { join } from 'path';
|
||||
import { AppController } from './app.controller';
|
||||
import * as express from 'express';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AuthModule,
|
||||
ConfigModule.forRoot({
|
||||
envFilePath: ['.env'],
|
||||
isGlobal: true,
|
||||
}),
|
||||
ServeStaticModule.forRoot({
|
||||
rootPath: join(__dirname, '../client'),
|
||||
exclude: ['*/api*'],
|
||||
}),
|
||||
TypeOrmModule.forRootAsync({
|
||||
useFactory: () => ({
|
||||
type: 'mysql',
|
||||
host: process.env.DATABASE_HOST,
|
||||
port: parseInt(process.env.DATABASE_PORT) || 3306,
|
||||
username: process.env.MYSQL_USER,
|
||||
password: process.env.MYSQL_PASSWORD,
|
||||
database: process.env.MYSQL_DATABASE,
|
||||
synchronize: true,
|
||||
autoLoadEntities: true,
|
||||
retryAttempts: 5,
|
||||
retryDelay: 10000,
|
||||
}),
|
||||
}),
|
||||
// TypeOrmModule.forRoot({
|
||||
// type: 'mysql',
|
||||
// host: '85.215.137.185', // MySQL Hostname
|
||||
// port: 3306, // MySQL Port (Standard ist 3306)
|
||||
// username: 'root', // Dein MySQL-Benutzername
|
||||
// password: 'Battlefield123', // Dein MySQL-Passwort
|
||||
// database: 'global_users', // Name der Datenbank
|
||||
// entities: [User, Client, RedirectUri, AuthorizationCode], // Hier werden deine Entitäten aufgelistet
|
||||
// synchronize: true, // Setze dies auf `false` in der Produktion
|
||||
// }),
|
||||
],
|
||||
controllers: [],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
8
idp/src/app.service.ts
Normal file
8
idp/src/app.service.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getHello(): string {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
18
idp/src/auth/auth.controller.spec.ts
Normal file
18
idp/src/auth/auth.controller.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AuthController } from './auth.controller';
|
||||
|
||||
describe('AuthController', () => {
|
||||
let controller: AuthController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AuthController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<AuthController>(AuthController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
75
idp/src/auth/auth.controller.ts
Normal file
75
idp/src/auth/auth.controller.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
import { ClientService } from 'src/client/client.service';
|
||||
import { FormDataRequest } from 'nestjs-form-data';
|
||||
import { Client } from 'src/model/client.entity';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(
|
||||
private usersService: UsersService,
|
||||
private clientService: ClientService,
|
||||
) {}
|
||||
|
||||
@Post('register')
|
||||
async register(
|
||||
@Body('username') username: string,
|
||||
@Body('password') password: string,
|
||||
) {
|
||||
const user = await this.usersService.createUser(username, password);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Post('login')
|
||||
async login(
|
||||
@Body('username') username: string,
|
||||
@Body('password') password: string,
|
||||
@Body('client_id') clientId: string,
|
||||
) {
|
||||
const token = await this.usersService.createToken(
|
||||
username,
|
||||
password,
|
||||
clientId,
|
||||
);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
@Get()
|
||||
async getClient(
|
||||
@Query('client_id') clientId: string,
|
||||
@Query('response_type') responseType: string,
|
||||
@Query('redirect_uri') redirectUri: string,
|
||||
@Query('scope') scope: string,
|
||||
): Promise<Client> {
|
||||
return this.clientService.getClient(
|
||||
clientId,
|
||||
responseType,
|
||||
redirectUri,
|
||||
scope,
|
||||
);
|
||||
}
|
||||
|
||||
@Post('token')
|
||||
@FormDataRequest()
|
||||
async getToken(
|
||||
@Body('client_id') clientId: string,
|
||||
@Body('client_secret') clientSecret: string,
|
||||
@Body('code') code: string,
|
||||
@Body('grant_type') grantType: string,
|
||||
@Body('redirect_uri') redirectUri: string,
|
||||
) {
|
||||
return this.usersService.verifyToken({
|
||||
clientId,
|
||||
clientSecret,
|
||||
code,
|
||||
grantType,
|
||||
redirectUri,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('verify')
|
||||
async verifyToken(@Body('access_token') token: string) {
|
||||
return this.usersService.verifyAccessToken(token);
|
||||
}
|
||||
}
|
||||
34
idp/src/auth/auth.module.ts
Normal file
34
idp/src/auth/auth.module.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
import { ClientService } from 'src/client/client.service';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { NestjsFormDataModule } from 'nestjs-form-data';
|
||||
import { User, UserRepository } from 'src/model/user.entity';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Client, ClientRepository } from 'src/model/client.entity';
|
||||
import { RedirectUri } from 'src/model/redirect-uri.entity';
|
||||
import {
|
||||
AuthorizationCode,
|
||||
AuthorizationCodeRepository,
|
||||
} from 'src/model/auth-code.entity';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
UsersService,
|
||||
ClientService,
|
||||
UserRepository,
|
||||
ClientRepository,
|
||||
AuthorizationCodeRepository,
|
||||
],
|
||||
controllers: [AuthController],
|
||||
imports: [
|
||||
JwtModule.register({
|
||||
secret: 'SECRET_KEY', // Ändere dies zu einer Umgebungsvariablen in einer realen Anwendung
|
||||
signOptions: { expiresIn: '60s' },
|
||||
}),
|
||||
NestjsFormDataModule,
|
||||
TypeOrmModule.forFeature([User, Client, RedirectUri, AuthorizationCode]),
|
||||
],
|
||||
})
|
||||
export class AuthModule {}
|
||||
18
idp/src/client/client.service.spec.ts
Normal file
18
idp/src/client/client.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ClientService } from './client.service';
|
||||
|
||||
describe('ClientService', () => {
|
||||
let service: ClientService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ClientService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ClientService>(ClientService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
62
idp/src/client/client.service.ts
Normal file
62
idp/src/client/client.service.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { Client, ClientRepository } from 'src/model/client.entity';
|
||||
import { RedirectUri } from 'src/model/redirect-uri.entity';
|
||||
|
||||
@Injectable()
|
||||
export class ClientService {
|
||||
constructor(private clientRepo: ClientRepository) {}
|
||||
|
||||
async createClient(
|
||||
clientName: string,
|
||||
clientSecret: string,
|
||||
redirectUris: string[],
|
||||
): Promise<Client> {
|
||||
const clientDto: Client = {
|
||||
id: uuidv4(),
|
||||
clientName,
|
||||
clientSecret,
|
||||
redirectUris: [],
|
||||
authorizationCodes: [],
|
||||
};
|
||||
const c = this.clientRepo.create(clientDto);
|
||||
const client = await this.clientRepo.save(c);
|
||||
|
||||
redirectUris.forEach(async (uri) => {
|
||||
const redirectUri = new RedirectUri();
|
||||
redirectUri.uri = uri;
|
||||
redirectUri.client = client;
|
||||
await this.clientRepo.manager.save(redirectUri);
|
||||
});
|
||||
return client;
|
||||
}
|
||||
|
||||
async getClient(
|
||||
clientId: string,
|
||||
responseType: string,
|
||||
redirectUri: string,
|
||||
scope: string,
|
||||
): Promise<Client> {
|
||||
if (responseType !== 'code') {
|
||||
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
const client = await this.clientRepo.findById(clientId);
|
||||
// console.log(client);
|
||||
if (!client) {
|
||||
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (!client.redirectUris.some((u: RedirectUri) => u.uri === redirectUri)) {
|
||||
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (!scope) {
|
||||
throw new HttpException('Invalid client', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
getClientById(clientId: string): Promise<Client> {
|
||||
return this.clientRepo.findById(clientId);
|
||||
}
|
||||
}
|
||||
22
idp/src/main.ts
Normal file
22
idp/src/main.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { NestFactory, Reflector } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { ClassSerializerInterceptor } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
|
||||
app.setGlobalPrefix('api', {
|
||||
exclude: ['/'],
|
||||
});
|
||||
|
||||
app.enableCors();
|
||||
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
transform: true, // Transform is recomended configuration for avoind issues with arrays of files transformations
|
||||
}),
|
||||
);
|
||||
await app.listen(3000);
|
||||
}
|
||||
bootstrap();
|
||||
39
idp/src/model/auth-code.entity.ts
Normal file
39
idp/src/model/auth-code.entity.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
DataSource,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
Repository,
|
||||
} from 'typeorm';
|
||||
import { Client } from './client.entity';
|
||||
import { User } from './user.entity';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Entity()
|
||||
export class AuthorizationCode {
|
||||
@PrimaryColumn()
|
||||
code: string;
|
||||
|
||||
@ManyToOne(() => Client, (client) => client.authorizationCodes, {
|
||||
eager: true,
|
||||
})
|
||||
client: Client;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.authorizationCodes, { eager: true })
|
||||
user: User;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AuthorizationCodeRepository extends Repository<AuthorizationCode> {
|
||||
constructor(dataSource: DataSource) {
|
||||
super(AuthorizationCode, dataSource.createEntityManager());
|
||||
}
|
||||
|
||||
findByCode(id: string): Promise<AuthorizationCode> {
|
||||
return this.findOneBy({ code: id });
|
||||
}
|
||||
|
||||
removeCode(code: AuthorizationCode) {
|
||||
this.delete(code);
|
||||
}
|
||||
}
|
||||
45
idp/src/model/client.entity.ts
Normal file
45
idp/src/model/client.entity.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
DataSource,
|
||||
Repository,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { RedirectUri } from './redirect-uri.entity';
|
||||
import { AuthorizationCode } from './auth-code.entity';
|
||||
|
||||
@Entity()
|
||||
export class Client {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
clientName: string;
|
||||
|
||||
@Exclude()
|
||||
@Column()
|
||||
clientSecret: string;
|
||||
|
||||
@OneToMany(() => RedirectUri, (uri) => uri.client, {
|
||||
cascade: true,
|
||||
eager: true,
|
||||
})
|
||||
redirectUris: RedirectUri[];
|
||||
|
||||
@OneToMany(() => AuthorizationCode, (code) => code.client)
|
||||
authorizationCodes: AuthorizationCode[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ClientRepository extends Repository<Client> {
|
||||
constructor(dataSource: DataSource) {
|
||||
super(Client, dataSource.createEntityManager());
|
||||
}
|
||||
|
||||
findById(id: string): Promise<Client> {
|
||||
return this.findOneBy({ id });
|
||||
}
|
||||
}
|
||||
14
idp/src/model/redirect-uri.entity.ts
Normal file
14
idp/src/model/redirect-uri.entity.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Entity, PrimaryGeneratedColumn, ManyToOne, Column } from 'typeorm';
|
||||
import { Client } from './client.entity';
|
||||
|
||||
@Entity()
|
||||
export class RedirectUri {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@ManyToOne(() => Client, (client) => client.redirectUris)
|
||||
client: Client;
|
||||
|
||||
@Column()
|
||||
uri: string;
|
||||
}
|
||||
50
idp/src/model/user.entity.ts
Normal file
50
idp/src/model/user.entity.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
export interface CreateUserDto {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
DataSource,
|
||||
Repository,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { AuthorizationCode } from './auth-code.entity';
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
username: string;
|
||||
|
||||
@Exclude()
|
||||
@Column()
|
||||
password: string;
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@OneToMany(() => AuthorizationCode, (code) => code.user)
|
||||
authorizationCodes: AuthorizationCode[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UserRepository extends Repository<User> {
|
||||
constructor(dataSource: DataSource) {
|
||||
super(User, dataSource.createEntityManager());
|
||||
}
|
||||
|
||||
findByUsername(username: string): Promise<User> {
|
||||
return this.findOneBy({ username });
|
||||
}
|
||||
|
||||
findById(id: string): Promise<User> {
|
||||
return this.findOneBy({ id });
|
||||
}
|
||||
}
|
||||
18
idp/src/users/users.service.spec.ts
Normal file
18
idp/src/users/users.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersService', () => {
|
||||
let service: UsersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
135
idp/src/users/users.service.ts
Normal file
135
idp/src/users/users.service.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { HttpException, Injectable } from '@nestjs/common';
|
||||
import { User, UserRepository } from 'src/model/user.entity';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { ClientService } from 'src/client/client.service';
|
||||
import {
|
||||
AuthorizationCode,
|
||||
AuthorizationCodeRepository,
|
||||
} from 'src/model/auth-code.entity';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(
|
||||
private clientService: ClientService,
|
||||
private readonly jwtService: JwtService,
|
||||
private userRepo: UserRepository,
|
||||
private tokenRepo: AuthorizationCodeRepository,
|
||||
) {}
|
||||
|
||||
async createUser(username: string, password: string): Promise<User> {
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
const user: User = {
|
||||
id: uuidv4(),
|
||||
username,
|
||||
password: hashedPassword,
|
||||
isActive: true,
|
||||
authorizationCodes: [],
|
||||
};
|
||||
const u = this.userRepo.create(user);
|
||||
const uu = await this.userRepo.save(u);
|
||||
return uu;
|
||||
}
|
||||
|
||||
async createToken(
|
||||
username: string,
|
||||
password: string,
|
||||
clientId: string,
|
||||
): Promise<{ code: string }> {
|
||||
const user = await this.userRepo.findByUsername(username);
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
const valid = await bcrypt.compare(password, user.password);
|
||||
if (!valid) {
|
||||
throw new HttpException('Invalid credentials', 401);
|
||||
}
|
||||
|
||||
const client = await this.clientService.getClientById(clientId);
|
||||
|
||||
if (!client) {
|
||||
throw new HttpException('Invalid client', 401);
|
||||
}
|
||||
|
||||
const token: AuthorizationCode = {
|
||||
code: uuidv4(),
|
||||
client,
|
||||
user,
|
||||
};
|
||||
const t = this.tokenRepo.create(token);
|
||||
await this.tokenRepo.save(t);
|
||||
return { code: token.code };
|
||||
}
|
||||
|
||||
async verifyToken({
|
||||
clientId,
|
||||
clientSecret,
|
||||
code,
|
||||
grantType,
|
||||
redirectUri,
|
||||
}: any) {
|
||||
if (grantType !== 'authorization_code') {
|
||||
throw new HttpException('Invalid grant type', 401);
|
||||
}
|
||||
const client = await this.clientService.getClientById(clientId);
|
||||
if (!client) {
|
||||
throw new HttpException('Invalid client', 401);
|
||||
}
|
||||
|
||||
if (client.clientSecret !== clientSecret) {
|
||||
throw new HttpException('Invalid client', 401);
|
||||
}
|
||||
|
||||
if (!client.redirectUris.some((uri) => uri.uri === redirectUri)) {
|
||||
throw new HttpException('Invalid redirect', 401);
|
||||
}
|
||||
|
||||
const token = await this.tokenRepo.findByCode(code);
|
||||
if (!token) {
|
||||
throw new HttpException('Invalid token', 401);
|
||||
}
|
||||
|
||||
if (token.client.id !== clientId) {
|
||||
throw new HttpException('Invalid token', 401);
|
||||
}
|
||||
|
||||
const user = await this.userRepo.findById(token.user.id);
|
||||
|
||||
if (!user) {
|
||||
throw new HttpException('Invalid token', 401);
|
||||
}
|
||||
|
||||
this.tokenRepo.removeCode(token);
|
||||
|
||||
const payload = {
|
||||
username: user.username,
|
||||
id: user.id,
|
||||
iss: 'http://localhost:3000',
|
||||
aud: 'http://localhost:3000',
|
||||
};
|
||||
|
||||
const access_token = this.jwtService.sign(payload);
|
||||
const refresh_token = this.jwtService.sign(
|
||||
{
|
||||
type: 'refresh',
|
||||
id: user.id,
|
||||
},
|
||||
{ expiresIn: '365d' },
|
||||
);
|
||||
return {
|
||||
access_token,
|
||||
refresh_token,
|
||||
};
|
||||
}
|
||||
|
||||
verifyAccessToken(token: string) {
|
||||
try {
|
||||
const decoded = this.jwtService.verify(token);
|
||||
return decoded;
|
||||
} catch (e) {
|
||||
throw new HttpException(e.message, 401);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
idp/test/app.e2e-spec.ts
Normal file
24
idp/test/app.e2e-spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
});
|
||||
9
idp/test/jest-e2e.json
Normal file
9
idp/test/jest-e2e.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": ".",
|
||||
"testEnvironment": "node",
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
}
|
||||
}
|
||||
4
idp/tsconfig.build.json
Normal file
4
idp/tsconfig.build.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
||||
21
idp/tsconfig.json
Normal file
21
idp/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
}
|
||||
}
|
||||
16
idp_client/.editorconfig
Normal file
16
idp_client/.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
42
idp_client/.gitignore
vendored
Normal file
42
idp_client/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
4
idp_client/.vscode/extensions.json
vendored
Normal file
4
idp_client/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
||||
20
idp_client/.vscode/launch.json
vendored
Normal file
20
idp_client/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
idp_client/.vscode/tasks.json
vendored
Normal file
42
idp_client/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
27
idp_client/README.md
Normal file
27
idp_client/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# IdpClient
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.0.2.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||
112
idp_client/angular.json
Normal file
112
idp_client/angular.json
Normal file
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"idp_client": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": {
|
||||
"base": "../idp/client",
|
||||
"browser": ""
|
||||
},
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss",
|
||||
"node_modules/@ngxpert/hot-toast/src/styles/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kB",
|
||||
"maximumError": "4kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.development.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "idp_client:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "idp_client:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13385
idp_client/package-lock.json
generated
Normal file
13385
idp_client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
40
idp_client/package.json
Normal file
40
idp_client/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "idp-client",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^18.0.0",
|
||||
"@angular/common": "^18.0.0",
|
||||
"@angular/compiler": "^18.0.0",
|
||||
"@angular/core": "^18.0.0",
|
||||
"@angular/forms": "^18.0.0",
|
||||
"@angular/platform-browser": "^18.0.0",
|
||||
"@angular/platform-browser-dynamic": "^18.0.0",
|
||||
"@angular/router": "^18.0.0",
|
||||
"@ngneat/overview": "^6.0.0",
|
||||
"@ngxpert/hot-toast": "^3.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.14.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^18.0.2",
|
||||
"@angular/cli": "^18.0.2",
|
||||
"@angular/compiler-cli": "^18.0.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"jasmine-core": "~5.1.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.4.2"
|
||||
}
|
||||
}
|
||||
BIN
idp_client/public/favicon.ico
Normal file
BIN
idp_client/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
2
idp_client/src/app/app.component.html
Normal file
2
idp_client/src/app/app.component.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<router-outlet></router-outlet>
|
||||
iodsuj
|
||||
0
idp_client/src/app/app.component.scss
Normal file
0
idp_client/src/app/app.component.scss
Normal file
29
idp_client/src/app/app.component.spec.ts
Normal file
29
idp_client/src/app/app.component.spec.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AppComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have the 'idp_client' title`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('idp_client');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, idp_client');
|
||||
});
|
||||
});
|
||||
13
idp_client/src/app/app.component.ts
Normal file
13
idp_client/src/app/app.component.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [RouterOutlet],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss'
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'idp_client';
|
||||
}
|
||||
10
idp_client/src/app/app.config.ts
Normal file
10
idp_client/src/app/app.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHotToastConfig } from '@ngxpert/hot-toast';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideHttpClient(), provideHotToastConfig(),]
|
||||
};
|
||||
6
idp_client/src/app/app.routes.ts
Normal file
6
idp_client/src/app/app.routes.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: 'login', component: LoginComponent },
|
||||
];
|
||||
27
idp_client/src/app/login/login.component.html
Normal file
27
idp_client/src/app/login/login.component.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="container">
|
||||
<div class="screen">
|
||||
<div class="screen__content">
|
||||
<h1>{{ client }}</h1>
|
||||
<form class="login" [formGroup]="loginForm" >
|
||||
<div class="login__field">
|
||||
<i class="login__icon fas fa-user user"></i>
|
||||
<input formControlName="username" type="text" class="login__input" placeholder="User name / Email">
|
||||
</div>
|
||||
<div class="login__field">
|
||||
<i class="login__icon fas fa-lock safe"></i>
|
||||
<input type="password" formControlName="password" class="login__input" placeholder="Password">
|
||||
</div>
|
||||
<button class="button login__submit" (click)="login()">
|
||||
<span class="button__text">Log In Now</span>
|
||||
<i class="button__icon fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="screen__background">
|
||||
<span class="screen__background__shape screen__background__shape4"></span>
|
||||
<span class="screen__background__shape screen__background__shape3"></span>
|
||||
<span class="screen__background__shape screen__background__shape2"></span>
|
||||
<span class="screen__background__shape screen__background__shape1"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
187
idp_client/src/app/login/login.component.scss
Normal file
187
idp_client/src/app/login/login.component.scss
Normal file
@@ -0,0 +1,187 @@
|
||||
// @import url('https://fonts.googleapis.com/css?family=Raleway:400,700');
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Raleway, sans-serif;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(90deg, #C7C5F4, #5a5a5c);
|
||||
// background: linear-gradient(90deg, #C7C5F4, #776BCC);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.screen {
|
||||
background: linear-gradient(90deg, #303033, #9c9ca3);
|
||||
position: relative;
|
||||
height: 600px;
|
||||
width: 360px;
|
||||
box-shadow: 0px 0px 24px #8b8b8f;
|
||||
}
|
||||
|
||||
.screen__content {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.screen__background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 0;
|
||||
-webkit-clip-path: inset(0 0 0 0);
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
|
||||
.screen__background__shape {
|
||||
transform: rotate(45deg);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.screen__background__shape1 {
|
||||
height: 520px;
|
||||
width: 520px;
|
||||
background: #FFF;
|
||||
top: -50px;
|
||||
right: 120px;
|
||||
border-radius: 0 72px 0 0;
|
||||
}
|
||||
|
||||
.screen__background__shape2 {
|
||||
height: 220px;
|
||||
width: 220px;
|
||||
background: #6C63AC;
|
||||
top: -172px;
|
||||
right: 0;
|
||||
border-radius: 32px;
|
||||
}
|
||||
|
||||
.screen__background__shape3 {
|
||||
height: 540px;
|
||||
width: 190px;
|
||||
background: linear-gradient(270deg, #5D54A4, #6A679E);
|
||||
top: -24px;
|
||||
right: 0;
|
||||
border-radius: 32px;
|
||||
}
|
||||
|
||||
.screen__background__shape4 {
|
||||
height: 400px;
|
||||
width: 200px;
|
||||
background: #7E7BB9;
|
||||
top: 420px;
|
||||
right: 50px;
|
||||
border-radius: 60px;
|
||||
}
|
||||
|
||||
.login {
|
||||
width: 320px;
|
||||
padding: 30px;
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
.login__field {
|
||||
padding: 20px 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login__icon {
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
color: #7875B5;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
.login__input {
|
||||
border: none;
|
||||
border-bottom: 2px solid #D1D1D4;
|
||||
background: none;
|
||||
padding: 10px;
|
||||
padding-left: 36px;
|
||||
font-weight: 700;
|
||||
width: 85%;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.login__input:active,
|
||||
.login__input:focus,
|
||||
.login__input:hover {
|
||||
outline: none;
|
||||
border-bottom-color: #6A679E;
|
||||
}
|
||||
|
||||
.login__submit {
|
||||
background: #fff;
|
||||
font-size: 14px;
|
||||
margin-top: 30px;
|
||||
padding: 16px 20px;
|
||||
border-radius: 26px;
|
||||
border: 1px solid #D4D3E8;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
color: #4C489D;
|
||||
box-shadow: 0px 2px 2px #5C5696;
|
||||
cursor: pointer;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.login__submit:active,
|
||||
.login__submit:focus,
|
||||
.login__submit:hover {
|
||||
border-color: #6A679E;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.button__icon {
|
||||
font-size: 24px;
|
||||
margin-left: auto;
|
||||
color: #7875B5;
|
||||
}
|
||||
|
||||
.social-login {
|
||||
position: absolute;
|
||||
height: 140px;
|
||||
width: 160px;
|
||||
text-align: center;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.social-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.social-login__icon {
|
||||
padding: 20px 10px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
text-shadow: 0px 0px 8px #7875B5;
|
||||
}
|
||||
|
||||
.social-login__icon:hover {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
23
idp_client/src/app/login/login.component.spec.ts
Normal file
23
idp_client/src/app/login/login.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [LoginComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
70
idp_client/src/app/login/login.component.ts
Normal file
70
idp_client/src/app/login/login.component.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { HotToastService } from '@ngxpert/hot-toast';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule, ReactiveFormsModule],
|
||||
templateUrl: './login.component.html',
|
||||
styleUrl: './login.component.scss'
|
||||
})
|
||||
export class LoginComponent {
|
||||
private http: HttpClient = inject(HttpClient);
|
||||
private route: ActivatedRoute = inject(ActivatedRoute);
|
||||
private toast: HotToastService = inject(HotToastService);
|
||||
|
||||
redirectUri = null;
|
||||
client: string = "";
|
||||
client_id = null;
|
||||
|
||||
loginForm = new FormGroup({
|
||||
username: new FormControl(''),
|
||||
password: new FormControl(''),
|
||||
})
|
||||
|
||||
constructor() {
|
||||
this.getclient();
|
||||
}
|
||||
|
||||
|
||||
getclient() {
|
||||
const params = (this.route.snapshot.queryParamMap as any)["params"];
|
||||
this.redirectUri = params.redirect_uri;
|
||||
this.client_id = params.client_id;
|
||||
this.route.snapshot.queryParamMap.keys.forEach((key) => {
|
||||
console.log(key, this.route.snapshot.queryParamMap.get(key));
|
||||
});
|
||||
|
||||
this.http.get<any>(environment.api_url + 'auth/', {
|
||||
params
|
||||
}).subscribe({
|
||||
next: (client) => {
|
||||
console.log(client)
|
||||
this.client = client.clientName;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(error);
|
||||
this.toast.error('Invalid client');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
login() {
|
||||
this.http.post(environment.api_url + 'auth/login?'+ 'client_id=' + this.client_id, this.loginForm.value).subscribe({
|
||||
next: (data) => {
|
||||
if (data["code"] != null) {
|
||||
location.href = this.redirectUri + "?code=" + data["code"];
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(error);
|
||||
this.toast.error('Invalid login');
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
1
idp_client/src/assets/icons/safe-box.svg
Normal file
1
idp_client/src/assets/icons/safe-box.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" enable-background="new 0 0 512 512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m211.486 202.271c-48.332 0-87.652 39.32-87.652 87.651 0 48.332 39.32 87.652 87.652 87.652 48.331 0 87.651-39.32 87.651-87.652 0-48.33-39.32-87.651-87.651-87.651zm0 160.304c-40.061 0-72.652-32.591-72.652-72.652 0-40.06 32.591-72.651 72.652-72.651 40.06 0 72.651 32.591 72.651 72.651s-32.591 72.652-72.651 72.652zm-7.5-117.875v-14.117c0-4.142 3.358-7.5 7.5-7.5s7.5 3.358 7.5 7.5v14.117c0 4.142-3.358 7.5-7.5 7.5s-7.5-3.358-7.5-7.5zm34.174 18.549c-2.929-2.929-2.929-7.678 0-10.606l9.982-9.982c2.929-2.929 7.678-2.929 10.606 0s2.929 7.678 0 10.606l-9.982 9.982c-1.464 1.464-3.384 2.197-5.303 2.197s-3.839-.733-5.303-2.197zm18.549 19.174h14.117c4.142 0 7.5 3.358 7.5 7.5s-3.358 7.5-7.5 7.5h-14.117c-4.142 0-7.5-3.358-7.5-7.5s3.358-7.5 7.5-7.5zm2.039 44.157c2.929 2.929 2.929 7.678 0 10.606-1.464 1.464-3.384 2.197-5.303 2.197s-3.839-.732-5.303-2.197l-9.982-9.982c-2.929-2.929-2.929-7.678 0-10.606 2.929-2.929 7.678-2.928 10.606 0zm-39.762 8.566v14.117c0 4.142-3.358 7.5-7.5 7.5s-7.5-3.358-7.5-7.5v-14.117c0-4.142 3.358-7.5 7.5-7.5s7.5 3.358 7.5 7.5zm-34.175-18.549c2.929 2.929 2.929 7.677 0 10.606l-9.982 9.982c-1.464 1.464-3.384 2.197-5.303 2.197s-3.839-.732-5.303-2.197c-2.929-2.929-2.929-7.677 0-10.606l9.982-9.982c2.928-2.929 7.676-2.929 10.606 0zm-18.549-19.174h-14.117c-4.142 0-7.5-3.358-7.5-7.5s3.358-7.5 7.5-7.5h14.117c4.142 0 7.5 3.358 7.5 7.5s-3.358 7.5-7.5 7.5zm-2.04-44.156c-2.929-2.929-2.929-7.678 0-10.606s7.678-2.929 10.606 0l9.982 9.982c2.929 2.929 2.929 7.678 0 10.606-1.464 1.464-3.384 2.197-5.303 2.197s-3.839-.732-5.303-2.197zm47.264 4.744c-17.597 0-31.913 14.316-31.913 31.912 0 17.597 14.316 31.913 31.913 31.913 17.596 0 31.912-14.316 31.912-31.913-.001-17.596-14.316-31.912-31.912-31.912zm0 48.824c-9.326 0-16.913-7.587-16.913-16.913 0-9.325 7.587-16.912 16.913-16.912 9.325 0 16.912 7.587 16.912 16.912-.001 9.327-7.587 16.913-16.912 16.913zm275.627-242.911c.186-3.415-1.963-6.523-5.223-7.556-31.323-9.925-74.637-29.192-102.265-53.499-2.833-2.492-7.075-2.492-9.908 0-27.628 24.307-70.942 43.573-102.265 53.499-3.26 1.033-5.409 4.141-5.223 7.556.725 13.361 2.92 26.53 6.454 39.39h-209.441c-18.95 0-34.366 15.417-34.366 34.366v304.487c0 18.485 14.672 33.601 32.982 34.331l7.941 28.984c.893 3.259 3.854 5.518 7.233 5.518h55.708c3.379 0 6.34-2.259 7.233-5.518l7.932-28.949h135.16l7.931 28.949c.893 3.259 3.854 5.518 7.233 5.518h55.708c3.379 0 6.34-2.259 7.233-5.518l7.942-28.984c18.31-.73 32.982-15.846 32.982-34.331v-221.748c53.659-40.812 85.778-96.787 89.019-156.495zm-364.095 432.076h-44.264l-5.334-19.467h54.932zm176.934 0-5.334-19.467h54.932l-5.333 19.467zm83.143-53.833c0 10.678-8.688 19.366-19.366 19.366h-304.487c-10.679 0-19.366-8.688-19.366-19.366v-304.487c0-10.679 8.688-19.366 19.366-19.366h214.21c3.715 10.188 8.306 20.135 13.72 29.777h-210.019c-4.142 0-7.5 3.358-7.5 7.5v31.635c-9.608 3.16-16.567 12.212-16.567 22.865v23.284c0 10.652 6.959 19.705 16.567 22.865v67.371c-9.608 3.16-16.567 12.212-16.567 22.865v23.284c0 10.652 6.959 19.705 16.567 22.865v31.634c0 4.142 3.358 7.5 7.5 7.5h268.665c4.142 0 7.5-3.358 7.5-7.5v-202.26c5.557 4.143 11.334 8.128 17.334 11.937 1.227.778 2.623 1.168 4.02 1.168s2.793-.39 4.02-1.168c1.487-.944 2.945-1.91 4.404-2.874v211.105zm-44.777-106.459h-8.116v-38.285h8.116zm0-53.285h-8.116v-38.285h8.116zm0-53.285h-8.494c-8.063 0-14.623 6.56-14.623 14.623v92.325c0 8.063 6.56 14.623 14.623 14.623h8.494v66.048h-253.665v-24.134c9.609-3.16 16.568-12.212 16.568-22.865v-23.284c0-10.652-6.959-19.705-16.568-22.865v-67.37c9.609-3.16 16.568-12.213 16.568-22.865v-23.284c0-10.652-6.959-19.705-16.568-22.865v-24.135h211.777c11.394 16.882 25.448 32.627 41.888 46.844zm-261.165-28.116c5 0 9.068 4.067 9.068 9.067v23.284c0 5-4.068 9.068-9.068 9.068s-9.067-4.068-9.067-9.068v-23.284c-.001-5 4.067-9.067 9.067-9.067zm0 136.384c5 0 9.068 4.067 9.068 9.067v23.284c0 5-4.068 9.067-9.068 9.067s-9.067-4.067-9.067-9.067v-23.284c-.001-5 4.067-9.067 9.067-9.067zm297.518-118.739c-56.883-37.535-91.918-91.665-97.057-149.833 37.67-12.594 73.114-31.057 97.057-50.517 23.943 19.46 59.386 37.922 97.056 50.517-5.138 58.168-40.173 112.298-97.056 149.833zm.001-154.262c-19.066 0-34.577 15.511-34.577 34.576 0 10.922 5.186 21.144 13.784 27.627v28.966c0 11.465 9.327 20.792 20.792 20.792s20.792-9.328 20.792-20.792v-28.965c8.598-6.483 13.785-16.706 13.785-27.627-.001-19.066-15.511-34.577-34.576-34.577zm9.603 51.64c-2.355 1.33-3.812 3.826-3.812 6.531v32.999c0 3.194-2.598 5.792-5.792 5.792s-5.792-2.599-5.792-5.792v-32.999c0-2.705-1.457-5.201-3.812-6.531-6.151-3.473-9.972-10.012-9.972-17.063 0-10.794 8.782-19.576 19.577-19.576 10.794 0 19.576 8.782 19.576 19.576-.001 7.052-3.822 13.59-9.973 17.063z"/></svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
1
idp_client/src/assets/icons/user.svg
Normal file
1
idp_client/src/assets/icons/user.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="-42 0 512 512.001" xmlns="http://www.w3.org/2000/svg"><path d="m210.351562 246.632812c33.882813 0 63.21875-12.152343 87.195313-36.128906 23.96875-23.972656 36.125-53.304687 36.125-87.191406 0-33.875-12.152344-63.210938-36.128906-87.191406-23.976563-23.96875-53.3125-36.121094-87.191407-36.121094-33.886718 0-63.21875 12.152344-87.191406 36.125s-36.128906 53.308594-36.128906 87.1875c0 33.886719 12.15625 63.222656 36.128906 87.195312 23.980469 23.96875 53.316406 36.125 87.191406 36.125zm-65.972656-189.292968c18.394532-18.394532 39.972656-27.335938 65.972656-27.335938 25.996094 0 47.578126 8.941406 65.976563 27.335938 18.394531 18.398437 27.339844 39.980468 27.339844 65.972656 0 26-8.945313 47.578125-27.339844 65.976562-18.398437 18.398438-39.980469 27.339844-65.976563 27.339844-25.992187 0-47.570312-8.945312-65.972656-27.339844-18.398437-18.394531-27.34375-39.976562-27.34375-65.976562 0-25.992188 8.945313-47.574219 27.34375-65.972656zm0 0"/><path d="m426.128906 393.703125c-.691406-9.976563-2.089844-20.859375-4.148437-32.351563-2.078125-11.578124-4.753907-22.523437-7.957031-32.527343-3.3125-10.339844-7.808594-20.550781-13.375-30.335938-5.769532-10.15625-12.550782-19-20.160157-26.277343-7.957031-7.613282-17.699219-13.734376-28.964843-18.199219-11.226563-4.441407-23.667969-6.691407-36.976563-6.691407-5.226563 0-10.28125 2.144532-20.042969 8.5-6.007812 3.917969-13.035156 8.449219-20.878906 13.460938-6.707031 4.273438-15.792969 8.277344-27.015625 11.902344-10.949219 3.542968-22.066406 5.339844-33.042969 5.339844-10.96875 0-22.085937-1.796876-33.042968-5.339844-11.210938-3.621094-20.300782-7.625-26.996094-11.898438-7.769532-4.964844-14.800782-9.496094-20.898438-13.46875-9.753906-6.355468-14.808594-8.5-20.035156-8.5-13.3125 0-25.75 2.253906-36.972656 6.699219-11.257813 4.457031-21.003906 10.578125-28.96875 18.199219-7.609375 7.28125-14.390625 16.121094-20.15625 26.273437-5.558594 9.785157-10.058594 19.992188-13.371094 30.339844-3.199219 10.003906-5.875 20.945313-7.953125 32.523437-2.0625 11.476563-3.457031 22.363282-4.148437 32.363282-.679688 9.777344-1.023438 19.953125-1.023438 30.234375 0 26.726562 8.496094 48.363281 25.25 64.320312 16.546875 15.746094 38.4375 23.730469 65.066406 23.730469h246.53125c26.621094 0 48.511719-7.984375 65.0625-23.730469 16.757813-15.945312 25.253906-37.589843 25.253906-64.324219-.003906-10.316406-.351562-20.492187-1.035156-30.242187zm-44.90625 72.828125c-10.933594 10.40625-25.449218 15.464844-44.378906 15.464844h-246.527344c-18.933594 0-33.449218-5.058594-44.378906-15.460938-10.722656-10.207031-15.933594-24.140625-15.933594-42.585937 0-9.59375.316406-19.066407.949219-28.160157.617187-8.921874 1.878906-18.722656 3.75-29.136718 1.847656-10.285156 4.199219-19.9375 6.996094-28.675782 2.683593-8.378906 6.34375-16.675781 10.882812-24.667968 4.332031-7.617188 9.316407-14.152344 14.816407-19.417969 5.144531-4.925781 11.628906-8.957031 19.269531-11.980469 7.066406-2.796875 15.007812-4.328125 23.628906-4.558594 1.050781.558594 2.921875 1.625 5.953125 3.601563 6.167969 4.019531 13.277344 8.605469 21.136719 13.625 8.859375 5.648437 20.273437 10.75 33.910156 15.152344 13.941406 4.507812 28.160156 6.796875 42.273437 6.796875 14.113282 0 28.335938-2.289063 42.269532-6.792969 13.648437-4.410156 25.058594-9.507813 33.929687-15.164063 8.042969-5.140624 14.953125-9.59375 21.121094-13.617187 3.03125-1.972656 4.902344-3.042969 5.953125-3.601563 8.625.230469 16.566406 1.761719 23.636719 4.558594 7.636719 3.023438 14.121093 7.058594 19.265625 11.980469 5.5 5.261719 10.484375 11.796875 14.816406 19.421875 4.542969 7.988281 8.207031 16.289062 10.886719 24.660156 2.800781 8.75 5.15625 18.398438 7 28.675782 1.867187 10.433593 3.132812 20.238281 3.75 29.144531v.007812c.636719 9.058594.957031 18.527344.960937 28.148438-.003906 18.449219-5.214844 32.378906-15.9375 42.582031zm0 0"/></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
4
idp_client/src/environments/environment.development.ts
Normal file
4
idp_client/src/environments/environment.development.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const environment = {
|
||||
|
||||
api_url: 'http://localhost:3000/api/',
|
||||
};
|
||||
4
idp_client/src/environments/environment.ts
Normal file
4
idp_client/src/environments/environment.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const environment = {
|
||||
|
||||
api_url: 'api/',
|
||||
};
|
||||
13
idp_client/src/index.html
Normal file
13
idp_client/src/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>IdpClient</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
6
idp_client/src/main.ts
Normal file
6
idp_client/src/main.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig)
|
||||
.catch((err) => console.error(err));
|
||||
14
idp_client/src/styles.scss
Normal file
14
idp_client/src/styles.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.safe {
|
||||
background-image: url("assets/icons/safe-box.svg");
|
||||
}
|
||||
|
||||
.user {
|
||||
background-image: url("assets/icons/user.svg");
|
||||
}
|
||||
14
idp_client/tsconfig.app.json
Normal file
14
idp_client/tsconfig.app.json
Normal file
@@ -0,0 +1,14 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
32
idp_client/tsconfig.json
Normal file
32
idp_client/tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/out-tsc",
|
||||
"strict": false,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "bundler",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"useDefineForClassFields": false,
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
14
idp_client/tsconfig.spec.json
Normal file
14
idp_client/tsconfig.spec.json
Normal file
@@ -0,0 +1,14 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user