change public app name for oauth

This commit is contained in:
lendry
2026-06-26 11:00:48 +03:00
parent b81c0cedbb
commit 489b4d4a23
8 changed files with 130 additions and 8 deletions

View File

@@ -5,7 +5,7 @@ import { firstValueFrom } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
import { OAuthAuthorizeIncomingDto, OAuthConsentActionDto, OAuthTokenIncomingDto } from '../dto/oauth.dto';
import { extractBearerToken } from '../auth-token';
import { appendQueryParams, mapUserInfoToOidc, mergeTokenCredentials, mapTokenResponseToStandard, normalizeAuthorizeQuery, normalizeConsentQuery, normalizeTokenBody } from '../lib/oauth-params';
import { appendQueryParams, mapOAuthClientPublicInfo, mapOAuthConsentCheckResponse, mapUserInfoToOidc, mapUserOAuthConsentsResponse, mergeTokenCredentials, mapTokenResponseToStandard, normalizeAuthorizeQuery, normalizeConsentQuery, normalizeTokenBody } from '../lib/oauth-params';
import { resolveFrontendUrl } from '../lib/oauth-issuer';
import { verifyAccessToken } from '../session-auth';
@@ -87,13 +87,14 @@ export class OAuthController {
if (!consentCheck.granted) {
if (wantsJson) {
return (await firstValueFrom(
const pendingConsent = (await firstValueFrom(
this.core.oauth.CheckOAuthConsent({
userId,
clientId: normalized.clientId,
scope: normalized.scope
})
)) as Record<string, unknown>;
return mapOAuthConsentCheckResponse(pendingConsent);
}
const frontendUrl = await resolveFrontendUrl(this.core);
const consentQuery = { ...(query as Record<string, unknown>) };
@@ -139,13 +140,26 @@ export class OAuthController {
) {
const payload = await verifyAccessToken(this.jwt, authorization);
const normalized = normalizeConsentQuery(query as Record<string, unknown>);
return firstValueFrom(
const result = (await firstValueFrom(
this.core.oauth.CheckOAuthConsent({
userId: payload.sub,
clientId: normalized.clientId,
scope: normalized.scope
})
);
)) as Record<string, unknown>;
return mapOAuthConsentCheckResponse(result);
}
@Get('clients/:clientId/public')
@ApiOperation({
summary: 'Публичная информация об OAuth-приложении',
description: 'Возвращает человекочитаемое название приложения для экрана согласия.'
})
async getClientPublicInfo(@Param('clientId') clientId: string) {
const result = (await firstValueFrom(
this.core.oauth.GetOAuthClientPublicInfo({ clientId })
)) as Record<string, unknown>;
return mapOAuthClientPublicInfo(result);
}
@Post('consent/approve')
@@ -186,7 +200,8 @@ export class OAuthController {
if (payload.sub !== userId) {
throw new UnauthorizedException('Можно просматривать только свои согласия');
}
return firstValueFrom(this.core.oauth.ListUserOAuthConsents({ userId }));
const result = (await firstValueFrom(this.core.oauth.ListUserOAuthConsents({ userId }))) as Record<string, unknown>;
return mapUserOAuthConsentsResponse(result);
}
@Delete('consents/users/:userId/:consentId')

View File

@@ -186,3 +186,63 @@ export function mergeTokenCredentials(
clientSecret: body.clientSecret || basic.clientSecret
};
}
function readRecord(value: unknown) {
return value && typeof value === 'object' ? (value as Record<string, unknown>) : null;
}
export function mapOAuthClientPublicInfo(raw: Record<string, unknown>) {
return {
clientId: String(raw.clientId ?? raw.client_id ?? ''),
name: String(raw.name ?? '')
};
}
export function mapOAuthScopeInfo(raw: Record<string, unknown>) {
const slug = String(raw.slug ?? '');
return {
slug,
name: String(raw.name ?? slug),
description: raw.description ? String(raw.description) : undefined
};
}
export function mapOAuthConsentCheckResponse(raw: Record<string, unknown>) {
const clientRaw = readRecord(raw.client);
const scopesRaw = Array.isArray(raw.requestedScopes)
? raw.requestedScopes
: Array.isArray(raw.requested_scopes)
? raw.requested_scopes
: [];
return {
granted: Boolean(raw.granted),
client: clientRaw
? mapOAuthClientPublicInfo(clientRaw)
: undefined,
requestedScopes: scopesRaw
.map((item) => readRecord(item))
.filter((item): item is Record<string, unknown> => Boolean(item))
.map(mapOAuthScopeInfo)
};
}
export function mapUserOAuthConsentsResponse(raw: Record<string, unknown>) {
const consentsRaw = Array.isArray(raw.consents) ? raw.consents : [];
return {
consents: consentsRaw
.map((item) => readRecord(item))
.filter((item): item is Record<string, unknown> => Boolean(item))
.map((consent) => ({
id: String(consent.id ?? ''),
clientId: String(consent.clientId ?? consent.client_id ?? ''),
clientName: String(consent.clientName ?? consent.client_name ?? consent.clientId ?? consent.client_id ?? ''),
scopes: (Array.isArray(consent.scopes) ? consent.scopes : [])
.map((item) => readRecord(item))
.filter((item): item is Record<string, unknown> => Boolean(item))
.map(mapOAuthScopeInfo),
grantedAt: String(consent.grantedAt ?? consent.granted_at ?? ''),
updatedAt: String(consent.updatedAt ?? consent.updated_at ?? '')
}))
};
}