Skip to content

Commit f2c1191

Browse files
committed
first commit
1 parent 6b66e86 commit f2c1191

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1126
-501
lines changed

README.md

+50-14
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,63 @@
1-
# Angular14RefreshToken
1+
# Angular 14 JWT Refresh Token example with Http Interceptor
22

3-
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.2.2.
3+
Implementing Angular 14 Refresh Token before Expiration with Http Interceptor and JWT.
4+
You can take a look at following flow to have an overview of Requests and Responses that Angular 14 Client will make or receive.
45

5-
## Development server
6+
## Angular JWT Refresh Token Flow
7+
![angular-14-refresh-token-jwt-interceptor-example](angular-14-refresh-token-jwt-interceptor-example.png)
68

7-
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.
9+
For more detail, please visit:
10+
> [Angular 14 Refresh Token with Interceptor and JWT example](https://www.bezkoder.com/angular-14-refresh-token/)
811
9-
## Code scaffolding
12+
> [Angular 14 JWT Authentication & Authorization with Web API example](https://www.bezkoder.com/angular-14-jwt-auth/)
1013
11-
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`.
14+
## Fullstack
15+
> [Angular 14 + Spring Boot: JWT Authentication and Authorization example](https://www.bezkoder.com/angular-14-spring-boot-jwt-auth/)
1216
13-
## Build
17+
> [Angular 14 + Node.js Express: JWT Authentication and Authorization example](https://www.bezkoder.com/node-js-angular-14-jwt-auth/)
1418
15-
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
19+
Run `ng serve --port 8081` for a dev server. Navigate to `http://localhost:8081/`.
1620

17-
## Running unit tests
21+
## More practice
22+
> [Angular CRUD example with Web API](https://www.bezkoder.com/angular-14-crud-example/)
1823
19-
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
24+
> [Angular Pagination example](https://www.bezkoder.com/angular-14-pagination-ngx/)
2025
21-
## Running end-to-end tests
26+
> [Angular File upload example with Progress bar](https://www.bezkoder.com/angular-14-file-upload/)
2227
23-
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.
28+
Fullstack with Node:
2429

25-
## Further help
30+
> [Angular + Node Express + MySQL example](https://www.bezkoder.com/angular-14-node-js-express-mysql/)
2631
27-
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.
32+
> [Angular + Node Express + PostgreSQL example](https://www.bezkoder.com/angular-14-node-js-express-postgresql/)
33+
34+
> [Angular + Node Express + MongoDB example](https://www.bezkoder.com/mean-stack-crud-example-angular-14/)
35+
36+
> [Angular + Node Express: File upload example](https://www.bezkoder.com/angular-14-node-express-file-upload/)
37+
38+
Fullstack with Spring Boot:
39+
40+
> [Angular + Spring Boot + H2 Embedded Database example](https://www.bezkoder.com/spring-boot-angular-14-crud/)
41+
42+
> [Angular + Spring Boot + MySQL example](https://www.bezkoder.com/spring-boot-angular-14-mysql/)
43+
44+
> [Angular + Spring Boot + PostgreSQL example](https://www.bezkoder.com/spring-boot-angular-14-postgresql//)
45+
46+
> [Angular + Spring Boot + MongoDB example](https://www.bezkoder.com/spring-boot-angular-14-mongodb/)
47+
48+
> [Angular + Spring Boot: File upload example](https://www.bezkoder.com/angular-14-spring-boot-file-upload/)
49+
50+
Fullstack with Django:
51+
> [Angular + Django example](https://www.bezkoder.com/django-angular-13-crud-rest-framework/)
52+
53+
Serverless with Firebase:
54+
> [Angular 14 Firebase CRUD with Realtime DataBase](https://www.bezkoder.com/angular-14-firebase-crud/)
55+
56+
> [Angular 14 Firestore CRUD example](https://www.bezkoder.com/angular-14-firestore-crud/)
57+
58+
> [Angular 14 Firebase Storage: File Upload/Display/Delete example](https://www.bezkoder.com/angular-14-firebase-storage/)
59+
60+
Integration (run back-end & front-end on same server/port)
61+
> [How to integrate Angular with Node Restful Services](https://www.bezkoder.com/integrate-angular-12-node-js/)
62+
63+
> [How to Integrate Angular with Spring Boot Rest API](https://www.bezkoder.com/integrate-angular-12-spring-boot/)
Loading

package-lock.json

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@angular/platform-browser": "^14.2.0",
1919
"@angular/platform-browser-dynamic": "^14.2.0",
2020
"@angular/router": "^14.2.0",
21+
"bootstrap": "^4.6.1",
2122
"rxjs": "~7.5.0",
2223
"tslib": "^2.3.0",
2324
"zone.js": "~0.11.4"

src/app/_helpers/http.interceptor.ts

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS, HttpErrorResponse } from '@angular/common/http';
3+
4+
import { StorageService } from '../_services/storage.service';
5+
import { AuthService } from '../_services/auth.service';
6+
7+
import { Observable, throwError } from 'rxjs';
8+
import { catchError, switchMap } from 'rxjs/operators';
9+
10+
import { EventBusService } from '../_shared/event-bus.service';
11+
import { EventData } from '../_shared/event.class';
12+
13+
@Injectable()
14+
export class HttpRequestInterceptor implements HttpInterceptor {
15+
private isRefreshing = false;
16+
17+
constructor(
18+
private storageService: StorageService,
19+
private authService: AuthService,
20+
private eventBusService: EventBusService
21+
) {}
22+
23+
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
24+
req = req.clone({
25+
withCredentials: true,
26+
});
27+
28+
return next.handle(req).pipe(
29+
catchError((error) => {
30+
if (
31+
error instanceof HttpErrorResponse &&
32+
!req.url.includes('auth/signin') &&
33+
error.status === 401
34+
) {
35+
return this.handle401Error(req, next);
36+
}
37+
38+
return throwError(() => error);
39+
})
40+
);
41+
}
42+
43+
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
44+
if (!this.isRefreshing) {
45+
this.isRefreshing = true;
46+
47+
if (this.storageService.isLoggedIn()) {
48+
return this.authService.refreshToken().pipe(
49+
switchMap(() => {
50+
this.isRefreshing = false;
51+
52+
return next.handle(request);
53+
}),
54+
catchError((error) => {
55+
this.isRefreshing = false;
56+
57+
if (error.status == '403') {
58+
this.eventBusService.emit(new EventData('logout', null));
59+
}
60+
61+
return throwError(() => error);
62+
})
63+
);
64+
}
65+
}
66+
67+
return next.handle(request);
68+
}
69+
}
70+
71+
export const httpInterceptorProviders = [
72+
{ provide: HTTP_INTERCEPTORS, useClass: HttpRequestInterceptor, multi: true },
73+
];
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { AuthService } from './auth.service';
4+
5+
describe('AuthService', () => {
6+
let service: AuthService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(AuthService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});

src/app/_services/auth.service.ts

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpClient, HttpHeaders } from '@angular/common/http';
3+
import { Observable } from 'rxjs';
4+
5+
const AUTH_API = 'http://localhost:8080/api/auth/';
6+
7+
const httpOptions = {
8+
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
9+
};
10+
11+
@Injectable({
12+
providedIn: 'root',
13+
})
14+
export class AuthService {
15+
constructor(private http: HttpClient) {}
16+
17+
login(username: string, password: string): Observable<any> {
18+
return this.http.post(
19+
AUTH_API + 'signin',
20+
{
21+
username,
22+
password,
23+
},
24+
httpOptions
25+
);
26+
}
27+
28+
register(username: string, email: string, password: string): Observable<any> {
29+
return this.http.post(
30+
AUTH_API + 'signup',
31+
{
32+
username,
33+
email,
34+
password,
35+
},
36+
httpOptions
37+
);
38+
}
39+
40+
logout(): Observable<any> {
41+
return this.http.post(AUTH_API + 'signout', { }, httpOptions);
42+
}
43+
44+
refreshToken() {
45+
return this.http.post(AUTH_API + 'refreshtoken', { }, httpOptions);
46+
}
47+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { StorageService } from './storage.service';
4+
5+
describe('StorageService', () => {
6+
let service: StorageService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(StorageService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});

src/app/_services/storage.service.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Injectable } from '@angular/core';
2+
3+
const USER_KEY = 'auth-user';
4+
5+
@Injectable({
6+
providedIn: 'root'
7+
})
8+
export class StorageService {
9+
constructor() {}
10+
11+
clean(): void {
12+
window.sessionStorage.clear();
13+
}
14+
15+
public saveUser(user: any): void {
16+
window.sessionStorage.removeItem(USER_KEY);
17+
window.sessionStorage.setItem(USER_KEY, JSON.stringify(user));
18+
}
19+
20+
public getUser(): any {
21+
const user = window.sessionStorage.getItem(USER_KEY);
22+
if (user) {
23+
return JSON.parse(user);
24+
}
25+
26+
return {};
27+
}
28+
29+
public isLoggedIn(): boolean {
30+
const user = window.sessionStorage.getItem(USER_KEY);
31+
if (user) {
32+
return true;
33+
}
34+
35+
return false;
36+
}
37+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { UserService } from './user.service';
4+
5+
describe('UserService', () => {
6+
let service: UserService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(UserService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});

src/app/_services/user.service.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpClient } from '@angular/common/http';
3+
import { Observable } from 'rxjs';
4+
5+
const API_URL = 'http://localhost:8080/api/test/';
6+
7+
@Injectable({
8+
providedIn: 'root',
9+
})
10+
export class UserService {
11+
constructor(private http: HttpClient) { }
12+
13+
getPublicContent(): Observable<any> {
14+
return this.http.get(API_URL + 'all', { responseType: 'text' });
15+
}
16+
17+
getUserBoard(): Observable<any> {
18+
return this.http.get(API_URL + 'user', { responseType: 'text' });
19+
}
20+
21+
getModeratorBoard(): Observable<any> {
22+
return this.http.get(API_URL + 'mod', { responseType: 'text' });
23+
}
24+
25+
getAdminBoard(): Observable<any> {
26+
return this.http.get(API_URL + 'admin', { responseType: 'text' });
27+
}
28+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { EventBusService } from './event-bus.service';
4+
5+
describe('EventBusService', () => {
6+
let service: EventBusService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(EventBusService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});

src/app/_shared/event-bus.service.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Injectable } from '@angular/core';
2+
import { Subject, Subscription } from 'rxjs';
3+
import { filter, map } from 'rxjs/operators';
4+
import { EventData } from './event.class';
5+
6+
@Injectable({
7+
providedIn: 'root'
8+
})
9+
export class EventBusService {
10+
private subject$ = new Subject<EventData>();
11+
12+
constructor() { }
13+
14+
emit(event: EventData) {
15+
this.subject$.next(event);
16+
}
17+
18+
on(eventName: string, action: any): Subscription {
19+
return this.subject$.pipe(
20+
filter((e: EventData) => e.name === eventName),
21+
map((e: EventData) => e["value"])).subscribe(action);
22+
}
23+
}

src/app/_shared/event.class.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export class EventData {
2+
name: string;
3+
value: any;
4+
5+
constructor(name: string, value: any) {
6+
this.name = name;
7+
this.value = value;
8+
}
9+
}

0 commit comments

Comments
 (0)