Browse Source

More work on auth0 integration

new_auth
Quildra 2 years ago
parent
commit
ce4196a099
  1. 7
      packages/bridge-server/src/seasons/seasons.controller.ts
  2. 30
      packages/bridge-ui/src/app/app.config.ts
  3. 13
      packages/bridge-ui/src/app/components/top-bar/top-bar.component.html
  4. 26
      packages/bridge-ui/src/app/components/top-bar/top-bar.component.ts
  5. 6
      packages/bridge-ui/src/app/pages/season-details/season-details.component.html
  6. 9
      packages/bridge-ui/src/app/pages/season-details/season-details.component.ts
  7. 2
      packages/bridge-ui/src/app/pages/seasons/seasons.component.html
  8. 7
      packages/bridge-ui/src/app/pages/seasons/seasons.component.ts
  9. 76
      packages/bridge-ui/src/app/services/auth.service.ts
  10. 2
      packages/bridge-ui/src/app/services/seasons.service.ts
  11. 8
      packages/bridge-ui/src/app/services/users.service.spec.ts
  12. 67
      packages/bridge-ui/src/app/services/users.service.ts

7
packages/bridge-server/src/seasons/seasons.controller.ts

@ -1,7 +1,7 @@
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
import { SeasonsService } from './seasons.service';
import { SeasonStandingsService } from 'src/season-standings/season-standings.service';
import { AuthGuard } from 'src/auth/auth.guard';
import { AuthGuard } from '@nestjs/passport';
@Controller('seasons')
export class SeasonsController {
@ -25,10 +25,9 @@ export class SeasonsController {
return this.seasonStandingsService.updateStandings(params.id);
}
@UseGuards(AuthGuard)
@Post()
@UseGuards(AuthGuard('jwt'))
@Post('create')
create(@Body() body: any) {
return this.seasonsService.create(body.title, body.subTitle, body.startingDate);
}
}

30
packages/bridge-ui/src/app/app.config.ts

@ -30,12 +30,34 @@ export const appConfig: ApplicationConfig = {
httpInterceptor: {
allowedList: [
{
// Match any request that starts 'https://{yourDomain}/api/v2/' (note the asterisk)
//uri: 'https://ponyta.pkmn.cloud/*',
uri: 'http://localhost:3000/*',
uriMatcher: (uri) => {
let is_create = uri.match('.+\/create');
return is_create != null;
},
tokenOptions: {
authorizationParams: {
audience: 'https://ponyta.pkmn.cloud'
}
}
},
{
uriMatcher: (uri) => {
let is_update = uri.match('.+\/update');
return is_update != null;
},
tokenOptions: {
authorizationParams: {
audience: 'https://ponyta.pkmn.cloud'
}
}
},
{
uriMatcher: (uri) => {
let is_delete = uri.match('.+\/delete');
return is_delete != null;
},
tokenOptions: {
authorizationParams: {
// The attached token should target this audience
audience: 'https://ponyta.pkmn.cloud'
}
}

13
packages/bridge-ui/src/app/components/top-bar/top-bar.component.html

@ -12,17 +12,18 @@
[options] = "options"
(themeChange)="themeChangeHandler($event)">
</app-theme-menu>
<button mat-icon-button class="example-icon" aria-label="Example icon-button with share icon" (click)="onProfile()">
<mat-icon>person_pin</mat-icon>
</button>
@if(isAuthenticated == false) {
<button mat-icon-button class="example-icon" aria-label="Example icon-button with share icon" (click)="onLoginClick()">
@if(usersService.isAuthenticated == false) {
<button mat-icon-button class="example-icon" aria-label="Example icon-button with share icon" (click)="onLoginClick()" matTooltip="Login">
<mat-icon>login</mat-icon>
</button>
}
@else {
<button mat-icon-button class="example-icon" aria-label="Example icon-button with share icon" (click)="onLogoutClick()">
{{usersService.getUserName()}}
<button mat-icon-button class="example-icon" aria-label="Example icon-button with share icon" matTooltip="Profile">
<mat-icon>person_pin</mat-icon>
</button>
<button mat-icon-button class="example-icon" aria-label="Example icon-button with share icon" (click)="onLogoutClick()" matTooltip="Logout">
<mat-icon>logout</mat-icon>
</button>
}
</mat-toolbar>

26
packages/bridge-ui/src/app/components/top-bar/top-bar.component.ts

@ -13,10 +13,9 @@ import { ThemeMenuComponent } from '../theme-menu/theme-menu.component';
import { ThemeService } from '../../services/theme.service';
import { ThemeOption } from '../../models/theme-option.model';
import { LoginDialogComponent } from '../login-dialog/login-dialog.component';
import { AuthService } from '../../services/auth.service';
//import { AuthService } from '@auth0/auth0-angular';
import { AuthService } from '@auth0/auth0-angular';
import { UsersService } from '../../services/users.service';
import { MatTooltipModule } from '@angular/material/tooltip';
@Component({
selector: 'app-top-bar',
@ -27,6 +26,7 @@ import { AuthService } from '../../services/auth.service';
MatToolbarModule,
MatButtonModule,
MatIconModule,
MatTooltipModule,
ThemeMenuComponent
],
templateUrl: './top-bar.component.html',
@ -37,16 +37,18 @@ export class TopBarComponent {
constructor(
private readonly themeService: ThemeService,
public authService: AuthService,
public usersService: UsersService,
public dialog: MatDialog
) {}
options: Array<ThemeOption> = [];
isAuthenticated: boolean = false;
async ngOnInit() {
ngOnInit() {
this.themeService.getThemeOptions().subscribe(data => {
this.options = data;
});
this.usersService.refreshUserDetails();
}
themeChangeHandler(seletedTheme: string) {
@ -54,20 +56,10 @@ export class TopBarComponent {
}
onLoginClick() {
this.authService.login();
this.authService.loginWithRedirect();
}
onLogoutClick() {
this.authService.logout();
}
isAuthed() {
return this.authService.isAuthenticated();
}
onProfile() {
this.authService.testProfile().subscribe(data => {
console.log(data)
});
}
}

6
packages/bridge-ui/src/app/pages/season-details/season-details.component.html

@ -3,9 +3,11 @@
<h1>{{season.title}}</h1>
<h3>{{season.subTitle}}</h3>
@if(isAuthed()) {
@if(usersService.isAuthenticated) {
@if(usersService.canCreateRaces()) {
<button mat-raised-button color="warn" (click)="openNewRaceDialog(season.id)">New Race</button>
<button mat-raised-button color="warn" (click)="forceUpdate(season.id)">Update Season</button>
}
<mat-divider></mat-divider>
<br/>
}

9
packages/bridge-ui/src/app/pages/season-details/season-details.component.ts

@ -14,10 +14,11 @@ import { UploadReplayDialogComponent } from '../../components/upload-replay-dial
import { SeasonsService } from '../../services/seasons.service';
import { RacesService } from '../../services/races.service';
import { AuthService } from '../../services/auth.service';
import { AuthService } from '@auth0/auth0-angular';
import { Season } from '../../models/season.model';
import { Race } from '../../models/race.model';
import { NewRaceDialogComponent } from '../../components/new-race-dialog/new-race-dialog.component';
import { UsersService } from '../../services/users.service';
@Component({
selector: 'app-season-details',
@ -46,7 +47,7 @@ export class SeasonDetailsComponent {
private seasonsService: SeasonsService,
private racesService: RacesService,
private dialog: MatDialog,
private authService: AuthService,
public usersService: UsersService,
) {}
ngOnInit() {
@ -60,10 +61,6 @@ export class SeasonDetailsComponent {
});
}
isAuthed() {
return this.authService.isAuthenticated();
}
openUploadReplayDialog(id: string) {
this.dialog.open(UploadReplayDialogComponent,
{

2
packages/bridge-ui/src/app/pages/seasons/seasons.component.html

@ -1,5 +1,5 @@
<div class="season-grid-list docs-guide-list">
@if(isAuthed()){
@if(userService.canCreateSeasons()){
<app-season-card-new></app-season-card-new>
}
@for (season of seasons; track season) {

7
packages/bridge-ui/src/app/pages/seasons/seasons.component.ts

@ -2,12 +2,12 @@ import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SeasonsService } from '../../services/seasons.service';
import { AuthService } from '../../services/auth.service';
import { SeasonCardComponent } from '../../components/season-card/season-card.component';
import { SeasonCardNewComponent } from '../../components/season-card-new/season-card-new.component';
import { Season } from '../../models/season.model';
import { UsersService } from '../../services/users.service';
@Component({
selector: 'app-seasons',
@ -26,7 +26,7 @@ export class SeasonsComponent {
constructor(
private seasonService: SeasonsService,
private authService: AuthService
public userService: UsersService
) {}
ngOnInit() {
@ -35,7 +35,4 @@ export class SeasonsComponent {
});
}
isAuthed() {
return this.authService.isAuthenticated();
}
}

76
packages/bridge-ui/src/app/services/auth.service.ts

@ -1,76 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable } from 'rxjs';
import { jwtDecode } from "jwt-decode";
import { ServerEndpointService } from './server-endpoint.service';
import { AuthService as Auth0Service } from '@auth0/auth0-angular';
@Injectable({
providedIn: 'root'
})
export class AuthService {
_isAuthenticated:boolean = false;
constructor(
private httpClient: HttpClient,
private serverEndpointService: ServerEndpointService,
private auth0: Auth0Service
)
{
localStorage.removeItem('token');
this.auth0.isAuthenticated$.subscribe(authed => {
this._isAuthenticated = authed;
});
this.auth0.user$.subscribe(user => {
console.log(user);
})
this.auth0.idTokenClaims$.subscribe(data => {
console.log(data)
if (data && data.__raw) {
localStorage.setItem('token', data.__raw);
}
})
}
login() {
this.auth0.loginWithRedirect();
}
testProfile(): Observable<any> {
return this.httpClient.get(this.serverEndpointService.getCurrentEndpoint()+"authz/test")
}
logout(): void {
this.auth0.logout();
localStorage.removeItem('token');
}
isAuthenticated(): boolean {
if(!this._isAuthenticated) {
return false;
}
const token = localStorage.getItem('token');
if (!token) {
return false;
}
try {
const decoded: any = jwtDecode(token);
// Check if the token is expired
const isTokenExpired = decoded.exp < Date.now() / 1000;
return !isTokenExpired;
} catch (error) {
console.error('Error decoding JWT:', error);
return false;
}
}
}

2
packages/bridge-ui/src/app/services/seasons.service.ts

@ -81,7 +81,7 @@ export class SeasonsService {
}
create(title: string, subTitle: string, startingDate: Date) {
return this.httpClient.post(this.serverEndpointService.getCurrentEndpoint()+"seasons/", {title, subTitle, startingDate});
return this.httpClient.post(this.serverEndpointService.getCurrentEndpoint()+"seasons/create", {title, subTitle, startingDate});
}
updateSeason(id: string) {

8
packages/bridge-ui/src/app/services/auth.service.spec.ts → packages/bridge-ui/src/app/services/users.service.spec.ts

@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
import { UsersService } from './users.service';
describe('AuthService', () => {
let service: AuthService;
describe('UsersService', () => {
let service: UsersService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
service = TestBed.inject(UsersService);
});
it('should be created', () => {

67
packages/bridge-ui/src/app/services/users.service.ts

@ -0,0 +1,67 @@
import { Injectable } from '@angular/core';
import { AuthService, User } from '@auth0/auth0-angular';
import * as jwt_decode from 'jwt-decode';
@Injectable({
providedIn: 'root'
})
export class UsersService {
isAuthenticated = false;
permissions: string[] = [];
user: User | null | undefined = null;
constructor(
public authService: AuthService,
) { }
private hasPermission(permission: string) {
return this.permissions.includes(permission);
}
refreshUserDetails() {
this.authService.isAuthenticated$.subscribe(isAuthed => {
this.isAuthenticated = isAuthed;
console.log(this.isAuthenticated);
if(!this.isAuthenticated) { return }
this.authService.user$.subscribe(data => {
this.user = data;
})
this.authService.getAccessTokenSilently().subscribe(data => {
try {
let decoded = jwt_decode.jwtDecode(data) as any;
this.permissions = decoded['permissions'];
}
catch (error) {
}
})
})
}
getUserName() :string {
if(!this.isAuthenticated || !this.user) {return ""}
return this.user.nickname || "";
}
canCreateSeasons() : boolean {
return this.hasPermission("create:seasons")
}
canEditSeasons() : boolean {
return this.hasPermission("edit:seasons")
}
canDeleteSeasons() : boolean {
return this.hasPermission("delete:seasons")
}
canCreateRaces(): boolean {
return this.hasPermission("create:races")
}
}
Loading…
Cancel
Save