-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
48 changed files
with
1,143 additions
and
501 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,91 @@ | ||
# Angular16JwtAuth | ||
# Angular 16 JWT Authentication & Authorization example with Rest API | ||
|
||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.0.2. | ||
Build Angular 16 JWT Authentication & Authorization example with Rest Api, HttpOnly Cookie and JWT (including HttpInterceptor, Router & Form Validation). | ||
- JWT Authentication Flow for User Registration (Signup) & User Login | ||
- Project Structure with HttpInterceptor, Router | ||
- Way to implement HttpInterceptor | ||
- How to store JWT token in HttpOnly Cookie | ||
- Creating Login, Signup Components with Form Validation | ||
- Angular Components for accessing protected Resources | ||
- How to add a dynamic Navigation Bar to Angular App | ||
- Working with Browser Session Storage | ||
|
||
## Development server | ||
## Flow for User Registration and User Login | ||
For JWT – Token based Authentication with Rest API, we’re gonna call 2 endpoints: | ||
- POST `api/auth/signup` for User Registration | ||
- POST `api/auth/signin` for User Login | ||
- POST `api/auth/signout` for User Logout | ||
|
||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. | ||
You can take a look at following flow to have an overview of Requests and Responses that Angular 16 JWT Authentication & Authorization Client will make or receive. | ||
|
||
## Code scaffolding | ||
![angular-16-jwt-authentication-authorization-flow](angular-16-jwt-authentication-authorization-flow.png) | ||
|
||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. | ||
## Angular JWT App Diagram with Router and HttpInterceptor | ||
![angular-16-jwt-authentication](angular-16-jwt-authentication.png) | ||
|
||
## Build | ||
For more detail, please visit the tutorial: | ||
> [Angular 16 JWT Authentication & Authorization with Web API example](https://www.bezkoder.com/angular-16-jwt-auth/) | ||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. | ||
> [Angular 16 Logout when Token is expired](https://www.bezkoder.com/logout-when-token-expired-angular-16/) | ||
## Running unit tests | ||
> [Angular 16 Refresh Token with Interceptor & JWT example](https://www.bezkoder.com/angular-16-refresh-token/) | ||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). | ||
## With Spring Boot back-end | ||
|
||
## Running end-to-end tests | ||
> [Angular 16 + Spring Boot: JWT Authentication and Authorization example](https://www.bezkoder.com/angular-16-spring-boot-jwt-auth/) | ||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. | ||
## With Node.js Express back-end | ||
|
||
## Further help | ||
> [Angular 16 + Node.js Express: JWT Authentication and Authorization example](https://www.bezkoder.com/node-js-angular-16-jwt-auth/) | ||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. | ||
Run `ng serve --port 8081` for a dev server. Navigate to `http://localhost:8081/`. | ||
|
||
## More practice | ||
> [Angular 16 CRUD example with Rest API](https://www.bezkoder.com/angular-16-crud-example/) | ||
> [Angular 16 Pagination example](https://www.bezkoder.com/angular-16-pagination-ngx/) | ||
> [Angular 16 File upload example with Progress bar](https://www.bezkoder.com/angular-16-file-upload/) | ||
> [Angular 16 Form Validation example](https://www.bezkoder.com/angular-16-form-validation/) | ||
Fullstack with Node: | ||
> [Angular 16 + Node Express + MySQL example](https://www.bezkoder.com/angular-16-node-js-express-mysql/) | ||
> [Angular 16 + Node Express + PostgreSQL example](https://www.bezkoder.com/angular-16-node-js-express-postgresql/) | ||
> [Angular 16 + Node Express + MongoDB example](https://www.bezkoder.com/angular-16-node-js-express-mongodb/) | ||
> [Angular 16 + Node Express: File upload example](https://www.bezkoder.com/angular-16-node-express-file-upload/) | ||
Fullstack with Spring Boot: | ||
> [Angular 16 + Spring Boot example](https://www.bezkoder.com/spring-boot-angular-16-crud/) | ||
> [Angular 16 + Spring Boot + MySQL example](https://www.bezkoder.com/spring-boot-angular-16-mysql/) | ||
> [Angular 16 + Spring Boot + PostgreSQL example](https://www.bezkoder.com/spring-boot-angular-16-postgresql/) | ||
> [Angular 16 + Spring Boot + MongoDB example](https://www.bezkoder.com/spring-boot-angular-16-mongodb/) | ||
> [Angular 16 + Spring Boot: File upload example](https://www.bezkoder.com/angular-16-spring-boot-file-upload/) | ||
Fullstack with Django: | ||
> [Angular + Django example](https://www.bezkoder.com/django-angular-13-crud-rest-framework/) | ||
> [Angular + Django + MySQL](https://www.bezkoder.com/django-angular-mysql/) | ||
> [Angular + Django + PostgreSQL](https://www.bezkoder.com/django-angular-postgresql/) | ||
> [Angular + Django + MongoDB](https://www.bezkoder.com/django-angular-mongodb/) | ||
Serverless with Firebase: | ||
> [Angular 16 Firebase CRUD with Realtime DataBase](https://www.bezkoder.com/angular-16-firebase-crud/) | ||
> [Angular 16 Firestore CRUD example](https://www.bezkoder.com/angular-16-firestore-crud/) | ||
> [Angular 16 Firebase Storage: File Upload/Display/Delete example](https://www.bezkoder.com/angular-16-firebase-storage/) | ||
Integration (run back-end & front-end on same server/port) | ||
> [How to integrate Angular with Node Restful Services](https://www.bezkoder.com/integrate-angular-12-node-js/) | ||
> [How to Integrate Angular with Spring Boot Rest API](https://www.bezkoder.com/integrate-angular-12-spring-boot/) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS, HttpErrorResponse } from '@angular/common/http'; | ||
import { Observable, throwError } from 'rxjs'; | ||
import { catchError } from 'rxjs/operators'; | ||
|
||
import { StorageService } from '../_services/storage.service'; | ||
import { EventBusService } from '../_shared/event-bus.service'; | ||
import { EventData } from '../_shared/event.class'; | ||
|
||
@Injectable() | ||
export class HttpRequestInterceptor implements HttpInterceptor { | ||
private isRefreshing = false; | ||
|
||
constructor(private storageService: StorageService, private eventBusService: EventBusService) { } | ||
|
||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | ||
req = req.clone({ | ||
withCredentials: true, | ||
}); | ||
|
||
return next.handle(req).pipe( | ||
catchError((error) => { | ||
// logout when token is expired | ||
/* | ||
if ( | ||
error instanceof HttpErrorResponse && | ||
!req.url.includes('auth/signin') && | ||
error.status === 401 | ||
) { | ||
return this.handle401Error(req, next); | ||
} | ||
*/ | ||
return throwError(() => error); | ||
}) | ||
); | ||
} | ||
|
||
private handle401Error(request: HttpRequest<any>, next: HttpHandler) { | ||
if (!this.isRefreshing) { | ||
this.isRefreshing = true; | ||
|
||
if (this.storageService.isLoggedIn()) { | ||
this.eventBusService.emit(new EventData('logout', null)); | ||
} | ||
} | ||
|
||
return next.handle(request); | ||
} | ||
} | ||
|
||
export const httpInterceptorProviders = [ | ||
{ provide: HTTP_INTERCEPTORS, useClass: HttpRequestInterceptor, multi: true }, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { AuthService } from './auth.service'; | ||
|
||
describe('AuthService', () => { | ||
let service: AuthService; | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({}); | ||
service = TestBed.inject(AuthService); | ||
}); | ||
|
||
it('should be created', () => { | ||
expect(service).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { HttpClient, HttpHeaders } from '@angular/common/http'; | ||
import { Observable } from 'rxjs'; | ||
|
||
const AUTH_API = 'http://localhost:8080/api/auth/'; | ||
|
||
const httpOptions = { | ||
headers: new HttpHeaders({ 'Content-Type': 'application/json' }) | ||
}; | ||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
export class AuthService { | ||
constructor(private http: HttpClient) {} | ||
|
||
login(username: string, password: string): Observable<any> { | ||
return this.http.post( | ||
AUTH_API + 'signin', | ||
{ | ||
username, | ||
password, | ||
}, | ||
httpOptions | ||
); | ||
} | ||
|
||
register(username: string, email: string, password: string): Observable<any> { | ||
return this.http.post( | ||
AUTH_API + 'signup', | ||
{ | ||
username, | ||
email, | ||
password, | ||
}, | ||
httpOptions | ||
); | ||
} | ||
|
||
logout(): Observable<any> { | ||
return this.http.post(AUTH_API + 'signout', { }, httpOptions); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { StorageService } from './storage.service'; | ||
|
||
describe('StorageService', () => { | ||
let service: StorageService; | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({}); | ||
service = TestBed.inject(StorageService); | ||
}); | ||
|
||
it('should be created', () => { | ||
expect(service).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Injectable } from '@angular/core'; | ||
|
||
const USER_KEY = 'auth-user'; | ||
|
||
@Injectable({ | ||
providedIn: 'root' | ||
}) | ||
export class StorageService { | ||
constructor() {} | ||
|
||
clean(): void { | ||
window.sessionStorage.clear(); | ||
} | ||
|
||
public saveUser(user: any): void { | ||
window.sessionStorage.removeItem(USER_KEY); | ||
window.sessionStorage.setItem(USER_KEY, JSON.stringify(user)); | ||
} | ||
|
||
public getUser(): any { | ||
const user = window.sessionStorage.getItem(USER_KEY); | ||
if (user) { | ||
return JSON.parse(user); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
public isLoggedIn(): boolean { | ||
const user = window.sessionStorage.getItem(USER_KEY); | ||
if (user) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { UserService } from './user.service'; | ||
|
||
describe('UserService', () => { | ||
let service: UserService; | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({}); | ||
service = TestBed.inject(UserService); | ||
}); | ||
|
||
it('should be created', () => { | ||
expect(service).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { HttpClient } from '@angular/common/http'; | ||
import { Observable } from 'rxjs'; | ||
|
||
const API_URL = 'http://localhost:8080/api/test/'; | ||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
export class UserService { | ||
constructor(private http: HttpClient) {} | ||
|
||
getPublicContent(): Observable<any> { | ||
return this.http.get(API_URL + 'all', { responseType: 'text' }); | ||
} | ||
|
||
getUserBoard(): Observable<any> { | ||
return this.http.get(API_URL + 'user', { responseType: 'text' }); | ||
} | ||
|
||
getModeratorBoard(): Observable<any> { | ||
return this.http.get(API_URL + 'mod', { responseType: 'text' }); | ||
} | ||
|
||
getAdminBoard(): Observable<any> { | ||
return this.http.get(API_URL + 'admin', { responseType: 'text' }); | ||
} | ||
} |
Oops, something went wrong.