feat: abstracted jwt service (#13016)
* remove unused JwtModule from the auth module * feat: create abstracted jwt service * refactor: tokens module and service use new jwt service * refactor: oauth-client module and repository use new jwt service * implement Morgans requests
This commit is contained in:
parent
78ecd2c9e1
commit
55327ce035
|
@ -2,6 +2,7 @@ import { AppLoggerMiddleware } from "@/app.logger.middleware";
|
||||||
import appConfig from "@/config/app";
|
import appConfig from "@/config/app";
|
||||||
import { AuthModule } from "@/modules/auth/auth.module";
|
import { AuthModule } from "@/modules/auth/auth.module";
|
||||||
import { EndpointsModule } from "@/modules/endpoints.module";
|
import { EndpointsModule } from "@/modules/endpoints.module";
|
||||||
|
import { JwtModule } from "@/modules/jwt/jwt.module";
|
||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
|
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
|
||||||
import { ConfigModule } from "@nestjs/config";
|
import { ConfigModule } from "@nestjs/config";
|
||||||
|
@ -18,6 +19,7 @@ import { AppController } from "./app.controller";
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
EndpointsModule,
|
EndpointsModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
|
JwtModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,18 +9,10 @@ import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.se
|
||||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||||
import { UsersModule } from "@/modules/users/users.module";
|
import { UsersModule } from "@/modules/users/users.module";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { JwtModule } from "@nestjs/jwt";
|
|
||||||
import { PassportModule } from "@nestjs/passport";
|
import { PassportModule } from "@nestjs/passport";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [PassportModule, ApiKeyModule, UsersModule, MembershipsModule, TokensModule],
|
||||||
PassportModule,
|
|
||||||
JwtModule.register({}),
|
|
||||||
ApiKeyModule,
|
|
||||||
UsersModule,
|
|
||||||
MembershipsModule,
|
|
||||||
TokensModule,
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
ApiKeyAuthStrategy,
|
ApiKeyAuthStrategy,
|
||||||
NextAuthGuard,
|
NextAuthGuard,
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { getEnv } from "@/env";
|
||||||
|
import { JwtService } from "@/modules/jwt/jwt.service";
|
||||||
|
import { Global, Module } from "@nestjs/common";
|
||||||
|
import { JwtModule as NestJwtModule } from "@nestjs/jwt";
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
imports: [NestJwtModule.register({ secret: getEnv("JWT_SECRET") })],
|
||||||
|
providers: [JwtService],
|
||||||
|
exports: [JwtService],
|
||||||
|
})
|
||||||
|
export class JwtModule {}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { JwtService as NestJwtService } from "@nestjs/jwt";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtService {
|
||||||
|
constructor(private readonly nestJwtService: NestJwtService) {}
|
||||||
|
|
||||||
|
signAccessToken(payload: Payload) {
|
||||||
|
const accessToken = this.sign({ type: "access_token", ...payload });
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
signRefreshToken(payload: Payload) {
|
||||||
|
const refreshToken = this.sign({ type: "refresh_token", ...payload });
|
||||||
|
return refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
sign(payload: Payload) {
|
||||||
|
const issuedAtTime = this.getIssuedAtTime();
|
||||||
|
|
||||||
|
const token = this.nestJwtService.sign({ ...payload, iat: issuedAtTime });
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIssuedAtTime() {
|
||||||
|
// divided by 1000 because iat (issued at time) is in seconds (not milliseconds) as informed by JWT speficication
|
||||||
|
return Math.floor(Date.now() / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Payload = Record<string | number | symbol, any>;
|
|
@ -1,4 +1,3 @@
|
||||||
import { getEnv } from "@/env";
|
|
||||||
import { AuthModule } from "@/modules/auth/auth.module";
|
import { AuthModule } from "@/modules/auth/auth.module";
|
||||||
import { MembershipsModule } from "@/modules/memberships/memberships.module";
|
import { MembershipsModule } from "@/modules/memberships/memberships.module";
|
||||||
import { OAuthClientUsersController } from "@/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller";
|
import { OAuthClientUsersController } from "@/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller";
|
||||||
|
@ -11,17 +10,10 @@ import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
import { TokensRepository } from "@/modules/tokens/tokens.repository";
|
import { TokensRepository } from "@/modules/tokens/tokens.repository";
|
||||||
import { UsersModule } from "@/modules/users/users.module";
|
import { UsersModule } from "@/modules/users/users.module";
|
||||||
import { Global, Module } from "@nestjs/common";
|
import { Global, Module } from "@nestjs/common";
|
||||||
import { JwtModule } from "@nestjs/jwt";
|
|
||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [PrismaModule, AuthModule, UsersModule, MembershipsModule],
|
||||||
PrismaModule,
|
|
||||||
AuthModule,
|
|
||||||
UsersModule,
|
|
||||||
MembershipsModule,
|
|
||||||
JwtModule.register({ secret: getEnv("JWT_SECRET") }),
|
|
||||||
],
|
|
||||||
providers: [OAuthClientRepository, OAuthClientCredentialsGuard, TokensRepository, OAuthFlowService],
|
providers: [OAuthClientRepository, OAuthClientCredentialsGuard, TokensRepository, OAuthFlowService],
|
||||||
controllers: [OAuthClientUsersController, OAuthClientsController, OAuthFlowController],
|
controllers: [OAuthClientUsersController, OAuthClientsController, OAuthFlowController],
|
||||||
exports: [OAuthClientRepository, OAuthClientCredentialsGuard],
|
exports: [OAuthClientRepository, OAuthClientCredentialsGuard],
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { JwtService } from "@/modules/jwt/jwt.service";
|
||||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { JwtService } from "@nestjs/jwt";
|
|
||||||
import type { PlatformOAuthClient } from "@prisma/client";
|
import type { PlatformOAuthClient } from "@prisma/client";
|
||||||
|
|
||||||
import type { CreateOAuthClientInput } from "@calcom/platform-types";
|
import type { CreateOAuthClientInput } from "@calcom/platform-types";
|
||||||
|
@ -18,7 +18,7 @@ export class OAuthClientRepository {
|
||||||
return this.dbWrite.prisma.platformOAuthClient.create({
|
return this.dbWrite.prisma.platformOAuthClient.create({
|
||||||
data: {
|
data: {
|
||||||
...data,
|
...data,
|
||||||
secret: await this.jwtService.signAsync(JSON.stringify(data)),
|
secret: this.jwtService.sign(data),
|
||||||
organizationId,
|
organizationId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
import { getEnv } from "@/env";
|
|
||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
import { TokensRepository } from "@/modules/tokens/tokens.repository";
|
import { TokensRepository } from "@/modules/tokens/tokens.repository";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { JwtModule } from "@nestjs/jwt";
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [PrismaModule],
|
||||||
JwtModule.register({
|
|
||||||
secret: getEnv("NEXTAUTH_SECRET"),
|
|
||||||
}),
|
|
||||||
PrismaModule,
|
|
||||||
],
|
|
||||||
providers: [TokensRepository],
|
providers: [TokensRepository],
|
||||||
exports: [TokensRepository],
|
exports: [TokensRepository],
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { JwtService } from "@/modules/jwt/jwt.service";
|
||||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { JwtService } from "@nestjs/jwt";
|
|
||||||
import { PlatformAuthorizationToken } from "@prisma/client";
|
import { PlatformAuthorizationToken } from "@prisma/client";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
|
|
||||||
|
@ -43,14 +43,10 @@ export class TokensRepository {
|
||||||
const accessExpiry = DateTime.now().plus({ days: 1 }).startOf("day").toJSDate();
|
const accessExpiry = DateTime.now().plus({ days: 1 }).startOf("day").toJSDate();
|
||||||
const refreshExpiry = DateTime.now().plus({ year: 1 }).startOf("day").toJSDate();
|
const refreshExpiry = DateTime.now().plus({ year: 1 }).startOf("day").toJSDate();
|
||||||
|
|
||||||
const issuedAtTime = Math.floor(Date.now() / 1000);
|
|
||||||
|
|
||||||
const [accessToken, refreshToken] = await this.dbWrite.prisma.$transaction([
|
const [accessToken, refreshToken] = await this.dbWrite.prisma.$transaction([
|
||||||
this.dbWrite.prisma.accessToken.create({
|
this.dbWrite.prisma.accessToken.create({
|
||||||
data: {
|
data: {
|
||||||
secret: this.jwtService.sign(
|
secret: this.jwtService.signAccessToken({ clientId, ownerId }),
|
||||||
JSON.stringify({ type: "access_token", clientId, ownerId, iat: issuedAtTime })
|
|
||||||
),
|
|
||||||
expiresAt: accessExpiry,
|
expiresAt: accessExpiry,
|
||||||
client: { connect: { id: clientId } },
|
client: { connect: { id: clientId } },
|
||||||
owner: { connect: { id: ownerId } },
|
owner: { connect: { id: ownerId } },
|
||||||
|
@ -58,9 +54,7 @@ export class TokensRepository {
|
||||||
}),
|
}),
|
||||||
this.dbWrite.prisma.refreshToken.create({
|
this.dbWrite.prisma.refreshToken.create({
|
||||||
data: {
|
data: {
|
||||||
secret: this.jwtService.sign(
|
secret: this.jwtService.signRefreshToken({ clientId, ownerId }),
|
||||||
JSON.stringify({ type: "refresh_token", clientId, ownerId, iat: issuedAtTime })
|
|
||||||
),
|
|
||||||
expiresAt: refreshExpiry,
|
expiresAt: refreshExpiry,
|
||||||
client: { connect: { id: clientId } },
|
client: { connect: { id: clientId } },
|
||||||
owner: { connect: { id: ownerId } },
|
owner: { connect: { id: ownerId } },
|
||||||
|
@ -103,8 +97,6 @@ export class TokensRepository {
|
||||||
const accessExpiry = DateTime.now().plus({ days: 1 }).startOf("day").toJSDate();
|
const accessExpiry = DateTime.now().plus({ days: 1 }).startOf("day").toJSDate();
|
||||||
const refreshExpiry = DateTime.now().plus({ year: 1 }).startOf("day").toJSDate();
|
const refreshExpiry = DateTime.now().plus({ year: 1 }).startOf("day").toJSDate();
|
||||||
|
|
||||||
const issuedAtTime = Math.floor(Date.now() / 1000);
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const [_, _refresh, accessToken, refreshToken] = await this.dbWrite.prisma.$transaction([
|
const [_, _refresh, accessToken, refreshToken] = await this.dbWrite.prisma.$transaction([
|
||||||
this.dbWrite.prisma.accessToken.deleteMany({
|
this.dbWrite.prisma.accessToken.deleteMany({
|
||||||
|
@ -113,9 +105,7 @@ export class TokensRepository {
|
||||||
this.dbWrite.prisma.refreshToken.delete({ where: { secret: refreshTokenSecret } }),
|
this.dbWrite.prisma.refreshToken.delete({ where: { secret: refreshTokenSecret } }),
|
||||||
this.dbWrite.prisma.accessToken.create({
|
this.dbWrite.prisma.accessToken.create({
|
||||||
data: {
|
data: {
|
||||||
secret: this.jwtService.sign(
|
secret: this.jwtService.signAccessToken({ clientId, userId: tokenUserId }),
|
||||||
JSON.stringify({ type: "access_token", clientId, userId: tokenUserId, iat: issuedAtTime })
|
|
||||||
),
|
|
||||||
expiresAt: accessExpiry,
|
expiresAt: accessExpiry,
|
||||||
client: { connect: { id: clientId } },
|
client: { connect: { id: clientId } },
|
||||||
owner: { connect: { id: tokenUserId } },
|
owner: { connect: { id: tokenUserId } },
|
||||||
|
@ -123,9 +113,7 @@ export class TokensRepository {
|
||||||
}),
|
}),
|
||||||
this.dbWrite.prisma.refreshToken.create({
|
this.dbWrite.prisma.refreshToken.create({
|
||||||
data: {
|
data: {
|
||||||
secret: this.jwtService.sign(
|
secret: this.jwtService.signRefreshToken({ clientId, userId: tokenUserId }),
|
||||||
JSON.stringify({ type: "refresh_token", clientId, userId: tokenUserId, iat: issuedAtTime })
|
|
||||||
),
|
|
||||||
expiresAt: refreshExpiry,
|
expiresAt: refreshExpiry,
|
||||||
client: { connect: { id: clientId } },
|
client: { connect: { id: clientId } },
|
||||||
owner: { connect: { id: tokenUserId } },
|
owner: { connect: { id: tokenUserId } },
|
||||||
|
|
Loading…
Reference in New Issue
Block a user