switched to supertokens

This commit is contained in:
2022-09-07 09:12:26 +02:00
parent 40eb1da379
commit 6bb920861a
36 changed files with 1083 additions and 67663 deletions

25
backend/.eslintrc.js Normal file
View 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',
},
};

View File

@@ -1,4 +0,0 @@
wwwroot/*.js
node_modules
typings
dist

View File

@@ -1 +0,0 @@
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm

View File

@@ -1,23 +0,0 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,70 +0,0 @@
// tslint:disable
/**
* Keycloak Admin REST API
* This is a REST API reference for the Keycloak Admin
*
* The version of the OpenAPI document: 1
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { Configuration } from "./configuration";
// Some imports not used depending on template conditions
// @ts-ignore
import globalAxios, { AxiosPromise, AxiosInstance } from 'axios';
export const BASE_PATH = "http://localhost".replace(/\/+$/, "");
/**
*
* @export
*/
export const COLLECTION_FORMATS = {
csv: ",",
ssv: " ",
tsv: "\t",
pipes: "|",
};
/**
*
* @export
* @interface RequestArgs
*/
export interface RequestArgs {
url: string;
options: any;
}
/**
*
* @export
* @class BaseAPI
*/
export class BaseAPI {
protected configuration: Configuration | undefined;
constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
if (configuration) {
this.configuration = configuration;
this.basePath = configuration.basePath || this.basePath;
}
}
};
/**
*
* @export
* @class RequiredError
* @extends {Error}
*/
export class RequiredError extends Error {
name: "RequiredError" = "RequiredError";
constructor(public field: string, msg?: string) {
super(msg);
}
}

View File

@@ -1,75 +0,0 @@
// tslint:disable
/**
* Keycloak Admin REST API
* This is a REST API reference for the Keycloak Admin
*
* The version of the OpenAPI document: 1
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export interface ConfigurationParameters {
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
username?: string;
password?: string;
accessToken?: string | ((name?: string, scopes?: string[]) => string);
basePath?: string;
baseOptions?: any;
}
export class Configuration {
/**
* parameter for apiKey security
* @param name security name
* @memberof Configuration
*/
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
/**
* parameter for basic security
*
* @type {string}
* @memberof Configuration
*/
username?: string;
/**
* parameter for basic security
*
* @type {string}
* @memberof Configuration
*/
password?: string;
/**
* parameter for oauth2 security
* @param name security name
* @param scopes oauth2 scope
* @memberof Configuration
*/
accessToken?: string | ((name?: string, scopes?: string[]) => string);
/**
* override base path
*
* @type {string}
* @memberof Configuration
*/
basePath?: string;
/**
* base options for axios calls
*
* @type {any}
* @memberof Configuration
*/
baseOptions?: any;
constructor(param: ConfigurationParameters = {}) {
this.apiKey = param.apiKey;
this.username = param.username;
this.password = param.password;
this.accessToken = param.accessToken;
this.basePath = param.basePath;
this.baseOptions = param.baseOptions;
}
}

View File

@@ -1,58 +0,0 @@
#!/bin/sh
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
#
# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com"
git_user_id=$1
git_repo_id=$2
release_note=$3
git_host=$4
if [ "$git_host" = "" ]; then
git_host="github.com"
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
fi
if [ "$git_user_id" = "" ]; then
git_user_id="GIT_USER_ID"
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
fi
if [ "$git_repo_id" = "" ]; then
git_repo_id="GIT_REPO_ID"
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
fi
if [ "$release_note" = "" ]; then
release_note="Minor update"
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
fi
# Initialize the local directory as a Git repository
git init
# Adds the files in the local repository and stages them for commit.
git add .
# Commits the tracked changes and prepares them to be pushed to a remote repository.
git commit -m "$release_note"
# Sets the new remote
git_remote=`git remote`
if [ "$git_remote" = "" ]; then # git remote not defined
if [ "$GIT_TOKEN" = "" ]; then
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
else
git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git
fi
fi
git pull origin master
# Pushes (Forces) the changes in the local repository up to the remote repository
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
git push origin master 2>&1 | grep -v 'To https'

View File

@@ -1,16 +0,0 @@
// tslint:disable
/**
* Keycloak Admin REST API
* This is a REST API reference for the Keycloak Admin
*
* The version of the OpenAPI document: 1
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export * from "./api";
export * from "./configuration";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
{
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "6.0.1"
}
}

View File

@@ -13,6 +13,7 @@
"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",
@@ -20,19 +21,13 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/axios": "^0.1.0",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^9.0.0",
"@nestjs/mapped-types": "*",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/swagger": "^6.1.2",
"axios": "^0.27.2",
"openid-client": "^5.1.9",
"reflect-metadata": "^0.1.13",
"request": "^2.88.2",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0"
"rxjs": "^7.2.0",
"supertokens-node": "^11.3.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
@@ -42,7 +37,11 @@
"@types/jest": "28.1.8",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11",
"isomorphic-fetch": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.1.3",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
@@ -51,7 +50,7 @@
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.0",
"typescript": "~4.7.4"
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [

View File

@@ -0,0 +1,22 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});

View File

@@ -1,11 +1,12 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
// constructor() {}
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return 'Home';
return this.appService.getHello();
}
}

View File

@@ -1,17 +1,10 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AuthModule } from './auth/auth.module';
import { AppService } from './app.service';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
expandVariables: true,
}),
AuthModule,
],
imports: [],
controllers: [AppController],
providers: [],
providers: [AppService],
})
export class AppModule {}

View File

@@ -0,0 +1,8 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}

View File

@@ -1,8 +0,0 @@
import { Module } from '@nestjs/common';
import { LoginModule } from './login/login.module';
import { RegisterModule } from './register/register.module';
@Module({
imports: [LoginModule, RegisterModule],
})
export class AuthModule {}

View File

@@ -1,8 +0,0 @@
export class KeycloakDataDto {
client_id: string;
client_secret: string;
grant_type: string;
username?: string;
email?: string;
password: string;
}

View File

@@ -1,5 +0,0 @@
export class LoginDto {
username?: string;
email?: string;
password: string;
}

View File

@@ -1,10 +0,0 @@
export class TokenDto {
access_token: string;
expires_in: number;
refresh_token: string;
refresh_expires_in: number;
token_type: string;
'not-before-policy': number;
session_state: string;
scope: string;
}

View File

@@ -1 +0,0 @@
export class Login {}

View File

@@ -1,18 +0,0 @@
import { Controller, Post, Body, Get } from '@nestjs/common';
import { LoginService } from './login.service';
import { LoginDto } from './dto/login.dto';
@Controller('login')
export class LoginController {
constructor(private readonly loginService: LoginService) {}
@Post()
login(@Body() createLoginDto: LoginDto) {
return this.loginService.login(createLoginDto);
}
@Post()
logout(@Body() createLoginDto: LoginDto) {
return "Logout";
}
}

View File

@@ -1,10 +0,0 @@
import { Module } from '@nestjs/common';
import { LoginService } from './login.service';
import { LoginController } from './login.controller';
@Module({
imports: [],
controllers: [LoginController],
providers: [LoginService],
})
export class LoginModule {}

View File

@@ -1,37 +0,0 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { LoginDto } from './dto/login.dto';
import { Issuer } from 'openid-client';
@Injectable()
export class LoginService {
async login(loginDto: LoginDto) {
const { username, password } = loginDto;
const keycloakIssuer = await Issuer.discover(
`${process.env.KC_BASE_URL}/auth/realms/${process.env.KC_REALM}`,
);
const openIdConnectClient = new keycloakIssuer.Client({
client_id: process.env.KC_CLIENT_ID || 'client_id',
client_secret: process.env.KC_CLIENT_SECRET || 'client_secret',
});
try {
const token = await openIdConnectClient.grant({
grant_type: process.env.KC_GRANT_TYPE || 'grant_type',
username,
password,
});
return token;
} catch (error) {
throw new HttpException(error.error_description, HttpStatus.UNAUTHORIZED);
}
}
}
// {
// "error": "invalid_grant",
// "error_description": "Invalid user credentials",
// "name": "OPError"
// }

View File

@@ -1 +0,0 @@
export class CreateUserDto {}

View File

@@ -1,4 +0,0 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
export class UpdateUserDto extends PartialType(CreateUserDto) {}

View File

@@ -1 +0,0 @@
export class Register {}

View File

@@ -1,46 +0,0 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Headers,
} from '@nestjs/common';
import { RegisterService } from './register.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('register')
export class RegisterController {
constructor(private readonly registerService: RegisterService) {}
@Post()
create(
@Headers('Authorization') accessToken: string,
@Body() createUserDto: CreateUserDto,
) {
return this.registerService.create(accessToken, createUserDto);
}
@Get()
findAll(@Headers('Authorization') accessToken: string) {
return this.registerService.findAll(accessToken);
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.registerService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.registerService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.registerService.remove(+id);
}
}

View File

@@ -1,10 +0,0 @@
import { Module } from '@nestjs/common';
import { RegisterService } from './register.service';
import { RegisterController } from './register.controller';
@Module({
imports: [],
controllers: [RegisterController],
providers: [RegisterService]
})
export class RegisterModule {}

View File

@@ -1,100 +0,0 @@
import { Body, Injectable } from '@nestjs/common';
import {
Configuration,
CredentialRepresentation,
UserRepresentation,
UsersApi,
} from '../../../keycloak/kc-client';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class RegisterService {
async create(accessToken: string, userRepresentation: UserRepresentation) {
accessToken = accessToken.replace('Bearer ', '');
const basePath = `${process.env.KC_BASE_URL}/auth/admin/realms`;
const usersApi = new UsersApi({ basePath, accessToken });
try {
const registereredUser = await usersApi.realmUsersPost(
process.env.KC_REALM,
userRepresentation,
);
// console.log(registereredUser);
try {
const newUserRegistered = await usersApi.realmUsersGet(
process.env.KC_REALM,
true,
userRepresentation.email,
);
if (newUserRegistered.data.length > 0) {
const userFound: UserRepresentation = newUserRegistered.data[0];
console.log(userFound.id);
try {
const credentialRepresentation: CredentialRepresentation = {temporary: false, value: "1234"}
const setUserPassword = await usersApi.realmUsersIdResetPasswordPut(process.env.KC_REALM, userFound.id, credentialRepresentation)
} catch (error) {
}
try {
// Ska nevoje te nsim email.
const sendMailToCreatedUser =
await usersApi.realmUsersIdSendVerifyEmailPut(
process.env.KC_REALM,
userFound.id,
process.env.KC_CLIENT_ID,
'http://localhost:3000',
{
headers: {
'Content-Type': 'application/json',
},
},
);
console.log(sendMailToCreatedUser);
} catch (error) {
console.log(error);
}
}
} catch (error) {
// console.log(error);
return error.data;
}
return registereredUser.data;
// try {
// const sendMail = await usersApi.realmUsersIdSendVerifyEmailPut(process.env.KC_REALM, registereredUser.data.id)
// } catch (error) {
// }
} catch (error) {
console.log(error);
return error.response.data;
}
}
async findAll(accessToken: string) {
accessToken = accessToken.replace('Bearer ', '');
const basePath = `${process.env.KC_BASE_URL}/auth/admin/realms`;
const usersApi = new UsersApi({ basePath, accessToken });
try {
const users = await usersApi.realmUsersGet(process.env.KC_REALM);
return users.data;
} catch (error) {
return error.data;
}
}
findOne(id: number) {
return `This action returns a #${id} register`;
}
update(id: number, updateRegisterDto: UpdateUserDto) {
return `This action updates a #${id} register`;
}
remove(id: number) {
return `This action removes a #${id} register`;
}
}

View File

@@ -3,6 +3,6 @@ import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.APP_PORT || '3000');
await app.listen(3000);
}
bootstrap();

File diff suppressed because it is too large Load Diff

4
yarn.lock Normal file
View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1