* feat(auth): add OAuth2 login with GitHub and Google * chore(translations): add files for Japanese * fix(auth): fix link function for GitHub * feat(oauth): basic oidc implementation * feat(oauth): oauth guard * fix: disable image optimizations for logo to prevent caching issues with custom logos * fix: memory leak while downloading large files * chore(translations): update translations via Crowdin (#278) * New translations en-us.ts (Japanese) * New translations en-us.ts (Japanese) * New translations en-us.ts (Japanese) * release: 0.18.2 * doc(translations): Add Japanese README (#279) * Added Japanese README. * Added JAPANESE README link to README.md. * Updated Japanese README. * Updated Environment Variable Table. * updated zh-cn README. * feat(oauth): unlink account * refactor(oauth): make providers extensible * fix(oauth): fix discoveryUri error when toggle google-enabled * feat(oauth): add microsoft and discord as oauth provider * docs(oauth): update README.md * docs(oauth): update oauth2-guide.md * set password to null for new oauth users * New translations en-us.ts (Japanese) (#281) * chore(translations): add Polish files * fix(oauth): fix random username and password * feat(oauth): add totp * fix(oauth): fix totp throttle * fix(oauth): fix qrcode and remove comment * feat(oauth): add error page * fix(oauth): i18n of error page * feat(auth): add OAuth2 login * fix(auth): fix link function for GitHub * feat(oauth): basic oidc implementation * feat(oauth): oauth guard * feat(oauth): unlink account * refactor(oauth): make providers extensible * fix(oauth): fix discoveryUri error when toggle google-enabled * feat(oauth): add microsoft and discord as oauth provider * docs(oauth): update README.md * docs(oauth): update oauth2-guide.md * set password to null for new oauth users * fix(oauth): fix random username and password * feat(oauth): add totp * fix(oauth): fix totp throttle * fix(oauth): fix qrcode and remove comment * feat(oauth): add error page * fix(oauth): i18n of error page * refactor: return null instead of `false` in `getIdOfCurrentUser` functiom * feat: show original oauth error if available * refactor: run formatter * refactor(oauth): error message i18n * refactor(oauth): make OAuth token available someone may use it (to revoke token or get other info etc.) also improved the i18n message * chore(oauth): remove unused import * chore: add database migration * fix: missing python installation for nanoid --------- Co-authored-by: Elias Schneider <login@eliasschneider.com> Co-authored-by: ふうせん <10260662+fusengum@users.noreply.github.com>
176 lines
4.8 KiB
TypeScript
176 lines
4.8 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
ForbiddenException,
|
|
HttpCode,
|
|
Param,
|
|
Patch,
|
|
Post,
|
|
Req,
|
|
Res,
|
|
UnauthorizedException,
|
|
UseGuards,
|
|
} from "@nestjs/common";
|
|
import { Throttle } from "@nestjs/throttler";
|
|
import { User } from "@prisma/client";
|
|
import { Request, Response } from "express";
|
|
import { ConfigService } from "src/config/config.service";
|
|
import { AuthService } from "./auth.service";
|
|
import { AuthTotpService } from "./authTotp.service";
|
|
import { GetUser } from "./decorator/getUser.decorator";
|
|
import { AuthRegisterDTO } from "./dto/authRegister.dto";
|
|
import { AuthSignInDTO } from "./dto/authSignIn.dto";
|
|
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
|
|
import { EnableTotpDTO } from "./dto/enableTotp.dto";
|
|
import { ResetPasswordDTO } from "./dto/resetPassword.dto";
|
|
import { TokenDTO } from "./dto/token.dto";
|
|
import { UpdatePasswordDTO } from "./dto/updatePassword.dto";
|
|
import { VerifyTotpDTO } from "./dto/verifyTotp.dto";
|
|
import { JwtGuard } from "./guard/jwt.guard";
|
|
|
|
@Controller("auth")
|
|
export class AuthController {
|
|
constructor(
|
|
private authService: AuthService,
|
|
private authTotpService: AuthTotpService,
|
|
private config: ConfigService,
|
|
) {}
|
|
|
|
@Post("signUp")
|
|
@Throttle(10, 5 * 60)
|
|
async signUp(
|
|
@Body() dto: AuthRegisterDTO,
|
|
@Res({ passthrough: true }) response: Response,
|
|
) {
|
|
if (!this.config.get("share.allowRegistration"))
|
|
throw new ForbiddenException("Registration is not allowed");
|
|
|
|
const result = await this.authService.signUp(dto);
|
|
|
|
this.authService.addTokensToResponse(
|
|
response,
|
|
result.refreshToken,
|
|
result.accessToken,
|
|
);
|
|
|
|
return result;
|
|
}
|
|
|
|
@Post("signIn")
|
|
@Throttle(10, 5 * 60)
|
|
@HttpCode(200)
|
|
async signIn(
|
|
@Body() dto: AuthSignInDTO,
|
|
@Res({ passthrough: true }) response: Response,
|
|
) {
|
|
const result = await this.authService.signIn(dto);
|
|
|
|
if (result.accessToken && result.refreshToken) {
|
|
this.authService.addTokensToResponse(
|
|
response,
|
|
result.refreshToken,
|
|
result.accessToken,
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@Post("signIn/totp")
|
|
@Throttle(10, 5 * 60)
|
|
@HttpCode(200)
|
|
async signInTotp(
|
|
@Body() dto: AuthSignInTotpDTO,
|
|
@Res({ passthrough: true }) response: Response,
|
|
) {
|
|
const result = await this.authTotpService.signInTotp(dto);
|
|
|
|
this.authService.addTokensToResponse(
|
|
response,
|
|
result.refreshToken,
|
|
result.accessToken,
|
|
);
|
|
|
|
return new TokenDTO().from(result);
|
|
}
|
|
|
|
@Post("resetPassword/:email")
|
|
@Throttle(5, 5 * 60)
|
|
@HttpCode(204)
|
|
async requestResetPassword(@Param("email") email: string) {
|
|
return await this.authService.requestResetPassword(email);
|
|
}
|
|
|
|
@Post("resetPassword")
|
|
@Throttle(5, 5 * 60)
|
|
@HttpCode(204)
|
|
async resetPassword(@Body() dto: ResetPasswordDTO) {
|
|
return await this.authService.resetPassword(dto.token, dto.password);
|
|
}
|
|
|
|
@Patch("password")
|
|
@UseGuards(JwtGuard)
|
|
async updatePassword(
|
|
@GetUser() user: User,
|
|
@Res({ passthrough: true }) response: Response,
|
|
@Body() dto: UpdatePasswordDTO,
|
|
) {
|
|
const result = await this.authService.updatePassword(
|
|
user,
|
|
dto.password,
|
|
dto.oldPassword,
|
|
);
|
|
|
|
this.authService.addTokensToResponse(response, result.refreshToken);
|
|
return new TokenDTO().from(result);
|
|
}
|
|
|
|
@Post("token")
|
|
@HttpCode(200)
|
|
async refreshAccessToken(
|
|
@Req() request: Request,
|
|
@Res({ passthrough: true }) response: Response,
|
|
) {
|
|
if (!request.cookies.refresh_token) throw new UnauthorizedException();
|
|
|
|
const accessToken = await this.authService.refreshAccessToken(
|
|
request.cookies.refresh_token,
|
|
);
|
|
this.authService.addTokensToResponse(response, undefined, accessToken);
|
|
return new TokenDTO().from({ accessToken });
|
|
}
|
|
|
|
@Post("signOut")
|
|
async signOut(
|
|
@Req() request: Request,
|
|
@Res({ passthrough: true }) response: Response,
|
|
) {
|
|
await this.authService.signOut(request.cookies.access_token);
|
|
response.cookie("access_token", "accessToken", { maxAge: -1 });
|
|
response.cookie("refresh_token", "", {
|
|
path: "/api/auth/token",
|
|
httpOnly: true,
|
|
maxAge: -1,
|
|
});
|
|
}
|
|
|
|
@Post("totp/enable")
|
|
@UseGuards(JwtGuard)
|
|
async enableTotp(@GetUser() user: User, @Body() body: EnableTotpDTO) {
|
|
return this.authTotpService.enableTotp(user, body.password);
|
|
}
|
|
|
|
@Post("totp/verify")
|
|
@UseGuards(JwtGuard)
|
|
async verifyTotp(@GetUser() user: User, @Body() body: VerifyTotpDTO) {
|
|
return this.authTotpService.verifyTotp(user, body.password, body.code);
|
|
}
|
|
|
|
@Post("totp/disable")
|
|
@UseGuards(JwtGuard)
|
|
async disableTotp(@GetUser() user: User, @Body() body: VerifyTotpDTO) {
|
|
// Note: We use VerifyTotpDTO here because it has both fields we need: password and totp code
|
|
return this.authTotpService.disableTotp(user, body.password, body.code);
|
|
}
|
|
}
|