Skip to content

Commit

Permalink
Merge pull request #2 from pimmerks/feature/auth
Browse files Browse the repository at this point in the history
Auth guard
  • Loading branch information
pimmerks authored Jun 2, 2020
2 parents e9770dc + 3a0663d commit 8c889d3
Show file tree
Hide file tree
Showing 20 changed files with 193 additions and 149 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- uses: actions/[email protected]

# Caches npm packages
- uses: actions/cache@v1
- uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
Expand Down
5 changes: 4 additions & 1 deletion src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from '@helpers/guards/auth/auth.guard';


const routes: Routes = [
{ path: 'home', loadChildren: () => import('./pages/home/home.module').then(m => m.HomeModule) },
{ path: 'home', loadChildren: () => import('./pages/home/home.module').then(m => m.HomeModule),
canActivate: [AuthGuard], canActivateChild: [AuthGuard] },

{ path: 'auth', loadChildren: () => import('./pages/auth/auth.module').then(m => m.AuthModule) },
];

Expand Down
24 changes: 21 additions & 3 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
<clr-main-container>
<clr-header>
<app-header></app-header>
<div class="branding">
<a [routerLink]="['/home']" class="nav-link">
<clr-icon shape="vm-bug"></clr-icon>
<span class="title">clarity-bootstrap</span>
</a>
</div>
<div class="header-nav" [clr-nav-level]="1">
<a [routerLink]="['/home']" routerLinkActive="active" class="nav-link"><span class="nav-text">Home</span></a>
</div>

<div class="header-actions" *ngIf="(isAuthenticated$ | async) === false">
<a [routerLink]="['/auth/login']" routerLinkActive="active" class="nav-link" aria-label="login">
Login
</a>
<a [routerLink]="['/auth/register']" routerLinkActive="active" class="nav-link" aria-label="register">
Register
</a>
</div>

</clr-header>

<div class="content-container">
<div class="content-area">
<router-outlet></router-outlet>
</div>
<nav class="sidenav">
<nav class="sidenav" *ngIf="isAuthenticated$ | async">
</nav>
</div>
</clr-main-container>
</clr-main-container>
6 changes: 6 additions & 0 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Component } from '@angular/core';
import { AuthenticationService } from '@services/authentication/authentication.service';

@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
constructor(
public readonly authService: AuthenticationService
) { }

public isAuthenticated$ = this.authService.isAuthenticated$;
}
10 changes: 9 additions & 1 deletion src/app/helpers/guards/auth/auth.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { TestBed } from '@angular/core/testing';

import { AuthGuard } from './auth.guard';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';

describe('AuthGuard', () => {
let guard: AuthGuard;

beforeEach(() => {
TestBed.configureTestingModule({});
TestBed.configureTestingModule({
imports: [
HttpClientModule,
RouterTestingModule,
]
});
guard = TestBed.inject(AuthGuard);
});

Expand Down
29 changes: 25 additions & 4 deletions src/app/helpers/guards/auth/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,45 @@
import { Injectable } from '@angular/core';
import { CanActivate, CanActivateChild, CanLoad, Route, UrlSegment, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { CanActivate, CanActivateChild, CanLoad, Route, UrlSegment, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from '@services/authentication/authentication.service';

@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
constructor(
private readonly authService: AuthenticationService,
private readonly router: Router
) { }

public canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return true;
if (!this.authService.isAuthenticated) {
const redirect = this.router.parseUrl('/auth/login');
redirect.queryParams = { returnUrl: state.url };
return redirect;
}

return true;
}

public canActivateChild(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return true;
if (!this.authService.isAuthenticated) {
const redirect = this.router.parseUrl('/auth/login');
redirect.queryParams = { returnUrl: state.url };
return redirect;
}

return true;
}

public canLoad(
route: Route,
segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean {
return true;
return this.authService.isAuthenticated;
}

}
48 changes: 26 additions & 22 deletions src/app/pages/auth/components/login/login.component.html
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
<div class="card">
<div class="card-block">

<h2>Login</h2>

<app-error-alert [error]="error"></app-error-alert>

<form clrForm [formGroup]="loginForm" (ngSubmit)="onSubmit()" clrLayout="horizontal">
<clr-input-container>
<label>Email</label>
<input clrInput placeholder="[email protected]" type="text" formControlName="email" autocomplete="username" />
<clr-control-error>Please provide an email address.</clr-control-error>
</clr-input-container>
<clr-password-container>
<label>Password</label>
<input clrPassword placeholder="Password" formControlName="password" autocomplete="current-password" />
<clr-control-error>Please provide a password.</clr-control-error>
</clr-password-container>
<br />
<button [clrLoading]="loginBtnState" [disabled]="loginForm.invalid" class="btn btn-primary">Login</button>
</form>
<app-center-card>

<div class="card">
<div class="card-block">

<h2>Login</h2>

<app-error-alert [error]="error"></app-error-alert>

<form clrForm [formGroup]="loginForm" (ngSubmit)="onSubmit()" clrLayout="horizontal">
<clr-input-container>
<label>Email</label>
<input clrInput placeholder="[email protected]" type="text" formControlName="email" autocomplete="username" />
<clr-control-error>Please provide an email address.</clr-control-error>
</clr-input-container>
<clr-password-container>
<label>Password</label>
<input clrPassword placeholder="Password" formControlName="password" autocomplete="current-password" />
<clr-control-error>Please provide a password.</clr-control-error>
</clr-password-container>
<br />
<button [clrLoading]="loginBtnState" [disabled]="loginForm.invalid" class="btn btn-primary">Login</button>
</form>

</div>
</div>
</div>

</app-center-card>
2 changes: 2 additions & 0 deletions src/app/pages/auth/components/login/login.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientModule } from '@angular/common/http';
import { ClarityModule } from '@clr/angular';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { SharedModule } from '@shared/shared.module';

describe('LoginComponent', () => {
let component: LoginComponent;
Expand All @@ -17,6 +18,7 @@ describe('LoginComponent', () => {
RouterTestingModule,
HttpClientModule,
ClarityModule,
SharedModule,
],
declarations: [
LoginComponent,
Expand Down
13 changes: 6 additions & 7 deletions src/app/pages/auth/components/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,12 @@ export class LoginComponent {
flatMap(_ => this.returnUrl$),
flatMap(returnUrl => from(this.router.navigateByUrl(returnUrl))))
.subscribe(token => {
this.loginBtnState = ClrLoadingState.SUCCESS;
},
(error: IError) => {
console.warn('error:', error);
this.error = error;
this.loginBtnState = ClrLoadingState.ERROR;
});
this.loginBtnState = ClrLoadingState.SUCCESS;
}, (error: IError) => {
console.warn('error:', error);
this.error = error;
this.loginBtnState = ClrLoadingState.ERROR;
});
}

}
50 changes: 26 additions & 24 deletions src/app/pages/auth/components/register/register.component.html
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
<div class="card">
<div class="card-block">
<app-center-card>
<div class="card">
<div class="card-block">

<h2>Register</h2>
<h2>Register</h2>

<app-error-alert [error]="error"></app-error-alert>
<app-error-alert [error]="error"></app-error-alert>

<form clrForm [formGroup]="registerForm" (ngSubmit)="onSubmit()" clrLayout="horizontal">
<clr-input-container>
<label>Email</label>
<input clrInput placeholder="[email protected]" type="text" formControlName="email" autocomplete="username" />
<clr-control-error>Please provide an email address.</clr-control-error>
</clr-input-container>
<form clrForm [formGroup]="registerForm" (ngSubmit)="onSubmit()" clrLayout="horizontal">
<clr-input-container>
<label>Email</label>
<input clrInput placeholder="[email protected]" type="text" formControlName="email" autocomplete="username" />
<clr-control-error>Please provide an email address.</clr-control-error>
</clr-input-container>

<clr-input-container>
<label>Name</label>
<input clrInput placeholder="John Doe" type="text" formControlName="name" autocomplete="name" />
<clr-control-error>Please provide a name.</clr-control-error>
</clr-input-container>
<clr-input-container>
<label>Name</label>
<input clrInput placeholder="John Doe" type="text" formControlName="name" autocomplete="name" />
<clr-control-error>Please provide a name.</clr-control-error>
</clr-input-container>

<clr-password-container>
<label>Password</label>
<input clrPassword placeholder="Password" formControlName="password" autocomplete="current-password" />
<clr-control-error>Please provide a password.</clr-control-error>
</clr-password-container>
<br />
<button [clrLoading]="registerBtnState" [disabled]="registerForm.invalid" class="btn btn-primary">register</button>
</form>
<clr-password-container>
<label>Password</label>
<input clrPassword placeholder="Password" formControlName="password" autocomplete="current-password" />
<clr-control-error>Please provide a password.</clr-control-error>
</clr-password-container>
<br />
<button [clrLoading]="registerBtnState" [disabled]="registerForm.invalid" class="btn btn-primary">register</button>
</form>

</div>
</div>
</div>
</app-center-card>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientModule } from '@angular/common/http';
import { ClarityModule } from '@clr/angular';
import { SharedModule } from '@shared/shared.module';

describe('RegisterComponent', () => {
let component: RegisterComponent;
Expand All @@ -17,6 +18,7 @@ describe('RegisterComponent', () => {
RouterTestingModule,
HttpClientModule,
ClarityModule,
SharedModule,
],
declarations: [ RegisterComponent ]
})
Expand Down
15 changes: 7 additions & 8 deletions src/app/pages/auth/components/register/register.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,12 @@ export class RegisterComponent {
flatMap(_ => this.returnUrl$),
flatMap(returnUrl => from(this.router.navigateByUrl(returnUrl))))
.subscribe(token => {
this.registerBtnState = ClrLoadingState.SUCCESS;
},
(error: IError) => {
console.warn('error:', error);
this.error = error;
this.registerBtnState = ClrLoadingState.ERROR;
});
this.registerBtnState = ClrLoadingState.SUCCESS;
},
(error: IError) => {
console.warn('error:', error);
this.error = error;
this.registerBtnState = ClrLoadingState.ERROR;
});
}

}
6 changes: 5 additions & 1 deletion src/app/services/authentication/authentication.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { JwtTokenService } from '@services/jwt-token/jwt-token.service';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { BaseClientService } from '@services/base-client/base-client.service';
import { IToken } from '@models/token.model';
import { map, tap } from 'rxjs/operators';
import { map, tap, take } from 'rxjs/operators';

@Injectable({
providedIn: 'root'
Expand All @@ -23,6 +23,10 @@ export class AuthenticationService {
return this.authSubject.asObservable();
}

public get isAuthenticated() {
return this.authSubject.value;
}

public login(email: string, password: string): Observable<boolean> {
return this.http.post<IToken>('auth/login', { email, password })
.pipe(
Expand Down
13 changes: 13 additions & 0 deletions src/app/shared/components/center-card/center-card.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="clr-row">

<div class="clr-col-xl-4 clr-offset-xl-4
clr-col-lg-6 clr-offset-lg-3
clr-col-md-8 clr-offset-md-2">

<ng-content></ng-content>
</div>

</div>



Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { CenterCardComponent } from './center-card.component';

describe('CenterCardComponent', () => {
let component: CenterCardComponent;
let fixture: ComponentFixture<CenterCardComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CenterCardComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(CenterCardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit 8c889d3

Please sign in to comment.