frontend: Implement participant role retrieval and enhance authentication guards and http interceptor
This commit is contained in:
parent
bc33e9c5d9
commit
28b65db651
@ -1,6 +1,6 @@
|
|||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivateFn, RedirectCommand, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
import { AuthService, ContextService } from '../services';
|
import { AuthService, ContextService, HttpService, SessionStorageService } from '../services';
|
||||||
import { AuthMode, ParticipantRole } from '@lib/typings/ce';
|
import { AuthMode, ParticipantRole } from '@lib/typings/ce';
|
||||||
|
|
||||||
export const checkUserAuthenticatedGuard: CanActivateFn = async (
|
export const checkUserAuthenticatedGuard: CanActivateFn = async (
|
||||||
@ -47,11 +47,26 @@ export const checkParticipantRoleAndAuthGuard: CanActivateFn = async (
|
|||||||
_route: ActivatedRouteSnapshot,
|
_route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot
|
state: RouterStateSnapshot
|
||||||
) => {
|
) => {
|
||||||
|
const router = inject(Router);
|
||||||
const authService = inject(AuthService);
|
const authService = inject(AuthService);
|
||||||
const contextService = inject(ContextService);
|
const contextService = inject(ContextService);
|
||||||
const router = inject(Router);
|
const sessionStorageService = inject(SessionStorageService);
|
||||||
|
const httpService = inject(HttpService);
|
||||||
|
|
||||||
|
// Get participant role by room secret
|
||||||
|
let participantRole: ParticipantRole;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const roomName = contextService.getRoomName();
|
||||||
|
const secret = contextService.getSecret();
|
||||||
|
const storageSecret = sessionStorageService.getModeratorSecret(roomName);
|
||||||
|
|
||||||
|
participantRole = await httpService.getParticipantRole(roomName, storageSecret || secret);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting participant role:', error);
|
||||||
|
return router.createUrlTree(['unauthorized'], { queryParams: { reason: 'unauthorized-participant' } });
|
||||||
|
}
|
||||||
|
|
||||||
const participantRole = contextService.getParticipantRole();
|
|
||||||
const authMode = await contextService.getAuthModeToEnterRoom();
|
const authMode = await contextService.getAuthModeToEnterRoom();
|
||||||
|
|
||||||
// If the user is a moderator and the room requires authentication for moderators only,
|
// If the user is a moderator and the room requires authentication for moderators only,
|
||||||
@ -60,16 +75,18 @@ export const checkParticipantRoleAndAuthGuard: CanActivateFn = async (
|
|||||||
const isAuthRequiredForModerators =
|
const isAuthRequiredForModerators =
|
||||||
authMode === AuthMode.MODERATORS_ONLY && participantRole === ParticipantRole.MODERATOR;
|
authMode === AuthMode.MODERATORS_ONLY && participantRole === ParticipantRole.MODERATOR;
|
||||||
const isAuthRequiredForAllUsers = authMode === AuthMode.ALL_USERS;
|
const isAuthRequiredForAllUsers = authMode === AuthMode.ALL_USERS;
|
||||||
console.log('Participant role:', participantRole);
|
|
||||||
|
|
||||||
if (isAuthRequiredForModerators || isAuthRequiredForAllUsers) {
|
if (isAuthRequiredForModerators || isAuthRequiredForAllUsers) {
|
||||||
// Check if user is authenticated
|
// Check if user is authenticated
|
||||||
const isAuthenticated = await authService.isUserAuthenticated();
|
const isAuthenticated = await authService.isUserAuthenticated();
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
// Redirect to the login page with query param to redirect back to the room
|
// Redirect to the login page with query param to redirect back to the room
|
||||||
return router.createUrlTree(['login'], {
|
const loginRoute = router.createUrlTree(['login'], {
|
||||||
queryParams: { redirectTo: state.url }
|
queryParams: { redirectTo: state.url }
|
||||||
});
|
});
|
||||||
|
return new RedirectCommand(loginRoute, {
|
||||||
|
skipLocationChange: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,8 @@ export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, ne
|
|||||||
const sessionStorageService = inject(SessionStorageService);
|
const sessionStorageService = inject(SessionStorageService);
|
||||||
const httpService: HttpService = inject(HttpService);
|
const httpService: HttpService = inject(HttpService);
|
||||||
|
|
||||||
const url = router.getCurrentNavigation()?.finalUrl?.toString() || router.url;
|
const pageUrl = router.getCurrentNavigation()?.finalUrl?.toString() || router.url;
|
||||||
|
const requestUrl = req.url;
|
||||||
|
|
||||||
req = req.clone({
|
req = req.clone({
|
||||||
withCredentials: true
|
withCredentials: true
|
||||||
@ -26,9 +27,15 @@ export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, ne
|
|||||||
}),
|
}),
|
||||||
catchError((error: HttpErrorResponse) => {
|
catchError((error: HttpErrorResponse) => {
|
||||||
if (error.url?.includes('/auth/refresh')) {
|
if (error.url?.includes('/auth/refresh')) {
|
||||||
console.error('Error refreshing access token. Logging out...');
|
console.error('Error refreshing access token');
|
||||||
const redirectTo = url.startsWith('/console') ? 'console/login' : 'login';
|
|
||||||
authService.logout(redirectTo);
|
// If the original request was not to the profile endpoint, logout and redirect to the login page
|
||||||
|
if (!requestUrl.includes('/profile')) {
|
||||||
|
console.log('Logging out...');
|
||||||
|
const redirectTo = pageUrl.startsWith('/console') ? 'console/login' : 'login';
|
||||||
|
authService.logout(redirectTo);
|
||||||
|
}
|
||||||
|
|
||||||
throw firstError;
|
throw firstError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,9 +86,12 @@ export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, ne
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expired access/participant token
|
// Expired access/participant token
|
||||||
if (url.startsWith('/room')) {
|
if (pageUrl.startsWith('/room') && !requestUrl.includes('/profile')) {
|
||||||
|
// If the error occurred in a room page and the request is not to the profile endpoint,
|
||||||
|
// refresh the participant token
|
||||||
return refreshParticipantToken(error);
|
return refreshParticipantToken(error);
|
||||||
} else if (!url.startsWith('/console/login') && !url.startsWith('/login')) {
|
} else if (!pageUrl.startsWith('/console/login') && !pageUrl.startsWith('/login')) {
|
||||||
|
// If the error occurred in a page that is not the login page, refresh the access token
|
||||||
return refreshAccessToken(error);
|
return refreshAccessToken(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,7 +63,8 @@ export class LoginComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.router.navigate([this.redirectTo]);
|
let urlTree = this.router.parseUrl(this.redirectTo);
|
||||||
|
this.router.navigateByUrl(urlTree);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if ((error as HttpErrorResponse).status === 429) {
|
if ((error as HttpErrorResponse).status === 429) {
|
||||||
this.loginErrorMessage = 'Too many login attempts. Please try again later';
|
this.loginErrorMessage = 'Too many login attempts. Please try again later';
|
||||||
|
|||||||
@ -35,12 +35,7 @@ export const baseRoutes: Routes = [
|
|||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: RoomCreatorComponent,
|
component: RoomCreatorComponent,
|
||||||
canActivate: [
|
canActivate: [runGuardsSerially(checkRoomCreatorEnabledGuard, checkUserAuthenticatedGuard)],
|
||||||
runGuardsSerially(
|
|
||||||
checkRoomCreatorEnabledGuard,
|
|
||||||
checkUserAuthenticatedGuard
|
|
||||||
)
|
|
||||||
],
|
|
||||||
data: {
|
data: {
|
||||||
checkSkipAuth: true,
|
checkSkipAuth: true,
|
||||||
expectedRoles: [UserRole.USER],
|
expectedRoles: [UserRole.USER],
|
||||||
@ -121,9 +116,9 @@ export const baseRoutes: Routes = [
|
|||||||
runGuardsSerially(
|
runGuardsSerially(
|
||||||
applicationModeGuard,
|
applicationModeGuard,
|
||||||
extractQueryParamsGuard,
|
extractQueryParamsGuard,
|
||||||
|
checkParticipantRoleAndAuthGuard,
|
||||||
checkParticipantNameGuard,
|
checkParticipantNameGuard,
|
||||||
validateRoomAccessGuard,
|
validateRoomAccessGuard,
|
||||||
checkParticipantRoleAndAuthGuard,
|
|
||||||
replaceModeratorSecretGuard
|
replaceModeratorSecretGuard
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { OpenViduMeetRoom, OpenViduMeetRoomOptions } from 'projects/shared-meet-components/src/lib/typings/ce/room';
|
import { OpenViduMeetRoom, OpenViduMeetRoomOptions } from 'projects/shared-meet-components/src/lib/typings/ce/room';
|
||||||
import { GlobalPreferences, RoomPreferences, TokenOptions, User } from '@lib/typings/ce';
|
import { GlobalPreferences, ParticipantRole, RoomPreferences, TokenOptions, User } from '@lib/typings/ce';
|
||||||
import { RecordingInfo, Room } from 'openvidu-components-angular';
|
import { RecordingInfo, Room } from 'openvidu-components-angular';
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@ -40,12 +40,24 @@ export class HttpService {
|
|||||||
return this.getRequest(path);
|
return this.getRequest(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getParticipantRole(roomName: string, secret: string): Promise<ParticipantRole> {
|
||||||
|
return this.getRequest(
|
||||||
|
`${this.INTERNAL_API_PATH_PREFIX}/${this.API_V1_VERSION}/rooms/${roomName}/participant-role?secret=${secret}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
generateParticipantToken(tokenOptions: TokenOptions): Promise<{ token: string }> {
|
generateParticipantToken(tokenOptions: TokenOptions): Promise<{ token: string }> {
|
||||||
return this.postRequest(`${this.INTERNAL_API_PATH_PREFIX}/${this.API_V1_VERSION}/participants/token`, tokenOptions);
|
return this.postRequest(
|
||||||
|
`${this.INTERNAL_API_PATH_PREFIX}/${this.API_V1_VERSION}/participants/token`,
|
||||||
|
tokenOptions
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshParticipantToken(tokenOptions: TokenOptions): Promise<{ token: string }> {
|
refreshParticipantToken(tokenOptions: TokenOptions): Promise<{ token: string }> {
|
||||||
return this.postRequest(`${this.INTERNAL_API_PATH_PREFIX}/${this.API_V1_VERSION}/participants/token/refresh`, tokenOptions);
|
return this.postRequest(
|
||||||
|
`${this.INTERNAL_API_PATH_PREFIX}/${this.API_V1_VERSION}/participants/token/refresh`,
|
||||||
|
tokenOptions
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user