Refactored Auth with Sessions #12
|
@ -15,10 +15,7 @@ export class UserCredentials {
|
||||||
public email: string;
|
public email: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
public hash: string;
|
public hashedPassword: string;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
public refreshToken?: string;
|
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
public createdAt: Date;
|
public createdAt: Date;
|
||||||
|
|
|
@ -18,9 +18,9 @@ export class UserData {
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
public isEmailConfirmed: boolean;
|
public isEmailConfirmed: boolean;
|
||||||
|
|
||||||
@OneToOne(() => UserCredentials)
|
@OneToOne(() => UserCredentials, { eager: true }) // eager: true lädt UserCredentials automatisch, wenn Sie UserData laden
|
||||||
@JoinColumn({ name: 'userCredentialsId' })
|
@JoinColumn() // Diese Dekoration sagt TypeORM, welche Spalte der Fremdschlüssel ist
|
||||||
public user: UserCredentials;
|
public userCredentials: UserCredentials;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
public createdAt: Date;
|
public createdAt: Date;
|
||||||
|
|
|
@ -19,7 +19,7 @@ export class AuthController {
|
||||||
@HttpCode(HttpStatus.CREATED)
|
@HttpCode(HttpStatus.CREATED)
|
||||||
public async signup(
|
public async signup(
|
||||||
@Body() userCredentials: UserCredentialsDto
|
@Body() userCredentials: UserCredentialsDto
|
||||||
): Promise<LoginResponseDto> {
|
): Promise<{ success: boolean }> {
|
||||||
return this.authService.signup(userCredentials);
|
return this.authService.signup(userCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,9 @@ export class UserCredentialsRepository {
|
||||||
|
|
||||||
public async createUser(
|
public async createUser(
|
||||||
email: string,
|
email: string,
|
||||||
hash: string
|
hashedPassword: string
|
||||||
): Promise<UserCredentials> {
|
): Promise<UserCredentials> {
|
||||||
const user = this.repository.create({ email, hash });
|
const user = this.repository.create({ email, hashedPassword });
|
||||||
|
|
||||||
return this.repository.save(user);
|
return this.repository.save(user);
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,12 @@ export class UserCredentialsRepository {
|
||||||
return this.repository.findOne({ where: { id: userId } });
|
return this.repository.findOne({ where: { id: userId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateUserRefreshToken(
|
// public async updateUserRefreshToken(
|
||||||
userId: string,
|
// userId: string,
|
||||||
refreshToken: string | null
|
// refreshToken: string | null
|
||||||
): Promise<number> {
|
// ): Promise<number> {
|
||||||
const result = await this.repository.update(userId, { refreshToken });
|
// const result = await this.repository.update(userId, { refreshToken });
|
||||||
|
|
||||||
return result.affected ?? 0;
|
// return result.affected ?? 0;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import {
|
||||||
|
ConflictException,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Injectable,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { EncryptionService } from 'src/shared';
|
import { EncryptionService } from 'src/shared';
|
||||||
|
|
||||||
import { PasswordConfirmationMailService } from '../../sendgrid-module/services/password-confirmation.mail.service';
|
import { PasswordConfirmationMailService } from '../../sendgrid-module/services/password-confirmation.mail.service';
|
||||||
|
@ -23,29 +28,52 @@ export class AuthService {
|
||||||
|
|
||||||
public async signup(
|
public async signup(
|
||||||
userCredentials: UserCredentialsDto
|
userCredentials: UserCredentialsDto
|
||||||
): Promise<LoginResponseDto> {
|
): Promise<{ success: boolean }> {
|
||||||
const passwordHashed = await EncryptionService.hashData(
|
try {
|
||||||
userCredentials.password
|
const existingUser = await this.userCredentialsRepository.findUserByEmail(
|
||||||
);
|
userCredentials.email
|
||||||
|
|
||||||
const user = await this.userCredentialsRepository.createUser(
|
|
||||||
userCredentials.email,
|
|
||||||
passwordHashed
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.userDataRepository.createInitialUserData(user);
|
|
||||||
|
|
||||||
const token =
|
|
||||||
await this.emailVerificationService.generateEmailVerificationToken(
|
|
||||||
user.id
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.passwordConfirmationMailService.sendPasswordConfirmationMail(
|
if (existingUser) {
|
||||||
user.email,
|
throw new ConflictException('User already exists');
|
||||||
token
|
}
|
||||||
);
|
|
||||||
|
|
||||||
//return this.generateAndPersistTokens(user.id, user.email);
|
const passwordHashed = await EncryptionService.hashData(
|
||||||
|
userCredentials.password
|
||||||
|
);
|
||||||
|
|
||||||
|
const user = await this.userCredentialsRepository.createUser(
|
||||||
|
userCredentials.email,
|
||||||
|
passwordHashed
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.userDataRepository.createInitialUserData(user);
|
||||||
|
|
||||||
|
const token =
|
||||||
|
await this.emailVerificationService.generateEmailVerificationToken(
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.passwordConfirmationMailService.sendPasswordConfirmationMail(
|
||||||
|
user.email,
|
||||||
|
token
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ConflictException) {
|
||||||
|
throw new ConflictException(
|
||||||
|
'Diese E-Mail-Adresse ist bereits registriert.'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
'Fehler bei der Registrierung',
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public async signin(
|
// public async signin(
|
||||||
|
@ -129,25 +157,17 @@ export class AuthService {
|
||||||
// return { access_token: newTokens.access_token };
|
// return { access_token: newTokens.access_token };
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// private async generateAndPersistTokens(
|
private async generateAndPersistTokens(
|
||||||
// userId: string,
|
userId: string,
|
||||||
// email: string,
|
email: string
|
||||||
// updateRefreshToken: boolean = false
|
): Promise<LoginResponseDto> {
|
||||||
// ): Promise<LoginResponseDto> {
|
const tokens = await this.tokenManagementService.generateTokens(
|
||||||
// const tokens = await this.tokenManagementService.generateTokens(
|
userId,
|
||||||
// userId,
|
email
|
||||||
// email
|
);
|
||||||
// );
|
|
||||||
|
|
||||||
// if (updateRefreshToken) {
|
return { access_token: tokens.access_token, email: email, userId: userId };
|
||||||
// await this.userCredentialsRepository.updateUserRefreshToken(
|
}
|
||||||
// userId,
|
|
||||||
// tokens.refresh_token
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return { access_token: tokens.access_token, email: email, userId: userId };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private async validateRefreshToken(userId: string): Promise<TokenPayload> {
|
// private async validateRefreshToken(userId: string): Promise<TokenPayload> {
|
||||||
// const user = await this.userCredentialsRepository.findUserById(userId);
|
// const user = await this.userCredentialsRepository.findUserById(userId);
|
||||||
|
|
|
@ -2,14 +2,13 @@ import { Injectable } from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
|
||||||
import { TokenPayload, Tokens } from '../models/types';
|
import { AccessTokenDto } from '../models/dto';
|
||||||
|
import { TokenPayload } from '../models/types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TokenManagementService {
|
export class TokenManagementService {
|
||||||
private readonly ACCESS_TOKEN_EXPIRY: string;
|
private readonly ACCESS_TOKEN_EXPIRY: string;
|
||||||
private readonly REFRESH_TOKEN_EXPIRY: string;
|
|
||||||
private readonly JWT_SECRET_AT: string;
|
private readonly JWT_SECRET_AT: string;
|
||||||
private readonly JWT_SECRET_RT: string;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly jwt: JwtService,
|
private readonly jwt: JwtService,
|
||||||
|
@ -18,23 +17,21 @@ export class TokenManagementService {
|
||||||
this.ACCESS_TOKEN_EXPIRY = this.configService.get<string>(
|
this.ACCESS_TOKEN_EXPIRY = this.configService.get<string>(
|
||||||
'ACCESS_TOKEN_EXPIRY'
|
'ACCESS_TOKEN_EXPIRY'
|
||||||
);
|
);
|
||||||
this.REFRESH_TOKEN_EXPIRY = this.configService.get<string>(
|
|
||||||
'REFRESH_TOKEN_EXPIRY'
|
|
||||||
);
|
|
||||||
this.JWT_SECRET_AT = this.configService.get<string>('JWT_SECRET_AT');
|
this.JWT_SECRET_AT = this.configService.get<string>('JWT_SECRET_AT');
|
||||||
this.JWT_SECRET_RT = this.configService.get<string>('JWT_SECRET_RT');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async generateTokens(userId: string, email: string): Promise<Tokens> {
|
public async generateTokens(
|
||||||
|
userId: string,
|
||||||
|
email: string
|
||||||
|
): Promise<AccessTokenDto> {
|
||||||
const access_token: string = await this.createAccessToken(userId, email);
|
const access_token: string = await this.createAccessToken(userId, email);
|
||||||
const refresh_token: string = await this.createRefreshToken(userId, email);
|
|
||||||
|
|
||||||
return { access_token, refresh_token };
|
return { access_token };
|
||||||
}
|
}
|
||||||
|
|
||||||
public async verifyRefreshToken(token: string): Promise<TokenPayload> {
|
public async verifyRefreshToken(token: string): Promise<TokenPayload> {
|
||||||
return this.jwt.verifyAsync(token, {
|
return this.jwt.verifyAsync(token, {
|
||||||
secret: this.JWT_SECRET_RT,
|
secret: this.JWT_SECRET_AT,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,17 +47,4 @@ export class TokenManagementService {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createRefreshToken(
|
|
||||||
userId: string,
|
|
||||||
email: string
|
|
||||||
): Promise<string> {
|
|
||||||
return this.jwt.signAsync(
|
|
||||||
{ sub: userId, email },
|
|
||||||
{
|
|
||||||
expiresIn: this.REFRESH_TOKEN_EXPIRY,
|
|
||||||
secret: this.JWT_SECRET_RT,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,16 +15,16 @@ export class UserDataRepository {
|
||||||
): Promise<UserData> {
|
): Promise<UserData> {
|
||||||
const userData = new UserData();
|
const userData = new UserData();
|
||||||
|
|
||||||
userData.user = userCredentials;
|
userData.userCredentials = userCredentials;
|
||||||
userData.isEmailConfirmed = false;
|
userData.isEmailConfirmed = false;
|
||||||
|
|
||||||
return this.repository.save(userData);
|
return this.repository.save(userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateEmailVerificationStatus(userId: string): Promise<void> {
|
// public async updateEmailVerificationStatus(userId: string): Promise<void> {
|
||||||
await this.repository.update(
|
// await this.repository.update(
|
||||||
{ user: { id: userId } },
|
// { user: { id: userId } },
|
||||||
{ isEmailConfirmed: true }
|
// { isEmailConfirmed: true }
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,9 +42,9 @@ export class EmailVerificationService {
|
||||||
await this.deleteEmailVerificationToken(tokenToVerify);
|
await this.deleteEmailVerificationToken(tokenToVerify);
|
||||||
|
|
||||||
if (emailVerification && emailVerification.user) {
|
if (emailVerification && emailVerification.user) {
|
||||||
await this.userDataRepository.updateEmailVerificationStatus(
|
// await this.userDataRepository.updateEmailVerificationStatus(
|
||||||
emailVerification.user.id
|
// emailVerification.user.id
|
||||||
);
|
// );
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue