Skip to content

Commit fea082a

Browse files
authored
Merge pull request #83 from topcoder-platform/dev
[PROD] - Trolley LIve
2 parents c519488 + abba554 commit fea082a

File tree

4 files changed

+58
-14
lines changed

4 files changed

+58
-14
lines changed

src/api/winnings/winnings.controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {
77
ApiBearerAuth,
88
} from '@nestjs/swagger';
99

10-
import { M2mScope } from 'src/core/auth/auth.constants';
11-
import { M2M, AllowedM2mScope, User } from 'src/core/auth/decorators';
10+
import { M2mScope, Role } from 'src/core/auth/auth.constants';
11+
import { AllowedM2mScope, User, Roles, M2M } from 'src/core/auth/decorators';
1212
import { ResponseDto, ResponseStatusType } from 'src/dto/api-response.dto';
1313
import { UserInfo } from 'src/dto/user.type';
1414
import {
@@ -29,8 +29,8 @@ export class WinningsController {
2929
) {}
3030

3131
@Post()
32-
@M2M()
3332
@AllowedM2mScope(M2mScope.CreatePayments)
33+
@Roles(Role.PaymentAdmin, Role.PaymentEditor)
3434
@ApiOperation({
3535
summary: 'Create winning with payments.',
3636
description: 'User must have "create:payments" scope to access.',

src/core/auth/guards/auth.guard.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class AuthGuard implements CanActivate {
2323
if (isPublic) return true;
2424

2525
const req = context.switchToHttp().getRequest();
26-
const isM2M = this.reflector.getAllAndOverride<boolean>(IS_M2M_KEY, [
26+
const routeM2MOnly = this.reflector.getAllAndOverride<boolean>(IS_M2M_KEY, [
2727
context.getHandler(),
2828
context.getClass(),
2929
]);
@@ -36,24 +36,45 @@ export class AuthGuard implements CanActivate {
3636
};
3737
}
3838

39-
// Regular authentication - check that we have user's email and have verified the id token
40-
if (!isM2M) {
39+
const tokenIsM2M = Boolean(req.m2mTokenScope);
40+
41+
// If route explicitly requires M2M, enforce M2M + scope
42+
if (routeM2MOnly) {
43+
if (!req.idTokenVerified || !tokenIsM2M) {
44+
throw new UnauthorizedException();
45+
}
46+
47+
const allowedM2mScopes = this.reflector.getAllAndOverride<M2mScope[]>(
48+
SCOPES_KEY,
49+
[context.getHandler(), context.getClass()],
50+
);
51+
const reqScopes = String(req.m2mTokenScope || '').split(' ');
52+
return reqScopes.some((s) => allowedM2mScopes.includes(s as M2mScope));
53+
}
54+
55+
// Hybrid (default) route behavior: allow either
56+
// - Verified user JWT (email present), OR
57+
// - Verified M2M token but only if scope matches when scopes are declared on the route
58+
59+
// User JWT branch
60+
if (!tokenIsM2M) {
4161
return Boolean(req.email && req.idTokenVerified);
4262
}
4363

44-
// M2M authentication - check scopes
45-
if (!req.idTokenVerified || !req.m2mTokenScope)
64+
// M2M branch on non-M2M-only route: require declared scopes, otherwise deny
65+
if (!req.idTokenVerified) {
4666
throw new UnauthorizedException();
67+
}
4768

4869
const allowedM2mScopes = this.reflector.getAllAndOverride<M2mScope[]>(
4970
SCOPES_KEY,
5071
[context.getHandler(), context.getClass()],
5172
);
52-
53-
const reqScopes = req.m2mTokenScope.split(' ');
54-
if (reqScopes.some((reqScope) => allowedM2mScopes.includes(reqScope))) {
55-
return true;
73+
if (!allowedM2mScopes || allowedM2mScopes.length === 0) {
74+
// No scopes declared for this route, do not allow M2M by default
75+
return false;
5676
}
57-
return false;
77+
const reqScopes = String(req.m2mTokenScope || '').split(' ');
78+
return reqScopes.some((s) => allowedM2mScopes.includes(s as M2mScope));
5879
}
5980
}

src/core/auth/guards/roles.guard.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ export class RolesGuard implements CanActivate {
1717
}
1818

1919
const request = context.switchToHttp().getRequest();
20-
const { auth0User } = request;
20+
const tokenIsM2M = Boolean(request.m2mTokenScope);
21+
if (tokenIsM2M) {
22+
return Boolean(request.idTokenVerified);
23+
}
24+
25+
const { auth0User = {} } = request;
2126
const userRoles = Object.keys(auth0User).reduce((roles, key) => {
2227
if (key.match(/claims\/roles$/gi)) {
2328
return auth0User[key] as string[];

src/shared/topcoder/topcoder-m2m.service.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,24 @@ export class TopcoderM2MService {
3636
}),
3737
});
3838

39+
if (!response.ok) {
40+
let jsonError: any;
41+
try {
42+
jsonError = await response.json();
43+
} catch {
44+
jsonError = null;
45+
}
46+
47+
this.logger.error(
48+
'Failed to fetch M2M token',
49+
tokenURL,
50+
response.status,
51+
response.statusText,
52+
jsonError,
53+
);
54+
return undefined;
55+
}
56+
3957
const jsonResponse = await response.json();
4058
const m2mToken = jsonResponse.access_token as string;
4159

0 commit comments

Comments
 (0)