fix sso core folder
This commit is contained in:
78
apps/sso-core/src/domain/oauth-core.service.ts
Normal file
78
apps/sso-core/src/domain/oauth-core.service.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { PrismaService } from '../infra/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class OAuthCoreService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly jwt: JwtService,
|
||||
private readonly config: ConfigService
|
||||
) {}
|
||||
|
||||
async authorize(command: { userId: string; clientId: string; redirectUri: string; scope: string; state?: string }) {
|
||||
const client = await this.prisma.oAuthClient.findUnique({ where: { clientId: command.clientId } });
|
||||
if (!client?.isActive || !client.redirectUris.includes(command.redirectUri)) {
|
||||
throw new BadRequestException('OAuth приложение не найдено или redirect_uri запрещен');
|
||||
}
|
||||
|
||||
const code = randomBytes(32).toString('base64url');
|
||||
await this.prisma.oAuthAuthorizationCode.create({
|
||||
data: {
|
||||
code,
|
||||
userId: command.userId,
|
||||
clientId: client.id,
|
||||
redirectUri: command.redirectUri,
|
||||
scopes: command.scope.split(' ').filter(Boolean),
|
||||
expiresAt: new Date(Date.now() + 5 * 60_000)
|
||||
}
|
||||
});
|
||||
|
||||
const url = new URL(command.redirectUri);
|
||||
url.searchParams.set('code', code);
|
||||
if (command.state) url.searchParams.set('state', command.state);
|
||||
return { redirectUrl: url.toString(), code, state: command.state };
|
||||
}
|
||||
|
||||
async token(command: { grantType: string; code?: string; refreshToken?: string; clientId: string; clientSecret?: string; redirectUri?: string }) {
|
||||
const client = await this.prisma.oAuthClient.findUnique({ where: { clientId: command.clientId } });
|
||||
if (!client?.isActive) throw new UnauthorizedException('OAuth приложение не найдено');
|
||||
if (client.clientSecret && client.clientSecret !== command.clientSecret) throw new UnauthorizedException('Неверный client_secret');
|
||||
|
||||
if (command.grantType === 'authorization_code') {
|
||||
if (!command.code || !command.redirectUri) throw new BadRequestException('Передайте code и redirect_uri');
|
||||
const authCode = await this.prisma.oAuthAuthorizationCode.findUnique({ where: { code: command.code }, include: { user: true } });
|
||||
if (!authCode || authCode.consumedAt || authCode.expiresAt < new Date() || authCode.redirectUri !== command.redirectUri || authCode.clientId !== client.id) {
|
||||
throw new UnauthorizedException('Код авторизации недействителен');
|
||||
}
|
||||
await this.prisma.oAuthAuthorizationCode.update({ where: { id: authCode.id }, data: { consumedAt: new Date() } });
|
||||
return this.issueOAuthTokens(authCode.user.id, client.clientId, authCode.scopes);
|
||||
}
|
||||
|
||||
if (command.grantType === 'refresh_token') {
|
||||
if (!command.refreshToken) throw new BadRequestException('Передайте refresh_token');
|
||||
const payload = await this.jwt.verifyAsync<{ sub: string; aud: string; scopes: string[]; typ: string }>(command.refreshToken, { secret: this.config.getOrThrow<string>('JWT_REFRESH_SECRET') });
|
||||
if (payload.typ !== 'refresh_token' || payload.aud !== client.clientId) throw new UnauthorizedException('Refresh token недействителен');
|
||||
return this.issueOAuthTokens(payload.sub, client.clientId, payload.scopes ?? []);
|
||||
}
|
||||
|
||||
throw new BadRequestException('Неподдерживаемый grant_type');
|
||||
}
|
||||
|
||||
async userInfo(accessToken: string) {
|
||||
const payload = await this.jwt.verifyAsync<{ sub: string }>(accessToken, { secret: this.config.getOrThrow<string>('JWT_ACCESS_SECRET') });
|
||||
const user = await this.prisma.user.findUnique({ where: { id: payload.sub } });
|
||||
if (!user) throw new UnauthorizedException('Пользователь не найден');
|
||||
return { sub: user.id, email: user.email, phone: user.phone, name: user.displayName, picture: user.avatarUrl };
|
||||
}
|
||||
|
||||
private async issueOAuthTokens(userId: string, clientId: string, scopes: string[]) {
|
||||
const accessToken = await this.jwt.signAsync({ sub: userId, aud: clientId, scopes, typ: 'access_token' }, { secret: this.config.getOrThrow<string>('JWT_ACCESS_SECRET'), expiresIn: '15m', issuer: 'id.lendry.ru' });
|
||||
const refreshToken = await this.jwt.signAsync({ sub: userId, aud: clientId, scopes, typ: 'refresh_token' }, { secret: this.config.getOrThrow<string>('JWT_REFRESH_SECRET'), expiresIn: '30d', issuer: 'id.lendry.ru' });
|
||||
const idToken = await this.jwt.signAsync({ sub: userId, aud: clientId }, { secret: this.config.getOrThrow<string>('JWT_ACCESS_SECRET'), expiresIn: '15m', issuer: 'id.lendry.ru' });
|
||||
return { accessToken, tokenType: 'Bearer', expiresIn: 900, refreshToken, idToken };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user